mirror of
https://github.com/iluvcapra/wavinfo.git
synced 2026-01-02 18:00:41 +00:00
Dolby metadata integration
This commit is contained in:
@@ -12,10 +12,11 @@ from struct import unpack
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional, Tuple, Any
|
from typing import Optional, Tuple, Any
|
||||||
|
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
class SegmentType(IntEnum):
|
class SegmentType(IntEnum):
|
||||||
"""
|
"""
|
||||||
|
Metadata segment type.
|
||||||
"""
|
"""
|
||||||
EndMarker = 0x0
|
EndMarker = 0x0
|
||||||
DolbyE = 0x1
|
DolbyE = 0x1
|
||||||
@@ -78,6 +79,9 @@ class DolbyDigitalPlusMetadata:
|
|||||||
|
|
||||||
|
|
||||||
class DolbySurroundEncodingMode(Enum):
|
class DolbySurroundEncodingMode(Enum):
|
||||||
|
"""
|
||||||
|
Dolby surround endcoding mode.
|
||||||
|
"""
|
||||||
RESERVED = 0b11
|
RESERVED = 0b11
|
||||||
IN_USE = 0b10
|
IN_USE = 0b10
|
||||||
NOT_IN_USE = 0b01
|
NOT_IN_USE = 0b01
|
||||||
@@ -169,13 +173,13 @@ class DolbyDigitalPlusMetadata:
|
|||||||
RESERVED = 0b11
|
RESERVED = 0b11
|
||||||
|
|
||||||
|
|
||||||
class LanguageCode(Enum):
|
class LanguageCode(int):
|
||||||
"""
|
"""
|
||||||
§ 4.3.4.1
|
§ 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):
|
class MixLevel(int):
|
||||||
@@ -204,6 +208,8 @@ class DolbyDigitalPlusMetadata:
|
|||||||
|
|
||||||
class PreferredDownMixMode(Enum):
|
class PreferredDownMixMode(Enum):
|
||||||
"""
|
"""
|
||||||
|
Indicates the creating engineer's preference of what the receiver should
|
||||||
|
downmix.
|
||||||
§ 4.3.8.1
|
§ 4.3.8.1
|
||||||
"""
|
"""
|
||||||
NOT_INDICATED = 0b00
|
NOT_INDICATED = 0b00
|
||||||
@@ -214,6 +220,7 @@ class DolbyDigitalPlusMetadata:
|
|||||||
|
|
||||||
class SurroundEXMode(IntEnum):
|
class SurroundEXMode(IntEnum):
|
||||||
"""
|
"""
|
||||||
|
Dolby Surround-EX mode.
|
||||||
`dsurexmod` § 4.3.9.1
|
`dsurexmod` § 4.3.9.1
|
||||||
"""
|
"""
|
||||||
NOT_INDICATED = 0b00
|
NOT_INDICATED = 0b00
|
||||||
@@ -332,10 +339,15 @@ class DolbyDigitalPlusMetadata:
|
|||||||
|
|
||||||
#: Dolby Headphone mode
|
#: Dolby Headphone mode
|
||||||
dolby_headphone_encoded: HeadphoneMode
|
dolby_headphone_encoded: HeadphoneMode
|
||||||
|
|
||||||
ad_converter_type: ADConverterType
|
ad_converter_type: ADConverterType
|
||||||
compression_profile: RFCompressionProfile
|
compression_profile: RFCompressionProfile
|
||||||
dynamic_range: 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
|
datarate_kbps: int
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -460,11 +472,11 @@ class DolbyDigitalPlusMetadata:
|
|||||||
ad_converter_type=ad_converter_type,
|
ad_converter_type=ad_converter_type,
|
||||||
compression_profile=compression,
|
compression_profile=compression,
|
||||||
dynamic_range=dynamic_range,
|
dynamic_range=dynamic_range,
|
||||||
stream_type=stream_info,
|
stream_dependency=stream_info,
|
||||||
datarate_kbps=data_rate)
|
datarate_kbps=data_rate)
|
||||||
|
|
||||||
|
|
||||||
class WavDolbyChunkReader:
|
class WavDolbyMetadataReader:
|
||||||
"""
|
"""
|
||||||
Reads Dolby bitstream metadata.
|
Reads Dolby bitstream metadata.
|
||||||
"""
|
"""
|
||||||
@@ -477,8 +489,23 @@ class WavDolbyChunkReader:
|
|||||||
#: not recognized).
|
#: not recognized).
|
||||||
segment_list: Tuple[SegmentType | int, bool, Any]
|
segment_list: Tuple[SegmentType | int, bool, Any]
|
||||||
|
|
||||||
|
version: str
|
||||||
|
|
||||||
def __init__(self, dbmd_data) -> None:
|
def __init__(self, dbmd_data) -> None:
|
||||||
self.segment_list = []
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from .wave_ixml_reader import WavIXMLFormat
|
|||||||
from .wave_bext_reader import WavBextReader
|
from .wave_bext_reader import WavBextReader
|
||||||
from .wave_info_reader import WavInfoChunkReader
|
from .wave_info_reader import WavInfoChunkReader
|
||||||
from .wave_adm_reader import WavADMReader
|
from .wave_adm_reader import WavADMReader
|
||||||
|
from .wave_dbmd_reader import WavDolbyMetadataReader
|
||||||
|
|
||||||
#: Calculated statistics about the audio data.
|
#: Calculated statistics about the audio data.
|
||||||
WavDataDescriptor = namedtuple('WavDataDescriptor', 'byte_count frame_count')
|
WavDataDescriptor = namedtuple('WavDataDescriptor', 'byte_count frame_count')
|
||||||
@@ -63,20 +64,23 @@ class WavInfoReader:
|
|||||||
|
|
||||||
self.path = absolute_path
|
self.path = absolute_path
|
||||||
|
|
||||||
|
#: Wave audio data format
|
||||||
self.fmt :Optional[WavAudioFormat] = None
|
self.fmt :Optional[WavAudioFormat] = None
|
||||||
":class:`wavinfo.wave_reader.WavAudioFormat`"
|
|
||||||
|
|
||||||
|
#: Broadcast-Wave metadata
|
||||||
self.bext :Optional[WavBextReader] = None
|
self.bext :Optional[WavBextReader] = None
|
||||||
":class:`wavinfo.wave_bext_reader.WavBextReader` with Broadcast-WAV metadata"
|
|
||||||
|
|
||||||
|
#: iXML metadata
|
||||||
self.ixml :Optional[WavIXMLFormat] = None
|
self.ixml :Optional[WavIXMLFormat] = None
|
||||||
":class:`wavinfo.wave_ixml_reader.WavIXMLFormat` with iXML metadata"
|
|
||||||
|
|
||||||
|
#: ADM Audio Definiton Model metadata
|
||||||
self.adm :Optional[WavADMReader]= None
|
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
|
self.info :Optional[WavInfoChunkReader]= None
|
||||||
":class:`wavinfo.wave_info_reader.WavInfoChunkReader` with RIFF INFO metadata"
|
|
||||||
|
|
||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
self.get_wav_info(f)
|
self.get_wav_info(f)
|
||||||
@@ -92,6 +96,7 @@ class WavInfoReader:
|
|||||||
self.ixml = self._get_ixml(wavfile)
|
self.ixml = self._get_ixml(wavfile)
|
||||||
self.adm = self._get_adm(wavfile)
|
self.adm = self._get_adm(wavfile)
|
||||||
self.info = self._get_info(wavfile, encoding=self.info_encoding)
|
self.info = self._get_info(wavfile, encoding=self.info_encoding)
|
||||||
|
self.dolby = self._get_dbmd(wavfile)
|
||||||
self.data = self._describe_data()
|
self.data = self._describe_data()
|
||||||
|
|
||||||
def _find_chunk_data(self, ident, from_stream, default_none=False):
|
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)
|
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
|
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):
|
def _get_ixml(self, f):
|
||||||
ixml_data = self._find_chunk_data(b'iXML', f, default_none=True)
|
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]:
|
def walk(self) -> Generator[str,str,Any]:
|
||||||
"""
|
"""
|
||||||
@@ -176,6 +185,5 @@ class WavInfoReader:
|
|||||||
for key in dict.keys():
|
for key in dict.keys():
|
||||||
yield scope, key, dict[key]
|
yield scope, key, dict[key]
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'WavInfoReader({}, {}, {})'.format(self.path, self.info_encoding, self.bext_encoding)
|
return 'WavInfoReader({}, {}, {})'.format(self.path, self.info_encoding, self.bext_encoding)
|
||||||
|
|||||||
Reference in New Issue
Block a user