From e83603cb47268120951b1ecf994ce94892c5d744 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Thu, 24 Nov 2022 22:05:31 -0800 Subject: [PATCH] Dolby metadata integration --- wavinfo/wave_dbmd_reader.py | 43 ++++++++++++++++++++++++++++++------- wavinfo/wave_reader.py | 22 +++++++++++++------ 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/wavinfo/wave_dbmd_reader.py b/wavinfo/wave_dbmd_reader.py index 57c7760..f6c6c0d 100644 --- a/wavinfo/wave_dbmd_reader.py +++ b/wavinfo/wave_dbmd_reader.py @@ -12,10 +12,11 @@ from struct import unpack from dataclasses import dataclass from typing import Optional, Tuple, Any +from io import BytesIO class SegmentType(IntEnum): """ - + Metadata segment type. """ EndMarker = 0x0 DolbyE = 0x1 @@ -78,6 +79,9 @@ class DolbyDigitalPlusMetadata: class DolbySurroundEncodingMode(Enum): + """ + Dolby surround endcoding mode. + """ RESERVED = 0b11 IN_USE = 0b10 NOT_IN_USE = 0b01 @@ -169,13 +173,13 @@ class DolbyDigitalPlusMetadata: RESERVED = 0b11 - class LanguageCode(Enum): + class LanguageCode(int): """ § 4.3.4.1 - Per ATSC/A52 § 5.4.2.12 this is not in use and always 0xFF - """ - NONE = 0xff + Per ATSC/A52 § 5.4.2.12, this is not in use and always 0xFF. + """ + pass class MixLevel(int): @@ -204,6 +208,8 @@ class DolbyDigitalPlusMetadata: class PreferredDownMixMode(Enum): """ + Indicates the creating engineer's preference of what the receiver should + downmix. § 4.3.8.1 """ NOT_INDICATED = 0b00 @@ -214,6 +220,7 @@ class DolbyDigitalPlusMetadata: class SurroundEXMode(IntEnum): """ + Dolby Surround-EX mode. `dsurexmod` § 4.3.9.1 """ NOT_INDICATED = 0b00 @@ -332,10 +339,15 @@ class DolbyDigitalPlusMetadata: #: Dolby Headphone mode dolby_headphone_encoded: HeadphoneMode + ad_converter_type: ADConverterType compression_profile: RFCompressionProfile dynamic_range: RFCompressionProfile - stream_type: StreamDependency + + #: Indicates if this stream can be decoded independently or not + stream_dependency: StreamDependency + + #: Data rate of this bitstream in kilobits per second datarate_kbps: int @staticmethod @@ -460,11 +472,11 @@ class DolbyDigitalPlusMetadata: ad_converter_type=ad_converter_type, compression_profile=compression, dynamic_range=dynamic_range, - stream_type=stream_info, + stream_dependency=stream_info, datarate_kbps=data_rate) -class WavDolbyChunkReader: +class WavDolbyMetadataReader: """ Reads Dolby bitstream metadata. """ @@ -477,8 +489,23 @@ class WavDolbyChunkReader: #: not recognized). segment_list: Tuple[SegmentType | int, bool, Any] + version: str + def __init__(self, dbmd_data) -> None: self.segment_list = [] + h = BytesIO(dbmd_data) + + v_vec = [] + for _ in range(4): + b = h.read(1) + v_vec.insert(0, str(unpack("B", b[0:1]))) + + self.version = ".".join(v_vec) + + + + + diff --git a/wavinfo/wave_reader.py b/wavinfo/wave_reader.py index 9ba1496..426f7c5 100644 --- a/wavinfo/wave_reader.py +++ b/wavinfo/wave_reader.py @@ -12,6 +12,7 @@ from .wave_ixml_reader import WavIXMLFormat from .wave_bext_reader import WavBextReader from .wave_info_reader import WavInfoChunkReader from .wave_adm_reader import WavADMReader +from .wave_dbmd_reader import WavDolbyMetadataReader #: Calculated statistics about the audio data. WavDataDescriptor = namedtuple('WavDataDescriptor', 'byte_count frame_count') @@ -63,20 +64,23 @@ class WavInfoReader: self.path = absolute_path + #: Wave audio data format self.fmt :Optional[WavAudioFormat] = None - ":class:`wavinfo.wave_reader.WavAudioFormat`" + #: Broadcast-Wave metadata self.bext :Optional[WavBextReader] = None - ":class:`wavinfo.wave_bext_reader.WavBextReader` with Broadcast-WAV metadata" + #: iXML metadata self.ixml :Optional[WavIXMLFormat] = None - ":class:`wavinfo.wave_ixml_reader.WavIXMLFormat` with iXML metadata" + #: ADM Audio Definiton Model metadata self.adm :Optional[WavADMReader]= None - ":class:`wavinfo.wave_axml_reader.WavADMReader` with ADM metadata" + #: Dolby Bitstream Metadata + self.dolby :Optional[WavDolbyMetadataReader] = None + + #: RIFF INFO Metadata self.info :Optional[WavInfoChunkReader]= None - ":class:`wavinfo.wave_info_reader.WavInfoChunkReader` with RIFF INFO metadata" with open(path, 'rb') as f: self.get_wav_info(f) @@ -92,6 +96,7 @@ class WavInfoReader: self.ixml = self._get_ixml(wavfile) self.adm = self._get_adm(wavfile) self.info = self._get_info(wavfile, encoding=self.info_encoding) + self.dolby = self._get_dbmd(wavfile) self.data = self._describe_data() def _find_chunk_data(self, ident, from_stream, default_none=False): @@ -150,9 +155,13 @@ class WavInfoReader: chna = self._find_chunk_data(b'chna', f, default_none=True) return WavADMReader(axml_data=axml, chna_data=chna) if axml and chna else None + def _get_dbmd(self, f): + dbmd_data = self._find_chunk_data(b'dbmd', f, default_none=True) + return WavDolbyMetadataReader(dbmd_data=dbmd_data) if dbmd_data else None + def _get_ixml(self, f): ixml_data = self._find_chunk_data(b'iXML', f, default_none=True) - return None if ixml_data is None else WavIXMLFormat(ixml_data.rstrip(b'\0')) + return WavIXMLFormat(ixml_data.rstrip(b'\0')) if ixml_data else None def walk(self) -> Generator[str,str,Any]: """ @@ -176,6 +185,5 @@ class WavInfoReader: for key in dict.keys(): yield scope, key, dict[key] - def __repr__(self): return 'WavInfoReader({}, {}, {})'.format(self.path, self.info_encoding, self.bext_encoding)