implement audio version and layer index parsing

This commit is contained in:
Rob Watson 2021-01-26 10:38:35 +01:00
parent f16a5c99c8
commit 85c66d74c2
2 changed files with 93 additions and 46 deletions

76
main.go
View File

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

View File

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