diff --git a/internal/middleware/sound_start.go b/internal/middleware/sound_start.go index de6e98f..2bb3fe7 100644 --- a/internal/middleware/sound_start.go +++ b/internal/middleware/sound_start.go @@ -10,16 +10,24 @@ import ( func SoundStart() leaf.HandlerFunc { return func(c *leaf.Context) { pm := leaf.Value[*schema.PlayModal](c, PayloadJSONKey) - tts.DefaultTTS.Sound(pm.TTS.Start) + + // 使用请求的 context,支持取消和超时 + if pm.TTS.Start != "" { + tts.DefaultTTS.SoundWithContext(c, pm.TTS.Start) + } defer func() { + var text string switch leaf.Value[leaf.EndType](c, leaf.EndKey) { case leaf.End: - tts.DefaultTTS.Sound(pm.TTS.End) + text = pm.TTS.End case leaf.EndTimeout: - tts.DefaultTTS.Sound(pm.TTS.Timeout) + text = pm.TTS.Timeout case leaf.EndStop: - tts.DefaultTTS.Sound(pm.TTS.Stop) + text = pm.TTS.Stop + } + if text != "" { + tts.DefaultTTS.SoundWithContext(c, text) } }() diff --git a/pkg/audio/play.go b/pkg/audio/play.go index 55af1b4..ef28586 100644 --- a/pkg/audio/play.go +++ b/pkg/audio/play.go @@ -22,6 +22,7 @@ func init() { } func PlayWav(c context.Context, r io.Reader) { + zap.S().Debugln("开始 WAV 解码") streamer, format, err := wav.Decode(r) if err != nil { zap.S().Errorln("WAV解码失败: ", err) @@ -29,20 +30,25 @@ func PlayWav(c context.Context, r io.Reader) { } defer streamer.Close() + zap.S().Debugln("WAV解码成功,采样率:", format.SampleRate) s := beep.Resample(4, format.SampleRate, DefaultSampleRate, streamer) ctrl := &beep.Ctrl{Streamer: s} done := make(chan struct{}) speaker.Play(beep.Seq(ctrl, beep.Callback(func() { + zap.S().Debugln("音频播放完成") close(done) }))) + zap.S().Debugln("等待音频播放完成...") for { select { case <-done: + zap.S().Infoln("音频播放正常结束") return case <-c.Done(): { + zap.S().Infoln("音频播放被 context 取消") speaker.Lock() ctrl.Streamer = nil speaker.Unlock() diff --git a/pkg/tts/aliyun.go b/pkg/tts/aliyun.go index a476d21..18f231b 100644 --- a/pkg/tts/aliyun.go +++ b/pkg/tts/aliyun.go @@ -48,6 +48,7 @@ func (tts *AliTTS) Sound(text string) { zap.S().Infof("开始播放TTS: %s", text) buf, err := tts.Get(text) if err == nil && buf != nil { + zap.S().Debugln("TTS合成成功,开始播放") audio.PlayWav(tts.ctx, buf) zap.S().Infof("TTS播放完成: %s", text) } else { @@ -55,6 +56,22 @@ func (tts *AliTTS) Sound(text string) { } } +// SoundWithContext 使用指定的 context 播放 TTS,支持取消和超时 +func (tts *AliTTS) SoundWithContext(ctx context.Context, text string) { + if text == "" { + return + } + zap.S().Infof("开始播放TTS: %s", text) + buf, err := tts.Get(text) + if err == nil && buf != nil { + zap.S().Debugln("TTS合成成功,开始播放") + audio.PlayWav(ctx, buf) + zap.S().Infof("TTS播放完成: %s", text) + } else { + zap.S().Errorln("AliTTS 请求异常: ", err) + } +} + func (tts *AliTTS) getToken() error { if tts.tokenResult.ExpireTime != 0 && time.Unix(tts.tokenResult.ExpireTime, 0).After(time.Now()) { return nil @@ -112,13 +129,22 @@ func (tts *AliTTS) Get(text string) (io.Reader, error) { case done := <-ch: { if !done { + zap.S().Errorln("TTS合成失败: done=false") + return ttsData.Data, errorsx.ThirdPartyErr + } + size := ttsData.Data.(*bytes.Buffer).Len() + zap.S().Debugf("TTS合成成功,数据大小: %d bytes", size) + if size == 0 { + zap.S().Errorln("TTS合成数据为空") return ttsData.Data, errorsx.ThirdPartyErr } return ttsData.Data, nil } case <-time.After(time.Duration(tts.Timeout) * time.Second): + zap.S().Errorln("TTS合成超时") return ttsData.Data, errorsx.DriverTimeoutErr case <-tts.ctx.Done(): + zap.S().Errorln("TTS合成被取消") return ttsData.Data, errorsx.DriverCancelErr } }