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" "os"
) )
const fname = "/home/rob/home-office/example.mp3" const fname = "/home/rob/share/home-office/example.mp3"
const ( type (
maskSync = 0b11111111111000000000000000000000 AudioVersionId int
maskVersion = 0b00000000000110000000000000000000 LayerIndex int
maskLayer = 0b00000000000001100000000000000000
maskProtection = 0b00000000000000010000000000000000 header uint32
maskBitrate = 0b00000000000000001111000000000000
maskSamplerate = 0b00000000000000000000110000000000
maskPadding = 0b00000000000000000000001000000000
maskPrivate = 0b00000000000000000000000100000000
maskChannelMode = 0b00000000000000000000000011000000
maskExtension = 0b00000000000000000000000000110000
maskCopyright = 0b00000000000000000000000000001000
maskOriginal = 0b00000000000000000000000000000100
maskEmphasis = 0b00000000000000000000000000000011
) )
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() { func main() {
f, err := os.Open(fname) f, err := os.Open(fname)
@ -36,8 +67,8 @@ func main() {
} }
bufreader := bufio.NewReader(f) bufreader := bufio.NewReader(f)
buf := make([]byte, 4) buf := make([]byte, 4)
var i int var i int
for { for {
_, err := io.ReadFull(bufreader, buf) _, err := io.ReadFull(bufreader, buf)
@ -48,10 +79,10 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
header, err := newHeader(buf) header, err := newHeaderFromBytes(buf)
if err == nil { if err == nil {
fmt.Printf("Got header: %08X\n", header) fmt.Printf("Got header: %08X, audio version: %d\n", header, header.AudioVersionId())
} }
i++ i++
@ -59,8 +90,7 @@ func main() {
} }
} }
// TODO use unsafe here? https://codereview.stackexchange.com/a/60161 func newHeaderFromBytes(buf []byte) (header, error) {
func newHeader(buf []byte) (header, error) {
if len(buf) < 4 { if len(buf) < 4 {
return 0, errors.New("insufficient bytes") return 0, errors.New("insufficient bytes")
} }
@ -70,6 +100,10 @@ func newHeader(buf []byte) (header, error) {
// always little-endian for now // always little-endian for now
n := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 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 return header(n), nil
} }

View File

@ -1,35 +1,48 @@
package main package main
import ( import (
"fmt" "encoding/binary"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestMain(t *testing.T) { func TestAudioVersionId(t *testing.T) {
testCases := []struct { bytes := []byte{0xff, 0xe8, 0, 0}
input []byte h, err := newHeader(binary.LittleEndian.Uint32(bytes))
expectedError string require.NoError(t, err)
}{ assert.Equal(t, AudioVersionReserved, h.AudioVersionId())
{
input: []byte{0, 0, 0, 0}, bytes = []byte{0xff, 0xf0, 0, 0}
expectedError: "no header", h, err = newHeader(binary.LittleEndian.Uint32(bytes))
}, require.NoError(t, err)
{ assert.Equal(t, AudioVersionMPEG2, h.AudioVersionId())
input: []byte{0, 0, 0, 1},
expectedError: "no header", bytes = []byte{0xff, 0xff, 0, 0}
}, h, err = newHeader(binary.LittleEndian.Uint32(bytes))
{ require.NoError(t, err)
input: []byte{0xff, 0xfe, 0, 0}, // wrong assert.Equal(t, AudioVersionMPEG1, h.AudioVersionId())
expectedError: "",
}, bytes = []byte{0xff, 0xe0, 0, 0}
h, err = newHeader(binary.LittleEndian.Uint32(bytes))
require.NoError(t, err)
assert.Equal(t, AudioVersionMPEG25, h.AudioVersionId())
} }
for _, tc := range testCases { func TestLayerIndex(t *testing.T) {
t.Run(fmt.Sprintf("Header %08b", tc.input), func(t *testing.T) { bytes := []byte{0xff, 0xe2, 0, 0}
_, err := newHeader(tc.input) h, err := newHeader(binary.LittleEndian.Uint32(bytes))
assert.EqualError(t, err, tc.expectedError) 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())
} }