이 글에서 시도한 것은 무엇인가
debpalash/VoiceStudio (AGPL-3.0)는 'ElevenLabs의 로컬 완결판'을 자처하는 음성 스튜디오입니다.
음성 클론, 텍스트를 통한 음질 디자인, 영상 더빙, 문자 인식 등을 하나의 로컬 구동 애플리케이션으로 통합하여 제공하며, 646개 언어(API 기준 648개 언어)를 지원한다고 주장합니다.
필자는 이를 로컬 PC에서 Docker로 구동하고, 실제로 음성 클론을 생성하여 청취 비교하는 것이 오늘 검증의 핵심이었습니다.
결론부터 말하자면, 도달하기까지 네트워크 문제나 라이선스 문제가 아니라, 순수하게 툴 자체의 의존성 버그에 직면했습니다. 이를 한 줄의 패치로 회피해야 비로소 음성이 생성되었고, 실제로 들었을 때의 인상은 '영어는 같은 화자에게서 들리지만, 일본어는 발음은 자연스러워도 네이티브 느낌에는 부족하다'는 것이었습니다.
먼저 Docker로 구동하기
공식적으로 안내하는 기본 획득처는 GHCR (GitHub Container Registry)이었습니다.
docker pull ghcr.io/debpalash/voicestudio:stable
docker run -d --name omnivoice -p 127.0.0.1:3900:3900 \
-e OMNIVOICE_API_KEY -e OMNIVOICE_DATA_DIR=/app/omnivoice_data \
-v omnivoice-data:/app/omnivoice_data \
...
획득 및 구동은 문제없었습니다. curl http://localhost:3900/health는 {"status":"ok","device":"cpu","version":"0.5.7"}를 반환했습니다.
Electron 데스크톱 앱을 전제로 한 툴이지만, 실제로는 OpenAI 호환 REST API도 공개되어 있어 브라우저나 GUI 없이 curl만으로 음성 클론까지 완결할 수 있는 설계였습니다.
라이선스는 이중 구조였다
기본 음성 합성 엔진(k2-fsa/OmniVoice)을 사용하려면 모델의 라이선스 승인이 필요했습니다.
VoiceStudio 본체는 AGPL-3.0이지만, **기본 모델의 학습済み 가중치(weight)는 별도의 라이선스(CC-BY-NC, 비상업적 한정)**이며, 상업적 추론은 명시적으로 "restricted"로 되어 있습니다.
{"license": "CC-BY-NC (version unspecified); Higgs Audio 2; Meta Llama 3",
"commercial_inference": "restricted", "license_category": "noncommercial"}
'ElevenLabs의 로컬 완결판'이라는 문구에서 순진하게 기대하는 '자유롭게 사용 가능한 OSS'와는, 적어도 기본 모델에 관해서는 다릅니다.
게다가 생성을 진행할수록 음성 토크나이저(eustlb/higgs-audio-v2-tokenizer)에 대해서도 별도의 라이선스 승인을 요구받았습니다.
こちらは 내용이 기대 이하였다는 느낌을 주었으며, 라이선스 자체가 NOASSERTION——업스트림의 Hugging Face 상에서 라이선스가 명기되어 있지 않다는 상태였습니다.
이 두 가지 라이선스 승인은 개인적인 검증 목적임을 확인한 후, 사람이 실제로 내용을 읽고 동의하는 방식으로 진행했습니다.
AI 에이전트가 인간을 대신하여 이용 약관에 동의하는 작업은 아닙니다.
라이선스를 수리해도 다른 버그로 작동하지 않았다
두 가지 라이선스 모두 승인하고, 드디어 /generate를 호출해 보았습니다.
돌아온 것은 라이선스와도 네트워크와도 관계없는 의외의 결과였습니다.
{"detail":"VoiceStudio hit an internal error; check the backend log for details.",
"error_class":"NETWORK_ERROR","error_type":"OmniVoiceModelAssetError"}
컨테이너 로그를 거슬러 올라가 보니, 진짜 원인은 이것이었습니다.
TypeError: process() takes no keyword arguments
음성 토크나이저 설정 파일(config.json)을 Hugging Face에서 다운로드할 때, 서버가 Brotli 압축(Content-Encoding: br)으로 응답했고, 이를 이미지에 포함된 HTTP 클라이언트(httpx2 패키지)가 해제하려던 과정에서 오류가 발생했습니다.
# httpx2/_decoders.py
decompressed = self._decompress(data, output_buffer_limit=output_buffer_limit)
이미지에 포함된 brotli 패키지의 실제 코드를 조사한 결과, Decompressor().process()는 단일 위치 인자(string)만을 받는 버전이었습니다.
$ docker exec omnivoice python3 -c "import brotli; help(brotli.Decompressor().process)"
Signature: decompress(string)
httpx2가 전제하는 Brotli 패키지의 API와, 이미지에 실제로 포함된 Brotli 패키지 버전 간의 불일치였습니다. 같은 URL을 직접 curl -L로 요청했을 때는 문제가 없이 가져올 수 있었으므로, 네트워크 접근성 문제는 아니었습니다. 이 불일치 때문에 Brotli 압축으로 응답하는 Hugging Face상의 에셋 중 하나라도 필요하면 다운로드 단계에서 반드시 실패하는 상태였습니다.
:stable 태그의 이 이미지를 그대로 사용한다면, 누가 시도해도 재현되는 버그입니다. 해결책은 간단했습니다. 해당 파일의 문제의 키워드 인자만 제거하고 코드를 수정한 후 컨테이너를 재시작했습니다.
docker exec omnivoice sed -i \
's/self._decompress(data, output_buffer_limit=output_buffer_limit)/self._decompress(data)/; \
s/self._decompress(b"", output_buffer_limit=output_buffer_limit)/self._decompress(b"")/' \
...
이 수정으로 인해 POST /generate가 200 OK를 반환하게 되었습니다.
render trace {'total_seconds': 37.82,
'stages': {'synthesis': {'seconds': 31.90}, 'watermark': {'seconds': 5.80}},
'outcome': 'complete'}
실제로 클론한 목소리를 들어보기
포함된 데모 음성을 참조 음성으로 사용하여 영어, 일본어, 프랑스어로 클론 음성을 생성했습니다(모두 CPU 추론으로 약 30~38초 소요).
생성된 음성은 필자(인간)가 직접 들어 평가했습니다.
영어: 참조 음성과는 다른 문장을 읽게 했음에도 불구하고, “거의 같은 화자로 들렸다.”
화자의 동일성은 유지되고 있었습니다.
일본어: “화자는 같다고 느껴지지만, 일본어에 익숙한 외국인이 말하는 발음에 가까운 분위기가 느껴져서, 일본어 네이티브 느낌은 그다지 받지 못했다. 다만, 문제없이 알아들을 수 있을 정도로 발음 자체는 자연스러웠다.”
화자의 동일성은 유지되면서도, 억양에는 영어 화자에게서 유래한 듯한 악센트가 남아있다는 평가였습니다.
보충적으로, 간단한 음향 특징(기본 주파수・스펙트럼 중심)도 기계적으로 비교했습니다.
| 평균 기본 주파수 | 스펙트럼 중심(평균) | |
|---|---|
| 참조 음성 | 215.3 Hz | 1762 Hz |
| ... |
피치(음의 높이)는 세 경우 모두 비슷한 값에 수렴하여, “같은 화자로 들린다”는 청각적 평가와 방향성은 일치합니다.
반면 스펙트럼 중심(음색 밝기의 기준)은 클론된 후가 훨씬 높게 나타나고 있었는데, 이는 워터마크 처리나 생성 과정에서의 음색 변화가 영향을 미쳤을 가능성이 있습니다.
이 수치만으로 목소리 질의 동일성을 판단할 수는 없으며, 어디까지나 인간의 청각적 평가를 보조하는 참고 정보입니다.
648개 언어 지원 주장에 대하여
README나 today.md의 조사 메모에서는 "646개 언어 지원"이라는 수치가 인용되었지만, 실제로 API에서 가져온 지원 언어 목록은 648건이었다.
완벽하게 일치하지는 않지만 근접한 값이며, 버전에 따라 증감하는 성질의 수치로 보인다.
또한, 기본 제공되는 omnivoice 외에도 cosyvoice /kittentts /mlx-audio /voxcpm2 등 총 18종류의 TTS 엔진 백엔드가 이 이미지에 포함되어 있다는 것도 알게 되었다.
라이선스
- VoiceStudio 본체(Electron/FastAPI 백엔드/CLI): AGPL-3.0.
- 포함된 omnivoice 패키지(TTS 엔진 구현): Apache-2.0.
- 기본 모델 k2-fsa/OmniVoice의 학습된 가중치: CC-BY-NC (비상업적 한정).
- 음성 토크나이저 eustlb/higgs-audio-v2-tokenizer: NOASSERTION (라이선스 미기재).
요약
- Docker 이미지의 가져오기 및 실행 자체는 문제가 없었다.
- 기본 모델의 라이선스는 VoiceStudio 본체(AGPL-3.0)와 별개로 CC-BY-NC (비상업적 한정)였으며, 게다가 음성 토크나이저에는 라이선스가 명시되지 않은("NOASSERTION") 컴포넌트도 포함되어 있었다.
- 라이선스 승인 후에도, 이미지에 포함된 httpx2와 brotli 패키지의 버전 불일치로 인해 Brotli 압축으로 응답하는 Hugging Face상의 에셋 다운로드가 반드시 실패한다는, 툴 자체의 버그를 만났다. 한 줄의 패치로 우회하여 실제 생성 단계에 도달했다.
- 생성된 영어 클론 음성은 "거의 같은 화자", 일본어는 "발음은 자연스럽지만, 네이티브 느낌이 부족하다"라는 평가였다.
- 648개 언어 지원이라는 주장은 today.md에 기재된 646이라는 수치와 완벽하게 일치하지는 않지만 근접한 값으로 확인할 수 있었다.
"로컬 완결의 ElevenLabs 대체품"이라는 홍보 문구를 시도하기 전에, 라이선스 구조를 정확히 이해하고 의존성 버그 하나를 넘어서야 했다.
작동하는 단계까지 도달하자, 보이스 클로닝의 기본적인 실력(적어도 화자의 동일성)을 확실히 갖춘 툴이었다.
Discussion