From 25a1bf5a1661a1af7fae3bd2bfb8e3cb8e6619de Mon Sep 17 00:00:00 2001 From: Lukas Herman Date: Sat, 5 Sep 2020 18:00:57 -0700 Subject: [PATCH] Convert underlying wave buffer to be uint8 This follows image package from the standard library. By having a homogenous data type for storing the samples, it makes easier to manipulate the raw data in a generic way. --- go.mod | 2 +- go.sum | 4 +- pkg/codec/opus/opus.go | 4 +- pkg/io/audio/buffer.go | 8 +- pkg/io/audio/buffer_test.go | 106 ++++++++++++++------- pkg/io/audio/mixer_test.go | 40 ++++---- pkg/wave/decoder_test.go | 172 +++++++++++++++-------------------- pkg/wave/float32.go | 61 ++++++++++--- pkg/wave/float32_test.go | 85 ++++++++++------- pkg/wave/int16.go | 45 ++++++--- pkg/wave/int16_test.go | 54 +++++------ pkg/wave/mixer/mixer_test.go | 18 ++-- 12 files changed, 345 insertions(+), 254 deletions(-) diff --git a/go.mod b/go.mod index e03ff0b1..2dbabf15 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.13 require ( github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539 github.com/jfreymuth/pulse v0.0.0-20200817093420-a82ccdb5e8aa - github.com/lherman-cs/opus v0.0.0-20200223204610-6a4b98199ea4 + github.com/lherman-cs/opus v0.0.0-20200925064139-8edf1852fd1f github.com/pion/webrtc/v2 v2.2.26 github.com/satori/go.uuid v1.2.0 golang.org/x/image v0.0.0-20200801110659-972c09e46d76 diff --git a/go.sum b/go.sum index 6637a117..109b0aec 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lherman-cs/opus v0.0.0-20200223204610-6a4b98199ea4 h1:2ydMA2KbxRkYmIw3R8Me8dn90bejxBR4MKYXJ5THK3I= -github.com/lherman-cs/opus v0.0.0-20200223204610-6a4b98199ea4/go.mod h1:v9KQvlDYMuvlwniumBVMlrB0VHQvyTgxNvaXjPmTmps= +github.com/lherman-cs/opus v0.0.0-20200925064139-8edf1852fd1f h1:xZKyjUoki95rRDQl3mDf20j2WZ+jZaFVzPZO72Jdi4A= +github.com/lherman-cs/opus v0.0.0-20200925064139-8edf1852fd1f/go.mod h1:v9KQvlDYMuvlwniumBVMlrB0VHQvyTgxNvaXjPmTmps= github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9 h1:tbuodUh2vuhOVZAdW3NEUvosFHUMJwUNl7jk/VSEiwc= github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA= diff --git a/pkg/codec/opus/opus.go b/pkg/codec/opus/opus.go index c6e360dd..64dbbc2d 100644 --- a/pkg/codec/opus/opus.go +++ b/pkg/codec/opus/opus.go @@ -80,13 +80,13 @@ func (e *encoder) Read(p []byte) (int, error) { switch b := buff.(type) { case *wave.Int16Interleaved: - n, err := e.engine.Encode(b.Data, p) + n, err := e.engine.EncodeBytes(b.Data, p, false) if err != nil { return n, err } return n, nil case *wave.Float32Interleaved: - n, err := e.engine.EncodeFloat32(b.Data, p) + n, err := e.engine.EncodeBytes(b.Data, p, true) if err != nil { return n, err } diff --git a/pkg/io/audio/buffer.go b/pkg/io/audio/buffer.go index 0df6d8f9..1be51b00 100644 --- a/pkg/io/audio/buffer.go +++ b/pkg/io/audio/buffer.go @@ -66,8 +66,8 @@ func NewBuffer(nSamples int) TransformFunc { case *wave.Int16Interleaved: ibCopy := *ib ibCopy.Size.Len = nSamples - n := nSamples * ib.Size.Channels - ibCopy.Data = make([]int16, n) + n := nSamples * ib.Size.Channels * 2 + ibCopy.Data = make([]uint8, n) copy(ibCopy.Data, ib.Data) ib.Data = ib.Data[n:] ib.Size.Len -= nSamples @@ -76,8 +76,8 @@ func NewBuffer(nSamples int) TransformFunc { case *wave.Float32Interleaved: ibCopy := *ib ibCopy.Size.Len = nSamples - n := nSamples * ib.Size.Channels - ibCopy.Data = make([]float32, n) + n := nSamples * ib.Size.Channels * 4 + ibCopy.Data = make([]uint8, n) copy(ibCopy.Data, ib.Data) ib.Data = ib.Data[n:] ib.Size.Len -= nSamples diff --git a/pkg/io/audio/buffer_test.go b/pkg/io/audio/buffer_test.go index 9f4b5114..bd448c55 100644 --- a/pkg/io/audio/buffer_test.go +++ b/pkg/io/audio/buffer_test.go @@ -9,41 +9,83 @@ import ( ) func TestBuffer(t *testing.T) { + input1 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 1, Channels: 2, SamplingRate: 1234}) + input1.SetInt16(0, 0, 1) + input1.SetInt16(0, 1, 2) + + input2 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}) + input2.SetInt16(0, 0, 3) + input2.SetInt16(0, 1, 4) + input2.SetInt16(1, 0, 5) + input2.SetInt16(1, 1, 6) + input2.SetInt16(2, 0, 7) + input2.SetInt16(2, 1, 8) + + input3 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 2, Channels: 2, SamplingRate: 1234}) + input3.SetInt16(0, 0, 9) + input3.SetInt16(0, 1, 10) + input3.SetInt16(1, 0, 11) + input3.SetInt16(1, 1, 12) + + input4 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 7, Channels: 2, SamplingRate: 1234}) + input4.SetInt16(0, 0, 13) + input4.SetInt16(0, 1, 14) + input4.SetInt16(1, 0, 15) + input4.SetInt16(1, 1, 16) + input4.SetInt16(2, 0, 17) + input4.SetInt16(2, 1, 18) + input4.SetInt16(3, 0, 19) + input4.SetInt16(3, 1, 20) + input4.SetInt16(4, 0, 21) + input4.SetInt16(4, 1, 22) + input4.SetInt16(5, 0, 23) + input4.SetInt16(5, 1, 24) + input4.SetInt16(6, 0, 25) + input4.SetInt16(6, 1, 26) + + expected1 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}) + expected1.SetInt16(0, 0, 1) + expected1.SetInt16(0, 1, 2) + expected1.SetInt16(1, 0, 3) + expected1.SetInt16(1, 1, 4) + expected1.SetInt16(2, 0, 5) + expected1.SetInt16(2, 1, 6) + + expected2 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}) + expected2.SetInt16(0, 0, 7) + expected2.SetInt16(0, 1, 8) + expected2.SetInt16(1, 0, 9) + expected2.SetInt16(1, 1, 10) + expected2.SetInt16(2, 0, 11) + expected2.SetInt16(2, 1, 12) + + expected3 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}) + expected3.SetInt16(0, 0, 13) + expected3.SetInt16(0, 1, 14) + expected3.SetInt16(1, 0, 15) + expected3.SetInt16(1, 1, 16) + expected3.SetInt16(2, 0, 17) + expected3.SetInt16(2, 1, 18) + + expected4 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}) + expected4.SetInt16(0, 0, 19) + expected4.SetInt16(0, 1, 20) + expected4.SetInt16(1, 0, 21) + expected4.SetInt16(1, 1, 22) + expected4.SetInt16(2, 0, 23) + expected4.SetInt16(2, 1, 24) + input := []wave.Audio{ - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 1, Channels: 2, SamplingRate: 1234}, - Data: []int16{1, 2}, - }, - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}, - Data: []int16{3, 4, 5, 6, 7, 8}, - }, - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 2, Channels: 2, SamplingRate: 1234}, - Data: []int16{9, 10, 11, 12}, - }, - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 7, Channels: 2, SamplingRate: 1234}, - Data: []int16{13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, - }, + input1, + input2, + input3, + input4, } expected := []wave.Audio{ - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}, - Data: []int16{1, 2, 3, 4, 5, 6}, - }, - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}, - Data: []int16{7, 8, 9, 10, 11, 12}, - }, - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}, - Data: []int16{13, 14, 15, 16, 17, 18}, - }, - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}, - Data: []int16{19, 20, 21, 22, 23, 24}, - }, + expected1, + expected2, + expected3, + expected4, } trans := NewBuffer(3) diff --git a/pkg/io/audio/mixer_test.go b/pkg/io/audio/mixer_test.go index a31ace92..48a147df 100644 --- a/pkg/io/audio/mixer_test.go +++ b/pkg/io/audio/mixer_test.go @@ -10,25 +10,33 @@ import ( ) func TestMixer(t *testing.T) { + input1 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 1, Channels: 2, SamplingRate: 1234}) + input1.SetInt16(0, 0, 1) + input1.SetInt16(0, 1, 3) + + input2 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}) + input2.SetInt16(0, 0, 2) + input2.SetInt16(0, 1, 4) + input2.SetInt16(1, 0, 3) + input2.SetInt16(1, 1, 5) + input2.SetInt16(2, 0, 4) + input2.SetInt16(2, 1, 6) + + expected1 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 1, Channels: 1, SamplingRate: 1234}) + expected1.SetInt16(0, 0, 2) + + expected2 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 1, SamplingRate: 1234}) + expected2.SetInt16(0, 0, 3) + expected2.SetInt16(1, 0, 4) + expected2.SetInt16(2, 0, 5) + input := []wave.Audio{ - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 1, Channels: 2, SamplingRate: 1234}, - Data: []int16{1, 3}, - }, - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234}, - Data: []int16{2, 4, 3, 5, 4, 6}, - }, + input1, + input2, } expected := []wave.Audio{ - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 1, Channels: 1, SamplingRate: 1234}, - Data: []int16{2}, - }, - &wave.Int16Interleaved{ - Size: wave.ChunkInfo{Len: 3, Channels: 1, SamplingRate: 1234}, - Data: []int16{3, 4, 5}, - }, + expected1, + expected2, } trans := NewChannelMixer(1, &mixer.MonoMixer{}) diff --git a/pkg/wave/decoder_test.go b/pkg/wave/decoder_test.go index 413d2f5c..5b4d0ef4 100644 --- a/pkg/wave/decoder_test.go +++ b/pkg/wave/decoder_test.go @@ -134,18 +134,15 @@ func TestDecodeInt16Interleaved(t *testing.T) { decoder, _ := newInt16InterleavedDecoder() t.Run("BigEndian", func(t *testing.T) { - expected := &Int16Interleaved{ - Data: []int16{ - int16(binary.BigEndian.Uint16([]byte{0x01, 0x02})), - int16(binary.BigEndian.Uint16([]byte{0x03, 0x04})), - int16(binary.BigEndian.Uint16([]byte{0x05, 0x06})), - int16(binary.BigEndian.Uint16([]byte{0x07, 0x08})), - }, - Size: ChunkInfo{ - Len: 2, - Channels: 2, - }, - } + expected := NewInt16Interleaved(ChunkInfo{ + Len: 2, + Channels: 2, + }) + + expected.SetInt16(0, 0, Int16Sample(binary.BigEndian.Uint16([]byte{0x01, 0x02}))) + expected.SetInt16(0, 1, Int16Sample(binary.BigEndian.Uint16([]byte{0x03, 0x04}))) + expected.SetInt16(1, 0, Int16Sample(binary.BigEndian.Uint16([]byte{0x05, 0x06}))) + expected.SetInt16(1, 1, Int16Sample(binary.BigEndian.Uint16([]byte{0x07, 0x08}))) actual, err := decoder.Decode(binary.BigEndian, raw, 2) if err != nil { t.Fatal(err) @@ -157,18 +154,15 @@ func TestDecodeInt16Interleaved(t *testing.T) { }) t.Run("LittleEndian", func(t *testing.T) { - expected := &Int16Interleaved{ - Data: []int16{ - int16(binary.LittleEndian.Uint16([]byte{0x01, 0x02})), - int16(binary.LittleEndian.Uint16([]byte{0x03, 0x04})), - int16(binary.LittleEndian.Uint16([]byte{0x05, 0x06})), - int16(binary.LittleEndian.Uint16([]byte{0x07, 0x08})), - }, - Size: ChunkInfo{ - Len: 2, - Channels: 2, - }, - } + expected := NewInt16Interleaved(ChunkInfo{ + Len: 2, + Channels: 2, + }) + + expected.SetInt16(0, 0, Int16Sample(binary.LittleEndian.Uint16([]byte{0x02, 0x01}))) + expected.SetInt16(0, 1, Int16Sample(binary.LittleEndian.Uint16([]byte{0x04, 0x03}))) + expected.SetInt16(1, 0, Int16Sample(binary.LittleEndian.Uint16([]byte{0x06, 0x05}))) + expected.SetInt16(1, 1, Int16Sample(binary.LittleEndian.Uint16([]byte{0x08, 0x07}))) actual, err := decoder.Decode(binary.LittleEndian, raw, 2) if err != nil { t.Fatal(err) @@ -190,16 +184,15 @@ func TestDecodeInt16NonInterleaved(t *testing.T) { decoder, _ := newInt16NonInterleavedDecoder() t.Run("BigEndian", func(t *testing.T) { - expected := &Int16NonInterleaved{ - Data: [][]int16{ - {int16(binary.BigEndian.Uint16([]byte{0x01, 0x02})), int16(binary.BigEndian.Uint16([]byte{0x03, 0x04}))}, - {int16(binary.BigEndian.Uint16([]byte{0x05, 0x06})), int16(binary.BigEndian.Uint16([]byte{0x07, 0x08}))}, - }, - Size: ChunkInfo{ - Len: 2, - Channels: 2, - }, - } + expected := NewInt16NonInterleaved(ChunkInfo{ + Len: 2, + Channels: 2, + }) + + expected.SetInt16(0, 0, Int16Sample(binary.BigEndian.Uint16([]byte{0x01, 0x02}))) + expected.SetInt16(0, 1, Int16Sample(binary.BigEndian.Uint16([]byte{0x05, 0x06}))) + expected.SetInt16(1, 0, Int16Sample(binary.BigEndian.Uint16([]byte{0x03, 0x04}))) + expected.SetInt16(1, 1, Int16Sample(binary.BigEndian.Uint16([]byte{0x07, 0x08}))) actual, err := decoder.Decode(binary.BigEndian, raw, 2) if err != nil { t.Fatal(err) @@ -211,16 +204,15 @@ func TestDecodeInt16NonInterleaved(t *testing.T) { }) t.Run("LittleEndian", func(t *testing.T) { - expected := &Int16NonInterleaved{ - Data: [][]int16{ - {int16(binary.LittleEndian.Uint16([]byte{0x01, 0x02})), int16(binary.LittleEndian.Uint16([]byte{0x03, 0x04}))}, - {int16(binary.LittleEndian.Uint16([]byte{0x05, 0x06})), int16(binary.LittleEndian.Uint16([]byte{0x07, 0x08}))}, - }, - Size: ChunkInfo{ - Len: 2, - Channels: 2, - }, - } + expected := NewInt16NonInterleaved(ChunkInfo{ + Len: 2, + Channels: 2, + }) + + expected.SetInt16(0, 0, Int16Sample(binary.LittleEndian.Uint16([]byte{0x02, 0x01}))) + expected.SetInt16(0, 1, Int16Sample(binary.LittleEndian.Uint16([]byte{0x06, 0x05}))) + expected.SetInt16(1, 0, Int16Sample(binary.LittleEndian.Uint16([]byte{0x04, 0x03}))) + expected.SetInt16(1, 1, Int16Sample(binary.LittleEndian.Uint16([]byte{0x08, 0x07}))) actual, err := decoder.Decode(binary.LittleEndian, raw, 2) if err != nil { t.Fatal(err) @@ -242,18 +234,15 @@ func TestDecodeFloat32Interleaved(t *testing.T) { decoder, _ := newFloat32InterleavedDecoder() t.Run("BigEndian", func(t *testing.T) { - expected := &Float32Interleaved{ - Data: []float32{ - math.Float32frombits(binary.BigEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})), - math.Float32frombits(binary.BigEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})), - math.Float32frombits(binary.BigEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})), - math.Float32frombits(binary.BigEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})), - }, - Size: ChunkInfo{ - Len: 2, - Channels: 2, - }, - } + expected := NewFloat32Interleaved(ChunkInfo{ + Len: 2, + Channels: 2, + }) + + expected.SetFloat32(0, 0, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})))) + expected.SetFloat32(0, 1, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})))) + expected.SetFloat32(1, 0, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})))) + expected.SetFloat32(1, 1, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})))) actual, err := decoder.Decode(binary.BigEndian, raw, 2) if err != nil { t.Fatal(err) @@ -265,18 +254,15 @@ func TestDecodeFloat32Interleaved(t *testing.T) { }) t.Run("LittleEndian", func(t *testing.T) { - expected := &Float32Interleaved{ - Data: []float32{ - math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})), - math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})), - math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})), - math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})), - }, - Size: ChunkInfo{ - Len: 2, - Channels: 2, - }, - } + expected := NewFloat32Interleaved(ChunkInfo{ + Len: 2, + Channels: 2, + }) + + expected.SetFloat32(0, 0, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x04, 0x03, 0x02, 0x01})))) + expected.SetFloat32(0, 1, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x08, 0x07, 0x06, 0x05})))) + expected.SetFloat32(1, 0, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x0c, 0x0b, 0x0a, 0x09})))) + expected.SetFloat32(1, 1, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x10, 0x0f, 0x0e, 0x0d})))) actual, err := decoder.Decode(binary.LittleEndian, raw, 2) if err != nil { t.Fatal(err) @@ -298,22 +284,15 @@ func TestDecodeFloat32NonInterleaved(t *testing.T) { decoder, _ := newFloat32NonInterleavedDecoder() t.Run("BigEndian", func(t *testing.T) { - expected := &Float32NonInterleaved{ - Data: [][]float32{ - { - math.Float32frombits(binary.BigEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})), - math.Float32frombits(binary.BigEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})), - }, - { - math.Float32frombits(binary.BigEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})), - math.Float32frombits(binary.BigEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})), - }, - }, - Size: ChunkInfo{ - Len: 2, - Channels: 2, - }, - } + expected := NewFloat32NonInterleaved(ChunkInfo{ + Len: 2, + Channels: 2, + }) + + expected.SetFloat32(0, 0, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})))) + expected.SetFloat32(0, 1, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})))) + expected.SetFloat32(1, 0, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})))) + expected.SetFloat32(1, 1, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})))) actual, err := decoder.Decode(binary.BigEndian, raw, 2) if err != nil { t.Fatal(err) @@ -325,22 +304,15 @@ func TestDecodeFloat32NonInterleaved(t *testing.T) { }) t.Run("LittleEndian", func(t *testing.T) { - expected := &Float32NonInterleaved{ - Data: [][]float32{ - { - math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})), - math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})), - }, - { - math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})), - math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})), - }, - }, - Size: ChunkInfo{ - Len: 2, - Channels: 2, - }, - } + expected := NewFloat32NonInterleaved(ChunkInfo{ + Len: 2, + Channels: 2, + }) + + expected.SetFloat32(0, 0, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x04, 0x03, 0x02, 0x01})))) + expected.SetFloat32(0, 1, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x0c, 0x0b, 0x0a, 0x09})))) + expected.SetFloat32(1, 0, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x08, 0x07, 0x06, 0x05})))) + expected.SetFloat32(1, 1, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x10, 0x0f, 0x0e, 0x0d})))) actual, err := decoder.Decode(binary.LittleEndian, raw, 2) if err != nil { t.Fatal(err) diff --git a/pkg/wave/float32.go b/pkg/wave/float32.go index 54d06e25..e0da434f 100644 --- a/pkg/wave/float32.go +++ b/pkg/wave/float32.go @@ -1,5 +1,7 @@ package wave +import "math" + // Float32Sample is a 32-bits float audio sample. type Float32Sample float32 @@ -9,7 +11,7 @@ func (s Float32Sample) Int() int64 { // Float32Interleaved multi-channel interlaced Audio. type Float32Interleaved struct { - Data []float32 + Data []uint8 Size ChunkInfo } @@ -23,22 +25,36 @@ func (a *Float32Interleaved) SampleFormat() SampleFormat { } func (a *Float32Interleaved) At(i, ch int) Sample { - return Float32Sample(a.Data[i*a.Size.Channels+ch]) + loc := 4 * (a.Size.Channels*i + ch) + + var v uint32 + v |= uint32(a.Data[loc]) << 24 + v |= uint32(a.Data[loc+1]) << 16 + v |= uint32(a.Data[loc+2]) << 8 + v |= uint32(a.Data[loc+3]) + + return Float32Sample(math.Float32frombits(v)) } func (a *Float32Interleaved) Set(i, ch int, s Sample) { - a.Data[i*a.Size.Channels+ch] = float32(Float32SampleFormat.Convert(s).(Float32Sample)) + a.SetFloat32(i, ch, Float32SampleFormat.Convert(s).(Float32Sample)) } func (a *Float32Interleaved) SetFloat32(i, ch int, s Float32Sample) { - a.Data[i*a.Size.Channels+ch] = float32(s) + loc := 4 * (a.Size.Channels*i + ch) + + v := math.Float32bits(float32(s)) + a.Data[loc] = uint8(v >> 24) + a.Data[loc+1] = uint8(v >> 16) + a.Data[loc+2] = uint8(v >> 8) + a.Data[loc+3] = uint8(v) } // SubAudio returns part of the original audio sharing the buffer. func (a *Float32Interleaved) SubAudio(offsetSamples, nSamples int) *Float32Interleaved { ret := *a - offset := offsetSamples * a.Size.Channels - n := nSamples * a.Size.Channels + offset := 4 * offsetSamples * a.Size.Channels + n := 4 * nSamples * a.Size.Channels ret.Data = ret.Data[offset : offset+n] ret.Size.Len = nSamples return &ret @@ -46,14 +62,14 @@ func (a *Float32Interleaved) SubAudio(offsetSamples, nSamples int) *Float32Inter func NewFloat32Interleaved(size ChunkInfo) *Float32Interleaved { return &Float32Interleaved{ - Data: make([]float32, size.Channels*size.Len), + Data: make([]uint8, size.Channels*size.Len*4), Size: size, } } // Float32NonInterleaved multi-channel interlaced Audio. type Float32NonInterleaved struct { - Data [][]float32 + Data [][]uint8 Size ChunkInfo } @@ -67,31 +83,48 @@ func (a *Float32NonInterleaved) SampleFormat() SampleFormat { } func (a *Float32NonInterleaved) At(i, ch int) Sample { - return Float32Sample(a.Data[ch][i]) + loc := i * 4 + + var v uint32 + v |= uint32(a.Data[ch][loc]) << 24 + v |= uint32(a.Data[ch][loc+1]) << 16 + v |= uint32(a.Data[ch][loc+2]) << 8 + v |= uint32(a.Data[ch][loc+3]) + + return Float32Sample(math.Float32frombits(v)) } func (a *Float32NonInterleaved) Set(i, ch int, s Sample) { - a.Data[ch][i] = float32(Float32SampleFormat.Convert(s).(Float32Sample)) + a.SetFloat32(i, ch, Float32SampleFormat.Convert(s).(Float32Sample)) } func (a *Float32NonInterleaved) SetFloat32(i, ch int, s Float32Sample) { - a.Data[ch][i] = float32(s) + loc := i * 4 + + v := math.Float32bits(float32(s)) + a.Data[ch][loc] = uint8(v >> 24) + a.Data[ch][loc+1] = uint8(v >> 16) + a.Data[ch][loc+2] = uint8(v >> 8) + a.Data[ch][loc+3] = uint8(v) } // SubAudio returns part of the original audio sharing the buffer. func (a *Float32NonInterleaved) SubAudio(offsetSamples, nSamples int) *Float32NonInterleaved { ret := *a + ret.Size.Len = nSamples + + offsetSamples *= 4 + nSamples *= 4 for i := range a.Data { ret.Data[i] = ret.Data[i][offsetSamples : offsetSamples+nSamples] } - ret.Size.Len = nSamples return &ret } func NewFloat32NonInterleaved(size ChunkInfo) *Float32NonInterleaved { - d := make([][]float32, size.Channels) + d := make([][]uint8, size.Channels) for i := 0; i < size.Channels; i++ { - d[i] = make([]float32, size.Len) + d[i] = make([]uint8, size.Len*4) } return &Float32NonInterleaved{ Data: d, diff --git a/pkg/wave/float32_test.go b/pkg/wave/float32_test.go index 4c1ba319..dd7f43b9 100644 --- a/pkg/wave/float32_test.go +++ b/pkg/wave/float32_test.go @@ -1,39 +1,51 @@ package wave import ( + "math" "reflect" "testing" ) +func float32ToUint8(vs ...float32) []uint8 { + var b []uint8 + + for _, v := range vs { + s := math.Float32bits(v) + b = append(b, uint8(s>>24), uint8(s>>16), uint8(s>>8), uint8(s)) + } + + return b +} + func TestFloat32(t *testing.T) { + expected := [][]float32{ + {0.0, 1.0, 2.0, 3.0}, + {4.0, 5.0, 6.0, 7.0}, + } + cases := map[string]struct { in Audio expected [][]float32 }{ "Interleaved": { in: &Float32Interleaved{ - Data: []float32{ - 0.1, -0.5, 0.2, -0.6, 0.3, -0.7, 0.4, -0.8, 0.5, -0.9, 0.6, -1.0, 0.7, -1.1, 0.8, -1.2, - }, - Size: ChunkInfo{8, 2, 48000}, - }, - expected: [][]float32{ - {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}, - {-0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2}, + Data: float32ToUint8( + 0.0, 4.0, 1.0, 5.0, + 2.0, 6.0, 3.0, 7.0, + ), + Size: ChunkInfo{4, 2, 48000}, }, + expected: expected, }, "NonInterleaved": { in: &Float32NonInterleaved{ - Data: [][]float32{ - {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}, - {-0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2}, + Data: [][]uint8{ + float32ToUint8(expected[0]...), + float32ToUint8(expected[1]...), }, - Size: ChunkInfo{8, 2, 48000}, - }, - expected: [][]float32{ - {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}, - {-0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2}, + Size: ChunkInfo{4, 2, 48000}, }, + expected: expected, }, } for name, c := range cases { @@ -55,38 +67,43 @@ func TestFloat32(t *testing.T) { func TestFloat32SubAudio(t *testing.T) { t.Run("Interleaved", func(t *testing.T) { in := &Float32Interleaved{ - Data: []float32{ - 0.1, -0.5, 0.2, -0.6, 0.3, -0.7, 0.4, -0.8, 0.5, -0.9, 0.6, -1.0, 0.7, -1.1, 0.8, -1.2, - }, - Size: ChunkInfo{8, 2, 48000}, + // Data: []uint8{ + // 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + // 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + // }, + Data: float32ToUint8( + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + ), + Size: ChunkInfo{4, 2, 48000}, } expected := &Float32Interleaved{ - Data: []float32{ - 0.3, -0.7, 0.4, -0.8, 0.5, -0.9, - }, - Size: ChunkInfo{3, 2, 48000}, + Data: float32ToUint8( + 5.0, 6.0, 7.0, 8.0, + ), + Size: ChunkInfo{2, 2, 48000}, } - out := in.SubAudio(2, 3) + out := in.SubAudio(2, 2) if !reflect.DeepEqual(expected, out) { t.Errorf("SubAudio differs, expected: %v, got: %v", expected, out) } }) t.Run("NonInterleaved", func(t *testing.T) { in := &Float32NonInterleaved{ - Data: [][]float32{ - {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}, - {-0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2}, + Data: [][]uint8{ + float32ToUint8(1.0, 2.0, 3.0, 4.0), + float32ToUint8(5.0, 6.0, 7.0, 8.0), }, - Size: ChunkInfo{8, 2, 48000}, + Size: ChunkInfo{4, 2, 48000}, } expected := &Float32NonInterleaved{ - Data: [][]float32{ - {0.3, 0.4, 0.5}, - {-0.7, -0.8, -0.9}, + Data: [][]uint8{ + float32ToUint8(3.0, 4.0), + float32ToUint8(7.0, 8.0), }, - Size: ChunkInfo{3, 2, 48000}, + Size: ChunkInfo{2, 2, 48000}, } - out := in.SubAudio(2, 3) + out := in.SubAudio(2, 2) if !reflect.DeepEqual(expected, out) { t.Errorf("SubAudio differs, expected: %v, got: %v", expected, out) } diff --git a/pkg/wave/int16.go b/pkg/wave/int16.go index 3747d47e..04168864 100644 --- a/pkg/wave/int16.go +++ b/pkg/wave/int16.go @@ -9,7 +9,7 @@ func (s Int16Sample) Int() int64 { // Int16Interleaved multi-channel interlaced Audio. type Int16Interleaved struct { - Data []int16 + Data []uint8 Size ChunkInfo } @@ -23,22 +23,29 @@ func (a *Int16Interleaved) SampleFormat() SampleFormat { } func (a *Int16Interleaved) At(i, ch int) Sample { - return Int16Sample(a.Data[i*a.Size.Channels+ch]) + loc := 2 * (i*a.Size.Channels + ch) + + var s Int16Sample + s |= Int16Sample(a.Data[loc]) << 8 + s |= Int16Sample(a.Data[loc+1]) + return s } func (a *Int16Interleaved) Set(i, ch int, s Sample) { - a.Data[i*a.Size.Channels+ch] = int16(Int16SampleFormat.Convert(s).(Int16Sample)) + a.SetInt16(i, ch, Int16SampleFormat.Convert(s).(Int16Sample)) } func (a *Int16Interleaved) SetInt16(i, ch int, s Int16Sample) { - a.Data[i*a.Size.Channels+ch] = int16(s) + loc := 2 * (i*a.Size.Channels + ch) + a.Data[loc] = uint8(s >> 8) + a.Data[loc+1] = uint8(s) } // SubAudio returns part of the original audio sharing the buffer. func (a *Int16Interleaved) SubAudio(offsetSamples, nSamples int) *Int16Interleaved { ret := *a - offset := offsetSamples * a.Size.Channels - n := nSamples * a.Size.Channels + offset := 2 * offsetSamples * a.Size.Channels + n := 2 * nSamples * a.Size.Channels ret.Data = ret.Data[offset : offset+n] ret.Size.Len = nSamples return &ret @@ -46,14 +53,14 @@ func (a *Int16Interleaved) SubAudio(offsetSamples, nSamples int) *Int16Interleav func NewInt16Interleaved(size ChunkInfo) *Int16Interleaved { return &Int16Interleaved{ - Data: make([]int16, size.Channels*size.Len), + Data: make([]uint8, size.Channels*size.Len*2), Size: size, } } // Int16NonInterleaved multi-channel interlaced Audio. type Int16NonInterleaved struct { - Data [][]int16 + Data [][]uint8 Size ChunkInfo } @@ -67,31 +74,41 @@ func (a *Int16NonInterleaved) SampleFormat() SampleFormat { } func (a *Int16NonInterleaved) At(i, ch int) Sample { - return Int16Sample(a.Data[ch][i]) + loc := i * 2 + + var s Int16Sample + s |= Int16Sample(a.Data[ch][loc]) << 8 + s |= Int16Sample(a.Data[ch][loc+1]) + return s } func (a *Int16NonInterleaved) Set(i, ch int, s Sample) { - a.Data[ch][i] = int16(Int16SampleFormat.Convert(s).(Int16Sample)) + a.SetInt16(i, ch, Int16SampleFormat.Convert(s).(Int16Sample)) } func (a *Int16NonInterleaved) SetInt16(i, ch int, s Int16Sample) { - a.Data[ch][i] = int16(s) + loc := i * 2 + a.Data[ch][loc] = uint8(s >> 8) + a.Data[ch][loc+1] = uint8(s) } // SubAudio returns part of the original audio sharing the buffer. func (a *Int16NonInterleaved) SubAudio(offsetSamples, nSamples int) *Int16NonInterleaved { ret := *a + ret.Size.Len = nSamples + + nSamples *= 2 + offsetSamples *= 2 for i := range a.Data { ret.Data[i] = ret.Data[i][offsetSamples : offsetSamples+nSamples] } - ret.Size.Len = nSamples return &ret } func NewInt16NonInterleaved(size ChunkInfo) *Int16NonInterleaved { - d := make([][]int16, size.Channels) + d := make([][]uint8, size.Channels) for i := 0; i < size.Channels; i++ { - d[i] = make([]int16, size.Len) + d[i] = make([]uint8, size.Len*2) } return &Int16NonInterleaved{ Data: d, diff --git a/pkg/wave/int16_test.go b/pkg/wave/int16_test.go index a016fb0a..6f447e29 100644 --- a/pkg/wave/int16_test.go +++ b/pkg/wave/int16_test.go @@ -12,27 +12,28 @@ func TestInt16(t *testing.T) { }{ "Interleaved": { in: &Int16Interleaved{ - Data: []int16{ - 1, -5, 2, -6, 3, -7, 4, -8, 5, -9, 6, -10, 7, -11, 8, -12, + Data: []uint8{ + 0, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, 6, 6, 7, 7, 8, }, - Size: ChunkInfo{8, 2, 48000}, + Size: ChunkInfo{4, 2, 48000}, }, expected: [][]int16{ - {1, 2, 3, 4, 5, 6, 7, 8}, - {-5, -6, -7, -8, -9, -10, -11, -12}, + {(0 << 8) | 1, (2 << 8) | 3, (4 << 8) | 5, (6 << 8) | 7}, + {(1 << 8) | 2, (3 << 8) | 4, (5 << 8) | 6, (7 << 8) | 8}, }, }, "NonInterleaved": { in: &Int16NonInterleaved{ - Data: [][]int16{ + Data: [][]uint8{ + {0, 1, 2, 3, 4, 5, 6, 7}, {1, 2, 3, 4, 5, 6, 7, 8}, - {-5, -6, -7, -8, -9, -10, -11, -12}, }, - Size: ChunkInfo{8, 2, 48000}, + Size: ChunkInfo{4, 2, 48000}, }, expected: [][]int16{ - {1, 2, 3, 4, 5, 6, 7, 8}, - {-5, -6, -7, -8, -9, -10, -11, -12}, + {(0 << 8) | 1, (2 << 8) | 3, (4 << 8) | 5, (6 << 8) | 7}, + {(1 << 8) | 2, (3 << 8) | 4, (5 << 8) | 6, (7 << 8) | 8}, }, }, } @@ -55,38 +56,39 @@ func TestInt16(t *testing.T) { func TestInt32SubAudio(t *testing.T) { t.Run("Interleaved", func(t *testing.T) { in := &Int16Interleaved{ - Data: []int16{ - 1, -5, 2, -6, 3, -7, 4, -8, 5, -9, 6, -10, 7, -11, 8, -12, + Data: []uint8{ + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, }, - Size: ChunkInfo{8, 2, 48000}, + Size: ChunkInfo{4, 2, 48000}, } expected := &Int16Interleaved{ - Data: []int16{ - 3, -7, 4, -8, 5, -9, + Data: []uint8{ + 9, 10, 11, 12, 13, 14, 15, 16, }, - Size: ChunkInfo{3, 2, 48000}, + Size: ChunkInfo{2, 2, 48000}, } - out := in.SubAudio(2, 3) + out := in.SubAudio(2, 2) if !reflect.DeepEqual(expected, out) { t.Errorf("SubAudio differs, expected: %v, got: %v", expected, out) } }) t.Run("NonInterleaved", func(t *testing.T) { in := &Int16NonInterleaved{ - Data: [][]int16{ - {1, 2, 3, 4, 5, 6, 7, 8}, - {-5, -6, -7, -8, -9, -10, -11, -12}, + Data: [][]uint8{ + {1, 2, 5, 6, 9, 10, 13, 14}, + {3, 4, 7, 8, 11, 12, 15, 16}, }, - Size: ChunkInfo{8, 2, 48000}, + Size: ChunkInfo{4, 2, 48000}, } expected := &Int16NonInterleaved{ - Data: [][]int16{ - {3, 4, 5}, - {-7, -8, -9}, + Data: [][]uint8{ + {9, 10, 13, 14}, + {11, 12, 15, 16}, }, - Size: ChunkInfo{3, 2, 48000}, + Size: ChunkInfo{2, 2, 48000}, } - out := in.SubAudio(2, 3) + out := in.SubAudio(2, 2) if !reflect.DeepEqual(expected, out) { t.Errorf("SubAudio differs, expected: %v, got: %v", expected, out) } diff --git a/pkg/wave/mixer/mixer_test.go b/pkg/wave/mixer/mixer_test.go index 40ebdb9b..34866e3a 100644 --- a/pkg/wave/mixer/mixer_test.go +++ b/pkg/wave/mixer/mixer_test.go @@ -19,10 +19,10 @@ func TestMonoMixer(t *testing.T) { Len: 3, Channels: 3, }, - Data: []int16{ - 0, 2, 4, - 1, -2, 1, - 3, 3, 6, + Data: []uint8{ + 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x03, 0x00, 0x03, 0x00, 0x06, }, }, dst: &wave.Int16Interleaved{ @@ -30,14 +30,14 @@ func TestMonoMixer(t *testing.T) { Len: 3, Channels: 1, }, - Data: make([]int16, 3), + Data: make([]uint8, 3*2), }, expected: &wave.Int16Interleaved{ Size: wave.ChunkInfo{ Len: 3, Channels: 1, }, - Data: []int16{2, 0, 4}, + Data: []uint8{0x00, 0x02, 0x00, 0x01, 0x00, 0x04}, }, }, "MonoToStereo": { @@ -46,21 +46,21 @@ func TestMonoMixer(t *testing.T) { Len: 3, Channels: 1, }, - Data: []int16{0, 2, 4}, + Data: []uint8{0x00, 0x00, 0x00, 0x02, 0x00, 0x04}, }, dst: &wave.Int16Interleaved{ Size: wave.ChunkInfo{ Len: 3, Channels: 2, }, - Data: make([]int16, 6), + Data: make([]uint8, 6*2), }, expected: &wave.Int16Interleaved{ Size: wave.ChunkInfo{ Len: 3, Channels: 2, }, - Data: []int16{0, 0, 2, 2, 4, 4}, + Data: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04}, }, }, }