🎉 live server seems to be working now
This commit is contained in:
84
third_party/sendspin-go/internal/server/resampler.go
vendored
Normal file
84
third_party/sendspin-go/internal/server/resampler.go
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user