From af5c83b8fc57110eee47681cb835b12e645569c8 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 26 Nov 2022 18:25:12 -0800 Subject: [PATCH] Documentation --- docs/source/command_line.rst | 94 ++++++++++++++++++++++++++++++++++++ docs/source/index.rst | 10 ++-- docs/source/quickstart.rst | 5 ++ tests/test_walk.py | 2 +- wavinfo/__main__.py | 19 +++++--- wavinfo/wave_reader.py | 17 ++++--- 6 files changed, 126 insertions(+), 21 deletions(-) create mode 100644 docs/source/command_line.rst diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst new file mode 100644 index 0000000..251919d --- /dev/null +++ b/docs/source/command_line.rst @@ -0,0 +1,94 @@ +Using `wavinfo` from the Command Line +===================================== + +`wavinfo` installs a command-line entry point that will read wav files +from the command line and output metadata to stdout. + +.. code-block:: shell + + $ wavinfo [--ixml | --adm] INFILE + + +By default, `wavinfo` will output a JSON dictionary for each file argument. + + +Options +------- + +Two option flags will change the behavior of the command: + +``--ixml`` + The *\-\-ixml* flag will cause `wavinfo` to output the iXML metadata payload + of each input wave file, or will emit an error message to stderr if iXML + metadata is not present. + +``--adm`` + The *\-\-adm* flag will cause `wavinfo` to output the ADM XML metadata + payload of each input wave file, or will emit an error message to stderr if + ADM XML metadata is not present. + +These options are mutually-exclusive, with `\-\-adm` taking precedence. + + +Example Output +-------------- + +.. code-block:: javascript + + { + "filename": "tests/test_files/sounddevices/A101_1.WAV", + "run_date": "2022-11-26T17:56:38.342935", + "application": "wavinfo 2.1.0", + "scopes": { + "fmt": { + "audio_format": 1, + "channel_count": 2, + "sample_rate": 48000, + "byte_rate": 288000, + "block_align": 6, + "bits_per_sample": 24 + }, + "data": { + "byte_count": 1441434, + "frame_count": 240239 + }, + "ixml": { + "track_list": [ + { + "channel_index": "1", + "interleave_index": "1", + "name": "MKH516 A", + "function": "" + }, + { + "channel_index": "2", + "interleave_index": "2", + "name": "Boom", + "function": "" + } + ], + "project": "BMH", + "scene": "A101", + "take": "1", + "tape": "18Y12M31", + "family_uid": "USSDVGR1112089007124001008206300", + "family_name": null + }, + "bext": { + "description": "sSPEED=023.976-ND\r\nsTAKE=1\r\nsUBITS=$12311801\r\nsSWVER=2.67\r\nsPROJECT=BMH\r\nsSCENE=A101\r\nsFILENAME=A101_1.WAV\r\nsTAPE=18Y12M31\r\nsTRK1=MKH516 A\r\nsTRK2=Boom\r\nsNOTE=\r\n", + "originator": "Sound Dev: 702T S#GR1112089007", + "originator_ref": "USSDVGR1112089007124001008206301", + "originator_date": "2018-12-31", + "originator_time": "12:40:00", + "time_reference": 2190940753, + "version": 1, + "umid": "0000000000000000000000000000000000000000000000000000000000000000", + "coding_history": "A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch\r\n", + "loudness_value": null, + "loudness_range": null, + "max_true_peak": null, + "max_momentary_loudness": null, + "max_shortterm_loudness": null + } + } + } + diff --git a/docs/source/index.rst b/docs/source/index.rst index 79ce1a5..047c11b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -13,14 +13,12 @@ music production metadata. .. toctree:: :maxdepth: 1 - :caption: Notes + :glob: + :numbered: quickstart - scopes/bext.rst - scopes/ixml.rst - scopes/adm.rst - scopes/dolby.rst - scopes/info.rst + command_line + scopes/* classes diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 37f4b13..1fcf208 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -1,6 +1,11 @@ wavinfo Quickstart ==================== +All metadata is read by an instance of :class:`WaveInfoReader`. +Each type of metadata, iXML, Broadcast-WAV etc. is accessible through *scopes*, properties on an +instance of :class:`WaveInfoReader`. + + .. code-block:: python :caption: Using wavinfo diff --git a/tests/test_walk.py b/tests/test_walk.py index 63df1ab..38d7aab 100644 --- a/tests/test_walk.py +++ b/tests/test_walk.py @@ -8,7 +8,7 @@ class TestWalk(unittest.TestCase): info = wavinfo.WavInfoReader(test_file) tested_data , tested_format = False, False - for scope, key, value in info.walk(): + for scope, key, value in info.iter(): if scope == 'fmt': if key == 'channel_count': tested_format = True diff --git a/wavinfo/__main__.py b/wavinfo/__main__.py index c3e585d..4420f54 100644 --- a/wavinfo/__main__.py +++ b/wavinfo/__main__.py @@ -1,6 +1,7 @@ from optparse import OptionParser, OptionGroup import datetime from . import WavInfoReader +from . import __version__ import sys import json from enum import Enum @@ -18,7 +19,7 @@ class MissingDataError(RuntimeError): def main(): parser = OptionParser() - parser.usage = 'wavinfo [FILES]' + parser.usage = 'wavinfo (--adm | --ixml) [FILES]' # parser.add_option('-f', dest='output_format', help='Set the output format', # default='json', @@ -38,15 +39,20 @@ def main(): if this_file.adm: sys.stdout.write(this_file.adm.xml_str()) else: - raise MissingDataError("--adm option active but ADM metadata not present") + raise MissingDataError("adm") elif options.ixml: if this_file.ixml: sys.stdout.write(this_file.ixml.xml_bytes()) else: - raise MissingDataError("--ixml option active but iXML metadata not present") + raise MissingDataError("ixml") else: - ret_dict = {'file_argument': arg, 'run_date': datetime.datetime.now().isoformat() , 'scopes': {}} - for scope, name, value in this_file.walk(): + ret_dict = { + 'filename': arg, + 'run_date': datetime.datetime.now().isoformat() , + 'application': "wavinfo " + __version__, + 'scopes': {} + } + for scope, name, value in this_file.iter(): if scope not in ret_dict['scopes'].keys(): ret_dict['scopes'][scope] = {} @@ -54,8 +60,7 @@ def main(): json.dump(ret_dict, cls=MyJSONEncoder, fp=sys.stdout, indent=2) except MissingDataError as e: - print("Missing metadata error in file %s" % arg, file=sys.stderr) - print(e, file=sys.stderr) + print("MissingDataError: Missing metadata (%s) in file %s" % (e, arg), file=sys.stderr) continue except Exception as e: raise e diff --git a/wavinfo/wave_reader.py b/wavinfo/wave_reader.py index 2bb18c3..cbc1fd1 100644 --- a/wavinfo/wave_reader.py +++ b/wavinfo/wave_reader.py @@ -64,22 +64,25 @@ class WavInfoReader: self.path = absolute_path - #: Wave audio data format + #: Wave audio data format. self.fmt :Optional[WavAudioFormat] = None - #: Broadcast-Wave metadata + #: Statistics of the `data` section. + self.data :Optional[WavDataDescriptor] = None + + #: Broadcast-Wave metadata. self.bext :Optional[WavBextReader] = None - #: iXML metadata + #: iXML metadata. self.ixml :Optional[WavIXMLFormat] = None - #: ADM Audio Definiton Model metadata + #: ADM Audio Definiton Model metadata. self.adm :Optional[WavADMReader]= None - #: Dolby Bitstream Metadata + #: Dolby bitstream metadata. self.dolby :Optional[WavDolbyMetadataReader] = None - #: RIFF INFO Metadata + #: RIFF INFO metadata. self.info :Optional[WavInfoChunkReader]= None with open(path, 'rb') as f: @@ -163,7 +166,7 @@ class WavInfoReader: ixml_data = self._find_chunk_data(b'iXML', f, default_none=True) return WavIXMLFormat(ixml_data.rstrip(b'\0')) if ixml_data else None - def walk(self) -> Generator[str,str,Any]: + def iter(self) -> Generator[str,str,Any]: """ Walk all of the available metadata fields.