Files
game-driver/pkg/audio/play.go
mapleafgo b5f7c823c8
All checks were successful
ci/woodpecker/tag/woodpecker Pipeline was successful
debug(audio): 添加 Resampler 数据读取测试
为诊断 TTS 音频播放停滞问题,添加手动测试代码验证 Resampler 是否能正常读取音频数据:
- 在 speaker.Play() 前尝试从 Resampler 读取 10 个样本
- 打印读取状态和样本数据,验证数据流是否正常
- 重新创建 Resampler 确保测试不影响正常播放

此调试代码用于确认问题是在 Resampler/WAV 解码层,还是在 speaker/mixer 层

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 16:59:13 +08:00

118 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package audio
import (
"context"
"io"
"time"
"github.com/gopxl/beep/v2"
"github.com/gopxl/beep/v2/mp3"
"github.com/gopxl/beep/v2/speaker"
"github.com/gopxl/beep/v2/wav"
"go.uber.org/zap"
)
var DefaultSampleRate = beep.SampleRate(44100)
func init() {
err := speaker.Init(DefaultSampleRate, DefaultSampleRate.N(time.Second/10))
if err != nil {
panic("扬声器初始化异常: " + err.Error())
}
zap.S().Infoln("扬声器初始化完成")
}
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)
return
}
defer streamer.Close()
// 获取音频长度信息
totalSamples := streamer.Len()
zap.S().Debugf("WAV解码成功采样率: %d, 总样本数: %d, 预计时长: %.2f秒",
format.SampleRate, totalSamples, float64(totalSamples)/float64(format.SampleRate))
s := beep.Resample(4, format.SampleRate, DefaultSampleRate, streamer)
ctrl := &beep.Ctrl{Streamer: s}
// 测试 Streamer 是否可以正常读取数据
testSamples := make([][2]float64, 10)
n, ok := s.Stream(testSamples)
zap.S().Debugf("测试读取 Resampler: 读取 %d 样本, ok=%v, 数据=%v", n, ok, testSamples[:n])
// 重置 streamer
s = beep.Resample(4, format.SampleRate, DefaultSampleRate, streamer)
ctrl.Streamer = s
done := make(chan struct{})
speaker.Play(beep.Seq(ctrl, beep.Callback(func() {
zap.S().Debugln("音频播放完成")
close(done)
})))
zap.S().Debugln("等待音频播放完成...")
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
lastPos := 0
for {
select {
case <-done:
zap.S().Infoln("音频播放正常结束")
return
case <-c.Done():
zap.S().Debugf("音频播放被 context 取消: %v", c.Err())
speaker.Lock()
ctrl.Streamer = nil
speaker.Unlock()
return
case <-ticker.C:
// 获取当前播放位置
pos := streamer.Position()
if pos != lastPos {
progress := float64(pos) / float64(totalSamples) * 100
currentTime := float64(pos) / float64(format.SampleRate)
zap.S().Debugf("播放进度: %d/%d (%.1f%%), %.2f秒", pos, totalSamples, progress, currentTime)
lastPos = pos
} else {
zap.S().Debugf("播放停滞在位置: %d/%d, Streamer状态: %v",
pos, totalSamples, ctrl.Streamer != nil)
}
}
}
}
func PlayMP3(c context.Context, r io.ReadCloser) {
streamer, format, err := mp3.Decode(r)
if err != nil {
zap.S().Errorln("MP3解码失败: ", err)
return
}
defer streamer.Close()
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() {
close(done)
})))
for {
select {
case <-done:
return
case <-c.Done():
speaker.Lock()
ctrl.Streamer = nil
speaker.Unlock()
return
}
}
}