implement audio version and layer index parsing
This commit is contained in:
parent
f16a5c99c8
commit
85c66d74c2
76
main.go
76
main.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
61
main_test.go
61
main_test.go
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue