생성 음악에 원하는 목소리를 앉히려고 캡션을 붙들고 계신다면, 그 방향으로는 도착하지 못합니다. 저희가 확인한 결론은 단순합니다. 목소리는 생성 단계에서 고를 수 없고, 다 만들어진 노래에서 보컬만 떼어내 갈아 끼워야 합니다.

먼저 두 곡 들어보시죠. 사내 GPU에 MiniMax-Music3를 올려 만든 1994년 한국 발라드와 1986년 일본 시티팝입니다. 가사와 캡션만 넣고 받은 완곡이고, 사람이 부른 구간이나 샘플은 들어 있지 않습니다.

1994 한국 발라드

1986 일본 시티팝

한 가지 미리 말씀드립니다. 이 글에서 다루는 목소리 교체는 실존 인물의 음성을 복제하는 작업이 아닙니다. 바꾸는 쪽도 바뀌는 쪽도 전부 합성 음성이고, 특정 가수의 목소리나 이름을 참조하지 않았습니다. 캐스팅에 쓴 것은 사내에서 만든 합성 앵커 몇 종뿐입니다.

캡션으로는 목소리가 움직이지 않습니다

Music3는 가사와 구조화된 캡션을 받아 32kHz 스테레오 완곡을 만듭니다. 캡션에는 성별과 음색을 적는 자리가 분명히 있습니다. 그래서 처음에는 그 자리를 고치는 일부터 했습니다.

여섯 축을 흔들었습니다. 캡션 형식과 분량, 노인이나 소녀나 오페라 베이스 같은 극단적 음색 묘사, 가사 언어 다섯 종, 인접 시드와 원거리 시드, 확산 스텝 30에서 200, 플로우 매칭 guidance 1.7에서 4.0, 그리고 모듈 상수로 박혀 있던 AR 단계의 CFG를 런타임에 1.5에서 5.0까지 밀어 올린 것까지 해봤습니다. 스텝을 올리면 음질은 확연히 좋아졌습니다. 목소리는 그대로였습니다.

무엇이 고정돼 있는지는 포먼트를 재고 나서 분명해졌습니다. 성도 길이를 반영하는 F4가 모든 조건에서 변동 6.2%에 머물렀습니다. AR CFG를 5.0까지 올린 조건에서도 남성 캡션 3048Hz, 여성 캡션 3061Hz로 사실상 같은 값이었습니다. 반면 음의 높이인 F0는 캡션을 따라 110Hz에서 318Hz까지 잘 움직였습니다. 같은 사람이 더 낮게도 더 높게도 부릅니다. 부르는 사람만 안 바뀝니다. 그리고 듣는 사람이 성별과 정체성을 판단하는 근거는 음의 높이가 아니라 성도이므로, 결과는 전부 한 사람으로 들립니다.

이게 저희 파이프라인의 결함이 아니라는 것도 확인했습니다. 공식 데모 공간이 같은 diffusers 코드를 쓰고, 공식 참조 오디오는 캡션이 굵은 저음 바리톤이라고 적혀 있는데 실제 F0가 228.9Hz로 저희 결과물과 같은 범위에 있습니다.

구조적인 이유도 찾았습니다. 오픈 릴리스는 디코더 쪽만 공개했고 잔차 양자화 인코더는 내놓지 않았습니다. 레퍼런스 오디오로 목소리를 지정할 통로가 애초에 없다는 뜻입니다. 캡션이 유일한 조건인데 그 캡션이 성도를 못 움직이니, 이 모델 안에서 가수를 고르는 길은 닫혀 있습니다.

그래서 보컬만 갈아 끼웁니다

작곡과 편곡은 모델이 잘합니다. 못 하는 것은 캐스팅 하나입니다. 그러면 잘하는 일은 모델에 맡기고 못 하는 일만 뒤에서 처리하면 됩니다.

flowchart LR
    A["Music3 완곡<br/>32kHz 스테레오"] --> B["demucs 분리<br/>보컬 / 반주"]
    B -->|보컬| C["chatterbox 변환<br/>목표 음색 부여"]
    B -->|반주| E
    C --> D["WORLD F0 이식<br/>+ 조옮김"]
    D --> E["재믹스<br/>최종 완곡"]
네 단계 체인입니다. 반주는 손대지 않고 보컬 경로만 갈아 끼웁니다.

분리는 htdemucs가 맡습니다. 곡당 십여 초면 끝납니다. 이 단계의 손실은 실제로 문제가 되지 않았습니다. 변환은 chatterbox가 합니다. 정체성은 여기서 생깁니다. 그 앞에 다른 엔진을 먼저 썼는데 앵커를 열 종에서 쉰다섯 종으로 늘리고 증폭과 블렌딩과 포먼트 배율까지 다 써도 사람 귀에는 계속 같은 사람으로 들렸습니다. 엔진을 바꾸고 나서야 정체성이 갈렸습니다.

