Skip to main content

JunkBox libs/shared-voice 가이드

1. 문서 목적

  • 이 문서는 JunkBox 전체에서 재사용 가능한 로컬 음성 런타임 라이브러리인 shared-voice의 현재 구조와 책임 경계를 설명하는 가이드임.
  • 본 문서는 계획 문서가 아니라 현재 런타임 기준의 구조, 설정 계약, provider 계약, 운영 제약을 이해하기 위한 기준 문서임.

2. 라이브러리 역할

  • shared-voice는 마이크 입력, STT, 호출어 감지, TTS, 음성 이벤트 모델을 제공하는 공통 음성 라이브러리임.
  • 앱은 음성 장치 제어, STT/TTS provider 구현, 호출어 매칭 로직을 직접 소유하지 않고 shared-voice를 사용함.
  • 현재 apps/jeevtrapinterfaces/voice에서 shared-voice를 사용해 로컬 음성 호출 PoC 런타임을 실행함.
  • shared-voice는 특정 앱의 실제 호출어 정책 파일을 소유하지 않음.
  • 실제 호출어, alias, STT/TTS provider, 오디오 장치 설정은 소비 앱의 YAML에서 관리함.

3. 패키지 구조

junkbox_shared_voice/
├── audio/
├── stt/
├── tts/
├── cli.py
├── config.py
├── events.py
├── runtime.py
├── wakeword.py
└── __init__.py

4. 설정 모델

4.1 SharedVoiceConfig

  • SharedVoiceConfig는 wake, audio, stt, tts 설정을 하나로 묶는 상위 설정 모델임.
  • load_shared_voice_config(path)는 UTF-8 YAML 파일을 읽어 SharedVoiceConfig로 변환함.
  • YAML 구조가 mapping이 아니면 오류로 처리함.
  • 앱은 shared-voice 안에 설정 파일을 두지 않고, 앱 소유 경로의 YAML을 load_shared_voice_config()에 전달함.

4.2 wake 설정

  • wake 설정은 호출어 primary, 호출어 alias, 호출 성공 응답 문구, loop 제한 값을 포함함.
  • wake.primary는 기본 호출어임.
  • wake.aliases는 STT provider가 호출어를 다르게 전사하는 경우를 흡수하기 위한 문자열 목록임.
  • reply.wake_detected는 호출어 감지 후 TTS provider로 출력할 응답 문구임.

4.3 audio 설정

  • audio 설정은 sample rate, channel 수, 녹음 chunk 길이, 입력 장치를 포함함.
  • 현재 기본 입력 방식은 짧은 chunk 녹음을 반복하고 각 chunk를 STT에 전달하는 구조임.
  • audio.input_devicesounddevice가 이해할 수 있는 장치명 또는 장치 식별자를 사용함.
  • PulseAudio/PipeWire 환경에서는 pulse 입력 장치를 사용할 수 있음.

4.4 stt 설정

  • stt 설정은 STT provider, 모델 크기, device, compute type, language, beam size를 포함함.
  • 현재 로컬 STT provider는 faster_whisper를 사용함.
  • Jeevtrap voice 설정은 CPU base 모델과 int8 compute type을 기본값으로 사용함.
  • 호출어 감지를 Whisper STT로 수행하면 상시 CPU 사용량이 발생하므로, 상시 운영 용도에서는 JEEVTRAP_VOICE_ENABLED를 신중하게 제어해야 함.

4.5 tts 설정

  • tts 설정은 TTS provider, system command, language, voice name, 합성 step 수, 속도, sample rate, 자동 다운로드 여부를 포함함.
  • provider=systemspd-say, espeak, macOS say 같은 시스템 TTS 명령을 사용함.
  • provider=supertonic은 Supertonic 로컬 TTS provider를 사용함.
  • Jeevtrap voice 설정은 supertonic, 한국어 ko, voice preset M1을 사용함.

5. STT 구조

5.1 SpeechToTextProvider

  • STT provider는 transcribe(audio, sample_rate=...) -> TranscriptResult 계약을 구현함.
  • TranscriptResult는 전사 텍스트, 언어, confidence 값을 포함함.

5.2 FasterWhisperSpeechToTextProvider

  • FasterWhisperSpeechToTextProviderfaster-whisper 모델을 lazy load함.
  • provider 생성 시점에는 모델을 즉시 로드하지 않고, 첫 전사 시 모델을 로드함.
  • vad_filter=True를 사용해 음성 구간 전사를 보조함.
  • faster-whisper가 설치되지 않은 환경에서 provider를 사용하면 명시적 RuntimeError를 발생시킴.

6. TTS 구조

6.1 TextToSpeechProvider

  • TTS provider는 speak(text) 계약을 구현함.
  • 앱은 provider 종류를 직접 분기하지 않고 설정값에 따라 provider를 구성함.

