Tomodachi 보이스 랩의 8비트 음성 합성 원리
Tomodachi 보이스 랩은 3개 노드로 구성된 Web Audio API 그래프로 8비트 Mii 음성을 만듭니다. 하나의 OscillatorNode가 음을 생성하고, 하나의 BiquadFilterNode가 그 음을 부드럽게 다듬으며, 하나의 GainNode가 볼륨 엔벨로프를 형성합니다. 이 가이드는 Tomodachi 보이스 랩에서 실제로 동작하는 파이프라인을 — 모든 상수, 모든 프리셋, 모든 스케줄링 결정까지 — 하나하나 해부합니다. Tomodachi Life: Living the Dream을 떠올리게 하는 비프 음성(beep-speech)의 미학을 이해하고, 재현하고, 확장할 수 있도록 하기 위해서입니다. 아래의 모든 숫자는 이 사이트의 소스 코드에서 인용한 것이며, 어떤 값이 Nintendo가 문서화한 내용이 아니라 커뮤니티 추정치일 경우에는 본문에 그렇게 명시합니다.
Tomodachi 음성이 비프음으로 말하는 이유
Tomodachi Life 사운드의 시그니처 특징은 비프 음성(beep-speech)입니다. 녹음된 대사 대신, 모든 Mii가 문장의 리듬을 따라가되 실제 단어는 절대 만들어내지 않는 짧은 합성음으로 말합니다. 이 방식은 카트리지 용량과 사운드 하드웨어 때문에 풀 보이스가 현실적이지 않았던 시리즈의 휴대용 게임기 시절로 거슬러 올라가며, 비프음이 이미 시리즈 정체성의 일부가 되었기 때문에 Switch의 Tomodachi Life: Living the Dream까지 그대로 이어졌습니다. 이 결과가 말소리처럼 들리는 이유는, 의도적으로 비어휘적인 성격을 유지하면서도 음높이의 움직임, 음절 타이밍, 쉼 같은 말소리의 운율을 그대로 복사하기 때문입니다.
Nintendo는 게임이 실제로 사용하는 합성 방식을 한 번도 공개한 적이 없으므로, 브라우저에서의 재현은 정의상 모두 커뮤니티 추정치입니다. 보이스 랩이 목표로 삼는 것은 비트 단위의 정확한 재현이 아니라 그 미학이며, 그 작업의 대부분은 세 가지 속성이 담당합니다. 첫째, 음이 짧다는 것. 엔벨로프는 수십 밀리초 안에 열리고 닫히므로 모든 음절이 깔끔하게 시작하고 멈춥니다. 둘째, 파형의 배음이 풍부하다는 것. 부웅거리는 음색은 순음보다 훨씬 쉽게 '칩튠'으로 들립니다. 셋째, 음높이가 결코 고정되지 않는다는 것. 음절마다 더해지는 작은 주파수 오프셋이 자연스러운 말소리의 굴곡을 흉내 냅니다. 이 가이드의 나머지 부분에서는 이 속성들이 각각 어떤 구체적인 Web Audio API 메커니즘에 대응하는지 살펴봅니다.
합성 파이프라인, 노드별 해설
보이스 랩이 생성하는 모든 소리는 오실레이터에서 스피커까지 정확히 3개의 처리 노드를 통과합니다. 재생할 때마다 새로운 AudioContext가 생성되며 — 보이스 랩은 글로벌 오디오 그래프를 상시 유지하지 않습니다 — 그 안에서 각 음절이 자신만의 노드 세트를 스케줄링합니다:
OscillatorNode ──▶ BiquadFilterNode (lowpass) ──▶ GainNode ──▶ AudioContext.destination
노인 프리셋만 해당:
LFO OscillatorNode (5 Hz) ──▶ GainNode (depth 15) ──▶ main osc.frequencyOscillatorNode가 음원입니다. type은 선택한 프리셋에서 정해지고(5개 프리셋 중 4개는 sawtooth, 로봇은 square), frequency는 피치 슬라이더 값으로 설정됩니다. 오실레이터는 본질적으로 파형 생성기일 뿐이어서 자체적인 음색 컨트롤을 갖지 못합니다. 한 프리셋을 다른 프리셋과 다르게 들리게 하는 모든 요소는 다운스트림 파라미터 스케줄링에 있습니다.
BiquadFilterNode는 항상 lowpass 필터로 설정되며, 컷오프는 프리셋의 filterFreq 값에서 가져옵니다(프리셋에 따라 900 – 2500 Hz). 로우패스 필터는 컷오프보다 위쪽 배음을 감쇠시키며, 바로 이 작용이 가공 전 톱니파의 강렬한 버저음을 말소리에 가까운 소리로 바꿔 줍니다. 어두운 프리셋(노인의 900 Hz)은 낮은 배음만 남기고, 밝은 프리셋(아이의 2500 Hz)은 목소리를 작고 어리게 들리게 하는 반짝임을 통과시킵니다.
엔벨로프가 자리하는 곳이 GainNode이며, 4개의 오토메이션 포인트로 스케줄링됩니다. 게인은 음절 시작 시점에 0에서 시작해 어택 시간 동안 프리셋의 목표값(0.2 – 0.3)까지 선형으로 상승하고, 끝나기 한 릴리스 시간 전까지 그 값을 유지한 다음 0까지 선형으로 되돌아갑니다. 고정점에는 setValueAtTime()을, 램프에는 linearRampToValueAtTime()을 사용합니다. 지수 곡선이 전혀 없는 단순한 어택/홀드/릴리스 엔벨로프로, 소리가 8비트 스타일링에 어울리는 특유의 즉각적인 끊김을 갖게 합니다.
선택적인 네 번째 노드 쌍이 하나 더 있는데, 바로 비브라토입니다. 프리셋이 비브라토를 켜면, LFO(저주파 오실레이터) 역할을 하는 두 번째 OscillatorNode가 프리셋의 vibratoRate로 실행되어 GainNode에 신호를 보냅니다. 이 노드의 게인값은 프리셋의 vibratoDepth이며, 메인 오실레이터의 frequency 파라미터에 연결됩니다. 이것은 고전적인 주파수 변조입니다. 노인 프리셋에서는 5 Hz LFO가 음높이를 ±15 Hz만큼 흔들어, 나이 든 목소리를 연상시키는 떨림을 만들어 냅니다. 비브라토를 켜는 프리셋은 노인뿐이며, 나머지 4개는 비브라토를 완전히 꺼둡니다.
각 파형의 음색과 선택 기준
파형 선택은 파이프라인 전체를 통틀어 가장 중요한 음색 결정입니다. 파형이, 필터와 엔벨로프가 이후에 다듬게 될 배음 성분을 결정하기 때문입니다. Web Audio API의 OscillatorNode는 4가지 표준 타입을 제공하며, 각각 뚜렷하게 다른 개성을 지닙니다:
| 파형 | 배음 성분 | 음색 | 사용하는 프리셋 |
|---|---|---|---|
sine | 기본음만 | 순수하고 플루트에 가까우며 버저음이 전혀 없음 | 없음(OscillatorType으로 선택 가능) |
square | 홀수 배음, 강함 | 공허한 느낌의 고전 NES 리드 채널 | 로봇 |
sawtooth | 모든 배음, 감쇠형 | 부웅거리고 갈대 같으며, 사람 목소리의 풍부함에 가장 가까움 | 성인 남성, 성인 여성, 노인, 아이 |
triangle | 소수의 홀수 배음, 약함 | 부드럽고 은은하며 약간 둔탁함 | 없음(OscillatorType으로 선택 가능) |
보이스 랩의 프리셋이 사용하는 것은 4가지 중 2가지뿐입니다. sawtooth가 4개의 유기적인 목소리를 맡고 square가 로봇을 맡습니다. 이 분담은 의도된 것입니다. 톱니파는 모든 배음에 에너지를 담고 있어서 로우패스 필터를 통과한 뒤에도 두텁고 목소리 같은 중심음을 남기며, 노래나 말소리를 다른 어떤 기본 파형보다 잘 근사할 수 있는 이유가 여기에 있습니다. 사각파는 홀수 배음만 남기면서 고역대 에너지가 더 많아, 로봇 프리셋이 원하는 공허하고 비음 섞인, 의심의 여지 없이 전자적인 음질을 만들어 냅니다. Sine과 triangle은 현재 어떤 프리셋에서도 사용되지 않지만, 같은 OscillatorType 필드에서 한 줄만 바꾸면 바로 쓸 수 있습니다. 사인파는 차임 같은 순수한 효과음에, 삼각파는 톱니파가 지나치게 공격적으로 들릴 부드러운 배경 블립에 어울립니다.
5가지 음성 프리셋 완전 해부
5개 프리셋은 같은 3노드 그래프 위에 얹힌 5개의 파라미터 묶음이며, 그 차이는 전부 나열할 수 있습니다. 다음은 소스 코드의 VOICE_PRESETS 상수에서 직접 인용한 전체 표입니다:
| 프리셋 | 파형 | 기본 주파수 | 로우패스 컷오프 | 게인 | 비브라토 | 어택 | 릴리스 |
|---|---|---|---|---|---|---|---|
| 성인 남성 | sawtooth | 180 Hz | 1200 Hz | 0.30 | 꺼짐 | 0.02 s | 0.05 s |
| 성인 여성 | sawtooth | 350 Hz | 1800 Hz | 0.25 | 꺼짐 | 0.02 s | 0.04 s |
| 노인 | sawtooth | 120 Hz | 900 Hz | 0.30 | 5 Hz, ±15 Hz | 0.04 s | 0.08 s |
| 아이 | sawtooth | 600 Hz | 2500 Hz | 0.20 | 꺼짐 | 0.01 s | 0.03 s |
| 로봇 | square | 250 Hz | 1500 Hz | 0.25 | 꺼짐 | 0 s | 0 s |
성인 남성은 180 Hz 기본 주파수로 세트의 기준을 잡습니다. 일반적인 성인 남성 발성 범위 중 낮은 끝에 자리하며, 1200 Hz 로우패스가 톱니파의 버저음을 둥글둥글한 소리로 다듬어 줍니다. 성인 여성은 기본 주파수를 거의 두 배인 350 Hz로 올리고, 더 밝은 음색을 위해 필터를 1800 Hz까지 열며, 발음을 살짝 더 또렷하게 만들기 위해 게인(0.25)과 릴리스(0.04 s)를 줄입니다.
노인은 가장 많이 가공된 프리셋입니다. 가장 낮은 음역(120 Hz), 가장 어두운 필터(900 Hz), 유일한 비브라토(5 Hz LFO, 깊이 ±15 Hz), 그리고 가장 느린 엔벨로프(어택 0.04 s, 릴리스 0.08 s)를 사용합니다. 마지막 두 값은 음높이만큼이나 중요합니다. 느린 어택이 각 음절의 시작을 부드럽게 만들고, 긴 릴리스가 음이 다음 음으로 살짝 번지게 하여, 이 프리셋이 지향하는 덜 정확한 발음의 느낌을 자아냅니다.
아이는 노인의 결정을 거의 모두 뒤집습니다. 가장 높은 기본 주파수(600 Hz), 가장 밝은 필터(2500 Hz), 가장 낮은 게인(0.20), 그리고 가장 빠른 엔벨로프(어택 0.01 s, 릴리스 0.03 s)입니다. 높은 음높이에 빠른 엔벨로프는 작은 생물의 목소리를 연출하는 고전적인 레시피로, 모든 음절이 짧게 짹는 소리처럼 착지합니다. 로봇은 두 가지 축에서 이질적입니다. 유일한 square 파형이고, 어택과 릴리스가 0인 유일한 프리셋이기도 해서, 게인이 순간적으로 켜졌다 꺼집니다. 이런 급격한 에지가 프리셋이 원하는 단단하고 게이트된 기계적인 음질을 만들어 냅니다. 램프가 없다는 것은 구조상 부드러움도 없다는 뜻입니다.
피치와 속도가 실제로 오실레이터를 움직이는 방식
두 개의 슬라이더가 이 그래프를 실시간으로 제어합니다. 피치는 100 – 800 Hz 범위에 기본값 300 Hz, 속도는 0.5x – 2.0x 범위에 기본값 1.0x입니다. 또한 각 프리셋은 자신만의 정규 baseFreq(위 표에 나열된 기준 중심값)를 선언해서, 그 음성 유형이 어디에 자리하도록 설계되었는지를 문서화합니다.
피치는 오실레이터 주파수를 직접 설정하지만 안전장치가 하나 있습니다. 아이 프리셋에서 재생되는 주파수는 Math.max(pitch, 500)이므로, 아이 모드에서 슬라이더를 500 Hz 아래로 끌어도 아무 일도 일어나지 않습니다. 오실레이터는 결코 이 바닥 아래로 내려가지 않습니다. 이 클램프는 프리셋의 개성을 지킵니다. 150 Hz의 아이 목소리는 그저 조용한 성인 남성처럼 들릴 것이기 때문입니다.
속도가 제어하는 것은 시간이지 주파수가 아닙니다. 재생 버튼을 한 번 누르면 0.5 / speed초 동안 지속되는 비프음이 한 번 재생됩니다 — 구체적으로는 1.0 s(0.5x), 0.5 s(1.0x), 0.25 s(2.0x)입니다. 같은 제수가 텍스트 모드의 모든 타이밍 상수에도 적용되므로, 2.0x 음성은 처음부터 끝까지 진짜로 두 배 빠른 것이지, 음높이가 이동했을 리샘플링이 아닙니다. 재생이 끝나면 인터페이스는 500 / speed + 50 밀리초 후 — 비프음 길이에 50 ms 여유를 더한 시간 — 에 재생 상태를 초기화합니다.
텍스트에서 음성으로: 한 글자에 비프음 하나
보이스 랩의 텍스트 읽기 모드는 음성 엔진이 아닙니다. 같은 3노드 오실레이터 파이프라인을 글자마다 한 번씩 스케줄링하는 것입니다. 최대 100자를 입력하고 읽기를 누르면 입력이 개별 글자로 분할되고, 공백이 아닌 각 글자가 글자 코드에서 도출된 피치 오프셋을 단 음절 비프음으로 스케줄링됩니다. 타이밍 상수는 다음과 같으며, 모두 속도로 나눈 값입니다:
- 글자 음 — 글자당
0.08 / speed초(0.08 s,1.0x기준). - 글자 사이 간격 — 연속된 글자 사이에
0.03 / speed초. - 단어 간격 — 공백 문자가
0.12 / speed초의 무음을 삽입합니다. 대략 글자 1.5개 분량입니다. - 구절 쉼 — 5글자마다(
i % 5 === 4)gap × 2의 추가 쉼을 넣어, 획일적인 소리 나열 대신 억양 있는 리듬을 만듭니다. - 리드인 — 스케줄링은
currentTime + 0.05초에 시작해, 첫 음이 울리기 전에 오디오 그래프가 준비되도록 합니다.
피치 변화가 교묘한 부분입니다. 각 글자의 주파수 오프셋은 ((charCode % 20) - 10) × 3으로 계산되며, 이는 기본 피치를 중심으로 -30 Hz에서 +27 Hz 사이의 결정론적인 분포를 만들어 냅니다. 결정론적이라는 점이 중요합니다. 같은 단어는 항상 같은 멜로디 윤곽을 만들어 내므로, 특정 구절이 Mii의 목소리가 그렇듯 알아볼 수 있는 것이 됩니다. 오프셋이 음성학이 아니라 글자 코드에서 오기 때문에, 출력은 텍스트의 리듬에 밀착해 따라가면서도 알아들을 수 없는 비어휘적 의사언어로 남습니다. 이것이 바로 보이스 랩이 근사하려는 비프 음성 효과 그 자체입니다.
성격 그룹별 음성 설계하기
설득력 있는 성격 음성은 대부분 피치 범위 선택의 문제입니다. 사이트의 참조 표가 여러분의 Mii 성격 그룹에 할당한 프리셋을 고른 뒤, 피치 슬라이더를 권장 범위 안에 두면 됩니다. 보이스 랩 참조 표가 사용하는 전체 매핑은 다음과 같습니다:
| 성격 그룹 | 예시 유형 | MBTI | 추천 프리셋 | 피치 범위 |
|---|---|---|---|---|
| 외교적 | 리더 | ESTJ | 성인 남성 | 180 – 250 Hz |
| 자신감 | 디자이너 | INTJ | 성인 남성 | 150 – 200 Hz |
| 독립심 | 아티스트 | INFP | 성인 여성 | 280 – 380 Hz |
| 느긋함 | 드리머 | INFJ | 노인 | 120 – 180 Hz |
| 아이 같은 캐릭터 | Any | Any | 아이 | 500 – 700 Hz |
| 로봇/AI 캐릭터 | Any | Any | 로봇 | 200 – 300 Hz |
이 표는 16가지 성격 전체가 아니라 그룹을 매핑한다는 점에 주목하세요. 우리의 MBTI 매핑에 나오는 4개 성격 그룹에는 각각 하나의 대표적인 목소리 실루엣이 할당되며, 개별 성격은 슬라이더를 범위의 어디에 두는지, 그리고 속도 컨트롤을 얼마나 빠르게 돌리는지로 표현됩니다. 단계별 레시피는 다음과 같습니다:
- 1그룹에 맞는 프리셋을 고릅니다. 외교적과 자신감 Mii는 성인 남성을, 독립심 Mii는 성인 여성을, 느긋함 Mii는 노인을 사용합니다. 노인의
5 Hz비브라토는 이 그룹의 상징인 여유롭고 서두르지 않는 느낌을 더해 줍니다. Mii의 그룹은 4개의 성격 슬라이더에서 나옵니다. 아직 모른다면 성격 차트를 참고하세요. - 2피치 슬라이더를 그룹 범위 안에 설정합니다. 자신감 디자이너라면
150 – 200 Hz를 뜻합니다.150 Hz쪽으로 움직이면 더 위엄 있게,200 Hz쪽으로 움직이면 더 활동적으로 들립니다. 아이 프리셋의500 Hz하한 덕분에500 – 700 Hz범위는 저절로 지켜집니다. - 3말투에 맞춰 속도를 고릅니다. 말이 빠른 엔터테이너 유형은
1.4x – 2.0x가 어울리고, 졸린 듯한 드리머는0.5x – 0.8x에 자연스럽게 자리합니다. 속도는 길이만 바꾸므로, 2단계에서 고른 목소리의 음정이 변하는 일은 결코 없습니다. - 4짧은 구절로 테스트합니다. 텍스트 창에
20 – 30자를 입력하고 5글자마다 오는 구절 쉼을 들어보세요. 리듬이 성격에 맞지 않게 느껴지면 피치를 만지기 전에 먼저 속도를 조정하세요. - 5기록과 비교하며 다듬습니다. 모든 재생 — 프리셋, 피치, 속도, 그리고 최대
100자의 텍스트 — 는 도구 안의 로컬IndexedDB기록 패널에 저장되므로, 적어두지 않고도 두 설정을 A/B 비교할 수 있습니다. 어떤 데이터도 브라우저 밖으로 나가지 않습니다.
아이 같은 캐릭터와 로봇/AI 캐릭터 행은 의도적으로 성격 시스템 밖에 있습니다. 어떤 Mii든 둘 중 하나의 목소리로 연출할 수 있기 때문에 MBTI 열이 Any로 표시됩니다. 로봇의 길이가 0인 엔벨로프는 템포의 영향을 받지 않아서, 어떤 속도에서도 같은 게이트된 딱딱함을 유지합니다. 그래서 속도가 순전히 코미디용 컨트롤이 되는 유일한 목소리입니다.
Web Speech API 폴백이 없는 이유
보이스 랩은 브라우저의 Web Speech API를 의도적으로 피합니다. 코드베이스 어디에도 speechSynthesis 호출이 없고, 텍스트 읽기 모드는 순수한 오실레이터 스케줄링입니다. 이것은 방어 가능한 근거와 실제 트레이드오프를 함께 지닌 설계 결정이므로, 양쪽 모두 명확히 짚어 볼 가치가 있습니다.
근거는 이렇습니다. speechSynthesis는 자연스러운 사람 목소리를 만들어 내는데, 그것이 바로 8비트 보이스 랩이 원하지 않는 것입니다. 또한 음성 선택을 운영체제에 위임하므로 같은 텍스트가 브라우저와 기기에 따라 다르게 들릴 수 있고, 몇몇 브라우저는 음성을 지연 로드해서 첫 발화의 지연이 뚜렷합니다. 글자당 비프음 방식은 모든 소리를 단일 비프 모드가 사용하는 것과 같은 3노드 그래프 안에 머물게 하고, Web Audio API가 동작하는 모든 환경에서 동일한 음색을 보장하며, 로드할 것이 없어 즉시 시작합니다.
트레이드오프는 출력이 알아들을 수 있는 음성이 아니라는 점입니다. 텍스트의 리듬과 윤곽은 따라가지만 알아볼 수 있는 단어를 만들어 내지 않으므로, Tomodachi Life 비프 음성의 이해 가능성이 아니라 그 억양을 떠오르게 합니다. 애초에 게임의 의사언어도 알아들을 수 있는 것이 아니니, 그것이야말로 요점이라 할 수 있습니다. 재생 정지도 같은 방식으로 단순하지만 확실합니다. 보이스 랩은 AudioContext 전체를 close()로 닫아서, 페이드아웃하는 대신 스케줄링된 모든 노드를 즉시 종료합니다.
한계를 솔직하게 밝힙니다
세 가지 한계가 이 신디사이저가 정직하게 주장할 수 있는 범위를 정합니다. 첫째, 이것이 근사하는 것은 미학이지 게임의 엔진이 아닙니다. Nintendo는 Tomodachi Life가 음성을 어떻게 생성하는지 문서화한 적이 없으므로, 여기 있는 프리셋 값은 시리즈의 사운드를 떠올리도록 조율된 커뮤니티 추정치이지 추출된 상수가 아닙니다. 이 목소리들은 게임의 목소리를 연상시키지만, 복제본은 아닙니다.
둘째, 이 합성은 단성(monophonic)이며 포먼트가 없습니다. 각 음절은 하나의 로우패스 필터로 빚어진 단일 오실레이터인 반면, 자연스러운 말소리 — 그리고 아마도 게임의 더 정교한 엔진 — 는 성도에서 나오는 포먼트 구조를 지닙니다. 출력이 샘플링된 음성이 아니라 칩튠 보이싱으로 들리는 이유입니다. 코드를 확장한다면 이 간극이 가장 탐구할 가치가 있습니다. 한 옥타브 위에 놓인 두 번째 오실레이터도, 주파수 이동이 스케줄링된 필터도, 결과를 목소리 영역에 한 걸음 더 가깝게 밀어 줍니다.
셋째, 모든 것이 브라우저 지원에 의존합니다. 여기서 사용하는 Web Audio API — AudioContext, OscillatorNode, BiquadFilterNode, GainNode — 는 현재 모든 주요 브라우저에서 지원되지만, Web Audio API의 출력 성격은 기기의 오디오 스택에 따라 여전히 미세하게 달라집니다. 또한 사용자 동작이 있을 때까지 자동 재생을 차단하는 브라우저에서는 보이스 랩이 이미 마련해 둔 재생 버튼 누르기가 필요합니다. 프라이버시 측면에서 이 도구는 완전한 클라이언트 사이드입니다. 합성은 브라우저에서 실행되고, 유일하게 남는 데이터는 로컬 IndexedDB 기록입니다. 오디오나 텍스트는 어디에도 업로드되지 않습니다.
파이프라인을 직접 체험해 보세요
이 모델을 가장 빨리 체득하는 방법은 두 슬라이더를 움직이며 그래프가 실시간으로 반응하는 소리를 듣는 것입니다:
- Tomodachi 보이스 랩 — 신디사이저 본체입니다. 5가지 프리셋,
100 – 800 Hz피치 슬라이더, 속도 컨트롤, 그리고 글자당 비프음 텍스트 모드를 갖추고 있습니다. - Tomodachi Life MBTI 매핑 — Mii의 4개 성격 슬라이더가 어떻게 그룹을 만들고, 그 그룹이 위 표의 프리셋을 어떻게 결정하는지 설명합니다.
- 성격 차트 — 목소리를 입힐 대표 성격을 고르기 위한 16유형 전체 참조 자료입니다.
- Mii QR 잠금 해제 — 설계한 목소리와 편집한 Mii 캐릭터를 결합하여 완전한 섬 주민을 완성하세요.