feat(voice): 百炼声音复刻create_voice + TTS instruction情绪驱动(主TTS-昔涟复刻音色)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

This commit is contained in:
Aska committed 2026-09-08 06:48:15 +08:00
1 parent 73dabb9dde
commit 03d7050e1e
15 files changed
+686 -30

No files matched your search

+17 -2
View File
@@ -12,7 +12,8 @@ import (
// provider is unavailable (nil direction) or fails. It implements STT and TTS so
// it can be used anywhere a single provider is expected.
type Chain struct {
steps []Provider
steps []Provider
cloner VoiceCloner
}
// NewChain builds a degrading chain from the given providers in the order they
@@ -42,6 +43,16 @@ func (c *Chain) STT(ctx context.Context, audio Audio) (string, error) {
return "", fmt.Errorf("voice: %w: %v", ErrProviderFailed, lastErr)
}
// CloneVoice creates a custom voice via the cloud provider's voice-enrollment
// endpoint (声音复刻 create_voice). A chain without a cloud rung returns
// ErrNotConfigured. It is not a degradation step — the caller gets one result.
func (c *Chain) CloneVoice(ctx context.Context, referenceURL, prefix string) (string, error) {
if c.cloner == nil {
return "", ErrNotConfigured
}
return c.cloner.CloneVoice(ctx, referenceURL, prefix)
}
// TTS walks the chain for synthesized audio.
func (c *Chain) TTS(ctx context.Context, text string) (Audio, error) {
if len(c.steps) == 0 {
@@ -98,8 +109,10 @@ func New(cfg config.VoiceConfig, opts ...Option) *Chain {
}
steps := make([]Provider, 0, 5)
var cloner VoiceCloner
if voiceConfigured(cfg) {
steps = append(steps, CloudProvider(newDashboardSTT(cfg), newDashboardTTS(cfg)))
cloner = newDashboardCloner(cfg)
}
switch cfg.TTSProvider {
case "edge-tts", "edge":
@@ -108,7 +121,9 @@ func New(cfg config.VoiceConfig, opts ...Option) *Chain {
steps = append(steps, EspeakProvider(newEspeak()))
}
steps = append(steps, SilentProvider())
return NewChain(steps...)
chain := NewChain(steps...)
chain.cloner = cloner
return chain
}
// voiceConfigured reports whether any cloud voice credential or model is set, so