mirror of
https://github.com/iluvcapra/wavinfo.git
synced 2026-01-02 09:50:41 +00:00
In-progress flake8 fixes
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
from optparse import OptionParser, OptionGroup
|
|
||||||
import datetime
|
import datetime
|
||||||
from . import WavInfoReader
|
from . import WavInfoReader
|
||||||
from . import __version__
|
from . import __version__
|
||||||
|
|
||||||
|
from optparse import OptionParser
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class MyJSONEncoder(json.JSONEncoder):
|
class MyJSONEncoder(json.JSONEncoder):
|
||||||
def default(self, o):
|
def default(self, o):
|
||||||
if isinstance(o, Enum):
|
if isinstance(o, Enum):
|
||||||
@@ -13,23 +15,25 @@ class MyJSONEncoder(json.JSONEncoder):
|
|||||||
else:
|
else:
|
||||||
return super().default(o)
|
return super().default(o)
|
||||||
|
|
||||||
|
|
||||||
class MissingDataError(RuntimeError):
|
class MissingDataError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
|
|
||||||
parser.usage = 'wavinfo (--adm | --ixml) <FILE> +'
|
parser.usage = 'wavinfo (--adm | --ixml) <FILE> +'
|
||||||
|
|
||||||
# parser.add_option('-f', dest='output_format', help='Set the output format',
|
parser.add_option('--adm', dest='adm',
|
||||||
# default='json',
|
help='Output ADM XML',
|
||||||
# metavar='FORMAT')
|
default=False,
|
||||||
|
action='store_true')
|
||||||
|
|
||||||
parser.add_option('--adm', dest='adm', help='Output ADM XML',
|
parser.add_option('--ixml', dest='ixml',
|
||||||
default=False, action='store_true')
|
help='Output iXML',
|
||||||
|
default=False,
|
||||||
parser.add_option('--ixml', dest='ixml', help='Output iXML',
|
action='store_true')
|
||||||
default=False, action='store_true')
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args(sys.argv)
|
(options, args) = parser.parse_args(sys.argv)
|
||||||
for arg in args[1:]:
|
for arg in args[1:]:
|
||||||
@@ -60,7 +64,7 @@ def main():
|
|||||||
|
|
||||||
json.dump(ret_dict, cls=MyJSONEncoder, fp=sys.stdout, indent=2)
|
json.dump(ret_dict, cls=MyJSONEncoder, fp=sys.stdout, indent=2)
|
||||||
except MissingDataError as e:
|
except MissingDataError as e:
|
||||||
print("MissingDataError: Missing metadata (%s) in file %s" % \
|
print("MissingDataError: Missing metadata (%s) in file %s" %
|
||||||
(e, arg), file=sys.stderr)
|
(e, arg), file=sys.stderr)
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import struct
|
import struct
|
||||||
from collections import namedtuple
|
# from collections import namedtuple
|
||||||
|
from typing import NamedTuple, Dict
|
||||||
|
|
||||||
from . import riff_parser
|
from . import riff_parser
|
||||||
|
|
||||||
RF64Context = namedtuple('RF64Context','sample_count bigchunk_table')
|
|
||||||
|
class RF64Context(NamedTuple):
|
||||||
|
sample_count: int
|
||||||
|
bigchunk_table: Dict[str, int]
|
||||||
|
|
||||||
|
|
||||||
def parse_rf64(stream, signature=b'RF64') -> RF64Context:
|
def parse_rf64(stream, signature=b'RF64') -> RF64Context:
|
||||||
start = stream.tell()
|
start = stream.tell()
|
||||||
assert( stream.read(4) == b'WAVE' )
|
assert stream.read(4) == b'WAVE'
|
||||||
|
|
||||||
ds64_chunk = riff_parser.parse_chunk(stream)
|
ds64_chunk = riff_parser.parse_chunk(stream)
|
||||||
assert type(ds64_chunk) is riff_parser.ChunkDescriptor, \
|
assert type(ds64_chunk) is riff_parser.ChunkDescriptor, \
|
||||||
@@ -16,10 +20,10 @@ def parse_rf64(stream, signature = b'RF64') -> RF64Context:
|
|||||||
|
|
||||||
ds64_field_spec = "<QQQI"
|
ds64_field_spec = "<QQQI"
|
||||||
ds64_fields_size = struct.calcsize(ds64_field_spec)
|
ds64_fields_size = struct.calcsize(ds64_field_spec)
|
||||||
assert(ds64_chunk.ident == b'ds64')
|
assert ds64_chunk.ident == b'ds64'
|
||||||
|
|
||||||
ds64_data = ds64_chunk.read_data(stream)
|
ds64_data = ds64_chunk.read_data(stream)
|
||||||
assert(len(ds64_data) >= ds64_fields_size)
|
assert len(ds64_data) >= ds64_fields_size
|
||||||
|
|
||||||
riff_size, data_size, sample_count, length_lookup_table = struct.unpack(
|
riff_size, data_size, sample_count, length_lookup_table = struct.unpack(
|
||||||
ds64_field_spec, ds64_data[0:ds64_fields_size]
|
ds64_field_spec, ds64_data[0:ds64_fields_size]
|
||||||
@@ -30,7 +34,8 @@ def parse_rf64(stream, signature = b'RF64') -> RF64Context:
|
|||||||
# chunksize64size = struct.calcsize(chunksize64format)
|
# chunksize64size = struct.calcsize(chunksize64format)
|
||||||
|
|
||||||
for _ in range(length_lookup_table):
|
for _ in range(length_lookup_table):
|
||||||
bigname, bigsize = struct.unpack_from(chunksize64format, ds64_data,
|
bigname, bigsize = struct.unpack_from(chunksize64format,
|
||||||
|
ds64_data,
|
||||||
offset=ds64_fields_size)
|
offset=ds64_fields_size)
|
||||||
bigchunk_table[bigname] = bigsize
|
bigchunk_table[bigname] = bigsize
|
||||||
|
|
||||||
@@ -40,4 +45,3 @@ def parse_rf64(stream, signature = b'RF64') -> RF64Context:
|
|||||||
stream.seek(start, 0)
|
stream.seek(start, 0)
|
||||||
return RF64Context(sample_count=sample_count,
|
return RF64Context(sample_count=sample_count,
|
||||||
bigchunk_table=bigchunk_table)
|
bigchunk_table=bigchunk_table)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
|
# from optparse import Option
|
||||||
from optparse import Option
|
|
||||||
import struct
|
import struct
|
||||||
from .rf64_parser import parse_rf64, RF64Context
|
from .rf64_parser import parse_rf64, RF64Context
|
||||||
from typing import NamedTuple, Union, List, Optional
|
from typing import NamedTuple, Union, List, Optional
|
||||||
@@ -56,7 +55,7 @@ def parse_chunk(stream, rf64_context=None):
|
|||||||
rf64_context = parse_rf64(stream=stream, signature=ident)
|
rf64_context = parse_rf64(stream=stream, signature=ident)
|
||||||
|
|
||||||
assert rf64_context is not None, \
|
assert rf64_context is not None, \
|
||||||
f"Sentinel data size 0xFFFFFFFF found outside of RF64 context"
|
"Sentinel data size 0xFFFFFFFF found outside of RF64 context"
|
||||||
|
|
||||||
data_size = rf64_context.bigchunk_table[ident]
|
data_size = rf64_context.bigchunk_table[ident]
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
|
|
||||||
# def binary_to_string(binary_value):
|
# def binary_to_string(binary_value):
|
||||||
# return reduce(lambda val, el: val + "{:02x}".format(el), binary_value, '')
|
# return reduce(lambda val, el: val + "{:02x}".format(el),
|
||||||
|
# binary_value, '')
|
||||||
|
|
||||||
# class UMIDParser:
|
# class UMIDParser:
|
||||||
# """
|
# """
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ from typing import Optional
|
|||||||
|
|
||||||
from lxml import etree as ET
|
from lxml import etree as ET
|
||||||
|
|
||||||
|
|
||||||
ChannelEntry = namedtuple('ChannelEntry', "track_index uid track_ref pack_ref")
|
ChannelEntry = namedtuple('ChannelEntry', "track_index uid track_ref pack_ref")
|
||||||
|
|
||||||
|
|
||||||
class WavADMReader:
|
class WavADMReader:
|
||||||
"""
|
"""
|
||||||
Reads XML data from an EBU ADM (Audio Definiton Model) WAV File.
|
Reads XML data from an EBU ADM (Audio Definiton Model) WAV File.
|
||||||
@@ -37,9 +39,13 @@ class WavADMReader:
|
|||||||
|
|
||||||
# these values are either ascii or all null
|
# these values are either ascii or all null
|
||||||
|
|
||||||
self.channel_uids.append(ChannelEntry(track_index - 1,
|
self.channel_uids.append(
|
||||||
uid.decode('ascii') , track_ref.decode('ascii'),
|
ChannelEntry(track_index - 1,
|
||||||
pack_ref.decode('ascii')))
|
uid.decode('ascii'),
|
||||||
|
track_ref.decode('ascii'),
|
||||||
|
pack_ref.decode('ascii')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
offset += calcsize(uid_fmt)
|
offset += calcsize(uid_fmt)
|
||||||
|
|
||||||
@@ -105,7 +111,7 @@ class WavADMReader:
|
|||||||
*object_name*, *object_id*,
|
*object_name*, *object_id*,
|
||||||
*pack_format_name*, *pack_type*, *channel_format_name*
|
*pack_format_name*, *pack_type*, *channel_format_name*
|
||||||
"""
|
"""
|
||||||
channel_info = next((x for x in self.channel_uids \
|
channel_info = next((x for x in self.channel_uids
|
||||||
if x.track_index == index), None)
|
if x.track_index == index), None)
|
||||||
|
|
||||||
if channel_info is None:
|
if channel_info is None:
|
||||||
@@ -115,12 +121,12 @@ class WavADMReader:
|
|||||||
|
|
||||||
nsmap = self.axml.getroot().nsmap
|
nsmap = self.axml.getroot().nsmap
|
||||||
|
|
||||||
afext = self.axml.find(".//audioFormatExtended", namespaces=nsmap)
|
afext = self.axml.find(".//audioFormatExtended",
|
||||||
|
namespaces=nsmap)
|
||||||
|
|
||||||
trackformat_elem = afext.find(
|
trackformat_elem = afext.find(
|
||||||
"audioTrackFormat[@audioTrackFormatID='%s']" \
|
"audioTrackFormat[@audioTrackFormatID='%s']"
|
||||||
% channel_info.track_ref,
|
% channel_info.track_ref, namespaces=nsmap)
|
||||||
namespaces=nsmap)
|
|
||||||
|
|
||||||
stream_id = trackformat_elem[0].text
|
stream_id = trackformat_elem[0].text
|
||||||
|
|
||||||
@@ -130,14 +136,14 @@ class WavADMReader:
|
|||||||
namespaces=nsmap)
|
namespaces=nsmap)
|
||||||
channelformat_id = channelformatref_elem.text
|
channelformat_id = channelformatref_elem.text
|
||||||
|
|
||||||
packformatref_elem = afext\
|
packformatref_elem = afext.find(
|
||||||
.find(("audioStreamFormat[@audioStreamFormatID='%s']"
|
("audioStreamFormat[@audioStreamFormatID='%s']"
|
||||||
"/audioPackFormatIDRef") % stream_id,
|
"/audioPackFormatIDRef") % stream_id,
|
||||||
namespaces=nsmap)
|
namespaces=nsmap)
|
||||||
packformat_id = packformatref_elem.text
|
packformat_id = packformatref_elem.text
|
||||||
|
|
||||||
channelformat_elem = afext\
|
channelformat_elem = afext\
|
||||||
.find("audioChannelFormat[@audioChannelFormatID='%s']" \
|
.find("audioChannelFormat[@audioChannelFormatID='%s']"
|
||||||
% channelformat_id,
|
% channelformat_id,
|
||||||
namespaces=nsmap)
|
namespaces=nsmap)
|
||||||
ret_dict['channel_format_name'] = channelformat_elem.get(
|
ret_dict['channel_format_name'] = channelformat_elem.get(
|
||||||
@@ -151,7 +157,7 @@ class WavADMReader:
|
|||||||
ret_dict['pack_format_name'] = packformat_elem.get(
|
ret_dict['pack_format_name'] = packformat_elem.get(
|
||||||
"audioPackFormatName")
|
"audioPackFormatName")
|
||||||
|
|
||||||
object_elem = afext.find("audioObject[audioPackFormatIDRef = '%s']" \
|
object_elem = afext.find("audioObject[audioPackFormatIDRef = '%s']"
|
||||||
% packformat_id,
|
% packformat_id,
|
||||||
namespaces=nsmap)
|
namespaces=nsmap)
|
||||||
|
|
||||||
@@ -159,7 +165,7 @@ class WavADMReader:
|
|||||||
object_id = object_elem.get("audioObjectID")
|
object_id = object_elem.get("audioObjectID")
|
||||||
ret_dict['object_id'] = object_id
|
ret_dict['object_id'] = object_id
|
||||||
|
|
||||||
content_elem = afext.find("audioContent/[audioObjectIDRef = '%s']" \
|
content_elem = afext.find("audioContent/[audioObjectIDRef = '%s']"
|
||||||
% object_id,
|
% object_id,
|
||||||
namespaces=nsmap)
|
namespaces=nsmap)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import struct
|
|||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
class WavBextReader:
|
class WavBextReader:
|
||||||
def __init__(self, bext_data, encoding):
|
def __init__(self, bext_data, encoding):
|
||||||
"""
|
"""
|
||||||
@@ -20,7 +21,7 @@ class WavBextReader:
|
|||||||
|
|
||||||
def sanitize_bytes(b: bytes) -> str:
|
def sanitize_bytes(b: bytes) -> str:
|
||||||
# honestly can't remember why I'm stripping nulls this way
|
# honestly can't remember why I'm stripping nulls this way
|
||||||
first_null = next((index for index, byte in enumerate(b) \
|
first_null = next((index for index, byte in enumerate(b)
|
||||||
if byte == 0), None)
|
if byte == 0), None)
|
||||||
trimmed = b if first_null is None else b[:first_null]
|
trimmed = b if first_null is None else b[:first_null]
|
||||||
decoded = trimmed.decode(encoding)
|
decoded = trimmed.decode(encoding)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ IBM Corporation and Microsoft Corporation
|
|||||||
https://www.aelius.com/njh/wavemetatools/doc/riffmci.pdf
|
https://www.aelius.com/njh/wavemetatools/doc/riffmci.pdf
|
||||||
"""
|
"""
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import encodings
|
|
||||||
from .riff_parser import ChunkDescriptor
|
from .riff_parser import ChunkDescriptor
|
||||||
|
|
||||||
from struct import unpack, calcsize
|
from struct import unpack, calcsize
|
||||||
@@ -252,8 +251,8 @@ class WavCuesReader:
|
|||||||
:returns: a tuple of the the cue's label (if present) and note (if
|
:returns: a tuple of the the cue's label (if present) and note (if
|
||||||
present)
|
present)
|
||||||
"""
|
"""
|
||||||
label = next((l.text for l in self.labels
|
label = next((label.text for label in self.labels
|
||||||
if l.name == cue_ident), None)
|
if label.name == cue_ident), None)
|
||||||
note = next((n.text for n in self.notes
|
note = next((n.text for n in self.notes
|
||||||
if n.name == cue_ident), None)
|
if n.name == cue_ident), None)
|
||||||
return (label, note)
|
return (label, note)
|
||||||
@@ -285,4 +284,3 @@ class WavCuesReader:
|
|||||||
retval[n]['length'] = r
|
retval[n]['length'] = r
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from typing import List, Tuple, Any, Union
|
|||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
class SegmentType(IntEnum):
|
class SegmentType(IntEnum):
|
||||||
"""
|
"""
|
||||||
Metadata segment type.
|
Metadata segment type.
|
||||||
@@ -77,7 +78,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
MUTE = 0b111
|
MUTE = 0b111
|
||||||
"-∞ dB"
|
"-∞ dB"
|
||||||
|
|
||||||
|
|
||||||
class DolbySurroundEncodingMode(Enum):
|
class DolbySurroundEncodingMode(Enum):
|
||||||
"""
|
"""
|
||||||
Dolby surround endcoding mode.
|
Dolby surround endcoding mode.
|
||||||
@@ -87,7 +87,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
NOT_IN_USE = 0b01
|
NOT_IN_USE = 0b01
|
||||||
NOT_INDICATED = 0b00
|
NOT_INDICATED = 0b00
|
||||||
|
|
||||||
|
|
||||||
class BitStreamMode(Enum):
|
class BitStreamMode(Enum):
|
||||||
"""
|
"""
|
||||||
Dolby Digital Plus `bsmod` field
|
Dolby Digital Plus `bsmod` field
|
||||||
@@ -122,7 +121,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
should be interpreted as karaoke.
|
should be interpreted as karaoke.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class AudioCodingMode(Enum):
|
class AudioCodingMode(Enum):
|
||||||
"""
|
"""
|
||||||
Dolby Digital Plus `acmod` field
|
Dolby Digital Plus `acmod` field
|
||||||
@@ -144,7 +142,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
CH_ORD_3_2 = 0b111
|
CH_ORD_3_2 = 0b111
|
||||||
"LCR + LR surround"
|
"LCR + LR surround"
|
||||||
|
|
||||||
|
|
||||||
class CenterDownMixLevel(Enum):
|
class CenterDownMixLevel(Enum):
|
||||||
"""
|
"""
|
||||||
§ 4.3.3.1
|
§ 4.3.3.1
|
||||||
@@ -161,7 +158,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
|
|
||||||
RESERVED = 0b11
|
RESERVED = 0b11
|
||||||
|
|
||||||
|
|
||||||
class SurroundDownMixLevel(Enum):
|
class SurroundDownMixLevel(Enum):
|
||||||
"""
|
"""
|
||||||
Dolby Digital Plus `surmixlev` field
|
Dolby Digital Plus `surmixlev` field
|
||||||
@@ -172,7 +168,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
MUTE = 0b10
|
MUTE = 0b10
|
||||||
RESERVED = 0b11
|
RESERVED = 0b11
|
||||||
|
|
||||||
|
|
||||||
class LanguageCode(int):
|
class LanguageCode(int):
|
||||||
"""
|
"""
|
||||||
§ 4.3.4.1
|
§ 4.3.4.1
|
||||||
@@ -181,21 +176,18 @@ class DolbyDigitalPlusMetadata:
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MixLevel(int):
|
class MixLevel(int):
|
||||||
"""
|
"""
|
||||||
§ 4.3.6.2
|
§ 4.3.6.2
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DialnormLevel(int):
|
class DialnormLevel(int):
|
||||||
"""
|
"""
|
||||||
§ 4.3.4.4
|
§ 4.3.4.4
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class RoomType(Enum):
|
class RoomType(Enum):
|
||||||
"""
|
"""
|
||||||
`roomtyp` 4.3.6.3
|
`roomtyp` 4.3.6.3
|
||||||
@@ -205,7 +197,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
SMALL_ROOM_FLAT_CURVE = 0b10
|
SMALL_ROOM_FLAT_CURVE = 0b10
|
||||||
RESERVED = 0b11
|
RESERVED = 0b11
|
||||||
|
|
||||||
|
|
||||||
class PreferredDownMixMode(Enum):
|
class PreferredDownMixMode(Enum):
|
||||||
"""
|
"""
|
||||||
Indicates the creating engineer's preference of what the receiver
|
Indicates the creating engineer's preference of what the receiver
|
||||||
@@ -217,7 +208,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
STEREO = 0b10
|
STEREO = 0b10
|
||||||
PRO_LOGIC_2 = 0b11
|
PRO_LOGIC_2 = 0b11
|
||||||
|
|
||||||
|
|
||||||
class SurroundEXMode(IntEnum):
|
class SurroundEXMode(IntEnum):
|
||||||
"""
|
"""
|
||||||
Dolby Surround-EX mode.
|
Dolby Surround-EX mode.
|
||||||
@@ -228,7 +218,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
SEX = 0b10
|
SEX = 0b10
|
||||||
PRO_LOGIC_2 = 0b11
|
PRO_LOGIC_2 = 0b11
|
||||||
|
|
||||||
|
|
||||||
class HeadphoneMode(IntEnum):
|
class HeadphoneMode(IntEnum):
|
||||||
"""
|
"""
|
||||||
`dheadphonmod` § 4.3.9.2
|
`dheadphonmod` § 4.3.9.2
|
||||||
@@ -238,12 +227,10 @@ class DolbyDigitalPlusMetadata:
|
|||||||
DOLBY_HEADPHONE = 0b10
|
DOLBY_HEADPHONE = 0b10
|
||||||
RESERVED = 0b11
|
RESERVED = 0b11
|
||||||
|
|
||||||
|
|
||||||
class ADConverterType(Enum):
|
class ADConverterType(Enum):
|
||||||
STANDARD = 0
|
STANDARD = 0
|
||||||
HDCD = 1
|
HDCD = 1
|
||||||
|
|
||||||
|
|
||||||
class StreamDependency(Enum):
|
class StreamDependency(Enum):
|
||||||
"""
|
"""
|
||||||
Encodes `ddplus_info1.stream_type` field § 4.3.12.1
|
Encodes `ddplus_info1.stream_type` field § 4.3.12.1
|
||||||
@@ -254,7 +241,6 @@ class DolbyDigitalPlusMetadata:
|
|||||||
INDEPENDENT_FROM_DOLBY_DIGITAL = 2
|
INDEPENDENT_FROM_DOLBY_DIGITAL = 2
|
||||||
RESERVED = 3
|
RESERVED = 3
|
||||||
|
|
||||||
|
|
||||||
class RFCompressionProfile(Enum):
|
class RFCompressionProfile(Enum):
|
||||||
"""
|
"""
|
||||||
`compr1` RF compression profile
|
`compr1` RF compression profile
|
||||||
@@ -363,9 +349,12 @@ class DolbyDigitalPlusMetadata:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def surround_config(b):
|
def surround_config(b):
|
||||||
return DolbyDigitalPlusMetadata.CenterDownMixLevel(b & 0x30 >> 4), \
|
return DolbyDigitalPlusMetadata\
|
||||||
DolbyDigitalPlusMetadata.SurroundDownMixLevel(b & 0xc >> 2), \
|
.CenterDownMixLevel(b & 0x30 >> 4),\
|
||||||
DolbyDigitalPlusMetadata.DolbySurroundEncodingMode(b & 0x3)
|
DolbyDigitalPlusMetadata\
|
||||||
|
.SurroundDownMixLevel(b & 0xc >> 2), \
|
||||||
|
DolbyDigitalPlusMetadata\
|
||||||
|
.DolbySurroundEncodingMode(b & 0x3)
|
||||||
|
|
||||||
def dialnorm_info(b):
|
def dialnorm_info(b):
|
||||||
return (b & 0x80) > 0, b & 0x40 > 0, b & 0x20 > 0, \
|
return (b & 0x80) > 0, b & 0x40 > 0, b & 0x20 > 0, \
|
||||||
@@ -521,7 +510,7 @@ class DolbyAtmosMetadata:
|
|||||||
|
|
||||||
return DolbyAtmosMetadata(tool_name=toolname,
|
return DolbyAtmosMetadata(tool_name=toolname,
|
||||||
tool_version=(major, minor, fix),
|
tool_version=(major, minor, fix),
|
||||||
warp_mode=DolbyAtmosMetadata\
|
warp_mode=DolbyAtmosMetadata
|
||||||
.WarpMode(warp_mode))
|
.WarpMode(warp_mode))
|
||||||
|
|
||||||
|
|
||||||
@@ -541,12 +530,10 @@ class DolbyAtmosSupplementalMetadata:
|
|||||||
MID = 0x03
|
MID = 0x03
|
||||||
NOT_INDICATED = 0x04
|
NOT_INDICATED = 0x04
|
||||||
|
|
||||||
|
|
||||||
object_count: int
|
object_count: int
|
||||||
render_modes: List['DolbyAtmosSupplementalMetadata.BinauralRenderMode']
|
render_modes: List['DolbyAtmosSupplementalMetadata.BinauralRenderMode']
|
||||||
trim_modes: List[int]
|
trim_modes: List[int]
|
||||||
|
|
||||||
|
|
||||||
MAGIC = 0xf8726fbd
|
MAGIC = 0xf8726fbd
|
||||||
TRIM_CONFIG_COUNT = 9
|
TRIM_CONFIG_COUNT = 9
|
||||||
|
|
||||||
@@ -607,7 +594,6 @@ class WavDolbyMetadataReader:
|
|||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, dbmd_data):
|
def __init__(self, dbmd_data):
|
||||||
self.segment_list = []
|
self.segment_list = []
|
||||||
|
|
||||||
@@ -646,21 +632,21 @@ class WavDolbyMetadataReader:
|
|||||||
"""
|
"""
|
||||||
Every valid Dolby Digital Plus metadata segment in the file.
|
Every valid Dolby Digital Plus metadata segment in the file.
|
||||||
"""
|
"""
|
||||||
return [x[2] for x in self.segment_list \
|
return [x[2] for x in self.segment_list
|
||||||
if x[0] == SegmentType.DolbyDigitalPlus and x[1]]
|
if x[0] == SegmentType.DolbyDigitalPlus and x[1]]
|
||||||
|
|
||||||
def dolby_atmos(self) -> List[DolbyAtmosMetadata]:
|
def dolby_atmos(self) -> List[DolbyAtmosMetadata]:
|
||||||
"""
|
"""
|
||||||
Every valid Dolby Atmos metadata segment in the file.
|
Every valid Dolby Atmos metadata segment in the file.
|
||||||
"""
|
"""
|
||||||
return [x[2] for x in self.segment_list \
|
return [x[2] for x in self.segment_list
|
||||||
if x[0] == SegmentType.DolbyAtmos and x[1]]
|
if x[0] == SegmentType.DolbyAtmos and x[1]]
|
||||||
|
|
||||||
def dolby_atmos_supplemental(self) -> List[DolbyAtmosSupplementalMetadata]:
|
def dolby_atmos_supplemental(self) -> List[DolbyAtmosSupplementalMetadata]:
|
||||||
"""
|
"""
|
||||||
Every valid Dolby Atmos Supplemental metadata segment in the file.
|
Every valid Dolby Atmos Supplemental metadata segment in the file.
|
||||||
"""
|
"""
|
||||||
return [x[2] for x in self.segment_list \
|
return [x[2] for x in self.segment_list
|
||||||
if x[0] == SegmentType.DolbyAtmosSupplemental and x[1]]
|
if x[0] == SegmentType.DolbyAtmosSupplemental and x[1]]
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from .riff_parser import parse_chunk, ListChunkDescriptor
|
|||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
class WavInfoChunkReader:
|
class WavInfoChunkReader:
|
||||||
|
|
||||||
def __init__(self, f, encoding):
|
def __init__(self, f, encoding):
|
||||||
@@ -52,8 +53,8 @@ class WavInfoChunkReader:
|
|||||||
self.commissioned: Optional[str] = self._get_field(f, b'ICMS')
|
self.commissioned: Optional[str] = self._get_field(f, b'ICMS')
|
||||||
|
|
||||||
def _get_field(self, f, field_ident) -> Optional[str]:
|
def _get_field(self, f, field_ident) -> Optional[str]:
|
||||||
search = next(((chunk.start, chunk.length) \
|
search = next(((chunk.start, chunk.length)
|
||||||
for chunk in self.info_chunk.children \
|
for chunk in self.info_chunk.children
|
||||||
if chunk.ident == field_ident),
|
if chunk.ident == field_ident),
|
||||||
None)
|
None)
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class IXMLTrack(NamedTuple):
|
|||||||
name: str
|
name: str
|
||||||
function: str
|
function: str
|
||||||
|
|
||||||
|
|
||||||
class SteinbergMetadata:
|
class SteinbergMetadata:
|
||||||
"""
|
"""
|
||||||
Vendor-specific Steinberg metadata.
|
Vendor-specific Steinberg metadata.
|
||||||
@@ -155,6 +156,7 @@ class WavIXMLFormat:
|
|||||||
"""
|
"""
|
||||||
iXML recorder metadata.
|
iXML recorder metadata.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, xml):
|
def __init__(self, xml):
|
||||||
"""
|
"""
|
||||||
Parse iXML.
|
Parse iXML.
|
||||||
@@ -192,14 +194,11 @@ class WavIXMLFormat:
|
|||||||
for track in self.parsed.find("./TRACK_LIST").iter():
|
for track in self.parsed.find("./TRACK_LIST").iter():
|
||||||
if track.tag == 'TRACK':
|
if track.tag == 'TRACK':
|
||||||
yield IXMLTrack(
|
yield IXMLTrack(
|
||||||
channel_index=
|
channel_index=track.xpath('string(CHANNEL_INDEX/text())'),
|
||||||
track.xpath('string(CHANNEL_INDEX/text())'),
|
interleave_index=track.xpath(
|
||||||
interleave_index=
|
'string(INTERLEAVE_INDEX/text())'),
|
||||||
track.xpath('string(INTERLEAVE_INDEX/text())'),
|
name=track.xpath('string(NAME/text())'),
|
||||||
name=
|
function=track.xpath('string(FUNCTION/text())')
|
||||||
track.xpath('string(NAME/text())'),
|
|
||||||
function=
|
|
||||||
track.xpath('string(FUNCTION/text())')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ from .wave_dbmd_reader import WavDolbyMetadataReader
|
|||||||
from .wave_cues_reader import WavCuesReader
|
from .wave_cues_reader import WavCuesReader
|
||||||
|
|
||||||
#: Calculated statistics about the audio data.
|
#: Calculated statistics about the audio data.
|
||||||
|
|
||||||
|
|
||||||
class WavDataDescriptor(NamedTuple):
|
class WavDataDescriptor(NamedTuple):
|
||||||
byte_count: int
|
byte_count: int
|
||||||
frame_count: int
|
frame_count: int
|
||||||
@@ -28,6 +30,7 @@ class WavAudioFormat(NamedTuple):
|
|||||||
block_align: int
|
block_align: int
|
||||||
bits_per_sample: int
|
bits_per_sample: int
|
||||||
|
|
||||||
|
|
||||||
class WavInfoReader:
|
class WavInfoReader:
|
||||||
"""
|
"""
|
||||||
Parse a WAV audio file for metadata.
|
Parse a WAV audio file for metadata.
|
||||||
@@ -112,7 +115,7 @@ class WavInfoReader:
|
|||||||
|
|
||||||
def _find_chunk_data(self, ident, from_stream,
|
def _find_chunk_data(self, ident, from_stream,
|
||||||
default_none=False) -> Optional[bytes]:
|
default_none=False) -> Optional[bytes]:
|
||||||
top_chunks = (chunk for chunk in self.main_list \
|
top_chunks = (chunk for chunk in self.main_list
|
||||||
if type(chunk) is ChunkDescriptor and chunk.ident == ident)
|
if type(chunk) is ChunkDescriptor and chunk.ident == ident)
|
||||||
|
|
||||||
chunk_descriptor = next(top_chunks, None) \
|
chunk_descriptor = next(top_chunks, None) \
|
||||||
@@ -122,14 +125,14 @@ class WavInfoReader:
|
|||||||
if chunk_descriptor else None
|
if chunk_descriptor else None
|
||||||
|
|
||||||
def _find_list_chunk(self, signature) -> Optional[ListChunkDescriptor]:
|
def _find_list_chunk(self, signature) -> Optional[ListChunkDescriptor]:
|
||||||
top_chunks = (chunk for chunk in self.main_list \
|
top_chunks = (chunk for chunk in self.main_list
|
||||||
if type(chunk) is ListChunkDescriptor and \
|
if type(chunk) is ListChunkDescriptor and
|
||||||
chunk.signature == signature)
|
chunk.signature == signature)
|
||||||
|
|
||||||
return next(top_chunks, None)
|
return next(top_chunks, None)
|
||||||
|
|
||||||
def _describe_data(self):
|
def _describe_data(self):
|
||||||
data_chunk = next(c for c in self.main_list \
|
data_chunk = next(c for c in self.main_list
|
||||||
if type(c) is ChunkDescriptor and c.ident == b'data')
|
if type(c) is ChunkDescriptor and c.ident == b'data')
|
||||||
|
|
||||||
assert isinstance(self.fmt, WavAudioFormat)
|
assert isinstance(self.fmt, WavAudioFormat)
|
||||||
@@ -155,7 +158,7 @@ class WavInfoReader:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _get_info(self, f, encoding):
|
def _get_info(self, f, encoding):
|
||||||
finder = (chunk.signature for chunk in self.main_list \
|
finder = (chunk.signature for chunk in self.main_list
|
||||||
if type(chunk) is ListChunkDescriptor)
|
if type(chunk) is ListChunkDescriptor)
|
||||||
|
|
||||||
if b'INFO' in finder:
|
if b'INFO' in finder:
|
||||||
@@ -181,8 +184,8 @@ class WavInfoReader:
|
|||||||
return WavIXMLFormat(ixml_data.rstrip(b'\0')) if ixml_data else None
|
return WavIXMLFormat(ixml_data.rstrip(b'\0')) if ixml_data else None
|
||||||
|
|
||||||
def _get_cue(self, f):
|
def _get_cue(self, f):
|
||||||
cue = next((cue_chunk for cue_chunk in self.main_list if \
|
cue = next((cue_chunk for cue_chunk in self.main_list if
|
||||||
type(cue_chunk) is ChunkDescriptor and \
|
type(cue_chunk) is ChunkDescriptor and
|
||||||
cue_chunk.ident == b'cue '), None)
|
cue_chunk.ident == b'cue '), None)
|
||||||
|
|
||||||
adtl = self._find_list_chunk(b'adtl')
|
adtl = self._find_list_chunk(b'adtl')
|
||||||
@@ -197,7 +200,8 @@ class WavInfoReader:
|
|||||||
return WavCuesReader.read_all(f, cue, labls, ltxts, notes,
|
return WavCuesReader.read_all(f, cue, labls, ltxts, notes,
|
||||||
fallback_encoding=self.info_encoding)
|
fallback_encoding=self.info_encoding)
|
||||||
|
|
||||||
def walk(self) -> Generator[str,str,Any]: #FIXME: this should probably be named "iter()"
|
# FIXME: this should probably be named "iter()"
|
||||||
|
def walk(self) -> Generator[str, str, Any]:
|
||||||
"""
|
"""
|
||||||
Walk all of the available metadata fields.
|
Walk all of the available metadata fields.
|
||||||
|
|
||||||
@@ -216,7 +220,8 @@ class WavInfoReader:
|
|||||||
yield scope, field, attr.__getattribute__(field)
|
yield scope, field, attr.__getattribute__(field)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
dict = self.__getattribute__(scope).to_dict() if self.__getattribute__(scope) else {}
|
dict = self.__getattribute__(scope).to_dict(
|
||||||
|
) if self.__getattribute__(scope) else {}
|
||||||
for key in dict.keys():
|
for key in dict.keys():
|
||||||
yield scope, key, dict[key]
|
yield scope, key, dict[key]
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ def main():
|
|||||||
help='Search descriptions',
|
help='Search descriptions',
|
||||||
metavar='DESC')
|
metavar='DESC')
|
||||||
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args(sys.argv)
|
(options, args) = parser.parse_args(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user