package audio import ( "io" resampling "github.com/tphakala/go-audio-resampler" "go.uber.org/zap" ) // minProcessSamples 是 FIR 滤波器产生可靠输出所需的最小输入样本数 const minProcessSamples = 64 // needsResampling 检查是否需要重采样 func needsResampling(sourceRate int) bool { return sourceRate != UniversalSampleRate } // sincResampler 基于 go-audio-resampler 的高质量重采样器 // 使用 Windowed Sinc + Polyphase FIR 算法,专业级音质 type sincResampler struct { decoder io.Reader resampler resampling.Resampler inputBuf []float64 // 输入缓冲区:int16→float64 转换后暂存 outputBuf []float64 // 输出缓冲区:Process/Flush 产出但未消费的样本 inputBytes []byte // 复用的字节读取缓冲区 flushed bool // 是否已完成 Flush eof bool // 上游是否已返回 EOF } // newSincResampler 创建高质量 Sinc 重采样器 // 使用场景:大广场音效、高保真音乐 func newSincResampler(src io.Reader, inRate, outRate, channels int) io.Reader { if inRate == outRate { return src } config := &resampling.Config{ InputRate: float64(inRate), OutputRate: float64(outRate), Channels: channels, Quality: resampling.QualitySpec{ Preset: resampling.QualityVeryHigh, }, } r, err := resampling.New(config) if err != nil { zap.S().Warnf("Sinc 重采样器创建失败,降级为透传: %v", err) return src } return &sincResampler{ decoder: src, resampler: r, inputBuf: make([]float64, 0, 4096), outputBuf: make([]float64, 0, 4096), inputBytes: make([]byte, 1024), } } func (r *sincResampler) Read(p []byte) (int, error) { if len(p) < 2 { return 0, io.ErrShortBuffer } maxSamples := len(p) / 2 // 主循环:直到有足够输出数据或 EOF for len(r.outputBuf) < maxSamples { // 阶段1:从上游读取数据,累积到 inputBuf for len(r.inputBuf) < minProcessSamples && !r.eof { nn, readErr := r.decoder.Read(r.inputBytes) if readErr != nil && readErr != io.EOF { return 0, readErr } if readErr == io.EOF || nn == 0 { r.eof = true break } sampleCount := nn / 2 for i := range sampleCount { sample := int16(r.inputBytes[i*2]) | int16(r.inputBytes[i*2+1])<<8 r.inputBuf = append(r.inputBuf, float64(sample)/32768.0) } } // 阶段2:处理输入数据 if len(r.inputBuf) > 0 { output, err := r.resampler.Process(r.inputBuf) if err != nil { return 0, err } r.inputBuf = r.inputBuf[:0] if len(output) > 0 { r.outputBuf = append(r.outputBuf, output...) } continue } // 阶段3:EOF 且 inputBuf 为空,调用 Flush 获取尾部残留 if r.eof && !r.flushed { r.flushed = true flushed, err := r.resampler.Flush() if err != nil { return 0, err } if len(flushed) > 0 { r.outputBuf = append(r.outputBuf, flushed...) } continue } // 无更多数据可获取 break } if len(r.outputBuf) == 0 { return 0, io.EOF } // 写入输出 n := min(len(r.outputBuf), maxSamples) writeFloat64ToLE16(p, r.outputBuf[:n]) if n < len(r.outputBuf) { r.outputBuf = r.outputBuf[n:] } else { r.outputBuf = r.outputBuf[:0] } return n * 2, nil } // writeFloat64ToLE16 将 float64 样本转换为 int16 LE 写入 buf func writeFloat64ToLE16(buf []byte, samples []float64) { for i, s := range samples { if s > 1.0 { s = 1.0 } else if s < -1.0 { s = -1.0 } v := int32(s * 32768.0) if v > 32767 { v = 32767 } buf[i*2] = byte(v) buf[i*2+1] = byte(v >> 8) } }