diff --git a/main.go b/main.go index 68b4d2b..3c891ac 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,6 @@ package main import ( "bufio" - "encoding/binary" - "errors" "fmt" "io" "log" @@ -16,7 +14,7 @@ type ( AudioVersionId int LayerIndex int - header uint32 + header []byte ) const ( @@ -57,26 +55,26 @@ var ( } ) -// TODO tests +// TODO: tests func (h header) HasSyncWord() bool { - return (h>>21)&0x7ff == 0x7ff + return h[0] == 0xff && h[1]&0xe0 == 0xe0 } func (h header) AudioVersionId() AudioVersionId { - return AudioVersionId((h >> 19) & 0x3) + return AudioVersionId((h[1] >> 3) & 0x3) } func (h header) LayerIndex() LayerIndex { - return LayerIndex(((h >> 8) & 0x6) >> 1) + return LayerIndex((h[1] >> 1) & 0x3) } func (h header) IsProtected() bool { - return (h>>8)&1 == 1 + return h[1]&1 == 1 } func (h header) SampleRate() uint32 { i := h.AudioVersionId() - j := (h >> 18) & 0xb11 + j := (h[2] >> 2) & 0x3 return sampleRates[i][j] } @@ -98,6 +96,7 @@ func (h header) IsValid() bool { } func (h header) BitRate() uint32 { + // TODO: improve this var j int switch h.AudioVersionId() { case AudioVersionMPEG1: @@ -124,8 +123,7 @@ func (h header) BitRate() uint32 { } } - k := ((h >> 20) & 0xf) - return bitRates[j][k] + return bitRates[j][h[2]>>4] } func main() { @@ -147,26 +145,12 @@ func main() { log.Fatal(err) } - header, err := newHeaderFromBytes(buf) - if header.IsValid() { - fmt.Printf("%+v\n", buf) - fmt.Printf("Got header: %032b, audio version: %d, layer index: %d, bitrate: %d\n", header, header.AudioVersionId(), header.LayerIndex(), header.BitRate()) + h := header(buf) + if h.IsValid() { + fmt.Printf("Got header: %08b, audio version: %d, layer index: %d, bitrate: %d\n", h, h.AudioVersionId(), h.LayerIndex(), h.BitRate()) } else { bufreader.Discard(3) } i++ } } - -func newHeaderFromBytes(buf []byte) (header, error) { - if len(buf) < 4 { - return 0, errors.New("insufficient bytes") - } - // assume little-endian for now - return newHeader(binary.LittleEndian.Uint32(buf)) -} - -// TODO use unsafe here? https://codereview.stackexchange.com/a/60161 -func newHeader(n uint32) (header, error) { - return header(n), nil -} diff --git a/main_test.go b/main_test.go index 4e8ffdd..8deb250 100644 --- a/main_test.go +++ b/main_test.go @@ -1,234 +1,226 @@ package main import ( - "encoding/binary" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestAudioVersionId(t *testing.T) { bytes := []byte{0xff, 0xe8, 0, 0} - h, err := newHeader(binary.BigEndian.Uint32(bytes)) - require.NoError(t, err) + h := header(bytes) assert.Equal(t, AudioVersionReserved, h.AudioVersionId()) bytes = []byte{0xff, 0xf0, 0, 0} - h, err = newHeader(binary.BigEndian.Uint32(bytes)) - require.NoError(t, err) + h = header(bytes) assert.Equal(t, AudioVersionMPEG2, h.AudioVersionId()) bytes = []byte{0xff, 0xff, 0, 0} - h, err = newHeader(binary.BigEndian.Uint32(bytes)) - require.NoError(t, err) + h = header(bytes) assert.Equal(t, AudioVersionMPEG1, h.AudioVersionId()) bytes = []byte{0xff, 0xe0, 0, 0} - h, err = newHeader(binary.BigEndian.Uint32(bytes)) - require.NoError(t, err) + h = header(bytes) assert.Equal(t, AudioVersionMPEG25, h.AudioVersionId()) } -// func TestLayerIndex(t *testing.T) { -// bytes := []byte{0xff, 0xe2, 0, 0} -// h, err := newHeader(binary.BigEndian.Uint32(bytes)) -// require.NoError(t, err) -// assert.Equal(t, LayerIndexIII, h.LayerIndex()) +func TestLayerIndex(t *testing.T) { + bytes := []byte{0xff, 0xe0, 0, 0} + h := header(bytes) + assert.Equal(t, LayerIndexReserved, h.LayerIndex()) -// bytes = []byte{0xff, 0xe4, 0, 0} -// h, err = newHeader(binary.BigEndian.Uint32(bytes)) -// require.NoError(t, err) -// assert.Equal(t, LayerIndexII, h.LayerIndex()) + bytes = []byte{0xff, 0xe2, 0, 0} + h = header(bytes) + assert.Equal(t, LayerIndexIII, h.LayerIndex()) -// bytes = []byte{0xff, 0xe6, 0, 0} -// h, err = newHeader(binary.BigEndian.Uint32(bytes)) -// require.NoError(t, err) -// assert.Equal(t, LayerIndexI, h.LayerIndex()) -// } + bytes = []byte{0xff, 0xe4, 0, 0} + h = header(bytes) + assert.Equal(t, LayerIndexII, h.LayerIndex()) -// func TestIsProtected(t *testing.T) { -// bytes := []byte{0xff, 0xe1, 0, 0} -// h, err := newHeader(binary.BigEndian.Uint32(bytes)) -// require.NoError(t, err) -// assert.True(t, h.IsProtected()) + bytes = []byte{0xff, 0xe6, 0, 0} + h = header(bytes) + assert.Equal(t, LayerIndexI, h.LayerIndex()) +} -// bytes = []byte{0xff, 0xe0, 0, 0} -// h, err = newHeader(binary.BigEndian.Uint32(bytes)) -// require.NoError(t, err) -// assert.False(t, h.IsProtected()) -// } +func TestIsProtected(t *testing.T) { + bytes := []byte{0xff, 0xe1, 0, 0} + h := header(bytes) + assert.True(t, h.IsProtected()) -// func TestBitRate(t *testing.T) { -// testCases := []struct { -// name string -// bytes []byte -// expBitRate uint32 -// }{ -// { -// "empty header", -// []byte{0, 0, 0, 0}, -// 0, -// }, -// { -// "MPEG1 LayerI 32kbps", -// []byte{0xff, 0xfe, 0x10, 0}, -// 32, -// }, -// { -// "MPEG1 LayerI 448kbps", -// []byte{0xff, 0xfe, 0xe0, 0}, -// 448, -// }, -// { -// "MPEG1 LayerII 32kbps", -// []byte{0xff, 0xfc, 0x10, 0}, -// 32, -// }, -// { -// "MPEG1 LayerII 448kbps", -// []byte{0xff, 0xfc, 0xe0, 0}, -// 384, -// }, -// { -// "MPEG1 LayerIII 32kbps", -// []byte{0xff, 0xfa, 0x10, 0}, -// 32, -// }, -// { -// "MPEG1 LayerIII 128kbps", -// []byte{0xff, 0xfa, 0x90, 0}, -// 128, -// }, -// { -// "MPEG1 LayerIII 160kbps", -// []byte{0xff, 0xfa, 0xa0, 0}, -// 160, -// }, -// { -// "MPEG1 LayerIII 192kbps", -// []byte{0xff, 0xfb, 0xb0, 0}, -// 192, -// }, -// { -// "MPEG1 LayerIII 320kbps", -// []byte{0xff, 0xfa, 0xe0, 0}, -// 320, -// }, -// { -// "MPEG1 LayerIII reserved", -// []byte{0xff, 0xfa, 0xf0, 0}, -// 0, -// }, -// { -// "MPEG2 LayerI 32kbps", -// []byte{0xff, 0xf6, 0x10, 0}, -// 32, -// }, -// { -// "MPEG2 LayerI 256", -// []byte{0xff, 0xf6, 0xe0, 0}, -// 256, -// }, -// { -// "MPEG2 LayerII 32kbps", -// []byte{0xff, 0xf4, 0x10, 0}, -// 8, -// }, -// { -// "MPEG2 LayerII 160kbps", -// []byte{0xff, 0xf4, 0xe0, 0}, -// 160, -// }, -// { -// "MPEG2 LayerIII 32kbps", -// []byte{0xff, 0xf2, 0x10, 0}, -// 8, -// }, -// { -// "MPEG2 LayerIII 160kbps", -// []byte{0xff, 0xf2, 0xe0, 0}, -// 160, -// }, -// { -// "MPEG25 LayerI 32kbps", -// []byte{0xff, 0xe6, 0x10, 0}, -// 32, -// }, -// { -// "MPEG25 LayerI 256", -// []byte{0xff, 0xe6, 0xe0, 0}, -// 256, -// }, -// { -// "MPEG25 LayerII 32kbps", -// []byte{0xff, 0xe4, 0x10, 0}, -// 8, -// }, -// { -// "MPEG25 LayerII 160kbps", -// []byte{0xff, 0xe4, 0xe0, 0}, -// 160, -// }, -// { -// "MPEG25 LayerIII 32kbps", -// []byte{0xff, 0xe2, 0x10, 0}, -// 8, -// }, -// { -// "MPEG25 LayerIII 160kbps", -// []byte{0xff, 0xe2, 0xe0, 0}, -// 160, -// }, -// { -// "full header", -// []byte{1, 1, 1, 1}, -// 0, -// }, -// } + bytes = []byte{0xff, 0xe0, 0, 0} + h = header(bytes) + assert.False(t, h.IsProtected()) +} -// for _, tc := range testCases { -// t.Run(tc.name, func(t *testing.T) { -// h, err := newHeader(binary.BigEndian.Uint32(tc.bytes)) -// require.NoError(t, err) -// assert.Equal(t, tc.expBitRate, h.BitRate()) -// }) -// } -// } +func TestBitRate(t *testing.T) { + testCases := []struct { + name string + bytes []byte + expBitRate uint32 + }{ + { + "empty header", + []byte{0, 0, 0, 0}, + 0, + }, + { + "MPEG1 LayerI 32kbps", + []byte{0xff, 0xfe, 0x10, 0}, + 32, + }, + { + "MPEG1 LayerI 448kbps", + []byte{0xff, 0xfe, 0xe0, 0}, + 448, + }, + { + "MPEG1 LayerII 32kbps", + []byte{0xff, 0xfc, 0x10, 0}, + 32, + }, + { + "MPEG1 LayerII 448kbps", + []byte{0xff, 0xfc, 0xe0, 0}, + 384, + }, + { + "MPEG1 LayerIII 32kbps", + []byte{0xff, 0xfa, 0x10, 0}, + 32, + }, + { + "MPEG1 LayerIII 128kbps", + []byte{0xff, 0xfa, 0x90, 0}, + 128, + }, + { + "MPEG1 LayerIII 160kbps", + []byte{0xff, 0xfa, 0xa0, 0}, + 160, + }, + { + "MPEG1 LayerIII 192kbps", + []byte{0xff, 0xfb, 0xb0, 0}, + 192, + }, + { + "MPEG1 LayerIII 320kbps", + []byte{0xff, 0xfa, 0xe0, 0}, + 320, + }, + { + "MPEG1 LayerIII reserved", + []byte{0xff, 0xfa, 0xf0, 0}, + 0, + }, + { + "MPEG2 LayerI 32kbps", + []byte{0xff, 0xf6, 0x10, 0}, + 32, + }, + { + "MPEG2 LayerI 256", + []byte{0xff, 0xf6, 0xe0, 0}, + 256, + }, + { + "MPEG2 LayerII 32kbps", + []byte{0xff, 0xf4, 0x10, 0}, + 8, + }, + { + "MPEG2 LayerII 160kbps", + []byte{0xff, 0xf4, 0xe0, 0}, + 160, + }, + { + "MPEG2 LayerIII 32kbps", + []byte{0xff, 0xf2, 0x10, 0}, + 8, + }, + { + "MPEG2 LayerIII 160kbps", + []byte{0xff, 0xf2, 0xe0, 0}, + 160, + }, + { + "MPEG25 LayerI 32kbps", + []byte{0xff, 0xe6, 0x10, 0}, + 32, + }, + { + "MPEG25 LayerI 256", + []byte{0xff, 0xe6, 0xe0, 0}, + 256, + }, + { + "MPEG25 LayerII 32kbps", + []byte{0xff, 0xe4, 0x10, 0}, + 8, + }, + { + "MPEG25 LayerII 160kbps", + []byte{0xff, 0xe4, 0xe0, 0}, + 160, + }, + { + "MPEG25 LayerIII 32kbps", + []byte{0xff, 0xe2, 0x10, 0}, + 8, + }, + { + "MPEG25 LayerIII 160kbps", + []byte{0xff, 0xe2, 0xe0, 0}, + 160, + }, + { + "full header", + []byte{1, 1, 1, 1}, + 0, + }, + } -// func TestSampleRate(t *testing.T) { -// testCases := []struct { -// name string -// bytes []byte -// expSampleRate uint32 -// }{ -// { -// "MPEG1 44.1k", -// []byte{0xff, 0xfa, 0x90, 0}, -// 44100, -// }, -// { -// "MPEG1 48k", -// []byte{0xff, 0xfa, 0x94, 0}, -// 48000, -// }, -// { -// "MPEG1 32k", -// []byte{0xff, 0xfa, 0x98, 0}, -// 32000, -// }, -// { -// "MPEG1 Reserved", -// []byte{0xff, 0xfa, 0x9c, 0}, -// 32000, -// }, -// } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + h := header(tc.bytes) + assert.Equal(t, tc.expBitRate, h.BitRate()) + }) + } +} -// for _, tc := range testCases { -// t.Run(tc.name, func(t *testing.T) { -// h, err := newHeader(binary.BigEndian.Uint32(tc.bytes)) -// require.NoError(t, err) -// assert.Equal(t, int(tc.expSampleRate), int(h.SampleRate())) -// }) -// } -// } +func TestSampleRate(t *testing.T) { + testCases := []struct { + name string + bytes []byte + expSampleRate uint32 + }{ + { + "MPEG1 44.1k", + []byte{0xff, 0xfa, 0x90, 0}, + 44100, + }, + { + "MPEG1 48k", + []byte{0xff, 0xfa, 0x94, 0}, + 48000, + }, + { + "MPEG1 32k", + []byte{0xff, 0xfa, 0x98, 0}, + 32000, + }, + { + "MPEG1 Reserved", + []byte{0xff, 0xfa, 0x9c, 0}, + 0, + }, + // TODO finish + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + h := header(tc.bytes) + assert.Equal(t, int(tc.expSampleRate), int(h.SampleRate())) + }) + } +}