mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 06:17:41 +00:00
Baseline groundwork for TTS expansion plan. Introduces a capabilities system that the follow-up plan (phase-01..04) builds on. Backend: - `audio.ParamSchema` / `ProviderCapabilities` types + per-provider capabilities.go for edge, elevenlabs, minimax, openai, gemini. - Gemini TTS provider (client, models, voices, wav encoder, multi-speaker via SpeakerVoice, audio-tag aware prompts). - TTSOptions gains `Params map[string]any` (read-only) + `Speakers`. - `VoiceListProvider` interface decouples HTTP voice handler from provider-specific impls. - `nested_keys.go` resolves dot-separated param paths for nested provider bodies (voice_settings.stability etc.). - Characterization + defaults-invariant tests per provider lock nil-params byte-equivalence before new params land. - `/v1/tts/capabilities` HTTP endpoint + integration coverage. - Dual-read tests (PG + SQLite) for tts_config. Frontend (web + desktop): - `DynamicParamForm` with depends-on evaluation, split into fields/logic modules. Slider primitive added. - `AudioTagPicker` + `MultiSpeakerEditor` for Gemini. - `voice-picker` refactored toward portal UX; combobox tightened. - tts-capabilities API client + typed hooks. - i18n catalogs (en/vi/zh) expanded; parity tests guard key drift. - TTS page reorganised (voice-model-section removed; playground + credentials + provider-setup split cleanly). Docs: codebase-summary, project-changelog, tts-provider-capabilities.
63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
// Package gemini implements Google Gemini TTS via generateContent API.
|
|
// Gemini returns base64 PCM 16-bit signed little-endian 24kHz mono; this
|
|
// package wraps it in a 44-byte RIFF/WAV header so browsers can play it.
|
|
package gemini
|
|
|
|
import "encoding/binary"
|
|
|
|
// WAV header constants for 24kHz mono 16-bit PCM.
|
|
const (
|
|
sampleRate24k uint32 = 24000
|
|
channelsMono uint16 = 1
|
|
bitsPerSample uint16 = 16
|
|
audioFormatPCM uint16 = 1
|
|
fmtChunkSize uint32 = 16
|
|
wavHeaderSize = 44
|
|
)
|
|
|
|
// Wrap prepends a RIFF/WAV header to raw PCM bytes and returns playable WAV.
|
|
// PCM must be 16-bit signed little-endian at 24kHz mono.
|
|
func Wrap(pcm []byte) []byte {
|
|
dataSize := uint32(len(pcm))
|
|
byteRate := uint32(sampleRate24k) * uint32(channelsMono) * uint32(bitsPerSample) / 8
|
|
blockAlign := channelsMono * bitsPerSample / 8
|
|
|
|
out := make([]byte, wavHeaderSize+dataSize)
|
|
pos := 0
|
|
|
|
// RIFF chunk descriptor
|
|
copy(out[pos:], "RIFF")
|
|
pos += 4
|
|
binary.LittleEndian.PutUint32(out[pos:], 36+dataSize) // chunk size = 36 + data
|
|
pos += 4
|
|
copy(out[pos:], "WAVE")
|
|
pos += 4
|
|
|
|
// fmt sub-chunk
|
|
copy(out[pos:], "fmt ")
|
|
pos += 4
|
|
binary.LittleEndian.PutUint32(out[pos:], fmtChunkSize)
|
|
pos += 4
|
|
binary.LittleEndian.PutUint16(out[pos:], audioFormatPCM)
|
|
pos += 2
|
|
binary.LittleEndian.PutUint16(out[pos:], channelsMono)
|
|
pos += 2
|
|
binary.LittleEndian.PutUint32(out[pos:], sampleRate24k)
|
|
pos += 4
|
|
binary.LittleEndian.PutUint32(out[pos:], byteRate)
|
|
pos += 4
|
|
binary.LittleEndian.PutUint16(out[pos:], blockAlign)
|
|
pos += 2
|
|
binary.LittleEndian.PutUint16(out[pos:], bitsPerSample)
|
|
pos += 2
|
|
|
|
// data sub-chunk
|
|
copy(out[pos:], "data")
|
|
pos += 4
|
|
binary.LittleEndian.PutUint32(out[pos:], dataSize)
|
|
pos += 4
|
|
|
|
copy(out[pos:], pcm)
|
|
return out
|
|
}
|