mirror of
https://github.com/pion/mediadevices.git
synced 2025-10-26 10:00:24 +08:00
Make raw audio decoder more practical
This commit is contained in:
@@ -9,16 +9,30 @@ import (
|
||||
)
|
||||
|
||||
// Format represents how audio is formatted in memory
|
||||
type Format string
|
||||
type Format fmt.Stringer
|
||||
|
||||
const (
|
||||
FormatInt16Interleaved Format = "Int16Interleaved"
|
||||
FormatInt16NonInterleaved = "Int16NonInterleaved"
|
||||
FormatFloat32Interleaved = "Float32Interleaved"
|
||||
FormatFloat32NonInterleaved = "Float32NonInterleaved"
|
||||
)
|
||||
type RawFormat struct {
|
||||
SampleSize int
|
||||
IsFloat bool
|
||||
Interleaved bool
|
||||
}
|
||||
|
||||
func (f *RawFormat) String() string {
|
||||
sampleSizeInBits := f.SampleSize * 8
|
||||
dataTypeStr := "Int"
|
||||
if f.IsFloat {
|
||||
dataTypeStr = "Float"
|
||||
}
|
||||
interleavedStr := "NonInterleaved"
|
||||
if f.Interleaved {
|
||||
interleavedStr = "Interleaved"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%d%s", dataTypeStr, sampleSizeInBits, interleavedStr)
|
||||
}
|
||||
|
||||
var hostEndian binary.ByteOrder
|
||||
var registeredDecoders = map[string]Decoder{}
|
||||
|
||||
func init() {
|
||||
switch v := *(*uint16)(unsafe.Pointer(&([]byte{0x12, 0x34}[0]))); v {
|
||||
@@ -29,6 +43,20 @@ func init() {
|
||||
default:
|
||||
panic(fmt.Sprintf("failed to determine host endianness: %x", v))
|
||||
}
|
||||
|
||||
decoderBuilders := []DecoderBuilderFunc{
|
||||
newInt16InterleavedDecoder,
|
||||
newInt16NonInterleavedDecoder,
|
||||
newFloat32InterleavedDecoder,
|
||||
newFloat32NonInterleavedDecoder,
|
||||
}
|
||||
|
||||
for _, decoderBuilder := range decoderBuilders {
|
||||
err := RegisterDecoder(decoderBuilder)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder decodes raw chunk to Audio
|
||||
@@ -44,21 +72,35 @@ func (f DecoderFunc) Decode(endian binary.ByteOrder, chunk []byte, channels int)
|
||||
return f(endian, chunk, channels)
|
||||
}
|
||||
|
||||
// NewDecoder creates a decoder to decode raw audio data in the given format
|
||||
func NewDecoder(f Format) (Decoder, error) {
|
||||
var decoder DecoderFunc
|
||||
// DecoderBuilder builds raw audio decoder
|
||||
type DecoderBuilder interface {
|
||||
// NewDecoder creates a new decoder for specified format
|
||||
NewDecoder() (Decoder, Format)
|
||||
}
|
||||
|
||||
switch f {
|
||||
case FormatInt16Interleaved:
|
||||
decoder = decodeInt16Interleaved
|
||||
case FormatInt16NonInterleaved:
|
||||
decoder = decodeInt16NonInterleaved
|
||||
case FormatFloat32Interleaved:
|
||||
decoder = decodeFloat32Interleaved
|
||||
case FormatFloat32NonInterleaved:
|
||||
decoder = decodeFloat32NonInterleaved
|
||||
default:
|
||||
return nil, fmt.Errorf("%s is not supported", f)
|
||||
// DecoderBuilderFunc is a proxy type for DecoderBuilder
|
||||
type DecoderBuilderFunc func() (Decoder, Format)
|
||||
|
||||
func (builderFunc DecoderBuilderFunc) NewDecoder() (Decoder, Format) {
|
||||
return builderFunc()
|
||||
}
|
||||
|
||||
func RegisterDecoder(builder DecoderBuilder) error {
|
||||
decoder, format := builder.NewDecoder()
|
||||
formatStr := format.String()
|
||||
if _, ok := registeredDecoders[formatStr]; ok {
|
||||
return fmt.Errorf("%v has already been registered", format)
|
||||
}
|
||||
|
||||
registeredDecoders[formatStr] = decoder
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDecoder creates a decoder to decode raw audio data in the given format
|
||||
func NewDecoder(format Format) (Decoder, error) {
|
||||
decoder, ok := registeredDecoders[format.String()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s format is not supported", format)
|
||||
}
|
||||
|
||||
return decoder, nil
|
||||
@@ -85,8 +127,15 @@ func calculateChunkInfo(chunk []byte, channels int, sampleSize int) (ChunkInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeInt16Interleaved(endian binary.ByteOrder, chunk []byte, channels int) (Audio, error) {
|
||||
sampleSize := 2
|
||||
func newInt16InterleavedDecoder() (Decoder, Format) {
|
||||
format := &RawFormat{
|
||||
SampleSize: 2,
|
||||
IsFloat: false,
|
||||
Interleaved: true,
|
||||
}
|
||||
|
||||
decoder := DecoderFunc(func(endian binary.ByteOrder, chunk []byte, channels int) (Audio, error) {
|
||||
sampleSize := format.SampleSize
|
||||
chunkInfo, err := calculateChunkInfo(chunk, channels, sampleSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -114,10 +163,21 @@ func decodeInt16Interleaved(endian binary.ByteOrder, chunk []byte, channels int)
|
||||
}
|
||||
|
||||
return container, nil
|
||||
|
||||
})
|
||||
|
||||
return decoder, format
|
||||
}
|
||||
|
||||
func decodeInt16NonInterleaved(endian binary.ByteOrder, chunk []byte, channels int) (Audio, error) {
|
||||
sampleSize := 2
|
||||
func newInt16NonInterleavedDecoder() (Decoder, Format) {
|
||||
format := &RawFormat{
|
||||
SampleSize: 2,
|
||||
IsFloat: false,
|
||||
Interleaved: false,
|
||||
}
|
||||
|
||||
decoder := DecoderFunc(func(endian binary.ByteOrder, chunk []byte, channels int) (Audio, error) {
|
||||
sampleSize := format.SampleSize
|
||||
chunkInfo, err := calculateChunkInfo(chunk, channels, sampleSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -146,10 +206,20 @@ func decodeInt16NonInterleaved(endian binary.ByteOrder, chunk []byte, channels i
|
||||
}
|
||||
|
||||
return container, nil
|
||||
})
|
||||
|
||||
return decoder, format
|
||||
}
|
||||
|
||||
func decodeFloat32Interleaved(endian binary.ByteOrder, chunk []byte, channels int) (Audio, error) {
|
||||
sampleSize := 4
|
||||
func newFloat32InterleavedDecoder() (Decoder, Format) {
|
||||
format := &RawFormat{
|
||||
SampleSize: 4,
|
||||
IsFloat: true,
|
||||
Interleaved: true,
|
||||
}
|
||||
|
||||
decoder := DecoderFunc(func(endian binary.ByteOrder, chunk []byte, channels int) (Audio, error) {
|
||||
sampleSize := format.SampleSize
|
||||
chunkInfo, err := calculateChunkInfo(chunk, channels, sampleSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -178,10 +248,20 @@ func decodeFloat32Interleaved(endian binary.ByteOrder, chunk []byte, channels in
|
||||
}
|
||||
|
||||
return container, nil
|
||||
})
|
||||
|
||||
return decoder, format
|
||||
}
|
||||
|
||||
func decodeFloat32NonInterleaved(endian binary.ByteOrder, chunk []byte, channels int) (Audio, error) {
|
||||
sampleSize := 4
|
||||
func newFloat32NonInterleavedDecoder() (Decoder, Format) {
|
||||
format := &RawFormat{
|
||||
SampleSize: 4,
|
||||
IsFloat: true,
|
||||
Interleaved: false,
|
||||
}
|
||||
|
||||
decoder := DecoderFunc(func(endian binary.ByteOrder, chunk []byte, channels int) (Audio, error) {
|
||||
sampleSize := format.SampleSize
|
||||
chunkInfo, err := calculateChunkInfo(chunk, channels, sampleSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -211,4 +291,7 @@ func decodeFloat32NonInterleaved(endian binary.ByteOrder, chunk []byte, channels
|
||||
}
|
||||
|
||||
return container, nil
|
||||
})
|
||||
|
||||
return decoder, format
|
||||
}
|
||||
|
||||
@@ -7,13 +7,6 @@ import (
|
||||
)
|
||||
|
||||
func BenchmarkDecoder(b *testing.B) {
|
||||
formats := []Format{
|
||||
FormatInt16Interleaved,
|
||||
FormatInt16NonInterleaved,
|
||||
FormatFloat32Interleaved,
|
||||
FormatFloat32NonInterleaved,
|
||||
}
|
||||
|
||||
var nonHostEndian binary.ByteOrder
|
||||
if hostEndian == binary.BigEndian {
|
||||
nonHostEndian = binary.LittleEndian
|
||||
@@ -21,12 +14,9 @@ func BenchmarkDecoder(b *testing.B) {
|
||||
nonHostEndian = binary.BigEndian
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
for format, decoder := range registeredDecoders {
|
||||
format := format
|
||||
decoder, err := NewDecoder(format)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
decoder := decoder
|
||||
|
||||
b.Run(fmt.Sprintf("%sHostEndian", format), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
@@ -93,12 +93,45 @@ func TestCalculateChunkInfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDecoder(t *testing.T) {
|
||||
rawFormats := []RawFormat{
|
||||
{
|
||||
SampleSize: 2,
|
||||
IsFloat: false,
|
||||
Interleaved: false,
|
||||
},
|
||||
{
|
||||
SampleSize: 4,
|
||||
IsFloat: true,
|
||||
Interleaved: false,
|
||||
},
|
||||
{
|
||||
SampleSize: 2,
|
||||
IsFloat: false,
|
||||
Interleaved: true,
|
||||
},
|
||||
{
|
||||
SampleSize: 4,
|
||||
IsFloat: true,
|
||||
Interleaved: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rawFormat := range rawFormats {
|
||||
_, err := NewDecoder(&rawFormat)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeInt16Interleaved(t *testing.T) {
|
||||
raw := []byte{
|
||||
// 16 bits per channel
|
||||
0x01, 0x02, 0x03, 0x04,
|
||||
0x05, 0x06, 0x07, 0x08,
|
||||
}
|
||||
decoder, _ := newInt16InterleavedDecoder()
|
||||
|
||||
t.Run("BigEndian", func(t *testing.T) {
|
||||
expected := &Int16Interleaved{
|
||||
@@ -113,7 +146,7 @@ func TestDecodeInt16Interleaved(t *testing.T) {
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
actual, err := decodeInt16Interleaved(binary.BigEndian, raw, 2)
|
||||
actual, err := decoder.Decode(binary.BigEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -136,7 +169,7 @@ func TestDecodeInt16Interleaved(t *testing.T) {
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
actual, err := decodeInt16Interleaved(binary.LittleEndian, raw, 2)
|
||||
actual, err := decoder.Decode(binary.LittleEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -154,6 +187,8 @@ func TestDecodeInt16NonInterleaved(t *testing.T) {
|
||||
0x05, 0x06, 0x07, 0x08,
|
||||
}
|
||||
|
||||
decoder, _ := newInt16NonInterleavedDecoder()
|
||||
|
||||
t.Run("BigEndian", func(t *testing.T) {
|
||||
expected := &Int16NonInterleaved{
|
||||
Data: [][]int16{
|
||||
@@ -165,7 +200,7 @@ func TestDecodeInt16NonInterleaved(t *testing.T) {
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
actual, err := decodeInt16NonInterleaved(binary.BigEndian, raw, 2)
|
||||
actual, err := decoder.Decode(binary.BigEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -186,7 +221,7 @@ func TestDecodeInt16NonInterleaved(t *testing.T) {
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
actual, err := decodeInt16NonInterleaved(binary.LittleEndian, raw, 2)
|
||||
actual, err := decoder.Decode(binary.LittleEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -204,6 +239,8 @@ func TestDecodeFloat32Interleaved(t *testing.T) {
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
}
|
||||
|
||||
decoder, _ := newFloat32InterleavedDecoder()
|
||||
|
||||
t.Run("BigEndian", func(t *testing.T) {
|
||||
expected := &Float32Interleaved{
|
||||
Data: []float32{
|
||||
@@ -217,7 +254,7 @@ func TestDecodeFloat32Interleaved(t *testing.T) {
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
actual, err := decodeFloat32Interleaved(binary.BigEndian, raw, 2)
|
||||
actual, err := decoder.Decode(binary.BigEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -240,7 +277,7 @@ func TestDecodeFloat32Interleaved(t *testing.T) {
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
actual, err := decodeFloat32Interleaved(binary.LittleEndian, raw, 2)
|
||||
actual, err := decoder.Decode(binary.LittleEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -258,6 +295,8 @@ func TestDecodeFloat32NonInterleaved(t *testing.T) {
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
}
|
||||
|
||||
decoder, _ := newFloat32NonInterleavedDecoder()
|
||||
|
||||
t.Run("BigEndian", func(t *testing.T) {
|
||||
expected := &Float32NonInterleaved{
|
||||
Data: [][]float32{
|
||||
@@ -275,7 +314,7 @@ func TestDecodeFloat32NonInterleaved(t *testing.T) {
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
actual, err := decodeFloat32NonInterleaved(binary.BigEndian, raw, 2)
|
||||
actual, err := decoder.Decode(binary.BigEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -302,7 +341,7 @@ func TestDecodeFloat32NonInterleaved(t *testing.T) {
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
actual, err := decodeFloat32NonInterleaved(binary.LittleEndian, raw, 2)
|
||||
actual, err := decoder.Decode(binary.LittleEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user