Files
viettranx 613b6e38d7 feat(tts): provider capabilities schema + Gemini TTS + dynamic param forms
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.
2026-04-20 00:05:19 +07:00

48 lines
1.1 KiB
Go

package edge
import (
"fmt"
"strconv"
"strings"
)
// sliderToRateString converts an integer slider value to the edge-tts rate
// string format "+N%" or "-N%". Zero becomes "+0%".
func sliderToRateString(n int) string {
if n >= 0 {
return fmt.Sprintf("+%d%%", n)
}
return fmt.Sprintf("%d%%", n)
}
// sliderToPitchString converts an integer slider value to the edge-tts pitch
// string format "+NHz" or "-NHz". Zero becomes "+0Hz".
func sliderToPitchString(n int) string {
if n >= 0 {
return fmt.Sprintf("+%dHz", n)
}
return fmt.Sprintf("%dHz", n)
}
// sliderToVolumeString converts an integer slider value to the edge-tts volume
// string format "+N%" or "-N%". Zero becomes "+0%".
func sliderToVolumeString(n int) string {
return sliderToRateString(n) // same format as rate
}
// rateStringToSlider parses a rate/volume string like "+15%" or "-25%" and
// returns the integer value. Returns 0 on parse failure.
func rateStringToSlider(s string) int {
s = strings.TrimSpace(s)
s = strings.TrimSuffix(s, "%")
s = strings.TrimSuffix(s, "Hz")
if s == "" {
return 0
}
n, err := strconv.Atoi(s)
if err != nil {
return 0
}
return n
}