package audio import ( "io" "sync" "github.com/zeozeozeo/gomplerate" ) const ( resampleBufferSize = 8192 // 重采样缓冲区大小(int16 样本数) ) var ( bufferPool = sync.Pool{ New: func() any { return make([]byte, resampleBufferSize*2) // int16 = 2 bytes }, } ) // resamplingReader 包装 io.Reader 并提供音频重采样 // 使用 io.Reader 接口实现流式重采样 type resamplingReader struct { source io.Reader resampler *gomplerate.Resampler inputBuf []byte // 原始数据缓冲区 outputBuf []byte // 重采样后的输出缓冲区 eof bool } // newResamplingReader 创建重采样 reader // 参数: // - src: 源数据 reader // - sourceRate: 源采样率(如 16000) // - targetRate: 目标采样率(如 44100) // - channels: 声道数(1=单声道, 2=立体声) func newResamplingReader(src io.Reader, sourceRate, targetRate, channels int) (io.Reader, error) { resampler, err := gomplerate.NewResampler(channels, sourceRate, targetRate) if err != nil { return nil, err } return &resamplingReader{ source: src, resampler: resampler, inputBuf: make([]byte, 0, resampleBufferSize*2), outputBuf: make([]byte, 0, resampleBufferSize*2), }, nil } func (r *resamplingReader) Read(p []byte) (n int, err error) { // 循环读取直到填满 p 或遇到错误 for len(r.outputBuf) < len(p) { if r.eof { break } // 读取源数据到输入缓冲区 if err := r.readSource(); err != nil { if err == io.EOF { r.eof = true } else { return n, err } } // 如果没有数据可处理,退出 if len(r.inputBuf) == 0 { break } // 将字节转换为 int16 并重采样 int16Data := bytesToInt16(r.inputBuf) resampled := r.resampler.ResampleInt16(int16Data) // 将重采样后的数据转回字节并追加到输出缓冲区 r.outputBuf = append(r.outputBuf, int16ToBytes(resampled)...) // 清空输入缓冲区(所有数据已处理) r.inputBuf = r.inputBuf[:0] } // 从输出缓冲区复制数据到 p n = copy(p, r.outputBuf) // 移除已读取的数据 if n < len(r.outputBuf) { r.outputBuf = r.outputBuf[n:] } else { r.outputBuf = r.outputBuf[:0] } // 如果没有更多数据,返回 EOF if n == 0 && r.eof && len(r.outputBuf) == 0 { return 0, io.EOF } return n, nil } // readSource 从源读取数据到输入缓冲区 func (r *resamplingReader) readSource() error { const readSize = 4096 // 从池中借用临时缓冲区 tempBuf := bufferPool.Get().([]byte) defer bufferPool.Put(tempBuf) // 读取数据 rn, err := r.source.Read(tempBuf[:readSize]) if rn > 0 { // 追加到输入缓冲区 r.inputBuf = append(r.inputBuf, tempBuf[:rn]...) } return err } // bytesToInt16 将字节切片转换为 int16 切片(小端序) func bytesToInt16(b []byte) []int16 { result := make([]int16, len(b)/2) for i := range result { result[i] = int16(b[i*2]) | int16(b[i*2+1])<<8 } return result } // int16ToBytes 将 int16 切片转换为字节切片(小端序) func int16ToBytes(i []int16) []byte { result := make([]byte, len(i)*2) for n, v := range i { result[n*2] = byte(v) result[n*2+1] = byte(v >> 8) } return result } // needsResampling 检查音频是否需要重采样到 UniversalSampleRate func needsResampling(sourceRate int) bool { return sourceRate != UniversalSampleRate }