TTS 스트리밍¶
SOHRI TTS 스트리밍은 프로바이더가 첫 오디오를 내는 즉시 재생을 시작하기 위한 선택 계약입니다. 다음 두 조건이 모두 참이어야 합니다.
- 클라이언트 핸드셰이크:
?ttsStream=v1또는?tts_stream=v1 - 서버 설정:
sohri.tts.streaming.enabled=true
판정은 매 턴 다시 수행합니다. 서버 플래그가 꺼지면 협상된 기존 세션도 다음 턴부터 레거시 WAV
한 프레임 경로로 돌아갑니다. 두 조건이 모두 참일 때만 connection_established.ttsStream이
v1로 에코됩니다.
SOHRI 스트리밍 와이어¶
sequenceDiagram
participant D as DM
participant S as Socket Backend
participant T as TTS Provider
participant C as Client/Gateway
D-->>S: 확정 BOT 텍스트
S->>T: streaming synthesis
T-->>S: format + first PCM
S-->>C: dialog_text
S-->>C: tts_stream_start
loop 오디오 청크
T-->>S: PCM s16le
S->>S: generation 확인
S-->>C: Binary PCM
end
T-->>S: 완료
S-->>C: tts_stream_end(status=completed)
S-->>C: dialog_response
프레임 순서는 다음과 같습니다.
dialog_text: 자막을 재생 시작에 맞추기 위한 추가 프레임tts_stream_start: generation, 포맷, provider,live, 선택적botMessageId- raw PCM s16le mono binary 청크 N개
tts_stream_end: 상태, 청크·바이트 수, 재생 길이, 합성 시간dialog_response: 기존 턴 완료 의미를 그대로 유지
dialog_text와 tts_stream_start는 같은 송신 임계 구역에서 연속 전송됩니다.
dialog_response를 앞당기지 않는 이유는 클라이언트가 이를 턴 종료·마이크·세션 마감 신호로도
사용하기 때문입니다.
취소와 스트림 종료¶
각 청크 송신 직전에 response generation을 확인합니다. 바지인이나 cancel_response로 세대가
바뀌면 남은 청크를 보내지 않고 프로바이더 스트림도 닫습니다.
취소된 스트림도 end를 보냅니다
tts_stream_start가 이미 전송됐다면 취소 시
tts_stream_end{status:"aborted", reason:"cancelled"}를 best-effort로 보냅니다.
클라이언트가 이미 연 재생 큐를 닫아야 하기 때문입니다. 오래된 binary 청크나
dialog_response는 보내지 않습니다.
첫 청크 전에 취소되거나 합성에 실패해 tts_stream_start를 전혀 보내지 않았다면 start/end를 모두
생략합니다. 현재 generation의 실제 합성 실패만 tts_error로 알립니다. status가 아니라 start
프레임의 실제 전송 여부가 기준입니다.
부분 전송 뒤 provider 오류, 총량 초과, 소켓 오류도 aborted end로 닫습니다. 대표 reason은
provider_error, limit_exceeded, send_error입니다.
포맷과 길이¶
- binary는 WAV 헤더 없는 raw PCM입니다. 포맷은
tts_stream_start.data.format이 전달합니다. durationMs는byteLength / (sampleRate × channels × 2)로 계산한 재생 길이입니다.synthesisMs는 합성 시작부터 종료까지의 벽시계 시간입니다.- WebSocket 자체가 순서를 보장하므로 청크 시퀀스 번호는 없습니다.
- 프레임 단위로만 송신 락을 잡으므로 start와 end 사이에 다른 JSON 이벤트가 끼어들 수 있습니다.
Fish Audio는 실제 HTTP 스트리밍(live=true)을 지원합니다. 그 밖의 어댑터는 완성 PCM을 같은
와이어로 나누는 의사 스트리밍(live=false)일 수 있습니다. 클라이언트 계약은 동일합니다.
레거시 경로¶
미협상 세션은 tts_audio_metadata와 완성 WAV binary 한 프레임을 받고, 그 뒤
dialog_response를 받습니다. generation은 metadata에 있으며 오디오는 WAV 헤더를 포함합니다.
이 경로도 합성 후와 송신 직전에 generation을 검사합니다.
OpenAI Realtime도 tts_stream_start/binary/tts_stream_end 형태를 쓰지만 provider native
이벤트를 중계하는 별도 엔진 계약입니다. 세부 차이는 이벤트 참조를
확인하세요.