Skip to content
Closed
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
179 changes: 155 additions & 24 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type Decoder struct {
resampleBuffer []float32
resampleChannelIn [2][]float32
resampleChannelOut [2][]float32
resampleChannelOut16 [2][]int16
silkResampler [2]silkresample.Resampler
silkResamplerBandwidth Bandwidth
silkResamplerChannels int
Expand All @@ -45,6 +46,7 @@ type Decoder struct {
silkRedundancyFades []silkRedundancyFade
silkCeltAdditions []silkCeltAddition
floatBuffer []float32
int16Buffer []int16
sampleRate int
channels int
}
Expand Down Expand Up @@ -140,6 +142,29 @@ func (d *Decoder) resampleSilk(in, out []float32, channelCount int, bandwidth Ba
return nil
}

func (d *Decoder) resampleSilkToInt16(in []float32, out []int16, channelCount int, bandwidth Bandwidth) error {
if err := d.initSilkResampler(channelCount, bandwidth); err != nil {
return err
}

samplesPerChannel := len(in) / channelCount
resampledSamplesPerChannel := samplesPerChannel * d.sampleRate / bandwidth.SampleRate()
for channelIndex := range channelCount {
if err := d.resampleSilkChannelToInt16(
in,
out,
channelIndex,
channelCount,
samplesPerChannel,
resampledSamplesPerChannel,
); err != nil {
return err
}
}

return nil
}

func (d *Decoder) initSilkResampler(channelCount int, bandwidth Bandwidth) error {
if d.silkResamplerBandwidth != bandwidth {
for i := range d.silkResampler {
Expand All @@ -163,7 +188,6 @@ func (d *Decoder) resampleSilkChannel(
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],
Expand Down Expand Up @@ -191,6 +215,39 @@ func (d *Decoder) resampleSilkChannel(
return nil
}

func (d *Decoder) resampleSilkChannelToInt16(
in []float32,
out []int16,
channelIndex, channelCount, samplesPerChannel, resampledSamplesPerChannel int,
) error {
if channelCount == 1 {
return d.silkResampler[channelIndex].ResampleToInt16(
in[:samplesPerChannel],
out[:resampledSamplesPerChannel],
)
}

if cap(d.resampleChannelIn[channelIndex]) < samplesPerChannel {
d.resampleChannelIn[channelIndex] = make([]float32, samplesPerChannel)
}
if cap(d.resampleChannelOut16[channelIndex]) < resampledSamplesPerChannel {
d.resampleChannelOut16[channelIndex] = make([]int16, resampledSamplesPerChannel)
}
channelIn := d.resampleChannelIn[channelIndex][:samplesPerChannel]
channelOut := d.resampleChannelOut16[channelIndex][:resampledSamplesPerChannel]
for i := range samplesPerChannel {
channelIn[i] = in[(i*channelCount)+channelIndex]
}
if err := d.silkResampler[channelIndex].ResampleToInt16(channelIn, channelOut); err != nil {
return err
}
for i := range resampledSamplesPerChannel {
out[(i*channelCount)+channelIndex] = channelOut[i]
}

return nil
}

// resetModeState applies the decoder resets required by RFC 6716 Section 4.5.2
// before the first frame decoded in a new operating mode.
func (d *Decoder) resetModeState(mode configurationMode) {
Expand Down Expand Up @@ -1185,6 +1242,55 @@ func (d *Decoder) decodeToFloat32(
return samplesPerChannel, bandwidth, isStereo, nil
}

func (d *Decoder) decodeToInt16(
in []byte,
out []int16,
) (samplesPerChannel int, bandwidth Bandwidth, isStereo bool, err error) {
if d.sampleRate == 0 {
return 0, 0, false, errInvalidSampleRate
}
if d.channels == 0 {
return 0, 0, false, errInvalidChannelCount
}

bandwidth, decodedSampleRate, isStereo, sampleCount, decodedChannelCount, err := d.decode(in, d.silkBuffer)
if err != nil {
return 0, 0, false, err
}

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

if cap(d.floatBuffer) < len(out) {
d.floatBuffer = make([]float32, len(out))
}
d.floatBuffer = d.floatBuffer[:len(out)]
samplesPerChannel, err = d.finishDecodeToFloat32(
d.floatBuffer,
bandwidth,
decodedSampleRate,
sampleCount,
decodedChannelCount,
)
if err != nil {
return 0, 0, false, err
}
float32ToInt16(d.floatBuffer, out, samplesPerChannel*d.channels)

return samplesPerChannel, bandwidth, isStereo, nil
}

func (d *Decoder) finishDecodeToFloat32(
out []float32,
bandwidth Bandwidth,
Expand Down Expand Up @@ -1223,13 +1329,53 @@ func (d *Decoder) finishDecodeToFloat32(
return samplesPerChannel, nil
}

func (d *Decoder) finishDecodeToInt16(
out []int16,
bandwidth Bandwidth,
decodedSampleRate int,
sampleCount int,
decodedChannelCount int,
) (samplesPerChannel int, handled bool, 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 {
return 0, false, errOutBufferTooSmall
}
if d.needsResampleBuffer(decodedChannelCount) {
return samplesPerChannel, false, nil
}

requiredSamples := samplesPerChannel * decodedChannelCount
decodedMode := d.previousMode
switch {
case decodedMode == configurationModeSilkOnly &&
decodedSampleRate == bandwidth.SampleRate() &&
bandwidth != BandwidthFullband:
err = d.resampleSilkToInt16(d.silkBuffer[:sampleCount], out[:requiredSamples], decodedChannelCount, bandwidth)
case d.sampleRate == decodedSampleRate:
float32ToInt16(d.silkBuffer[:sampleCount], out, sampleCount)
default:
err = d.resampleSilkToInt16(d.silkBuffer[:sampleCount], out[:requiredSamples], decodedChannelCount, bandwidth)
}
if err != nil {
return 0, false, err
}

return samplesPerChannel, true, 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
}

Expand Down Expand Up @@ -1365,22 +1511,17 @@ func float32ToInt16(in []float32, out []int16, sampleCount int) {

// Decode decodes the Opus bitstream into S16LE PCM.
func (d *Decoder) Decode(in, out []byte) (bandwidth Bandwidth, isStereo bool, err error) {
if cap(d.floatBuffer) < len(out)/2 {
d.floatBuffer = make([]float32, len(out)/2)
if cap(d.int16Buffer) < len(out)/2 {
d.int16Buffer = make([]int16, len(out)/2)
}
d.floatBuffer = d.floatBuffer[:len(out)/2]
d.int16Buffer = d.int16Buffer[:len(out)/2]

sampleCount, bandwidth, isStereo, err := d.decodeToFloat32(in, d.floatBuffer)
sampleCount, bandwidth, isStereo, err := d.decodeToInt16(in, d.int16Buffer)
if err != nil {
return
}

err = bitdepth.ConvertFloat32LittleEndianToSigned16LittleEndian(
d.floatBuffer[:sampleCount*d.channels],
out,
d.channels,
1,
)
err = bitdepth.Signed16ToLittleEndian(d.int16Buffer[:sampleCount*d.channels], out)

return
}
Expand All @@ -1394,19 +1535,9 @@ func (d *Decoder) DecodeFloat32(in []byte, out []float32) (bandwidth Bandwidth,

// DecodeToInt16 decodes Opus data into signed 16-bit PCM and returns the sample count per channel.
func (d *Decoder) DecodeToInt16(in []byte, out []int16) (int, error) {
if cap(d.floatBuffer) < len(out) {
d.floatBuffer = make([]float32, len(out))
}
d.floatBuffer = d.floatBuffer[:len(out)]

sampleCount, _, _, err := d.decodeToFloat32(in, d.floatBuffer)
if err != nil {
return 0, err
}
sampleCount, _, _, err := d.decodeToInt16(in, out)

float32ToInt16(d.floatBuffer, out, sampleCount*d.channels)

return sampleCount, nil
return sampleCount, err
}

// DecodeToFloat32 decodes Opus data into float32 PCM and returns the sample count per channel.
Expand Down
34 changes: 34 additions & 0 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"sync"
"testing"

"github.com/pion/opus/internal/bitdepth"
"github.com/pion/opus/pkg/oggreader"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -171,6 +172,18 @@ func TestFinishDecodeToFloat32ClearsSilkRedundancyOnError(t *testing.T) {
assert.Empty(t, decoder.silkCeltAdditions)
}

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

_, _, err := decoder.finishDecodeToInt16(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 Expand Up @@ -205,6 +218,27 @@ func TestDecodeToInt16(t *testing.T) {
assert.Equal(t, 80, sampleCount)
}

func TestDecodeMatchesDecodeToInt16(t *testing.T) {
packet := []byte{byte(8<<3) | byte(frameCodeOneFrame)}

byteDecoder, err := NewDecoderWithOutput(48000, 1)
assert.NoError(t, err)
byteOut := make([]byte, 480*2)
_, _, err = byteDecoder.Decode(packet, byteOut)
assert.NoError(t, err)

int16Decoder, err := NewDecoderWithOutput(48000, 1)
assert.NoError(t, err)
int16Out := make([]int16, 480)
sampleCount, err := int16Decoder.DecodeToInt16(packet, int16Out)
assert.NoError(t, err)
assert.Equal(t, 480, sampleCount)

expectedByteOut := make([]byte, sampleCount*2)
assert.NoError(t, bitdepth.Signed16ToLittleEndian(int16Out[:sampleCount], expectedByteOut))
assert.Equal(t, expectedByteOut, byteOut[:len(expectedByteOut)])
}

func TestDecodeSilkFrameDurations(t *testing.T) {
for _, test := range []struct {
name string
Expand Down
17 changes: 17 additions & 0 deletions internal/bitdepth/bitdepth.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,20 @@ func ConvertFloat32LittleEndianToSigned16LittleEndian(

return nil
}

// Signed16ToLittleEndian converts signed 16-bit PCM to little-endian bytes.
func Signed16ToLittleEndian(in []int16, out []byte) error {
if len(in)*2 > len(out) {
return errOutBufferTooSmall
}

currIndex := 0
for _, sample := range in {
out[currIndex] = byte(sample & 0b11111111)
currIndex++
out[currIndex] = byte(uint16(sample) >> 8) // #nosec G115,G602 -- output length was checked above
currIndex++
}

return nil
}
12 changes: 12 additions & 0 deletions internal/bitdepth/bitdepth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,15 @@ func TestConvertFloat32LittleEndianToSigned16LittleEndianInvalidChannelCount(t *
func TestConvertFloat32LittleEndianToSigned16LittleEndianInvalidResampleCount(t *testing.T) {
assert.Error(t, ConvertFloat32LittleEndianToSigned16LittleEndian([]float32{0.3}, make([]byte, 2), 1, 0))
}

func TestSigned16ToLittleEndian(t *testing.T) {
in := []int16{0x2666, 0, 0x4666, 0x5c29, -1638}
out := make([]byte, len(in)*2)

assert.NoError(t, Signed16ToLittleEndian(in, out))
assert.Equal(t, []byte{0x66, 0x26, 0x00, 0x00, 0x66, 0x46, 0x29, 0x5c, 0x9a, 0xf9}, out)
}

func TestSigned16ToLittleEndianOutTooSmall(t *testing.T) {
assert.ErrorIs(t, errOutBufferTooSmall, Signed16ToLittleEndian([]int16{1}, make([]byte, 1)))
}
11 changes: 6 additions & 5 deletions internal/resample/silk/fixed.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ func silkRShiftRound(a32 int32, shift int) int32 {
}

func silkSAT16(a32 int32) int16 {
if a32 > math.MaxInt16 {
return math.MaxInt16
}
if a32 < math.MinInt16 {
if uint32(a32-math.MinInt16) > math.MaxUint16 { //nolint:gosec // G115
if a32 > math.MaxInt16 {
return math.MaxInt16
}

return math.MinInt16
}

return int16(a32)
return int16(a32) //nolint:gosec // G115
}
Loading
Loading