세 번째 단계가 이 체인의 핵심이자 함정입니다. 변환기는 음정을 자기 방식대로 밀어 놓습니다. 앵커에 따라 반음 몇 개에서 열 반음 넘게까지 밀립니다. 그대로 두면 보컬이 반주와 어긋나므로 원곡 보컬의 F0 곡선을 다시 얹어 제자리에 앉힙니다. WORLD가 그 일을 합니다.

같은 곡, 세 목소리

발라드 한 곡에서 같은 구간을 잘라 나란히 놓았습니다. 위가 모델이 만든 원본이고, 아래 둘이 같은 반주 위에 목소리만 갈아 끼운 결과입니다.

원본 (Music3 출력 그대로)

여성 캐스팅

남성 캐스팅

시티팝도 같은 방식으로 갈렸습니다.

시티팝 원본 / 여성 / 남성


재합성한 보컬 스템에서 유성 구간 F0 중앙값을 재면 발라드가 251.1Hz와 126.3Hz, 시티팝이 242.6Hz와 123.4Hz입니다. 두 벌의 간격이 열두 반음 근처에서 안정적으로 유지됩니다.

조옮김에 자유도가 거의 없습니다

여기서 한 번 크게 헛디뎠습니다. 남녀 두 벌을 만들었는데 두 벌이 같은 음고로 나왔습니다. 파일이 덮어써진 줄 알고 해시부터 봤는데 전부 달랐습니다. 원인은 세 번째 단계였습니다. F0 이식은 원곡 가수의 음고 곡선을 되돌리는 작업인데, 두 벌이 같은 원본에서 곡선을 받아 가면 음고가 같아집니다. 남는 차이는 음색뿐이고, 사람은 성별을 주로 음고로 읽으므로 두 벌이 갈리지 않습니다.

고치는 방법은 조옮김인데, 여기에 음악적 제약이 걸립니다. 반주는 모델이 정한 조성에 고정돼 있습니다. 보컬만 완전5도를 올리면 선율이 조를 벗어납니다. 반주와 정확히 맞는 조옮김은 0과 ±12뿐입니다. 그래서 여성 벌은 여성 앵커에 조옮김 0으로 두고, 남성 벌은 모델 자신의 음색을 유지한 채 한 옥타브만 내렸습니다. 옥타브라 조성이 유지되고, 음색을 갈지 않으니 품질 상한도 높습니다.

같은 사고를 다시 겪지 않으려고 믹스 직전 보컬 스템에 게이트를 하나 걸었습니다. 두 벌의 간격이 3반음 미만이면 실패입니다. 장르와 무관하게 사고를 잡아내는 축이라 이쪽을 하드 게이트로 두고, 절대 음역은 보조로만 씁니다. 절대 음역을 하드 게이트로 두면 힙합에서 오작동합니다. 랩은 말소리 박자로 하는 발성이라 F0가 가창만큼 올라가지 않기 때문입니다.

무엇이 결과를 갈랐나

품질이 안 나올 때 저희가 세운 가설은 재합성 부담이었습니다. 변환기가 음정을 크게 밀어 놓을수록 되돌리는 폭이 커지고 그만큼 인공적으로 들린다는 설명입니다. 부담을 여섯 반음대에서 반음 수준까지 줄인 판을 만들어 같은 곡, 같은 음색으로 열두 쌍을 나란히 들려드렸습니다.

사람이 두 팔을 구분하지 않았습니다. 둘 다 채택이 여섯 쌍, 둘 다 탈락이 두 쌍이었고, 부담이 낮은 쪽이 이긴 것은 한 쌍인 반면 높은 쪽이 이긴 것이 세 쌍이었습니다. 가장 강한 반례는 옥타브로 내려 반주와 정확히 맞고 부담도 절반 이하인 팔이 네 곡 중 세 곡에서 탈락한 경우입니다. 가설대로면 이겼어야 할 팔이 졌습니다. 이 축은 닫았습니다.

단계를 하나씩 떼어 물어보니 그림이 분명해졌습니다. 원본이 네 곡 중 네 곡, 변환 없이 분석하고 재합성만 한 팔도 네 곡 중 네 곡 채택입니다. 재합성 자체는 들리는 손상을 만들지 않습니다. 변환과 이식을 다 거친 현재 체인 역시 네 곡 전부 원본과 같은 등급으로 통과했습니다.

