refactor to use a byte slice with bigendian ordering

this matches both the data structure and the underlying data that we
receive from the network, so we should also use them internally.
This commit is contained in:
Rob Watson 2021-01-27 06:51:08 +01:00
parent e99b2923c1
commit 88c8e210a6
2 changed files with 208 additions and 232 deletions

40
main.go
View File

@ -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
}

View File

@ -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()))
})
}
}