멀티모달 콘텐츠 제작 파이프라인이 점점 더 정교해지면서, 텍스트-이미지 생성에서 한 걸음 나아가 오디오까지 자연스럽게 통합하려는 움직임이 뚜렷해지고 있다. 특히 AI 아나운서, 교육용 해설 영상, 가상 캐릭터 더빙 등의 분야에서는 고품질 음성 합성이 필수 요소로 자리 잡고 있다. 하지만 대부분의 비주얼 기반 AI 도구는 이미지 처리에 집중되어 있어, 음성 생성을 위해 별도 시스템을 오가야 하는 번거로움이 존재한다.
이러한 문제를 해결하기 위해, VoxCPM-1.5-TTS-WEB-UI를 ComfyUI의 커스텀 노드로 패키징하여 워크플로 내에서 직접 고음질 TTS를 실행할 수 있도록 구성했다. 이 접근 방식은 복잡한 명령줄 작업 없이도 그래픽 인터페이스를 통해 전문가 수준의 음성 합성을 가능하게 하며, ComfyUI의 노드 연결 방식과 완벽하게 조화를 이룬다.
VoxCPM-1.5가 제공하는 핵심 이점
오픈소스 TTS 솔루션 중에서도 VoxCPM-1.5-TTS-WEB-UI는 특히 중국어 음성 합성에 최적화된 성능을 보여준다. 이 모델이 주목받는 이유는 두 가지 기술적 지표, 즉 44.1kHz 초고음질 출력과 6.25Hz의 극히 낮은 토큰 생성률에 있다.
44.1kHz는 CD 음질 표준으로, 사람의 귀가 감지할 수 있는 20kHz 이상의 주파수까지 충실히 재현한다. 일반적인 TTS가 16kHz 또는 24kHz에 머무르는 것과 비교하면, 공기음이나 치찰음 같은 고주파 성분이 훨씬 선명하게 살아난다. 이는 단순한 수치 차이가 아니라 실제 청취 체감에서 "먹먹한" 느낌을 없애고 생동감을 부여하는 결정적인 요소다.
토큰 생성률 측면에서도 혁신적이다. 기존 자기회귀(autoregressive) 방식은 매초 약 50개의 토큰을 순차적으로 생성해야 하므로 지연 시간이 길고 GPU 메모리 부담이 컸다. VoxCPM은 비자기회귀 구조와 시퀀스 압축 기술을 통해 유효 토큰율을 6.25Hz로 낮추었다. 예를 들어 10초짜리 음성을 합성할 때 전통적인 방식은 500회의 추론 단계가 필요하지만, VoxCPM은 약 62회만으로 동일한 결과를 얻을 수 있다. 이론적으로 약 8배의 속도 향상이 가능한 셈이다.
또한 참조 음성을 업로드해 특정 화자의 음색과 억양을 복제하는 음성 클로닝 기능을 제공하며, 웹 기반 UI 덕분에 코딩 없이도 테스트할 수 있어 개발 부담이 크게 줄어든다.
| 평가 항목 | 기존 TTS | VoxCPM-1.5-TTS-WEB-UI |
|---|---|---|
| 음질 | 24kHz 이하 | 44.1kHz CD급 |
| 추론 속도 | 자기회귀 기반, 느림 | 비자기회귀 + 저토큰율, 고속 |
| 인터페이스 | CLI 중심 | 웹 GUI |
| 맞춤화 | 제한적 | 음성 클로닝 및 파라미터 조정 |
| 하드웨어 요구사항 | 낮은 사양 가능 | GPU 권장, 최적화 우수 |
ComfyUI 노드로의 통합 구조
ComfyUI는 노드 기반 시각적 프로그래밍을 통해 Stable Diffusion 워크플로를 구성하는 도구다. 여기에 TTS를 "빌딩 블록"처럼 추가하면, 예를 들어 "프롬프트 생성 → 텍스트 다듬기 → 음성 생성 → 영상 자막 렌더링 → 최종 숏폼 비디오" 같은 일련의 과정을 하나의 캔버스 안에서 처리할 수 있다.
커스텀 노드의 핵심은 독립적으로 실행 중인 TTS 서비스(기본 6006 포트)와 HTTP로 통신하며, 음성 생성 요청과 결과 수신을 담당하는 클라이언트 역할을 수행하는 것이다. 이렇게 설계하면 모델을 ComfyUI 프로세스 안에 직접 탑재하지 않아도 되어 메모리 충돌을 피하고, TTS 엔진을 교체하거나 재시작해도 ComfyUI의 안정성을 유지할 수 있다.
아래 다이어그램은 전체 아키텍처를 간략히 보여준다.
+-------------------+ +---------------------------+
| | | |
| ComfyUI 클라이언트 |<------>| ComfyUI 백엔드 (노드) |
| (웹 브라우저) | HTTP | - tts_voxcpm.py |
| | | |
+-------------------+ +------------+--------------+
|
| HTTP 요청
v
+-----------------------------+
| |
| VoxCPM-1.5-TTS-WEB-UI |
| - Flask/FastAPI 서버 |
| - 모델 추론 엔진 |
| - 보코더 |
| |
+-----------------------------+
사용자 관점에서 작업 흐름은 다음과 같다: ComfyUI 캔버스에 "VoxCPM TTS" 노드를 추가하고, 변환할 텍스트와 말하기 속도를 입력한 후 (필요하면 참조 오디오 업로드) 실행을 지시한다. 그러면 노드는 백엔드 TTS 서비스에 POST 요청을 보내고, 생성된 WAV 파일의 경로를 받아와 ComfyUI가 후속 노드로 전달할 수 있도록 한다.
구현 시 주의할 점은 파일 경로 일관성이다. ComfyUI와 TTS 서비스가 동일한 스토리지 볼륨을 공유하거나, 반환된 상대 경로를 프록시를 통해 노출해야 한다. 또한 긴 텍스트의 경우 처리 시간이 수십 초에 달할 수 있으므로, 적절한 타임아웃 설정과 예외 처리가 필수적이다.
노드 구현 코드 분석
다음은 Python으로 작성된 커스텀 노드의 핵심 로직이다. 이 클래스는 입력 필드 정의, HTTP 통신, 오류 처리, ComfyUI 호환 출력 형식 변환을 모두 담당한다.
# comfy_nodes/tts_voxcpm.py
import json
import logging
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
from pathlib import Path
logger = logging.getLogger(__name__)
class VoxCPMNode:
"""ComfyUI 커스텀 노드: VoxCPM-1.5 TTS 서비스 연동"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_text": ("STRING", {
"multiline": True,
"default": "안녕하세요, AI 음성 합성입니다."
}),
"rate": ("FLOAT", {
"default": 1.0,
"min": 0.5,
"max": 2.0,
"step": 0.05
}),
},
"optional": {
"voice_sample": ("AUDIO",)
}
}
RETURN_TYPES = ("AUDIO",)
FUNCTION = "synthesize"
CATEGORY = "audio/tts"
def synthesize(self, input_text: str, rate: float, voice_sample: dict = None):
endpoint = "http://localhost:6006/api/tts"
payload = {
"text": input_text.strip(),
"speed": rate
}
if voice_sample and "filepath" in voice_sample:
payload["ref_audio"] = voice_sample["filepath"]
data = json.dumps(payload).encode("utf-8")
req = Request(endpoint, data=data, headers={"Content-Type": "application/json"})
try:
with urlopen(req, timeout=30) as resp:
result = json.loads(resp.read().decode("utf-8"))
except HTTPError as e:
logger.error(f"TTS 서비스 오류: {e.code} - {e.reason}")
raise RuntimeError(f"TTS 요청 실패 (HTTP {e.code})")
except URLError as e:
logger.error(f"TTS 서비스 연결 실패: {e.reason}")
raise ConnectionError("TTS 서버에 접속할 수 없습니다") from e
except Exception as e:
logger.error(f"예상치 못한 오류: {str(e)}")
raise
audio_path = result.get("audio_path")
if not audio_path or not Path(audio_path).exists():
raise FileNotFoundError(f"오디오 파일을 찾을 수 없음: {audio_path}")
return ({
"filename": Path(audio_path).name,
"subfolder": "",
"type": "output",
"filepath": audio_path
},)
# 노드 등록
NODE_CLASS_MAPPINGS = {
"VoxCPMNode": VoxCPMNode
}
NODE_DISPLAY_NAME_MAPPINGS = {
"VoxCPMNode": "VoxCPM TTS"
}
위 코드는 requests 대신 표준 라이브러리 urllib를 사용해 외부 의존성을 최소화했으며, 예외를 세분화하여 디버깅 편의성을 높였다. INPUT_TYPES에서 multiline 텍스트 필드와 속도 조절 슬라이더를 제공하고, 반환 형식은 ComfyUI가 기대하는 AUDIO 타입 규격을 정확히 따른다. 이 구조를 그대로 커스텀 노드 폴더에 추가하면 곧바로 사용할 수 있다.
실전 활용 팁
- 서비스 사전 점검: 노드 실행 전 반드시
VoxCPM-1.5-TTS-WEB-UI가 6006 포트에서 정상 동작 중인지 확인한다. - 네트워크 구성: Docker 컨테이너나 원격 서버에 TTS 서비스를 배포한 경우 방화벽 및 CORS 설정을 적절히 조정해야 한다.
- 공유 볼륨: ComfyUI의 출력 디렉터리와 TTS 서비스의 오디오 저장 경로를 동일하게 마운트하거나, 공유 스토리지를 사용해야 파일 접근 오류를 방지할 수 있다.
- 장문 처리 전략: 30초 이상의 초장문 음성 생성은 동기 방식으로 처리하면 블로킹이 발생할 수 있으므로, 비동기 큐잉 시스템(예: Celery + Redis)을 도입하거나, 청크 단위로 분할 합성한 뒤 이어 붙이는 방법을 고려할 수 있다.
응용 시나리오
이 통합을 통해 달성할 수 있는 대표적인 워크플로는 다음과 같다.
- AI 숏폼 영상 제작: 텍스트 프롬프트로 이미지 생성 → 배경 음악 생성 → TTS로 내레이션 추가 → 자동 자막 입히기 → 하나의 버튼으로 완성.
- 개인 맞춤형 오디오북: 전자책 텍스트를 불러와 각 챕터별로 음성 파일을 배치 처리하고, 클로닝된 목소리로 감정 표현을 더함.
- 실시간 대화형 아바타: STT 노드로 음성을 텍스트로 변환 → LLM으로 응답 생성 → TTS로 음성 출력 → 립싱크 애니메이션 노드로 전달. 이 모든 과정이 ComfyUI 캔버스에서 시각적으로 연결된다.
이처럼 VoxCPM-1.5-TTS-WEB-UI를 모듈화함으로써, 음성 합성이 더 이상 특수한 도구가 아니라 누구나 조합할 수 있는 하나의 컴포넌트가 된다. 이는 단순한 기술 통합을 넘어, 창작자가 기술적 장벽 대신 콘텐츠 본연의 창의성에 집중할 수 있도록 돕는 중요한 전환점이다.