Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 125 additions & 30 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@ func (d *Decoder) Init(sampleRate, channels int) error {
d.silkResamplerChannels = 0
d.hybridSilkResampler = [2]silkresample.Resampler{}
d.hybridSilkChannels = 0
d.silkRedundancyFades = d.silkRedundancyFades[:0]
d.silkCeltAdditions = d.silkCeltAdditions[:0]
d.clearSilkRedundancyTransitions()
d.rangeFinal = 0
d.previousMode = 0
d.previousRedundancy = false
Expand Down Expand Up @@ -163,6 +162,14 @@ func (d *Decoder) resampleSilkChannel(
in, out []float32,
channelIndex, channelCount, samplesPerChannel, resampledSamplesPerChannel int,
) error {
if channelCount == 1 {
// Mono samples are already contiguous, so skip deinterleave/reinterleave scratch.
return d.silkResampler[channelIndex].Resample(
in[:samplesPerChannel],
out[:resampledSamplesPerChannel],
)
}

if cap(d.resampleChannelIn[channelIndex]) < samplesPerChannel {
d.resampleChannelIn[channelIndex] = make([]float32, samplesPerChannel)
}
Expand Down Expand Up @@ -548,9 +555,27 @@ func (d *Decoder) decode(
tocHeader := tableOfContentsHeader(in[0])
cfg := tocHeader.configuration()

encodedFrames, err := parsePacketFrames(in, tocHeader)
if err != nil {
return 0, 0, false, 0, 0, err
var encodedFrames [][]byte
if tocHeader.frameCode() == frameCodeOneFrame {
// [R2] Code 0 uses an implicit frame length for the whole payload, so it
// must not exceed the 1275-byte maximum.
if len(in[1:]) > maxOpusFrameSize {
return 0, 0, false, 0, 0, fmt.Errorf(
"%w: frame size %d exceeds %d",
errMalformedPacket,
len(in[1:]),
maxOpusFrameSize,
)
}
var singleFrame [1][]byte
singleFrame[0] = in[1:]
encodedFrames = singleFrame[:]
} else {
Comment thread
zshang-oai marked this conversation as resolved.
var err error
encodedFrames, err = parsePacketFrames(in, tocHeader)
if err != nil {
return 0, 0, false, 0, 0, err
}
}

switch cfg.mode() {
Expand Down Expand Up @@ -1029,8 +1054,7 @@ func (d *Decoder) decodeSilkFrames(
frameSamplesPerChannel := cfg.silkFrameSampleCount()
decodedChannelCount = silkOutputChannelCount(tocHeader.isStereo(), d.channels)
frameSampleCount := frameSamplesPerChannel * decodedChannelCount
d.silkRedundancyFades = d.silkRedundancyFades[:0]
d.silkCeltAdditions = d.silkCeltAdditions[:0]
d.clearSilkRedundancyTransitions()
requiredSamples := frameSampleCount * len(encodedFrames)
if cap(out) < requiredSamples {
d.silkBuffer = make([]float32, requiredSamples)
Expand Down Expand Up @@ -1147,37 +1171,108 @@ func (d *Decoder) decodeToFloat32(
return 0, 0, false, err
}

samplesPerChannel, err = d.finishDecodeToFloat32(
out,
bandwidth,
decodedSampleRate,
sampleCount,
decodedChannelCount,
)
if err != nil {
return 0, 0, false, err
}

return samplesPerChannel, bandwidth, isStereo, nil
}

func (d *Decoder) finishDecodeToFloat32(
out []float32,
bandwidth Bandwidth,
decodedSampleRate int,
sampleCount int,
decodedChannelCount int,
) (samplesPerChannel int, err error) {
defer func() {
if err != nil {
// Redundancy transitions belong to this packet only.
d.clearSilkRedundancyTransitions()
}
}()

samplesPerChannel = (sampleCount / decodedChannelCount) * d.sampleRate / decodedSampleRate
if len(out) < samplesPerChannel*d.channels {
Comment thread
zshang-oai marked this conversation as resolved.
return 0, errOutBufferTooSmall
}

requiredSamples := samplesPerChannel * decodedChannelCount
resampleOut, useResampleBuffer := d.prepareResampleOutput(out, requiredSamples, decodedChannelCount)
if err = d.writeDecodedOutput(
resampleOut,
bandwidth,
decodedSampleRate,
sampleCount,
decodedChannelCount,
); err != nil {
return 0, err
}
if useResampleBuffer {
d.applySilkRedundancyFades(decodedChannelCount)
d.copyResampledSamples(out, decodedChannelCount)
}

return samplesPerChannel, nil
}

func (d *Decoder) prepareResampleOutput(
out []float32,
requiredSamples int,
decodedChannelCount int,
) (resampleOut []float32, useResampleBuffer bool) {
if !d.needsResampleBuffer(decodedChannelCount) {
// Direct output is safe when no channel remap or redundancy mix runs afterward.
return out[:requiredSamples], false
}

if cap(d.resampleBuffer) < requiredSamples {
d.resampleBuffer = make([]float32, requiredSamples)
}
d.resampleBuffer = d.resampleBuffer[:requiredSamples]

return d.resampleBuffer, true
}

func (d *Decoder) needsResampleBuffer(decodedChannelCount int) bool {
return decodedChannelCount != d.channels ||
len(d.silkRedundancyFades) > 0 ||
len(d.silkCeltAdditions) > 0
}

func (d *Decoder) clearSilkRedundancyTransitions() {
d.silkRedundancyFades = d.silkRedundancyFades[:0]
d.silkCeltAdditions = d.silkCeltAdditions[:0]
}

func (d *Decoder) writeDecodedOutput(
out []float32,
bandwidth Bandwidth,
decodedSampleRate int,
sampleCount int,
decodedChannelCount int,
) error {
decodedMode := d.previousMode
switch {
case decodedMode == configurationModeSilkOnly &&
decodedSampleRate == bandwidth.SampleRate() &&
bandwidth != BandwidthFullband:
// The RFC SILK decoder resampler has delay even for same-rate copy paths.
if err = d.resampleSilk(d.silkBuffer[:sampleCount], d.resampleBuffer, decodedChannelCount, bandwidth); err != nil {
return 0, 0, false, err
}
return d.resampleSilk(d.silkBuffer[:sampleCount], out, decodedChannelCount, bandwidth)
case d.sampleRate == decodedSampleRate:
copy(d.resampleBuffer, d.silkBuffer[:sampleCount])
default:
if err = d.resampleSilk(d.silkBuffer[:sampleCount], d.resampleBuffer, decodedChannelCount, bandwidth); err != nil {
return 0, 0, false, err
}
}
d.applySilkRedundancyFades(decodedChannelCount)
copy(out, d.silkBuffer[:sampleCount])

if len(out) < samplesPerChannel*d.channels {
return 0, 0, false, errOutBufferTooSmall
return nil
default:
return d.resampleSilk(d.silkBuffer[:sampleCount], out, decodedChannelCount, bandwidth)
}

d.copyResampledSamples(out, decodedChannelCount)

return samplesPerChannel, bandwidth, isStereo, nil
}

// applySilkRedundancyFades applies the leading/trailing 2.5 ms cross-laps from
Expand All @@ -1187,8 +1282,7 @@ func (d *Decoder) decodeToFloat32(
func (d *Decoder) applySilkRedundancyFades(channelCount int) {
fades := d.silkRedundancyFades
additions := d.silkCeltAdditions
d.silkRedundancyFades = d.silkRedundancyFades[:0]
d.silkCeltAdditions = d.silkCeltAdditions[:0]
d.clearSilkRedundancyTransitions()
fadeSampleCount := celtFadeSampleCount(d.sampleRate)
for _, addition := range additions {
if addition.channelCount != channelCount {
Expand Down Expand Up @@ -1243,14 +1337,15 @@ func (d *Decoder) applySilkRedundancyFades(channelCount int) {
}

func (d *Decoder) copyResampledSamples(out []float32, channelCount int) {
if channelCount == d.channels {
copy(out, d.resampleBuffer)

return
}

outIndex := 0
for i := 0; i < len(d.resampleBuffer); i += channelCount {
switch {
case channelCount == d.channels:
for c := 0; c < d.channels; c++ {
out[outIndex] = d.resampleBuffer[i+c]
outIndex++
}
case channelCount == 1 && d.channels == 2:
out[outIndex] = d.resampleBuffer[i]
out[outIndex+1] = d.resampleBuffer[i]
Expand Down
12 changes: 12 additions & 0 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ func TestDecodeToFloat32(t *testing.T) {
assert.ErrorIs(t, err, errOutBufferTooSmall)
}

func TestFinishDecodeToFloat32ClearsSilkRedundancyOnError(t *testing.T) {
decoder := NewDecoder()
decoder.silkRedundancyFades = append(decoder.silkRedundancyFades, silkRedundancyFade{})
decoder.silkCeltAdditions = append(decoder.silkCeltAdditions, silkCeltAddition{})

_, err := decoder.finishDecodeToFloat32(nil, BandwidthWideband, 16000, 160, 1)

assert.ErrorIs(t, err, errOutBufferTooSmall)
assert.Empty(t, decoder.silkRedundancyFades)
assert.Empty(t, decoder.silkCeltAdditions)
}

func TestDecodeCeltAtBandwidthSampleRateSkipsSilkResampler(t *testing.T) {
decoder, err := NewDecoderWithOutput(8000, 1)
assert.NoError(t, err)
Expand Down
22 changes: 20 additions & 2 deletions internal/bitdepth/bitdepth.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ var (
// Float32ToSigned16 quantizes a float32 PCM sample to signed 16-bit PCM.
func Float32ToSigned16(sample float32) int16 {
sample64 := math.Round(float64(sample * 32768))
sample64 = math.Max(sample64, -32768)
sample64 = math.Min(sample64, 32767)
if sample64 < -32768 {
sample64 = -32768
} else if sample64 > 32767 {
sample64 = 32767
}

return int16(sample64)
}
Expand All @@ -45,6 +48,21 @@ func ConvertFloat32LittleEndianToSigned16LittleEndian(
return errOutBufferTooSmall
}

if resampleCount == 1 {
currIndex := 0
for _, sample := range in {
res := Float32ToSigned16(sample)

out[currIndex] = byte(res & 0b11111111)
currIndex++

out[currIndex] = byte(uint16(res) >> 8) // #nosec G115,G602 -- output length was checked above
currIndex++
}

return nil
}

currIndex := 0
for i := 0; i < len(in); i += channelCount {
for j := resampleCount; j > 0; j-- {
Expand Down
Loading
Loading