@@ -5,87 +5,62 @@ package riff
5
5
// https://github.com/DolbyLaboratories/dbmd-atmos-parser
6
6
7
7
import (
8
- "fmt"
9
- "strings"
10
-
11
8
"github.com/wader/fq/pkg/decode"
12
9
"github.com/wader/fq/pkg/scalar"
13
10
)
14
11
15
- func dbmdDecode (d * decode.D , size int64 ) any {
16
- version := d . U32 ()
17
- major := ( version >> 24 ) & 0xFF
18
- minor := ( version >> 16 ) & 0xFF
19
- patch := ( version >> 8 ) & 0xFF
20
- build := version & 0xFF
21
- d . FieldValueStr ( "version" , fmt . Sprintf ( "%d.%d.%d.%d" , major , minor , patch , build ) )
12
+ func dbmdDecode (d * decode.D ) any {
13
+ d . FieldStruct ( " version" , func ( d * decode. D ) {
14
+ d . FieldU8 ( "major" )
15
+ d . FieldU8 ( "minor" )
16
+ d . FieldU8 ( "patch" )
17
+ d . FieldU8 ( " build" )
18
+ } )
22
19
23
20
d .FieldArray ("metadata_segments" , func (d * decode.D ) {
24
- for {
21
+ seenEnd := false
22
+ for ! seenEnd {
25
23
d .FieldStruct ("metadata_segment" , func (d * decode.D ) {
26
- segmentID := d .FieldU8 ("metadata_segment_id" )
27
-
28
- // TODO(jmarnell): I think I need a loop until, but not creating these empty segments
29
- // spec says we're done with 0 ID, so I'd like to not make the empty segment(s)
24
+ segmentID := d .FieldU8 ("id" , metadataSegmentTypeMap )
30
25
if segmentID == 0 {
31
- if d .BitsLeft () > 0 {
32
- d .SeekRel (d .BitsLeft () * 8 )
33
- }
26
+ seenEnd = true
34
27
return
35
28
}
36
29
37
- segmentSize := d .FieldU16 ("metadata_segment_size" )
38
- bitsLeft := d .BitsLeft ()
30
+ segmentSize := d .FieldU16 ("size" )
39
31
40
32
switch segmentID {
41
- case 1 :
33
+ case metadataSegmentTypeDolyEMetadata :
42
34
parseDolbyE (d )
43
- case 3 :
35
+ case metadataSegmentTypeDolyEDigitaletadata :
44
36
parseDolbyDigital (d )
45
- case 7 :
37
+ case metadataSegmentTypeDolyDigitalPlusMetadata :
46
38
parseDolbyDigitalPlus (d )
47
- case 8 :
39
+ case metadataSegmentTypeAudioInfo :
48
40
parseAudioInfo (d )
49
- case 9 :
50
- parseDolbyAtmos (d , segmentSize )
51
- case 10 :
52
- parseDolbyAtmosSupplemental (d , segmentSize )
41
+ case metadataSegmentTypeDolyAtmos :
42
+ parseDolbyAtmos (d )
43
+ case metadataSegmentTypeDolbyAtmosSupplemental :
44
+ parseDolbyAtmosSupplemental (d )
53
45
default :
54
- d .FieldRawLen ("unknown_segment_raw " , int64 (segmentSize * 8 ))
46
+ d .FieldRawLen ("unknown " , int64 (segmentSize * 8 ))
55
47
}
56
48
57
- bytesRemaining := (bitsLeft - d .BitsLeft ())/ 8 - int64 (segmentSize )
58
- if bytesRemaining < 0 {
59
- d .Fatalf ("Read too many bytes for segment %d, read %d over, expected %d" , segmentID , - bytesRemaining , segmentSize )
60
- } else if bytesRemaining > 0 {
61
- d .FieldValueUint ("SKIPPED_BYTES" , uint64 (bytesRemaining ))
62
- d .SeekRel ((int64 (segmentSize ) - bytesRemaining ) * 8 )
63
- }
64
-
65
- d .FieldU8 ("metadata_segment_checksum" )
49
+ d .FieldU8 ("checksum" , scalar .UintHex )
66
50
})
67
51
}
68
52
})
69
53
70
54
return nil
71
55
}
72
56
73
- var compressionDesc = scalar.UintMapDescription {
57
+ var compressionDescMap = scalar.UintMapSymStr {
74
58
0 : "none" ,
75
- 1 : "Film, Standard" ,
76
- 2 : "Film, Light" ,
77
- 3 : "Music, Standard" ,
78
- 4 : "Music, Light" ,
79
- 5 : "Speech" ,
80
- // TODO(jmarnell): Can I handle rest is "Reserved"?
81
- }
82
-
83
- // TODO(jmarnell): Better way to handle "Reserved"?
84
- func mapWithReserved (m map [uint64 ]string , key uint64 ) string {
85
- if val , ok := m [key ]; ok {
86
- return val
87
- }
88
- return "Reserved"
59
+ 1 : "film_standard" ,
60
+ 2 : "film_light" ,
61
+ 3 : "music_standard" ,
62
+ 4 : "music_light" ,
63
+ 5 : "speech" ,
89
64
}
90
65
91
66
var bitstreamMode = scalar.UintMapDescription {
@@ -101,20 +76,20 @@ var bitstreamMode = scalar.UintMapDescription{
101
76
0b1000 : "associated service: karaoke (K)" ,
102
77
}
103
78
104
- var binaural = scalar.UintMapDescription {
79
+ var binauralRenderModeMap = scalar.UintMapSymStr {
105
80
0 : "bypass" ,
106
81
1 : "near" ,
107
82
2 : "far" ,
108
83
3 : "mid" ,
109
- 4 : "not indicated " ,
84
+ 4 : "not_indicated " ,
110
85
}
111
86
112
- var warpMode = scalar.UintMapDescription {
113
- 0 : "normal" ,
114
- 1 : "warping" ,
115
- 2 : "downmix Dolby Pro Logic IIx" ,
116
- 3 : "downmix LoRo" ,
117
- 4 : "not indicated ( Default warping will be applied.)" ,
87
+ var warpModeMap = scalar.UintMap {
88
+ 0 : { Sym : "normal" } ,
89
+ 1 : { Sym : "warping" } ,
90
+ 2 : { Sym : "downmix_dolby_pro_logic_iix" } ,
91
+ 3 : { Sym : "downmix_loro" } ,
92
+ 4 : { Sym : "not_indicated" , Description : " Default warping will be applied" } ,
118
93
}
119
94
120
95
var trimConfigName = scalar.UintMapDescription {
@@ -134,9 +109,35 @@ var trimType = scalar.UintMapDescription{
134
109
1 : "automatic" ,
135
110
}
136
111
137
- func parseDolbyE (d * decode.D ) {
138
- d .FieldValueStr ("metadata_segment_type" , "dolby_e" )
112
+ const (
113
+ metadataSegmentTypeEnd = 0
114
+ metadataSegmentTypeDolyEMetadata = 1
115
+ metadataSegmentTypeDolyReserved2 = 2
116
+ metadataSegmentTypeDolyEDigitaletadata = 3
117
+ metadataSegmentTypeDolyReserved4 = 4
118
+ metadataSegmentTypeDolyReserved5 = 5
119
+ metadataSegmentTypeDolyReserved6 = 6
120
+ metadataSegmentTypeDolyDigitalPlusMetadata = 7
121
+ metadataSegmentTypeAudioInfo = 8
122
+ metadataSegmentTypeDolyAtmos = 9
123
+ metadataSegmentTypeDolbyAtmosSupplemental = 10
124
+ )
125
+
126
+ var metadataSegmentTypeMap = scalar.UintMapSymStr {
127
+ metadataSegmentTypeEnd : "end" ,
128
+ metadataSegmentTypeDolyEMetadata : "doly_e_metadata" ,
129
+ metadataSegmentTypeDolyReserved2 : "reserved2" ,
130
+ metadataSegmentTypeDolyEDigitaletadata : "doly_e_digitale_tadata" ,
131
+ metadataSegmentTypeDolyReserved4 : "reserved4" ,
132
+ metadataSegmentTypeDolyReserved5 : "reserved5" ,
133
+ metadataSegmentTypeDolyReserved6 : "reserved6" ,
134
+ metadataSegmentTypeDolyDigitalPlusMetadata : "doly_digital_plus_metadata" ,
135
+ metadataSegmentTypeAudioInfo : "audio_info" ,
136
+ metadataSegmentTypeDolyAtmos : "doly_atmos" ,
137
+ metadataSegmentTypeDolbyAtmosSupplemental : "dolby_atmos_supplemental" ,
138
+ }
139
139
140
+ func parseDolbyE (d * decode.D ) {
140
141
d .FieldU8 ("program_config" )
141
142
d .FieldU8 ("frame_rate_code" )
142
143
d .FieldRawLen ("e_SMPTE_time_code" , 8 * 8 )
@@ -146,8 +147,6 @@ func parseDolbyE(d *decode.D) {
146
147
}
147
148
148
149
func parseDolbyDigital (d * decode.D ) {
149
- d .FieldValueStr ("metadata_segment_type" , "dolby_digital" )
150
-
151
150
d .FieldU8 ("ac3_program_id" )
152
151
d .FieldU8 ("program_info" )
153
152
d .FieldU8 ("datarate_info" )
@@ -167,9 +166,8 @@ func parseDolbyDigital(d *decode.D) {
167
166
}
168
167
169
168
func parseDolbyDigitalPlus (d * decode.D ) {
170
- d .FieldValueStr ("metadata_segment_type" , "dolby_digital_plus" )
171
-
172
169
d .FieldU8 ("program_id" )
170
+ // TODO: make struct and read U1(?) U1 (lfeon) U3 (bsmod) U3(acmod) fields?
173
171
programInfo := d .FieldU8 ("program_info" )
174
172
lfeon := programInfo & 0b1_000_000
175
173
bsmod := programInfo & 0b0_111_000
@@ -192,8 +190,8 @@ func parseDolbyDigitalPlus(d *decode.D) {
192
190
193
191
d .FieldU24LE ("ddplus_reserved_b" )
194
192
195
- d .FieldValueStr ( "compr1_type " , mapWithReserved ( compressionDesc , d . FieldU8 ( "compr1" )) )
196
- d .FieldValueStr ( "dynrng1_type " , mapWithReserved ( compressionDesc , d . FieldU8 ( "dynrng1" )) )
193
+ d .FieldU8 ( "compr1 " , scalar . UintSym ( "reserved" ), compressionDescMap )
194
+ d .FieldU8 ( "dynrng1 " , scalar . UintSym ( "reserved" ), compressionDescMap )
197
195
198
196
d .FieldU24LE ("ddplus_reserved_c" )
199
197
@@ -206,8 +204,6 @@ func parseDolbyDigitalPlus(d *decode.D) {
206
204
}
207
205
208
206
func parseAudioInfo (d * decode.D ) {
209
- d .FieldValueStr ("metadata_segment_type" , "audio_info" )
210
-
211
207
d .FieldU8 ("program_id" )
212
208
d .FieldUTF8 ("audio_origin" , 32 )
213
209
d .FieldU32LE ("largest_sample_value" )
@@ -224,36 +220,30 @@ func parseAudioInfo(d *decode.D) {
224
220
d .FieldUTF8 ("segment_modified_date" , 32 )
225
221
}
226
222
227
- func parseDolbyAtmos (d * decode.D , size uint64 ) {
228
- d . FieldValueStr ( "metadata_segment_type" , "dolby_atmos" )
229
-
230
- // d.SeekRel(32 * 8 )
231
- str := d . FieldUTF8Null ( "atmos_dbmd_content_creation_preamble" )
232
- d . SeekRel ( int64 ( 32 - len ( str ) - 1 ) * 8 )
233
-
234
- str = d . FieldUTF8Null ( "atmos_dbmd_content_creation_tool " )
235
- d . SeekRel ( int64 ( 64 - len ( str ) - 1 ) * 8 )
223
+ func parseDolbyAtmos (d * decode.D ) {
224
+ // TODO: both these are fixed size null terminated strings?
225
+ d . FieldUTF8NullFixedLen ( "atmos_dbmd_content_creation_preamble" , 32 )
226
+ d . FieldUTF8NullFixedLen ( "atmos_dbmd_content_creation_tool" , 64 )
227
+ d . FieldStruct ( "version" , func ( d * decode. D ) {
228
+ d . FieldU8 ( "major" )
229
+ d . FieldU8 ( "minor" )
230
+ d . FieldU8 ( "micro " )
231
+ } )
236
232
237
- major := d .U8 ()
238
- minor := d .U8 ()
239
- micro := d .U8 ()
240
- d .FieldValueStr ("version" , fmt .Sprintf ("%d.%d.%d" , major , minor , micro ))
241
- d .SeekRel (53 * 8 )
233
+ // TODO: what is this?
234
+ d .FieldRawLen ("unknown0" , 53 * 8 )
242
235
243
- warpBedReserved := d .U8 ()
244
- d .FieldValueUint ("warp_mode" , warpBedReserved & 0x7 )
245
- d .FieldValueStr ("warp_mode_type" , warpMode [warpBedReserved & 0x7 ])
236
+ d .FieldU8 ("warp_mode" , warpModeMap )
246
237
247
- d .SeekRel (15 * 8 )
248
- d .SeekRel (80 * 8 )
238
+ // TODO: what is this?
239
+ d .FieldRawLen ("unknown1" , 15 * 8 )
240
+ d .FieldRawLen ("unknown2" , 80 * 8 )
249
241
}
250
242
251
- func parseDolbyAtmosSupplemental (d * decode.D , size uint64 ) {
252
- d .FieldValueStr ("metadata_segment_type" , "dolby_atmos_supplemental" )
253
-
254
- sync := d .FieldU32LE ("dasms_sync" )
255
- d .FieldValueBool ("dasms_sync_valid" , sync == 0xf8726fbd )
243
+ func parseDolbyAtmosSupplemental (d * decode.D ) {
244
+ d .FieldU32LE ("dasms_sync" , d .UintAssert (0xf8726fbd ), scalar .UintHex )
256
245
246
+ // TODO: wav.go sets LE default i think?
257
247
objectCount := int64 (d .FieldU16LE ("object_count" ))
258
248
d .FieldU8LE ("reserved" )
259
249
@@ -265,30 +255,33 @@ func parseDolbyAtmosSupplemental(d *decode.D, size uint64) {
265
255
d .FieldValueStr ("trim_type" , trimType [autoTrim ])
266
256
d .FieldValueStr ("trim_config_name" , trimConfigName [uint64 (i )])
267
257
268
- //d.SeekRel(14 * 8)
269
- // d.FieldUTF8("raw", 14)
270
- str := d .UTF8 (14 )
271
- bytes := []byte (str )
272
- var nonZeroBytes []string
273
- for _ , b := range bytes {
274
- if b != 0 {
275
- nonZeroBytes = append (nonZeroBytes , fmt .Sprintf ("%d" , b ))
276
- }
277
- }
258
+ // TODO: this is null separted list of def strings?
259
+ d .FieldUTF8 ("raw" , 14 )
260
+ // str := d.UTF8(14)
261
+ // bytes := []byte(str)
262
+ // var nonZeroBytes []string
263
+ // for _, b := range bytes {
264
+ // if b != 0 {
265
+ // nonZeroBytes = append(nonZeroBytes, fmt.Sprintf("%d", b))
266
+ // }
267
+ // }
278
268
// TODO(jmarnell): I think the +3dB trim settings are here.
279
269
// Would like this at least as an array of numbers, instead of this CSV string
280
- d .FieldValueStr ("trim_defs" , strings .Join (nonZeroBytes , ", " ))
270
+ // d.FieldValueStr("trim_defs", strings.Join(nonZeroBytes, ", "))
281
271
282
272
i ++
283
273
})
284
274
285
- d .FieldStructNArray ("objects" , "object" , objectCount , func (d * decode.D ) {
286
- d .FieldU8LE ("object_value" )
275
+ d .FieldArray ("objects" , func (d * decode.D ) {
276
+ for i := int64 (0 ); i < objectCount ; i ++ {
277
+ d .FieldU8 ("object_value" )
278
+ }
287
279
})
288
280
289
- d .FieldStructNArray ("binaural_render_modes" , "binaural_render_mode" , objectCount , func (d * decode.D ) {
290
- mode := d .U8LE () & 0x7
291
- d .FieldValueUint ("render_mode" , mode )
292
- d .FieldValueStr ("render_mode_type" , binaural [mode ])
281
+ d .FieldArray ("binaural_render_modes" , func (d * decode.D ) {
282
+ // TODO: 0x7 mask needed?
283
+ for i := int64 (0 ); i < objectCount ; i ++ {
284
+ d .FieldU8 ("render_mode" , scalar .UintActualFn (func (a uint64 ) uint64 { return a & 0x7 }), binauralRenderModeMap )
285
+ }
293
286
})
294
287
}
0 commit comments