diff --git a/main.go b/main.go index 75e0c42..fac0117 100644 --- a/main.go +++ b/main.go @@ -9,25 +9,56 @@ import ( "os" ) -const fname = "/home/rob/home-office/example.mp3" +const fname = "/home/rob/share/home-office/example.mp3" -const ( - maskSync = 0b11111111111000000000000000000000 - maskVersion = 0b00000000000110000000000000000000 - maskLayer = 0b00000000000001100000000000000000 - maskProtection = 0b00000000000000010000000000000000 - maskBitrate = 0b00000000000000001111000000000000 - maskSamplerate = 0b00000000000000000000110000000000 - maskPadding = 0b00000000000000000000001000000000 - maskPrivate = 0b00000000000000000000000100000000 - maskChannelMode = 0b00000000000000000000000011000000 - maskExtension = 0b00000000000000000000000000110000 - maskCopyright = 0b00000000000000000000000000001000 - maskOriginal = 0b00000000000000000000000000000100 - maskEmphasis = 0b00000000000000000000000000000011 +type ( + AudioVersionId int + LayerIndex int + + header uint32 ) -type header uint32 +const ( + AudioVersionMPEG25 AudioVersionId = iota + AudioVersionReserved + AudioVersionMPEG2 + AudioVersionMPEG1 +) + +const ( + LayerIndexReserved LayerIndex = iota + LayerIndexIII + LayerIndexII + LayerIndexI +) + +func (h header) AudioVersionId() AudioVersionId { + b := h >> 8 + if b&0x18 == 0x18 { + return AudioVersionMPEG1 + } + if b&0x10 == 0x10 { + return AudioVersionMPEG2 + } + if b&0x8 == 0x8 { + return AudioVersionReserved + } + return AudioVersionMPEG25 +} + +func (h header) LayerIndex() LayerIndex { + b := h >> 8 + if b&0x6 == 0x6 { + return LayerIndexI + } + if b&0x4 == 0x4 { + return LayerIndexII + } + if b&0x2 == 0x2 { + return LayerIndexIII + } + return LayerIndexReserved +} func main() { f, err := os.Open(fname) @@ -36,8 +67,8 @@ func main() { } bufreader := bufio.NewReader(f) - buf := make([]byte, 4) + var i int for { _, err := io.ReadFull(bufreader, buf) @@ -48,10 +79,10 @@ func main() { log.Fatal(err) } - header, err := newHeader(buf) + header, err := newHeaderFromBytes(buf) if err == nil { - fmt.Printf("Got header: %08X\n", header) + fmt.Printf("Got header: %08X, audio version: %d\n", header, header.AudioVersionId()) } i++ @@ -59,8 +90,7 @@ func main() { } } -// TODO use unsafe here? https://codereview.stackexchange.com/a/60161 -func newHeader(buf []byte) (header, error) { +func newHeaderFromBytes(buf []byte) (header, error) { if len(buf) < 4 { return 0, errors.New("insufficient bytes") } @@ -70,6 +100,10 @@ func newHeader(buf []byte) (header, error) { // always little-endian for now n := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + return newHeader(n) +} +// 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 11f2218..e2dc941 100644 --- a/main_test.go +++ b/main_test.go @@ -1,35 +1,48 @@ package main import ( - "fmt" + "encoding/binary" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func TestMain(t *testing.T) { - testCases := []struct { - input []byte - expectedError string - }{ - { - input: []byte{0, 0, 0, 0}, - expectedError: "no header", - }, - { - input: []byte{0, 0, 0, 1}, - expectedError: "no header", - }, - { - input: []byte{0xff, 0xfe, 0, 0}, // wrong - expectedError: "", - }, - } +func TestAudioVersionId(t *testing.T) { + bytes := []byte{0xff, 0xe8, 0, 0} + h, err := newHeader(binary.LittleEndian.Uint32(bytes)) + require.NoError(t, err) + assert.Equal(t, AudioVersionReserved, h.AudioVersionId()) - for _, tc := range testCases { - t.Run(fmt.Sprintf("Header %08b", tc.input), func(t *testing.T) { - _, err := newHeader(tc.input) - assert.EqualError(t, err, tc.expectedError) - }) - } + bytes = []byte{0xff, 0xf0, 0, 0} + h, err = newHeader(binary.LittleEndian.Uint32(bytes)) + require.NoError(t, err) + assert.Equal(t, AudioVersionMPEG2, h.AudioVersionId()) + + bytes = []byte{0xff, 0xff, 0, 0} + h, err = newHeader(binary.LittleEndian.Uint32(bytes)) + require.NoError(t, err) + assert.Equal(t, AudioVersionMPEG1, h.AudioVersionId()) + + bytes = []byte{0xff, 0xe0, 0, 0} + h, err = newHeader(binary.LittleEndian.Uint32(bytes)) + require.NoError(t, err) + assert.Equal(t, AudioVersionMPEG25, h.AudioVersionId()) +} + +func TestLayerIndex(t *testing.T) { + bytes := []byte{0xff, 0xe2, 0, 0} + h, err := newHeader(binary.LittleEndian.Uint32(bytes)) + require.NoError(t, err) + assert.Equal(t, LayerIndexIII, h.LayerIndex()) + + bytes = []byte{0xff, 0xe4, 0, 0} + h, err = newHeader(binary.LittleEndian.Uint32(bytes)) + require.NoError(t, err) + assert.Equal(t, LayerIndexII, h.LayerIndex()) + + bytes = []byte{0xff, 0xe6, 0, 0} + h, err = newHeader(binary.LittleEndian.Uint32(bytes)) + require.NoError(t, err) + assert.Equal(t, LayerIndexI, h.LayerIndex()) }