6.2 SystemTextToSpeechProvider

  • SystemTextToSpeechProvider는 OS 명령 기반 TTS fallback provider임.
  • Linux에서는 spd-say, espeak 순서로 감지함.
  • macOS에서는 say 명령을 감지함.
  • 사용 가능한 명령이 없으면 표준 출력으로 텍스트를 출력함.

6.3 SupertonicTextToSpeechProvider

  • SupertonicTextToSpeechProvider는 Supertonic 로컬 TTS를 사용함.
  • Supertonic TTS 객체와 voice style은 lazy load됨.
  • 합성 결과는 sounddevice를 통해 재생함.
  • supertonic, numpy, sounddevice 의존성이 없으면 명시적 RuntimeError를 발생시킴.
  • 첫 합성 시 모델 다운로드 또는 초기화 지연이 발생할 수 있음.

7. Audio 구조

7.1 SoundDeviceAudioRecorder

  • SoundDeviceAudioRecordersounddevicenumpy를 사용해 짧은 mono audio chunk를 녹음함.
  • 녹음 결과는 AudioChunk(samples, sample_rate, duration_seconds) 구조로 반환됨.
  • sounddevice 또는 numpy가 설치되지 않은 환경에서 사용하면 명시적 RuntimeError를 발생시킴.

7.2 시스템 오디오 전제

  • Linux Docker 컨테이너에서 PulseAudio/PipeWire 출력과 입력을 사용하려면 Pulse socket과 cookie 마운트가 필요함.
  • Jeevtrap Docker 이미지는 PortAudio/ALSA/PulseAudio 연동 런타임 패키지를 포함함.
  • compose 설정은 PULSE_SERVER=unix:/run/user/1000/pulse/native/run/user/1000/pulse/native, Pulse cookie 마운트를 사용할 수 있음.

8. 호출어 감지

  • wakeword.py는 한국어/영어 호출어 텍스트를 비교하기 위해 NFKC 정규화, lowercase, 공백/구두점 제거를 수행함.
  • contains_wake_word(transcript, wake_word, aliases)는 primary 호출어와 alias 목록 중 하나가 transcript에 포함되면 true를 반환함.
  • 호출어 alias는 STT 전사 오인식을 흡수하기 위한 장치이며, 너무 넓은 alias는 오작동 가능성을 높일 수 있음.
  • 호출어 변경은 앱 YAML에서 수행하고 라이브러리 기본값을 변경하지 않음.

9. Runtime 구조

  • WakeWordRuntimeAudioRecorder, SpeechToTextProvider, TextToSpeechProvider, WakeWordRuntimeConfig를 조합함.
  • run_once()는 한 번 녹음하고 STT 결과를 이벤트로 발행한 뒤 호출어가 감지되면 TTS 응답을 출력함.
  • run_forever()max_loops 또는 stop flag가 만족될 때까지 run_once()를 반복함.
  • stop()은 내부 stop flag를 설정하며, 현재 실행 중인 녹음 chunk 이후 루프가 종료됨.
  • on_event handler가 주어지면 VoiceTranscriptReceived, VoiceWakeDetected, VoiceRecognitionFailed 이벤트를 앱으로 전달할 수 있음.
  • 현재 Jeevtrap voice runtime은 blocking run_forever()를 별도 thread에서 실행함.

10. CLI

  • shared-voice-wake-demo는 로컬 호출어 PoC CLI임.
  • --config로 앱 소유 voice YAML을 전달할 수 있음.
  • CLI 옵션은 YAML 값을 override할 수 있음.

예시:

uv run --package junkbox-shared-voice --extra poc shared-voice-wake-demo \
--config apps/jeevtrap/var/voice.yml

11. 의존성 extra

  • audio extra는 numpy, sounddevice를 포함함.
  • local-stt extra는 faster-whisper를 포함함.
  • supertonic extra는 Supertonic TTS를 포함함.
  • poc extra는 audio, local-stt, supertonic 실행에 필요한 의존성을 함께 포함함.

12. Jeevtrap 사용 계약

  • apps/jeevtrap/var/voice.yml은 Jeevtrap voice runtime의 기본 YAML임.
  • JEEVTRAP_VOICE_ENABLED가 true이면 interfaces/voice.JeevtrapVoiceRuntimeshared-voice 런타임을 시작함.
  • JEEVTRAP_VOICE_CONFIG_YAML_PATH가 설정되면 기본 YAML 경로를 override함.
  • 운영 기본값은 voice disabled임.
  • voice runtime은 현재 호출어 감지와 TTS 응답까지만 담당함.
  • 음성 명령 의도 판별, 실행 승인, 스크립트 실행 연결은 별도 상태 머신과 승인 흐름을 필요로 함.