실제로 갈린 축은 곡과 음색의 궁합이었습니다. 채택률이 재즈팝 7/7, 발라드 6/7인 반면 힙합과 트로트는 3/7이었습니다. 음색 쪽도 갈렸습니다. 어떤 앵커는 여덟 곡 중 일곱 곡에서 살아남았고 어떤 앵커는 네 곡에 그쳤습니다. 힙합에서는 한 앵커만 남고 나머지는 전멸했습니다. 마흔 조합 중 서른한 개가 채택됐는데, 떨어진 아홉 개는 품질이 무너져서가 아니라 그 곡에 그 목소리가 안 맞아서 떨어졌습니다.

판정은 끝까지 사람이 했습니다

비싸게 배운 쪽은 지표였습니다. 정체성이 충분히 갈렸는지 재려고 세 가지를 썼습니다. 화자 임베딩 코사인 거리, 포먼트 F4, 배율입니다. 셋 다 귀와 어긋났습니다. 임베딩으로는 쉰다섯 종이 서른두 명으로 갈리는데 사람은 그것을 한 명에서 네 명으로 듣습니다. 이 축으로 다양성을 주장했다면 자신 있게 틀렸을 겁니다.

그래서 지금은 코드가 재는 것과 사람이 재는 것을 나눠 둡니다. 성별과 두 벌의 간격은 정의상 음고 축이라 코드가 재고 게이트로 씁니다. 음색이 뭉개졌는지, 이 목소리가 이 곡에 어울리는지는 사람이 듣고 판단합니다. 청취 판을 만들 때도 품질이 괜찮은지와 이 목소리가 이 곡에 맞는지를 따로 묻습니다. 한 질문으로 물으면 취향 신호를 품질 결함으로 읽고 엉뚱한 축을 파게 됩니다.

만드는 쪽 경제성

곡 하나를 여러 목소리로 굽는 일이 성립하려면 곡값이 싸야 합니다. 같은 B200 한 장에 같은 가중치를 올리고 실행 경로만 바꿨더니 처리량이 시간당 36.4곡에서 463.2곡으로 12.7배 벌어졌고, 곡당 원가가 0.007달러가 됐습니다. VRAM은 곡 길이와 무관하게 24.5GB로 고정이라 용량 계획이 단순합니다.

확산 스텝은 생각보다 쌉니다. 30에서 80으로 2.7배 올려도 생성 시간은 1.36배에 그칩니다. 확산이 전체 비용의 일부이기 때문입니다. 기본값 30을 그대로 쓰실 이유가 별로 없습니다.

한 가지 더 말씀드릴 것은 재현성입니다. 같은 시드에 같은 캡션으로 돌려도 결과가 흔들려서 비교 실험을 조건당 다섯 시드로 설계해야 했는데, 결정론 커널 옵션을 켜니 같은 시드가 해시까지 같아졌습니다. 모델의 성질이 아니라 커널 비결정성이었습니다. 덕분에 비교 실험 비용이 다섯 배 줄었습니다. 다만 적중률처럼 분포 자체를 재는 실험에서는 여전히 시드를 흔들어야 합니다.

ThakiCloud가 Metis로 파는 것이 정확히 이 구간입니다. 모델을 고르는 일보다 그 모델을 어떤 실행 스택에 어떻게 올리느냐가 원가를 한 자릿수 배수로 바꿉니다. 오디오처럼 산출물 하나가 분 단위로 길고 후처리 체인이 붙는 워크로드일수록 그 차이가 그대로 제품 가능 여부로 넘어갑니다.

쓰신다면

체인 네 단계의 라이선스는 분리와 변환이 MIT, F0 이식이 수정 BSD입니다. 생성 모델은 자체 라이선스를 쓰고 상업 제품 화면에 모델명 표기를 요구합니다. 이 글의 오디오는 전부 MiniMax-Music3로 만든 곡입니다. 참고로 음색 클로닝을 네이티브로 지원해 이 체인을 통째로 건너뛸 수 있어 보이는 후보들이 있는데, 저희가 확인한 범위에서는 가중치가 비상업 조건이거나 코드가 강한 카피레프트여서 채택하지 못했습니다. 코드 라이선스만 보고 판단하면 늦습니다. 실제로 쓰는 것은 가중치입니다.

마지막으로 길이 이야기를 덧붙입니다. 이 모델에서 곡 길이를 만드는 것은 요청 시간이 아니라 가사 음절 수입니다. 300초를 요청해도 가사가 짧으면 105초에서 끝나고, 150초를 요청해도 가사가 길면 207초가 나옵니다. 그래서 무보컬 트랙은 짧습니다. 가사 자리에 지시어 한 줄뿐이면 길이를 만들 재료가 없습니다.

여기 실린 수치는 전부 사내 B200과 H200에서 잰 실측값이고, 목소리에 대한 채택 여부는 사람이 듣고 판정한 결과입니다.

태그: audio-pipeline, formant-analysis, inference-serving, 멀티모달, music-generation, voice-conversion

카테고리:

업데이트: