Rust에서 오프라인 텍스트 음성 변환
Rust에는 운영체제의 음성 엔진을 감싼 크레이트가 있어 접근성 용도로는 괜찮지만, 목소리가 로봇 같고 플랫폼마다 다릅니다. 글자 수로 요금을 받는 클라우드 SDK도 있습니다. 어디서나 같은 자연스러운 목소리가 필요한 게임, 봇, 명령줄 도구에는 둘 다 이상적이지 않습니다.
이 가이드에서는 무료 SuperClonica 엔진을 로컬 HTTP 서버로 띄워 Rust에서 호출하는 방법을 소개합니다. 자연스러운 목소리, 내 손에 남는 WAV 파일, reqwest로 몇 줄.
Rust에서 말하는 방법
스크린 리더 같은 출력이라면 시스템 목소리로 충분하고, 제품이나 게임의 목소리라면 엔진을 쓰세요.
- 시스템 목소리를 감싼 크레이트: 오프라인이고 가볍지만 로봇 같고 Windows, macOS, Linux마다 다름.
- 클라우드 SDK: 자연스러운 목소리지만 계정, 키, 글자 수 요금, 그리고 텍스트가 남의 서버로.
- SuperClonica: 자연스러운 목소리, 캐릭터·맞춤 목소리를 쓰는 로컬 HTTP 서버. Docker가 돌아가는 곳이면 오프라인으로.
- 31개 언어를 말합니다. 중국어는 아직 지원하지 않습니다.
목소리를 무료로 들어 보기
코드를 쓰기 전에 아무 목소리 페이지에서 30~100자 문장을 입력하고 언어를 고른 뒤 생성 버튼을 누르세요. 몇 초 만에 앱이 가질 수 있는 목소리를 들을 수 있습니다. 계정은 필요 없습니다.
음성 서버 시작
NVIDIA 그래픽 카드가 있으면 cuda 이미지로 더 빨라지지만, 필수는 아닙니다.
- 어떤 CPU에서든 서버 시작: docker run -d -p 8000:8000 -v superclonica-models:/models superclonica/superclonica:cpu.
- 첫 실행 때 Supertonic 3 모델(약 385MB)을 볼륨에 내려받습니다. 준비 여부는 GET /health로 확인합니다.
- POST /synthesize는 text, voice, lang, seed, normalize가 담긴 JSON을 받아 16비트 모노 WAV로 답합니다.
- voice는 기본 이름 M1~M5, F1~F5이거나 목소리 파일의 JSON 내용입니다.
Rust에서 호출
reqwest 크레이트의 blocking 기능을 쓰면 Result를 반환하는 함수 안에서 요청은 몇 줄이면 됩니다: let voice = std::fs::read_to_string("voice.json")?; let body = format!(r#"{{"text": "Rust에서 안녕하세요!", "lang": "ko", "seed": 7, "voice": {voice}}}"#); let wav = reqwest::blocking::Client::new().post("http://localhost:8000/synthesize").header("Content-Type", "application/json").body(body).send()?.bytes()?; std::fs::write("hello.wav", wav)?; 비동기 코드에서는 reqwest의 비동기 클라이언트를 같은 방식으로 쓰세요. 기본 목소리라면 파일 대신 "voice": "F1"을 보내세요. 같은 텍스트, 목소리, seed면 항상 같은 소리가 나므로 반복되는 문구의 파일은 캐시하세요. 큰 요청은 serde_json으로 깔끔하게 만드세요.
운영 환경 습관
reqwest 클라이언트는 하나를 재사용하고 타임아웃을 두세요. 포트 8000을 그대로 인터넷에 열지 마세요. SUPERCLONICA_API_KEY를 설정해 Bearer 토큰으로 보내거나, 사설 네트워크 안에 두세요. 동시 요청이 많다면 SUPERCLONICA_LANES로 모델 사본을 늘리고, 내 하드웨어에서 측정하세요. 우리 테스트에서 짧은 문장은 RTX 5070 Ti에서 0.12초, CPU 4스레드에서 0.43초 걸렸습니다.