Documentation

This commit is contained in:
Jamie Hardt
2019-01-04 19:58:03 -08:00
parent 9e259e9d6c
commit 83bf656ad3
8 changed files with 223 additions and 132 deletions

View File

@@ -53,104 +53,6 @@ The length of the file in frames (interleaved samples) and bytes is available, a
>>> (48000, 2, 6, 24)
```
### Broadcast WAV Extension
A WAV file produced to Broadcast-WAV specifications will have the broadcast metadata extension,
which includes a 256-character free text descrption, creating entity identifier (usually the
recording application or equipment), the date and time of recording and a time reference for
timecode synchronization.
The `coding_history` is designed to contain a record of every conversion performed on the audio
file.
In this example (from a Sound Devices 702T) the bext metadata contains scene/take slating
information in the `description`. Here also the `originator_ref` is a serial number conforming
to EBU Rec 99.
If the bext metadata conforms to EBU 3285 v1, it will contain the WAV's 32 or 64 byte SMPTE
330M UMID. The 32-byte version of the UMID is usually just a random number, while the 64-byte
UMID will also have information on the recording date and time, recording equipment and entity,
and geolocation data.
If the bext metadata conforms to EBU 3285 v2, it will hold precomputed program loudness values
as described by EBU Rec 128.
```python
print(info.bext.description)
print("----------")
print("Originator:", info.bext.originator)
print("Originator Ref:", info.bext.originator_ref)
print("Originator Date:", info.bext.originator_date)
print("Originator Time:", info.bext.originator_time)
print("Time Reference:", info.bext.time_reference)
print(info.bext.coding_history)
```
sSPEED=023.976-ND
sTAKE=1
sUBITS=$12311801
sSWVER=2.67
sPROJECT=BMH
sSCENE=A101
sFILENAME=A101_1.WAV
sTAPE=18Y12M31
sTRK1=MKH516 A
sTRK2=Boom
sNOTE=
----------
Originator: Sound Dev: 702T S#GR1112089007
Originator Ref: USSDVGR1112089007124001008206301
Originator Date: 2018-12-31
Originator Time: 12:40:00
Time Reference: 2190940753
A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch
### iXML Production Recorder Metadata
iXML allows an XML document to be embedded in a WAV file.
The iXML website recommends a schema for recorder information but
there is no official DTD and vendors mostly do their own thing, apart from
hitting a few key xpaths. iXML is used by most location/production recorders
to save slating information, timecode and sync points in a reliable way.
iXML is also used to link "families" of WAV files together, so WAV files
recorded simultaneously or contiguously can be related by a receiving client.
```python
print("iXML Project:", info.ixml.project)
print("iXML Scene:", info.ixml.scene)
print("iXML Take:", info.ixml.take)
print("iXML Tape:", info.ixml.tape)
print("iXML File Family Name:", info.ixml.family_name)
print("iXML File Family UID:", info.ixml.family_uid)
```
iXML Project: BMH
iXML Scene: A101
iXML Take: 1
iXML Tape: 18Y12M31
iXML File Family Name: None
iXML File Family UID: USSDVGR1112089007124001008206300
### INFO Metadata
INFO Metadata is a standard method for saving tagged text data in a WAV or AVI
file. INFO fields are often read by the file explorer and host OS, and used in
music library software.
```python
bullet_path = '../tests/test_files/BULLET Impact Plastic LCD TV Screen Shatter Debris 2x.wav'
bullet = WavInfoReader(bullet_path)
```
print("INFO Artist:", bullet.info.artist)
print("INFO Copyright:", bullet.info.copyright)
print("INFO Comment:", bullet.info.comment)

View File

@@ -6,9 +6,6 @@
Welcome to wavinfo's documentation!
===================================
.. toctree::
:maxdepth: 2
:caption: Contents:
.. module:: wavinfo
@@ -24,6 +21,17 @@ Welcome to wavinfo's documentation!
.. autoclass:: wavinfo.wave_reader.WavDataDescriptor
:members:
.. toctree::
:maxdepth: 2
:caption: Notes:
metadata_scopes/bext.rst
metadata_scopes/ixml.rst
metadata_scopes/info.rst
Indices and tables
==================

View File

@@ -0,0 +1,65 @@
Broadcast WAV Extension
=======================
.. module:: wavinfo
.. autoclass:: wavinfo.wave_bext_reader.WavBextReader
:members:
Notes
-----
A WAV file produced to Broadcast-WAV specifications will have the broadcast metadata extension,
which includes a 256-character free text descrption, creating entity identifier (usually the
recording application or equipment), the date and time of recording and a time reference for
timecode synchronization.
The `coding_history` is designed to contain a record of every conversion performed on the audio
file.
In this example (from a Sound Devices 702T) the bext metadata contains scene/take slating
information in the `description`. Here also the `originator_ref` is a serial number conforming
to EBU Rec 99.
If the bext metadata conforms to EBU 3285 v1, it will contain the WAV's 32 or 64 byte SMPTE
330M UMID. The 32-byte version of the UMID is usually just a random number, while the 64-byte
UMID will also have information on the recording date and time, recording equipment and entity,
and geolocation data.
If the bext metadata conforms to EBU 3285 v2, it will hold precomputed program loudness values
as described by EBU Rec 128.
.. code:: python
print(info.bext.description)
print("----------")
print("Originator:", info.bext.originator)
print("Originator Ref:", info.bext.originator_ref)
print("Originator Date:", info.bext.originator_date)
print("Originator Time:", info.bext.originator_time)
print("Time Reference:", info.bext.time_reference)
print(info.bext.coding_history)
Result:
::
sSPEED=023.976-ND
sTAKE=1
sUBITS=$12311801
sSWVER=2.67
sPROJECT=BMH
sSCENE=A101
sFILENAME=A101_1.WAV
sTAPE=18Y12M31
sTRK1=MKH516 A
sTRK2=Boom
sNOTE=
----------
Originator: Sound Dev: 702T S#GR1112089007
Originator Ref: USSDVGR1112089007124001008206301
Originator Date: 2018-12-31
Originator Time: 12:40:00
Time Reference: 2190940753
A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch

View File

@@ -0,0 +1,32 @@
INFO Metadata
=============
.. module:: wavinfo
.. autoclass:: wavinfo.wave_info_reader.WavInfoChunkReader
:members:
Notes
-----
INFO Metadata is a standard method for saving tagged text data in a WAV or AVI
file. INFO fields are often read by the file explorer and host OS, and used in
music library software.
.. code:: python
bullet_path = '../tests/test_files/BULLET Impact Plastic LCD TV Screen Shatter Debris 2x.wav'
bullet = WavInfoReader(bullet_path)
print("INFO Artist:", bullet.info.artist)
print("INFO Copyright:", bullet.info.copyright)
print("INFO Comment:", bullet.info.comment)

View File

@@ -0,0 +1,44 @@
iXML Production Recorder Metadata
=================================
.. module:: wavinfo
.. autoclass:: wavinfo.wave_ixml_reader.WavIXMLFormat
:members:
Notes
-----
iXML allows an XML document to be embedded in a WAV file.
The iXML website recommends a schema for recorder information but
there is no official DTD and vendors mostly do their own thing, apart from
hitting a few key xpaths. iXML is used by most location/production recorders
to save slating information, timecode and sync points in a reliable way.
iXML is also used to link "families" of WAV files together, so WAV files
recorded simultaneously or contiguously can be related by a receiving client.
.. code:: python
print("iXML Project:", info.ixml.project)
print("iXML Scene:", info.ixml.scene)
print("iXML Take:", info.ixml.take)
print("iXML Tape:", info.ixml.tape)
print("iXML File Family Name:", info.ixml.family_name)
print("iXML File Family UID:", info.ixml.family_uid)
Result:
::
iXML Project: BMH
iXML Scene: A101
iXML Take: 1
iXML Tape: 18Y12M31
iXML File Family Name: None
iXML File Family UID: USSDVGR1112089007124001008206300

View File

@@ -2,28 +2,12 @@ import struct
class WavBextReader:
def __init__(self,bext_data,encoding):
# description[256]
# originator[32]
# originatorref[32]
# originatordate[10] "YYYY:MM:DD"
# originatortime[8] "HH:MM:SS"
# lowtimeref U32
# hightimeref U32
# version U16
#
# V1 field
# umid[64]
#
# V2 fields
# loudnessvalue S16 (in LUFS*100)
# loudnessrange S16 (in LUFS*100)
# maxtruepeak S16 (in dbTB*100)
# maxmomentaryloudness S16 (LUFS*100)
# maxshorttermloudness S16 (LUFS*100)
#
# reserved[180]
# codinghistory []
"""
Read Broadcast-WAV extended metadata.
:param best_data: The bytes-like data.
"param encoding: The encoding to use when decoding the text fields of the
BEXT metadata scope. According to EBU Rec 3285 this shall be ASCII.
"""
packstring = "<256s"+ "32s" + "32s" + "10s" + "8s" + "QH" + "64s" + "hhhhh" + "180s"
rest_starts = struct.calcsize(packstring)
@@ -39,20 +23,38 @@ class WavBextReader:
decoded = trimmed.decode(encoding)
return decoded
#: Description. A free-text field up to 256 characters long.
self.description = sanatize_bytes(unpacked[0])
#: Originator. Usually the name of the encoding application, sometimes
#: a artist name.
self.originator = sanatize_bytes(unpacked[1])
#: A unique identifer for the file, a serial number.
self.originator_ref = sanatize_bytes(unpacked[2])
#: Date of the recording, in the format YYY-MM-DD
self.originator_date = sanatize_bytes(unpacked[3])
#: Time of the recording, in the format HH:MM:SS.
self.originator_time = sanatize_bytes(unpacked[4])
#: The sample offset of the start of the file relative to an
#: epoch, usually midnight the day of the recording.
self.time_reference = unpacked[5]
self.version = unpacked[6]
self.umid = None
self.loudness_value = None
self.loudness_range = None
self.max_true_peak = None
self.max_momentary_loudness = None
self.max_shortterm_loudness = None
#: A variable-length text field containing a list of processes and
#: and conversions performed on the file.
self.coding_history = sanatize_bytes(bext_data[rest_starts:])
#: BEXT version.
self.version = unpacked[6]
#: SMPTE 330M UMID of this audio file, 64 bytes are allocated though the UMID
#: may only be 32 bytes long.
self.umid = None
#: EBU R128 Integrated loudness, in LUFS.
self.loudness_value = None
#: EBU R128 Loudness rante, in LUFS.
self.loudness_range = None
#: True peak level, in dBFS TP
self.max_true_peak = None
#: EBU R128 Maximum momentary loudness, in LUFS
self.max_momentary_loudness = None
#: EBU R128 Maximum short-term loudness, in LUFS.
self.max_shortterm_loudness = None
if self.version > 0:
self.umid = unpacked[7]

View File

@@ -15,17 +15,29 @@ class WavInfoChunkReader:
self.info_chunk = next((chunk for chunk in list_chunks \
if chunk.signature == b'INFO'), None)
#: 'ICOP' Copyright
self.copyright = self._get_field(f,b'ICOP')
#: 'IPRD' Product
self.product = self._get_field(f,b'IPRD')
#: 'IGNR' Genre
self.genre = self._get_field(f,b'IGNR')
#: 'IART' Artist, composer, author
self.artist = self._get_field(f,b'IART')
#: 'ICMT' Comment
self.comment = self._get_field(f,b'ICMT')
#: 'ISFT' Software, encoding application
self.software = self._get_field(f,b'ISFT')
#: 'ICRD' Created date
self.created_date = self._get_field(f,b'ICRD')
#: 'IENG' Engineer
self.engineer = self._get_field(f,b'IENG')
#: 'IKEY' Keywords, keyword list
self.keywords = self._get_field(f,b'IKEY')
#: 'INAM' Name, title
self.title = self._get_field(f,b'INAM')
#: 'ISRC' Source
self.source = self._get_field(f,b'ISRC')
#: 'TAPE' Tape
self.tape = self._get_field(f,b'TAPE')
@@ -43,6 +55,9 @@ class WavInfoChunkReader:
def to_dict(self):
"""
A dictionary with all of the key/values read from the INFO scope.
"""
return {'copyright': self.copyright,
'product': self.product,
'genre': self.genre,

View File

@@ -3,9 +3,13 @@ import io
class WavIXMLFormat:
"""
iXML recorder metadata, as defined by iXML 2.0
iXML recorder metadata.
"""
def __init__(self, xml):
"""
Parse iXML.
:param xml: A bytes-like object containing the iXML payload.
"""
self.source = xml
xmlBytes = io.BytesIO(xml)
self.parsed = ET.parse(xmlBytes)
@@ -17,26 +21,45 @@ class WavIXMLFormat:
@property
def project(self):
"""
The project/film name entered for the recording.
"""
return self._get_text_value("PROJECT")
@property
def scene(self):
"""
Scene/slate.
"""
return self._get_text_value("SCENE")
@property
def take(self):
"""
Take number.
"""
return self._get_text_value("TAKE")
@property
def tape(self):
"""
Tape name.
"""
return self._get_text_value("TAPE")
@property
def family_uid(self):
"""
The globally-unique ID for this file family. This may be in the format
of a GUID, or an EBU Rec 9 source identifier, or some other dumb number.
"""
return self._get_text_value("FILE_SET/FAMILY_UID")
@property
def family_name(self):
"""
The name of this file's file family.
"""
return self._get_text_value("FILE_SET/FAMILY_NAME")