Skip to content

Commit

Permalink
Various fix and cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
wader committed Aug 21, 2024
1 parent 83abdda commit f2a6987
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 118 deletions.
5 changes: 2 additions & 3 deletions format/riff/adm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ func chnaDecode(d *decode.D, size int64) {
d.FieldUTF8("uid", 12)
d.FieldUTF8("track_format_id_reference", 14)
d.FieldUTF8("pack_format_id_reference", 11)
// Skip padding single byte
d.U8()
d.FieldRawLen("padding", 8)
})
}

func axmlDecode(d *decode.D, size int64) {
// TODO(jmarnell): this chunk is all variable xml, so leave as is?
d.FieldRawLen("xml", size*8)
d.FieldUTF8("xml", int(size))
}
4 changes: 2 additions & 2 deletions format/riff/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ var chunkIDDescriptions = scalar.StrMapDescription{

"dmlh": "Extended AVI header",

"chna": "<chna> Chunk, Track UIDs of Audio Definition Model",
"axml": "<axml> Chunk, BWF XML Metadata, e.g. for Audio Definition Model ambisonics and elements",
"chna": "Track UIDs of Audio Definition Model",
"axml": "Audio Definition Model ambisonics and elements",
"dbmd": "Dolby Metadata, e.g. Atmos, AC3, Dolby Digital [Plus]",

"ISMP": "SMPTE timecode",
Expand Down
217 changes: 105 additions & 112 deletions format/riff/dolby.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,87 +5,62 @@ package riff
// https://github.com/DolbyLaboratories/dbmd-atmos-parser

import (
"fmt"
"strings"

"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/scalar"
)

func dbmdDecode(d *decode.D, size int64) any {
version := d.U32()
major := (version >> 24) & 0xFF
minor := (version >> 16) & 0xFF
patch := (version >> 8) & 0xFF
build := version & 0xFF
d.FieldValueStr("version", fmt.Sprintf("%d.%d.%d.%d", major, minor, patch, build))
func dbmdDecode(d *decode.D) any {
d.FieldStruct("version", func(d *decode.D) {
d.FieldU8("major")
d.FieldU8("minor")
d.FieldU8("patch")
d.FieldU8("build")
})

d.FieldArray("metadata_segments", func(d *decode.D) {
for {
seenEnd := false
for !seenEnd {
d.FieldStruct("metadata_segment", func(d *decode.D) {
segmentID := d.FieldU8("metadata_segment_id")

// TODO(jmarnell): I think I need a loop until, but not creating these empty segments
// spec says we're done with 0 ID, so I'd like to not make the empty segment(s)
segmentID := d.FieldU8("id", metadataSegmentTypeMap)
if segmentID == 0 {
if d.BitsLeft() > 0 {
d.SeekRel(d.BitsLeft() * 8)
}
seenEnd = true
return
}

segmentSize := d.FieldU16("metadata_segment_size")
bitsLeft := d.BitsLeft()
segmentSize := d.FieldU16("size")

switch segmentID {
case 1:
case metadataSegmentTypeDolyEMetadata:
parseDolbyE(d)
case 3:
case metadataSegmentTypeDolyEDigitaletadata:
parseDolbyDigital(d)
case 7:
case metadataSegmentTypeDolyDigitalPlusMetadata:
parseDolbyDigitalPlus(d)
case 8:
case metadataSegmentTypeAudioInfo:
parseAudioInfo(d)
case 9:
parseDolbyAtmos(d, segmentSize)
case 10:
parseDolbyAtmosSupplemental(d, segmentSize)
case metadataSegmentTypeDolyAtmos:
parseDolbyAtmos(d)
case metadataSegmentTypeDolbyAtmosSupplemental:
parseDolbyAtmosSupplemental(d)
default:
d.FieldRawLen("unknown_segment_raw", int64(segmentSize*8))
d.FieldRawLen("unknown", int64(segmentSize*8))
}

bytesRemaining := (bitsLeft-d.BitsLeft())/8 - int64(segmentSize)
if bytesRemaining < 0 {
d.Fatalf("Read too many bytes for segment %d, read %d over, expected %d", segmentID, -bytesRemaining, segmentSize)
} else if bytesRemaining > 0 {
d.FieldValueUint("SKIPPED_BYTES", uint64(bytesRemaining))
d.SeekRel((int64(segmentSize) - bytesRemaining) * 8)
}

d.FieldU8("metadata_segment_checksum")
d.FieldU8("checksum", scalar.UintHex)
})
}
})

return nil
}

var compressionDesc = scalar.UintMapDescription{
var compressionDescMap = scalar.UintMapSymStr{
0: "none",
1: "Film, Standard",
2: "Film, Light",
3: "Music, Standard",
4: "Music, Light",
5: "Speech",
// TODO(jmarnell): Can I handle rest is "Reserved"?
}

// TODO(jmarnell): Better way to handle "Reserved"?
func mapWithReserved(m map[uint64]string, key uint64) string {
if val, ok := m[key]; ok {
return val
}
return "Reserved"
1: "film_standard",
2: "film_light",
3: "music_standard",
4: "music_light",
5: "speech",
}

var bitstreamMode = scalar.UintMapDescription{
Expand All @@ -101,20 +76,20 @@ var bitstreamMode = scalar.UintMapDescription{
0b1000: "associated service: karaoke (K)",
}

var binaural = scalar.UintMapDescription{
var binauralRenderModeMap = scalar.UintMapSymStr{
0: "bypass",
1: "near",
2: "far",
3: "mid",
4: "not indicated",
4: "not_indicated",
}

var warpMode = scalar.UintMapDescription{
0: "normal",
1: "warping",
2: "downmix Dolby Pro Logic IIx",
3: "downmix LoRo",
4: "not indicated (Default warping will be applied.)",
var warpModeMap = scalar.UintMap{
0: {Sym: "normal"},
1: {Sym: "warping"},
2: {Sym: "downmix_dolby_pro_logic_iix"},
3: {Sym: "downmix_loro"},
4: {Sym: "not_indicated", Description: "Default warping will be applied"},
}

var trimConfigName = scalar.UintMapDescription{
Expand All @@ -134,9 +109,35 @@ var trimType = scalar.UintMapDescription{
1: "automatic",
}

func parseDolbyE(d *decode.D) {
d.FieldValueStr("metadata_segment_type", "dolby_e")
const (
metadataSegmentTypeEnd = 0
metadataSegmentTypeDolyEMetadata = 1
metadataSegmentTypeDolyReserved2 = 2
metadataSegmentTypeDolyEDigitaletadata = 3
metadataSegmentTypeDolyReserved4 = 4
metadataSegmentTypeDolyReserved5 = 5
metadataSegmentTypeDolyReserved6 = 6
metadataSegmentTypeDolyDigitalPlusMetadata = 7
metadataSegmentTypeAudioInfo = 8
metadataSegmentTypeDolyAtmos = 9
metadataSegmentTypeDolbyAtmosSupplemental = 10
)

var metadataSegmentTypeMap = scalar.UintMapSymStr{
metadataSegmentTypeEnd: "end",
metadataSegmentTypeDolyEMetadata: "doly_e_metadata",
metadataSegmentTypeDolyReserved2: "reserved2",
metadataSegmentTypeDolyEDigitaletadata: "doly_e_digitale_tadata",
metadataSegmentTypeDolyReserved4: "reserved4",
metadataSegmentTypeDolyReserved5: "reserved5",
metadataSegmentTypeDolyReserved6: "reserved6",
metadataSegmentTypeDolyDigitalPlusMetadata: "doly_digital_plus_metadata",
metadataSegmentTypeAudioInfo: "audio_info",
metadataSegmentTypeDolyAtmos: "doly_atmos",
metadataSegmentTypeDolbyAtmosSupplemental: "dolby_atmos_supplemental",
}

func parseDolbyE(d *decode.D) {
d.FieldU8("program_config")
d.FieldU8("frame_rate_code")
d.FieldRawLen("e_SMPTE_time_code", 8*8)
Expand All @@ -146,8 +147,6 @@ func parseDolbyE(d *decode.D) {
}

func parseDolbyDigital(d *decode.D) {
d.FieldValueStr("metadata_segment_type", "dolby_digital")

This comment has been minimized.

Copy link
@JohnnyMarnell

JohnnyMarnell Oct 23, 2024

I don't know if it would ever be needed, but, since it seems the spec can have segments of differing types in any order (kind of like how it is for riff chunk id's?), so my thinking was an explicit type would be nice, e.g. for select(.type == "dolby_digital"), or for clarity just looking. Otherwise, user/reader needs to switch on which props are there/not there?

This comment has been minimized.

Copy link
@JohnnyMarnell

JohnnyMarnell Oct 23, 2024

lol nm I see id now!


d.FieldU8("ac3_program_id")
d.FieldU8("program_info")
d.FieldU8("datarate_info")
Expand All @@ -167,9 +166,8 @@ func parseDolbyDigital(d *decode.D) {
}

func parseDolbyDigitalPlus(d *decode.D) {
d.FieldValueStr("metadata_segment_type", "dolby_digital_plus")

d.FieldU8("program_id")
// TODO: make struct and read U1(?) U1 (lfeon) U3 (bsmod) U3(acmod) fields?
programInfo := d.FieldU8("program_info")
lfeon := programInfo & 0b1_000_000
bsmod := programInfo & 0b0_111_000
Expand All @@ -192,8 +190,8 @@ func parseDolbyDigitalPlus(d *decode.D) {

d.FieldU24LE("ddplus_reserved_b")

d.FieldValueStr("compr1_type", mapWithReserved(compressionDesc, d.FieldU8("compr1")))
d.FieldValueStr("dynrng1_type", mapWithReserved(compressionDesc, d.FieldU8("dynrng1")))
d.FieldU8("compr1", scalar.UintSym("reserved"), compressionDescMap)
d.FieldU8("dynrng1", scalar.UintSym("reserved"), compressionDescMap)

d.FieldU24LE("ddplus_reserved_c")

Expand All @@ -206,8 +204,6 @@ func parseDolbyDigitalPlus(d *decode.D) {
}

func parseAudioInfo(d *decode.D) {
d.FieldValueStr("metadata_segment_type", "audio_info")

d.FieldU8("program_id")
d.FieldUTF8("audio_origin", 32)
d.FieldU32LE("largest_sample_value")
Expand All @@ -224,36 +220,30 @@ func parseAudioInfo(d *decode.D) {
d.FieldUTF8("segment_modified_date", 32)
}

func parseDolbyAtmos(d *decode.D, size uint64) {
d.FieldValueStr("metadata_segment_type", "dolby_atmos")

// d.SeekRel(32 * 8)
str := d.FieldUTF8Null("atmos_dbmd_content_creation_preamble")
d.SeekRel(int64(32-len(str)-1) * 8)

str = d.FieldUTF8Null("atmos_dbmd_content_creation_tool")
d.SeekRel(int64(64-len(str)-1) * 8)
func parseDolbyAtmos(d *decode.D) {
// TODO: both these are fixed size null terminated strings?
d.FieldUTF8NullFixedLen("atmos_dbmd_content_creation_preamble", 32)

This comment has been minimized.

Copy link
@JohnnyMarnell

JohnnyMarnell Oct 23, 2024

If they're ever a different length, will this break? (Though if my original unidiomatic weirdness wouldn't've helped, disregard :)

This comment has been minimized.

Copy link
@wader

wader Oct 25, 2024

Author Owner

If they are not fixed 32 bytes i guess both mine and your would fail in different ways :) would be great to somehow verify what is actually is. mediainfo seem to read it as 64 byte utf8 hmm https://github.com/MediaArea/MediaInfoLib/blob/master/Source/MediaInfo/Audio/File_DolbyAudioMetadata.cpp#L255

d.FieldUTF8NullFixedLen("atmos_dbmd_content_creation_tool", 64)
d.FieldStruct("version", func(d *decode.D) {
d.FieldU8("major")
d.FieldU8("minor")
d.FieldU8("micro")
})

major := d.U8()
minor := d.U8()
micro := d.U8()
d.FieldValueStr("version", fmt.Sprintf("%d.%d.%d", major, minor, micro))
d.SeekRel(53 * 8)
// TODO: what is this?
d.FieldRawLen("unknown0", 53*8)

warpBedReserved := d.U8()
d.FieldValueUint("warp_mode", warpBedReserved&0x7)
d.FieldValueStr("warp_mode_type", warpMode[warpBedReserved&0x7])
d.FieldU8("warp_mode", warpModeMap)

d.SeekRel(15 * 8)
d.SeekRel(80 * 8)
// TODO: what is this?
d.FieldRawLen("unknown1", 15*8)
d.FieldRawLen("unknown2", 80*8)
}

func parseDolbyAtmosSupplemental(d *decode.D, size uint64) {
d.FieldValueStr("metadata_segment_type", "dolby_atmos_supplemental")

sync := d.FieldU32LE("dasms_sync")
d.FieldValueBool("dasms_sync_valid", sync == 0xf8726fbd)
func parseDolbyAtmosSupplemental(d *decode.D) {
d.FieldU32LE("dasms_sync", d.UintAssert(0xf8726fbd), scalar.UintHex)

// TODO: wav.go sets LE default i think?
objectCount := int64(d.FieldU16LE("object_count"))
d.FieldU8LE("reserved")

Expand All @@ -265,30 +255,33 @@ func parseDolbyAtmosSupplemental(d *decode.D, size uint64) {
d.FieldValueStr("trim_type", trimType[autoTrim])
d.FieldValueStr("trim_config_name", trimConfigName[uint64(i)])

//d.SeekRel(14 * 8)
// d.FieldUTF8("raw", 14)
str := d.UTF8(14)
bytes := []byte(str)
var nonZeroBytes []string
for _, b := range bytes {
if b != 0 {
nonZeroBytes = append(nonZeroBytes, fmt.Sprintf("%d", b))
}
}
// TODO: this is null separted list of def strings?
d.FieldUTF8("raw", 14)
// str := d.UTF8(14)
// bytes := []byte(str)
// var nonZeroBytes []string
// for _, b := range bytes {
// if b != 0 {
// nonZeroBytes = append(nonZeroBytes, fmt.Sprintf("%d", b))
// }
// }
// TODO(jmarnell): I think the +3dB trim settings are here.
// Would like this at least as an array of numbers, instead of this CSV string
d.FieldValueStr("trim_defs", strings.Join(nonZeroBytes, ", "))
// d.FieldValueStr("trim_defs", strings.Join(nonZeroBytes, ", "))

i++
})

d.FieldStructNArray("objects", "object", objectCount, func(d *decode.D) {
d.FieldU8LE("object_value")
d.FieldArray("objects", func(d *decode.D) {
for i := int64(0); i < objectCount; i++ {
d.FieldU8("object_value")
}
})

d.FieldStructNArray("binaural_render_modes", "binaural_render_mode", objectCount, func(d *decode.D) {
mode := d.U8LE() & 0x7
d.FieldValueUint("render_mode", mode)
d.FieldValueStr("render_mode_type", binaural[mode])
d.FieldArray("binaural_render_modes", func(d *decode.D) {
// TODO: 0x7 mask needed?
for i := int64(0); i < objectCount; i++ {
d.FieldU8("render_mode", scalar.UintActualFn(func(a uint64) uint64 { return a & 0x7 }), binauralRenderModeMap)
}
})
}
2 changes: 1 addition & 1 deletion format/riff/wav.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func wavDecode(d *decode.D) any {
axmlDecode(d, size)
return false, nil
case "dbmd":
dbmdDecode(d, size)
dbmdDecode(d)
return false, nil

default:
Expand Down

0 comments on commit f2a6987

Please sign in to comment.