mirror of
https://github.com/wader/fq.git
synced 2024-11-28 03:02:55 +03:00
125 lines
3.3 KiB
Go
125 lines
3.3 KiB
Go
package opus
|
|
|
|
// https://tools.ietf.org/html/rfc7845
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/wader/fq/format"
|
|
"github.com/wader/fq/pkg/decode"
|
|
"github.com/wader/fq/pkg/interp"
|
|
)
|
|
|
|
var vorbisComment decode.Group
|
|
|
|
func init() {
|
|
interp.RegisterFormat(decode.Format{
|
|
Name: format.OPUS_PACKET,
|
|
Description: "Opus packet",
|
|
DecodeFn: opusDecode,
|
|
Dependencies: []decode.Dependency{
|
|
{Names: []string{format.VORBIS_COMMENT}, Group: &vorbisComment},
|
|
},
|
|
})
|
|
}
|
|
|
|
func opusDecode(d *decode.D, _ any) any {
|
|
d.Endian = decode.LittleEndian
|
|
|
|
var prefix []byte
|
|
if d.BitsLeft() >= 8*8 {
|
|
prefix = d.PeekBytes(8)
|
|
}
|
|
switch {
|
|
case bytes.Equal(prefix, []byte("OpusHead")):
|
|
d.FieldValueStr("type", "head")
|
|
d.FieldUTF8("prefix", 8)
|
|
d.FieldU8("version")
|
|
channelCount := d.FieldU8("channel_count")
|
|
d.FieldU16("pre_skip")
|
|
d.FieldU32("sample_rate")
|
|
d.FieldU16("output_gain")
|
|
mapFamily := d.FieldU8("map_family")
|
|
if mapFamily != 0 {
|
|
d.FieldU8("stream_count")
|
|
d.FieldU8("coupled_count")
|
|
i := uint64(0)
|
|
d.FieldArrayLoop("channel_mappings", func() bool { return i < channelCount }, func(d *decode.D) {
|
|
d.FieldU8("channel_mapping")
|
|
})
|
|
}
|
|
case bytes.Equal(prefix, []byte("OpusTags")):
|
|
d.FieldValueStr("type", "tags")
|
|
d.FieldUTF8("prefix", 8)
|
|
d.FieldFormat("comment", vorbisComment, nil)
|
|
default:
|
|
d.FieldValueStr("type", "audio")
|
|
d.FieldStruct("toc", func(d *decode.D) {
|
|
d.FieldStruct("config", func(d *decode.D) {
|
|
configurations := map[uint64]struct {
|
|
mode string
|
|
bandwidth string
|
|
frameSize float64
|
|
}{
|
|
0: {"SILK-only", "NB", 10},
|
|
1: {"SILK-only", "NB", 20},
|
|
2: {"SILK-only", "NB", 40},
|
|
3: {"SILK-only", "NB", 60},
|
|
4: {"SILK-only", "MB", 10},
|
|
5: {"SILK-only", "MB", 20},
|
|
6: {"SILK-only", "MB", 40},
|
|
7: {"SILK-only", "MB", 60},
|
|
8: {"SILK-only", "WB", 10},
|
|
9: {"SILK-only", "WB", 20},
|
|
10: {"SILK-only", "WB", 40},
|
|
11: {"SILK-only", "WB", 60},
|
|
12: {"Hybrid", "SWB", 10},
|
|
13: {"Hybrid", "SWB", 20},
|
|
14: {"Hybrid", "FB", 10},
|
|
15: {"Hybrid", "FB", 20},
|
|
16: {"CELT-only", "NB", 2.5},
|
|
17: {"CELT-only", "NB", 5},
|
|
18: {"CELT-only", "NB", 10},
|
|
19: {"CELT-only", "NB", 20},
|
|
20: {"CELT-only", "WB", 2.5},
|
|
21: {"CELT-only", "WB", 5},
|
|
22: {"CELT-only", "WB", 10},
|
|
23: {"CELT-only", "WB", 20},
|
|
24: {"CELT-only", "SWB", 2.5},
|
|
25: {"CELT-only", "SWB", 5},
|
|
26: {"CELT-only", "SWB", 10},
|
|
27: {"CELT-only", "SWB", 20},
|
|
28: {"CELT-only", "FB", 2.5},
|
|
29: {"CELT-only", "FB", 5},
|
|
30: {"CELT-only", "FB", 10},
|
|
31: {"CELT-only", "FB", 20},
|
|
}
|
|
n := d.FieldU5("config")
|
|
config := configurations[n]
|
|
d.FieldValueStr("mode", config.mode)
|
|
d.FieldValueStr("bandwidth", config.bandwidth)
|
|
d.FieldValueFloat("frame_size", config.frameSize)
|
|
})
|
|
d.FieldBool("stereo")
|
|
d.FieldStruct("frames_per_packet", func(d *decode.D) {
|
|
framesPerPacketConfigs := map[uint64]struct {
|
|
frames uint64
|
|
mode string
|
|
}{
|
|
0: {1, "1 frame"},
|
|
1: {2, "2 frames, equal size"},
|
|
2: {2, "2 frames, different size"},
|
|
3: {0, "arbitrary number of frames"},
|
|
}
|
|
n := d.FieldU2("config")
|
|
config := framesPerPacketConfigs[n]
|
|
d.FieldValueU("frames", config.frames)
|
|
d.FieldValueStr("mode", config.mode)
|
|
})
|
|
d.FieldRawLen("data", d.BitsLeft())
|
|
})
|
|
}
|
|
|
|
return nil
|
|
}
|