Files
-rpi-sendspin/third_party/sendspin-go/internal/server/resampler.go

85 lines
2.1 KiB
Go

// ABOUTME: Simple linear resampler for converting audio sample rates
// ABOUTME: Used to convert MP3 files to 48kHz for Opus encoding
package server
// Resampler performs linear interpolation to convert between sample rates
type Resampler struct {
inputRate int
outputRate int
channels int
ratio float64
position float64
lastSample []int32 // one sample per channel
}
func NewResampler(inputRate, outputRate, channels int) *Resampler {
return &Resampler{
inputRate: inputRate,
outputRate: outputRate,
channels: channels,
ratio: float64(inputRate) / float64(outputRate),
position: 0.0,
lastSample: make([]int32, channels),
}
}
// Resample converts interleaved input samples at inputRate to outputRate via linear interpolation.
func (r *Resampler) Resample(input []int32, output []int32) int {
if len(input) == 0 {
return 0
}
inputFrames := len(input) / r.channels
outputFrames := len(output) / r.channels
outIdx := 0
for outIdx < outputFrames {
// Calculate which input frame we need
inputPos := r.position
inputIdx := int(inputPos)
// If we've consumed all input, stop
if inputIdx >= inputFrames-1 {
break
}
frac := inputPos - float64(inputIdx)
for ch := 0; ch < r.channels; ch++ {
sample1 := input[inputIdx*r.channels+ch]
sample2 := input[(inputIdx+1)*r.channels+ch]
interpolated := float64(sample1)*(1.0-frac) + float64(sample2)*frac
output[outIdx*r.channels+ch] = int32(interpolated)
}
outIdx++
r.position += r.ratio
}
// Reset position for next chunk, keeping fractional part
r.position -= float64(int(r.position))
return outIdx * r.channels
}
func (r *Resampler) Reset() {
r.position = 0.0
for i := range r.lastSample {
r.lastSample[i] = 0
}
}
func (r *Resampler) OutputSamplesNeeded(inputSamples int) int {
inputFrames := inputSamples / r.channels
outputFrames := int(float64(inputFrames) / r.ratio)
return outputFrames * r.channels
}
func (r *Resampler) InputSamplesNeeded(outputSamples int) int {
outputFrames := outputSamples / r.channels
inputFrames := int(float64(outputFrames) * r.ratio)
return inputFrames * r.channels
}