feat(audio): 添加音频重采样支持,修复播放速度问题
问题: - TTS 返回 16000 Hz 音频,但 Context 使用 44100 Hz - 播放速度快 2.75 倍(44100/16000) - 不同采样率的音频播放速度不正确 解决方案: - 集成 gomplerate 库(纯 Go,零依赖) - 自动检测音频采样率并重采样到 44100 Hz - 支持任意采样率的音频文件正常播放 技术实现: - resampler.go: 封装 gomplerate,实现流式重采样 - play.go: WAV/MP3 播放自动重采样 - loop.go: BGM 循环播放支持重采样 测试: - 所有单元测试通过(6/6) - 支持采样率自动转换(如 16000 Hz → 44100 Hz) 依赖: - github.com/zeozeozeo/gomplerate v0.0.0
This commit is contained in:
@@ -32,29 +32,43 @@ func PlayWav(ctx context.Context, r io.ReadCloser) error {
|
||||
|
||||
// 获取音频格式信息
|
||||
format, err := dec.Format()
|
||||
if err == nil {
|
||||
duration, _ := dec.Duration()
|
||||
zap.S().Debugf("WAV 格式: %d ch, %d Hz, %d bits, 时长: %v",
|
||||
format.NumChannels, format.SampleRate, format.BitsPerSample, duration)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取 WAV 格式失败: %w", err)
|
||||
}
|
||||
|
||||
player := otoCtx.NewPlayer(dec)
|
||||
duration, _ := dec.Duration()
|
||||
sourceRate := int(format.SampleRate)
|
||||
targetRate := UniversalSampleRate
|
||||
channels := int(format.NumChannels)
|
||||
|
||||
zap.S().Infof("WAV 音频: %d ch, %d Hz → %d Hz, 时长: %v",
|
||||
channels, sourceRate, targetRate, duration)
|
||||
|
||||
// 需要重采样
|
||||
var reader io.Reader = dec
|
||||
if needsResample(sourceRate, targetRate) {
|
||||
zap.S().Infof("重采样: %d Hz → %d Hz", sourceRate, targetRate)
|
||||
resampleReader, err := newResamplingReader(dec, sourceRate, targetRate, channels)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建重采样器失败: %w", err)
|
||||
}
|
||||
reader = resampleReader
|
||||
}
|
||||
|
||||
player := otoCtx.NewPlayer(reader)
|
||||
defer player.Close()
|
||||
|
||||
player.Play()
|
||||
|
||||
// 等待播放完成 - 确保 Play() 调用后数据都被播放
|
||||
// 等待播放完成
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
// 先确保播放器开始播放
|
||||
for !player.IsPlaying() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// 等待播放结束(播放完所有数据)
|
||||
for player.IsPlaying() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// 额外等待 200ms 确保缓冲区数据完全播放
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
close(done)
|
||||
}()
|
||||
@@ -82,26 +96,39 @@ func PlayMP3(ctx context.Context, r io.ReadCloser) error {
|
||||
defer r.Close()
|
||||
|
||||
// MP3 解码器信息
|
||||
zap.S().Debugf("MP3 采样率: %d Hz, 时长: %d samples",
|
||||
dec.SampleRate(), dec.Length())
|
||||
sampleRate := int(dec.SampleRate())
|
||||
sampleCount := dec.Length()
|
||||
targetRate := UniversalSampleRate
|
||||
channels := 2 // MP3 通常是立体声
|
||||
duration := time.Duration(float64(sampleCount)/float64(sampleRate)*1000) * time.Millisecond
|
||||
|
||||
player := otoCtx.NewPlayer(dec)
|
||||
zap.S().Infof("MP3 音频: %d Hz → %d Hz, 时长约: %v", sampleRate, targetRate, duration)
|
||||
|
||||
// 需要重采样
|
||||
var reader io.Reader = dec
|
||||
if needsResample(sampleRate, targetRate) {
|
||||
zap.S().Infof("重采样: %d Hz → %d Hz", sampleRate, targetRate)
|
||||
resampleReader, err := newResamplingReader(dec, sampleRate, targetRate, channels)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建重采样器失败: %w", err)
|
||||
}
|
||||
reader = resampleReader
|
||||
}
|
||||
|
||||
player := otoCtx.NewPlayer(reader)
|
||||
defer player.Close()
|
||||
|
||||
player.Play()
|
||||
|
||||
// 等待播放完成 - 确保 Play() 调用后数据都被播放
|
||||
// 等待播放完成
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
// 先确保播放器开始播放
|
||||
for !player.IsPlaying() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// 等待播放结束(播放完所有数据)
|
||||
for player.IsPlaying() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// 额外等待 200ms 确保缓冲区数据完全播放
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
Reference in New Issue
Block a user