Qwen3VoiceClone phoneme bleed 해결 — x_vector_only_mode=True

개요: Qwen3-TTS Base 모델의 VoiceClone에서 reference 오디오의 끝음절이 생성 음성 앞부분에 묻어나는(bleed) 현상이 자주 발생한다. ICL(In-Context Learning) 모드의 구조적 결함으로, x_vector_only_mode=True 로 ICL을 끄면 깨끗하게 해결된다. ComfyUI-Qwen3-TTS 커스텀 노드는 기본적으로 이 옵션을 노출하지 않으므로 직접 패치해야 한다. 2026-05-20 예나샘 보이스 클로닝 작업에서 검증.

증상

ref_audio 의 마지막 음절이 합성 결과 앞부분에 묻어 나옴. 예시:

ref_audio 끝 발화합성 목표 시작실제 결과
”…있어""구글이 검색을…""구글이…”, “구글이…"
"…쓰고 있어” (트림)“구글이 검색을…""쓰고 있어 구글이…” (음절 그대로 묻음)

ref_audio 를 트림하든, 다른 시드를 쓰든, target 앞에 마침표/호흡어를 넣든 — ICL 모드를 쓰는 한 끝음 leakage 는 사라지지 않는다. 시드 변주는 빈도만 줄일 뿐.

원인 (Notion 가이드 + 코드 검증)

Qwen3-TTS Base 모델(Qwen3-TTS-12Hz-1.7B-Base 등)의 ICL 모드는 reference 오디오를 prompt/context 로 사용한다. 이때:

  1. ref_audiospeech_tokenizer 로 토큰화돼 ref_code 생성
  2. ICL 모드는 ref_code + ref_text 를 prompt 로 박아 모델이 그 뒤를 “이어서 말하는” 식으로 합성
  3. → reference 의 마지막 acoustic token(음소) 이 새 합성의 첫 토큰에 직접 영향을 줌
  4. → 끝음절 bleed 발생

이건 Qwen3 만의 문제가 아니라 CosyVoice 계열 모델 전반의 전형적 phoneme bleed / tail leakage 패턴.

해결책 (효과 순)

1. ★ x_vector_only_mode=True (정공)

ICL 자체를 끄고 speaker embedding 만 추출해서 클로닝.

  • ref_text 불필요 (어차피 무시됨)
  • ref 음소가 prompt 로 안 들어가니까 leakage 거의 0
  • timbre(음색) 재현은 비슷하거나 더 안정적
  • prosody(억양·리듬) 재현력은 ICL 보다 약간 떨어질 수 있으나, 키노트 내레이션 같은 차분한 발화에선 차이 미세

라이브러리 시그니처 (qwen_tts/inference/qwen3_tts_model.py:476):

def generate_voice_clone(
    self,
    text, language=None,
    ref_audio=None,
    ref_text=None,
    x_vector_only_mode: Union[bool, List[bool]] = False,  # ← 이거
    voice_clone_prompt=None,
    ...
):

2. silence padding (완화책)

ref_audio 끝에 0.5초 무음을 붙이면 leakage 빈도가 줄어든다. 하지만 발화 내용에 따라 여전히 발생.

  • ICL 모드 자체의 결함이라 padding 만으로 완전 해결 안 됨
  • timbre 보존이 더 중요한 케이스에선 padding + ICL 조합

3. 생성 결과 앞부분 후처리 트림 (땜빵)

생성된 wav 의 앞 0.3~0.5초를 잘라내는 후처리. 결과물에 따라 자연스러운 시작이 잘리기도 함 → 비추천.

ComfyUI-Qwen3-TTS 노드 패치 (필수)

DarioFT/ComfyUI-Qwen3-TTS 커스텀 노드는 x_vector_only_mode 를 INPUT_TYPES 에 노출 안 함. 직접 패치 필요.

위치: C:\ComfyUI_windows_portable\ComfyUI\custom_nodes\ComfyUI-Qwen3-TTS\nodes.py (class Qwen3VoiceClone, 라인 ~696)

패치 3곳:

# 1) INPUT_TYPES optional 에 추가
"x_vector_only_mode": ("BOOLEAN", {"default": False}),
 
# 2) IS_CHANGED + generate() 시그니처에 x_vector_only_mode=False 추가
 
# 3) generate() 안에서 ref_audio 분기 조건과 호출 수정
elif ref_audio is not None and (x_vector_only_mode or (ref_text and ref_text.strip())):
    ...
    wavs, sr = model.generate_voice_clone(
        text=text,
        language=lang,
        ref_audio=audio_tuple,
        ref_text=ref_text if not x_vector_only_mode else None,
        x_vector_only_mode=bool(x_vector_only_mode),
        max_new_tokens=max_new_tokens
    )

패치 후 ComfyUI 서버 재시작 필수 (custom_node 핫 리로드 안 됨).

워크플로우 (API form) 패턴

{
    "1": {"class_type": "Qwen3Loader",
          "inputs": {"repo_id": "Qwen/Qwen3-TTS-12Hz-1.7B-Base", ...}},
    "2": {"class_type": "LoadAudio", "inputs": {"audio": "yena_ref_audio.wav"}},
    "3": {"class_type": "Qwen3VoiceClone",
          "inputs": {
              "model": ["1", 0],
              "ref_audio": ["2", 0],
              "text": "합성할 한국어 텍스트",
              "language": "Korean",
              "seed": 12345,
              "max_new_tokens": 2048,
              "ref_audio_max_seconds": 30.0,
              "x_vector_only_mode": True,   # ← 핵심
              # ref_text 는 입력 안 함 (어차피 무시됨)
          }},
    "4": {"class_type": "SaveAudio",
          "inputs": {"audio": ["3", 0], "filename_prefix": "out"}},
}

검증 사례 (2026-05-20, 예나샘 보이스 클로닝)

  • 작업: Google I/O 2026 keynote 4분 27초 요약 영상 46컷 한국어 내레이션
  • ref_audio: yena_ref_audio.wav (LTX 컷1 v3 추출 5초, 끝 “…있어요”)
  • 시도 순서:
    1. ICL 모드 + 시드 변주 → 46컷 중 절반 가까이 “요/여 구글” leakage
    2. ref_audio 트림 (끝 0.6초 제거) → 다른 끝음 “쓰고 있어” 가 leak
    3. target prefix “음, ” / 마침표 추가 → 효과 미미
    4. x_vector_only_mode=True → leakage 완전 소거 (제이크 검증)

함정

  • x_vector_only_mode=True 일 때 ref_text 를 같이 넘기면 라이브러리가 무시함. 패치 코드에선 명시적으로 ref_text=None 으로 비워 보냄.
  • 모델은 반드시 Base 계열 (Qwen3-TTS-12Hz-1.7B-Base). VoiceDesign / CustomVoice 는 voice clone 자체 미지원.
  • 패치 후 서버 재시작 안 하면 ComfyUI 가 옛 INPUT_TYPES 로 캐시 — /object_info/Qwen3VoiceClonex_vector_only_mode 키 보이는지 확인 후 사용.
  • prosody 보존이 결정적인 케이스(감정 연기, 호흡 디테일)면 ICL + silence padding 도 고려. 정보 전달 내레이션에선 xvec only 로 충분.

한 줄 요약

Qwen3VoiceClone 첫 음 묻음 = ICL 결함. x_vector_only_mode=True 가 정공. ComfyUI 노드는 패치 + 재시작.