mirror of
https://github.com/iluvcapra/wavinfo.git
synced 2025-12-31 17:00:41 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24c6871bbc | ||
|
|
c62dfb7a8a | ||
|
|
6c8cc47788 | ||
|
|
a90d3f4b38 | ||
|
|
3ede4de06a | ||
|
|
5d4bb13ad6 | ||
|
|
e132c5846c | ||
|
|
b87b43aab4 | ||
|
|
6b42a6bb09 | ||
|
|
7a315be242 | ||
|
|
eb3e2adc27 | ||
|
|
3e7faefedb | ||
|
|
54ce5abe77 | ||
|
|
2f124b0d56 | ||
|
|
cd11b0924b | ||
|
|
ff862aafe9 | ||
|
|
e0432458cc | ||
|
|
b4613ed6f4 | ||
|
|
8d44d411d7 | ||
|
|
6005f79e60 | ||
|
|
841b86f3f4 | ||
|
|
5c90d5ff47 | ||
|
|
60e329fdb4 | ||
|
|
15db4c9ffa | ||
|
|
49ac961b94 | ||
|
|
7fc530b2cd | ||
|
|
4dfc1ab33c | ||
|
|
4770c781b2 | ||
|
|
45c2aae640 |
26
.github/workflows/pythonpublish.yml
vendored
Normal file
26
.github/workflows/pythonpublish.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: Upload Python Package
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install setuptools wheel twine lxml
|
||||||
|
- name: Build and publish
|
||||||
|
env:
|
||||||
|
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
||||||
|
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
python setup.py sdist bdist_wheel
|
||||||
|
twine upload dist/*
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -105,3 +105,4 @@ venv.bak/
|
|||||||
|
|
||||||
# vim swap
|
# vim swap
|
||||||
*.swp
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
|||||||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
# Default ignored files
|
||||||
|
/workspace.xml
|
||||||
8
.idea/dictionaries/jamiehardt.xml
generated
Normal file
8
.idea/dictionaries/jamiehardt.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="jamiehardt">
|
||||||
|
<words>
|
||||||
|
<w>ident</w>
|
||||||
|
<w>umid</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
4
.idea/misc.xml
generated
Normal file
4
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/wavinfo.iml" filepath="$PROJECT_DIR$/.idea/wavinfo.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
.idea/wavinfo.iml
generated
Normal file
11
.idea/wavinfo.iml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.7" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="TestRunnerService">
|
||||||
|
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
17
.travis.yml
17
.travis.yml
@@ -5,19 +5,22 @@ python:
|
|||||||
- "3.6"
|
- "3.6"
|
||||||
- "3.5"
|
- "3.5"
|
||||||
- "3.7"
|
- "3.7"
|
||||||
|
- "3.8"
|
||||||
script:
|
script:
|
||||||
- "gunzip tests/test_files/rf64/*.gz"
|
- "gunzip tests/test_files/rf64/*.gz"
|
||||||
- "python setup.py test"
|
- "python setup.py test"
|
||||||
# - "py.test tests/ -v --cov wavinfo --cov-report term-missing"
|
# - "python -m pytest tests/ -v --cov wavinfo --cov-report term-missing"
|
||||||
before_install:
|
before_install:
|
||||||
- "sudo apt-get update"
|
- "sudo apt-get update"
|
||||||
- "sudo add-apt-repository universe"
|
- "sudo add-apt-repository universe"
|
||||||
- "sudo apt-get install -y ffmpeg"
|
- "sudo apt-get install -y ffmpeg"
|
||||||
- "pip install coverage"
|
- "pip install pytest"
|
||||||
- "pip install codecov"
|
- "pip install lxml"
|
||||||
- "pip install pytest-cov==2.5.0"
|
# - "pip install coverage"
|
||||||
# - "pip install coverage==4.4"
|
# - "pip install codecov"
|
||||||
|
# - "pip install pytest-cov==2.5.0"
|
||||||
|
# - "pip install coverage==4.4"
|
||||||
install:
|
install:
|
||||||
- "pip install setuptools"
|
- "pip install setuptools"
|
||||||
after_success:
|
# after_success:
|
||||||
- "codecov"
|
# - "codecov"
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018 Jamie Hardt
|
Copyright (c) 2020 Jamie Hardt
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -1,5 +1,4 @@
|
|||||||
[](https://travis-ci.com/iluvcapra/wavinfo)
|
[](https://travis-ci.com/iluvcapra/wavinfo)
|
||||||
[](https://codecov.io/gh/iluvcapra/wavinfo)
|
|
||||||
[](https://wavinfo.readthedocs.io/en/latest/?badge=latest)   [](https://pypi.org/project/wavinfo/) 
|
[](https://wavinfo.readthedocs.io/en/latest/?badge=latest)   [](https://pypi.org/project/wavinfo/) 
|
||||||
|
|
||||||
|
|
||||||
@@ -42,6 +41,12 @@ path = '../tests/test_files/A101_1.WAV'
|
|||||||
info = WavInfoReader(path)
|
info = WavInfoReader(path)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The package also installs a shell command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ wavinfo test_files/A101_1.WAV
|
||||||
|
```
|
||||||
|
|
||||||
### Basic WAV Data
|
### Basic WAV Data
|
||||||
|
|
||||||
The length of the file in frames (interleaved samples) and bytes is available, as is the contents of the format chunk.
|
The length of the file in frames (interleaved samples) and bytes is available, as is the contents of the format chunk.
|
||||||
@@ -53,6 +58,10 @@ The length of the file in frames (interleaved samples) and bytes is available, a
|
|||||||
>>> (48000, 2, 6, 24)
|
>>> (48000, 2, 6, 24)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Other Resources
|
||||||
|
|
||||||
|
* For other file formats and ID3 decoding, look at [audio-metadata](https://github.com/thebigmunch/audio-metadata).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
python3 -m twine upload --repository-url https://upload.pypi.org/legacy/ dist/*
|
|
||||||
29
setup.py
29
setup.py
@@ -1,23 +1,40 @@
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
from wavinfo import __author__, __license__, __version__
|
||||||
|
|
||||||
with open("README.md", "r") as fh:
|
with open("README.md", "r") as fh:
|
||||||
long_description = fh.read()
|
long_description = fh.read()
|
||||||
|
|
||||||
setup(name='wavinfo',
|
setup(name='wavinfo',
|
||||||
version='1.2',
|
version=__version__,
|
||||||
author='Jamie Hardt',
|
author=__author__,
|
||||||
author_email='jamiehardt@me.com',
|
author_email='jamiehardt@me.com',
|
||||||
description='Probe WAVE Files for iXML, Broadcast-WAVE and other metadata.',
|
description='Probe WAVE Files for iXML, Broadcast-WAVE and other metadata.',
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
|
license=__license__,
|
||||||
url='https://github.com/iluvcapra/wavinfo',
|
url='https://github.com/iluvcapra/wavinfo',
|
||||||
|
project_urls={
|
||||||
|
'Source':
|
||||||
|
'https://github.com/iluvcapra/wavinfo',
|
||||||
|
'Documentation':
|
||||||
|
'https://wavinfo.readthedocs.io/',
|
||||||
|
'Issues':
|
||||||
|
'https://github.com/iluvcapra/wavinfo/issues',
|
||||||
|
},
|
||||||
|
packages=['wavinfo'],
|
||||||
classifiers=['Development Status :: 5 - Production/Stable',
|
classifiers=['Development Status :: 5 - Production/Stable',
|
||||||
'License :: OSI Approved :: MIT License',
|
'License :: OSI Approved :: MIT License',
|
||||||
'Topic :: Multimedia',
|
'Topic :: Multimedia',
|
||||||
'Topic :: Multimedia :: Sound/Audio',
|
'Topic :: Multimedia :: Sound/Audio',
|
||||||
"Programming Language :: Python :: 3.5",
|
"Programming Language :: Python :: 3.5",
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.6",
|
||||||
"Programming Language :: Python :: 3.7"],
|
"Programming Language :: Python :: 3.7",
|
||||||
packages=['wavinfo'],
|
"Programming Language :: Python :: 3.8"],
|
||||||
install_requires=['lxml']
|
keywords='waveform metadata audio ebu smpte avi library film tv editing editorial',
|
||||||
|
install_requires=['lxml'],
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': [
|
||||||
|
'wavinfo = wavinfo.__main__:main'
|
||||||
|
]
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -87,3 +87,10 @@ class TestWaveInfo(TestCase):
|
|||||||
self.assertEqual( e['take'], info.ixml.take )
|
self.assertEqual( e['take'], info.ixml.take )
|
||||||
self.assertEqual( e['tape'], info.ixml.tape )
|
self.assertEqual( e['tape'], info.ixml.tape )
|
||||||
self.assertEqual( e['family_uid'], info.ixml.family_uid )
|
self.assertEqual( e['family_uid'], info.ixml.family_uid )
|
||||||
|
|
||||||
|
for track in info.ixml.track_list:
|
||||||
|
self.assertIsNotNone(track.channel_index)
|
||||||
|
if basename == 'A101_4.WAV' and track.channel_index == '1':
|
||||||
|
self.assertTrue(track.name == 'MKH516 A')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
# -*- coding: utf-8 -*-
|
"""
|
||||||
|
methods to probe a WAV file for various kinds of production metadata.
|
||||||
# :module:`wavinfo` provides methods to probe a WAV file for
|
|
||||||
# various kinds of production metadata.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
|
Go to the documentation for wavinfo.WavInfoReader for more information.
|
||||||
|
"""
|
||||||
|
|
||||||
from .wave_reader import WavInfoReader
|
from .wave_reader import WavInfoReader
|
||||||
|
from .riff_parser import WavInfoEOFError
|
||||||
|
|
||||||
__version__ = '1.1'
|
__version__ = '1.5'
|
||||||
__author__ = 'Jamie Hardt'
|
__author__ = 'Jamie Hardt <jamiehardt@gmail.com>'
|
||||||
|
__license__ = "MIT"
|
||||||
32
wavinfo/__main__.py
Normal file
32
wavinfo/__main__.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
from optparse import OptionParser, OptionGroup
|
||||||
|
import datetime
|
||||||
|
from . import WavInfoReader
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = OptionParser()
|
||||||
|
|
||||||
|
parser.usage = 'wavinfo [FILE.wav]*'
|
||||||
|
|
||||||
|
# parser.add_option('-f', dest='output_format', help='Set the output format',
|
||||||
|
# default='json',
|
||||||
|
# metavar='FORMAT')
|
||||||
|
|
||||||
|
(options, args) = parser.parse_args(sys.argv)
|
||||||
|
for arg in args[1:]:
|
||||||
|
try:
|
||||||
|
this_file = WavInfoReader(path=arg)
|
||||||
|
ret_dict = {'file_argument': arg, 'run_date': datetime.datetime.now().isoformat() , 'scopes': {}}
|
||||||
|
for scope, name, value in this_file.walk():
|
||||||
|
if scope not in ret_dict['scopes'].keys():
|
||||||
|
ret_dict['scopes'][scope] = {}
|
||||||
|
|
||||||
|
ret_dict['scopes'][scope][name] = value
|
||||||
|
|
||||||
|
json.dump(ret_dict, fp=sys.stdout, indent=2)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,70 +1,72 @@
|
|||||||
|
|
||||||
import struct
|
import struct
|
||||||
import pdb
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from .rf64_parser import parse_rf64
|
from .rf64_parser import parse_rf64
|
||||||
|
|
||||||
class ListChunkDescriptor(namedtuple('ListChunkDescriptor' , 'signature children')):
|
|
||||||
|
|
||||||
def find(chunk_path):
|
class WavInfoEOFError(EOFError):
|
||||||
|
def __init__(self, identifier, chunk_start):
|
||||||
|
self.identifier = identifier
|
||||||
|
self.chunk_start = chunk_start
|
||||||
|
|
||||||
|
|
||||||
|
class ListChunkDescriptor(namedtuple('ListChunkDescriptor', 'signature children')):
|
||||||
|
def find(self, chunk_path):
|
||||||
if len(chunk_path) > 1:
|
if len(chunk_path) > 1:
|
||||||
for chunk in self.children:
|
for chunk in self.children:
|
||||||
if type(chunk) is ListChunkDescriptor and \
|
if type(chunk) is ListChunkDescriptor and \
|
||||||
chunk.signature is chunk_path[0]:
|
chunk.signature is chunk_path[0]:
|
||||||
return chunk.find(chunk_path[1:])
|
return chunk.find(chunk_path[1:])
|
||||||
else:
|
else:
|
||||||
for chunk in self.children:
|
for chunk in self.children:
|
||||||
if type(chunk) is ChunkDescriptor and \
|
if type(chunk) is ChunkDescriptor and \
|
||||||
chunk.ident is chunk_path[0]:
|
chunk.ident is chunk_path[0]:
|
||||||
return chunk
|
return chunk
|
||||||
|
|
||||||
|
|
||||||
class ChunkDescriptor(namedtuple('ChunkDescriptor', 'ident start length rf64_context') ):
|
class ChunkDescriptor(namedtuple('ChunkDescriptor', 'ident start length rf64_context')):
|
||||||
def read_data(self, from_stream):
|
def read_data(self, from_stream):
|
||||||
from_stream.seek(self.start)
|
from_stream.seek(self.start)
|
||||||
return from_stream.read(self.length)
|
return from_stream.read(self.length)
|
||||||
|
|
||||||
def parse_list_chunk(stream, length, rf64_context =None):
|
|
||||||
start = stream.tell()
|
|
||||||
|
|
||||||
|
def parse_list_chunk(stream, length, rf64_context=None):
|
||||||
|
start = stream.tell()
|
||||||
signature = stream.read(4)
|
signature = stream.read(4)
|
||||||
|
|
||||||
#print("Parsing list chunk with siganture: ", signature)
|
|
||||||
children = []
|
children = []
|
||||||
while (stream.tell() - start) < length:
|
while (stream.tell() - start + 8) < length:
|
||||||
child_chunk = parse_chunk(stream, rf64_context= rf64_context)
|
child_chunk = parse_chunk(stream, rf64_context=rf64_context)
|
||||||
if child_chunk:
|
children.append(child_chunk)
|
||||||
children.append(child_chunk)
|
|
||||||
else:
|
stream.seek(start + length)
|
||||||
break
|
|
||||||
|
|
||||||
return ListChunkDescriptor(signature=signature, children=children)
|
return ListChunkDescriptor(signature=signature, children=children)
|
||||||
|
|
||||||
def parse_chunk(stream, rf64_context=None):
|
|
||||||
ident = stream.read(4)
|
|
||||||
if len(ident) != 4:
|
|
||||||
return
|
|
||||||
|
|
||||||
sizeb = stream.read(4)
|
def parse_chunk(stream, rf64_context=None):
|
||||||
size = struct.unpack('<I',sizeb)[0]
|
header_start = stream.tell()
|
||||||
|
ident = stream.read(4)
|
||||||
if size == 0xFFFFFFFF:
|
size_bytes = stream.read(4)
|
||||||
|
|
||||||
|
if len(ident) != 4 or len(size_bytes) != 4:
|
||||||
|
raise WavInfoEOFError(identifier=ident, chunk_start=header_start)
|
||||||
|
|
||||||
|
data_size = struct.unpack('<I', size_bytes)[0]
|
||||||
|
|
||||||
|
if data_size == 0xFFFFFFFF:
|
||||||
if rf64_context is None and ident == b'RF64':
|
if rf64_context is None and ident == b'RF64':
|
||||||
rf64_context = parse_rf64(stream=stream)
|
rf64_context = parse_rf64(stream=stream)
|
||||||
|
|
||||||
size = rf64_context.bigchunk_table[ident]
|
data_size = rf64_context.bigchunk_table[ident]
|
||||||
|
|
||||||
displacement = size
|
displacement = data_size
|
||||||
if displacement % 2 is not 0:
|
if displacement % 2 is not 0:
|
||||||
displacement = displacement + 1
|
displacement = displacement + 1
|
||||||
|
|
||||||
if ident in [b'RIFF',b'LIST', b'RF64']:
|
if ident in [b'RIFF', b'LIST', b'RF64']:
|
||||||
#print("Parsing list chunk with ident: ", ident)
|
return parse_list_chunk(stream=stream, length=data_size, rf64_context=rf64_context)
|
||||||
return parse_list_chunk(stream=stream, length=size, rf64_context=rf64_context)
|
|
||||||
else:
|
else:
|
||||||
start = stream.tell()
|
data_start = stream.tell()
|
||||||
stream.seek(displacement,1)
|
stream.seek(displacement, 1)
|
||||||
#print("Parsing chunk with start=%i, ident=%s" % (start, ident))
|
return ChunkDescriptor(ident=ident, start=data_start, length=data_size, rf64_context=rf64_context)
|
||||||
return ChunkDescriptor(ident=ident, start=start, length=size, rf64_context=rf64_context)
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
129
wavinfo/umid_parser.py
Normal file
129
wavinfo/umid_parser.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import struct
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
class UMIDParser:
|
||||||
|
"""
|
||||||
|
Parse a raw binary SMPTE 330M Universal Materials Identifier
|
||||||
|
|
||||||
|
This implementation is based on SMPTE ST 330:2011
|
||||||
|
"""
|
||||||
|
def __init__(self, raw_umid: bytearray):
|
||||||
|
self.raw_umid = raw_umid
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def binary_to_string(cls, binary_value):
|
||||||
|
result_str = ''
|
||||||
|
for n in range(len(binary_value)):
|
||||||
|
result_str = f'{binary_value[n]:x}' + result_str
|
||||||
|
|
||||||
|
return result_str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def universal_label(self) -> bytearray:
|
||||||
|
return self.raw_umid[0:12]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def basic_umid(self):
|
||||||
|
return self.raw_umid[0:32]
|
||||||
|
|
||||||
|
def basic_umid_to_str(self):
|
||||||
|
return "%024x-%06x-%032x" % (self.binary_to_string(self.universal_label),
|
||||||
|
self.binary_to_string(self.instance_number),
|
||||||
|
self.binary_to_string(self.material_number))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def universal_label_is_valid(self) -> bool:
|
||||||
|
valid_preamble = b'\x06\x0a\x2b\x34\x01\x01\x01\x05\x01\x01'
|
||||||
|
return self.universal_label[0:len(valid_preamble)] == valid_preamble
|
||||||
|
|
||||||
|
@property
|
||||||
|
def material_type(self) -> str:
|
||||||
|
material_byte = self.raw_umid[10]
|
||||||
|
if material_byte == 0x1:
|
||||||
|
return 'picture'
|
||||||
|
elif material_byte == 0x2:
|
||||||
|
return 'audio'
|
||||||
|
elif material_byte == 0x3:
|
||||||
|
return 'data'
|
||||||
|
elif material_byte == 0x4:
|
||||||
|
return 'other'
|
||||||
|
elif material_byte == 0x5:
|
||||||
|
return 'picture_single_component'
|
||||||
|
elif material_byte == 0x6:
|
||||||
|
return 'picture_multiple_component'
|
||||||
|
elif material_byte == 0x7:
|
||||||
|
return 'audio_single_component'
|
||||||
|
elif material_byte == 0x9:
|
||||||
|
return 'audio_multiple_component'
|
||||||
|
elif material_byte == 0xb:
|
||||||
|
return 'auxiliary_single_component'
|
||||||
|
elif material_byte == 0xc:
|
||||||
|
return 'auxiliary_multiple_component'
|
||||||
|
elif material_byte == 0xd:
|
||||||
|
return 'mixed_components'
|
||||||
|
elif material_byte == 0xf:
|
||||||
|
return 'not_identified'
|
||||||
|
else:
|
||||||
|
return 'not_recognized'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def material_number_creation_method(self) -> str:
|
||||||
|
method_byte = self.raw_umid[11]
|
||||||
|
method_byte = (method_byte << 4) & 0xf
|
||||||
|
if method_byte == 0x0:
|
||||||
|
return 'undefined'
|
||||||
|
elif method_byte == 0x1:
|
||||||
|
return 'smpte'
|
||||||
|
elif method_byte == 0x2:
|
||||||
|
return 'uuid'
|
||||||
|
elif method_byte == 0x3:
|
||||||
|
return 'masked'
|
||||||
|
elif method_byte == 0x4:
|
||||||
|
return 'ieee1394'
|
||||||
|
elif 0x5 <= method_byte <= 0x7:
|
||||||
|
return 'reserved_undefined'
|
||||||
|
else:
|
||||||
|
return 'unrecognized'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def instance_number_creation_method(self) -> str:
|
||||||
|
method_byte = self.raw_umid[11]
|
||||||
|
method_byte = method_byte & 0xf
|
||||||
|
if method_byte == 0x0:
|
||||||
|
return 'undefined'
|
||||||
|
elif method_byte == 0x01:
|
||||||
|
return 'local_registration'
|
||||||
|
elif method_byte == 0x02:
|
||||||
|
return '24_bit_prs'
|
||||||
|
elif method_byte == 0x03:
|
||||||
|
return 'copy_number_and_16_bit_prs'
|
||||||
|
elif 0x04 <= method_byte <= 0x0e:
|
||||||
|
return 'reserved_undefined'
|
||||||
|
elif method_byte == 0x0f:
|
||||||
|
return 'live_stream'
|
||||||
|
else:
|
||||||
|
return 'unrecognized'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def indicated_length(self) -> str:
|
||||||
|
if self.raw_umid[12] == 0x13:
|
||||||
|
return 'basic'
|
||||||
|
elif self.raw_umid[12] == 0x33:
|
||||||
|
return 'extended'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def instance_number(self) -> bytearray:
|
||||||
|
return self.raw_umid[13:3]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def material_number(self) -> bytearray:
|
||||||
|
return self.raw_umid[16:16]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source_pack(self) -> Union[bytearray, None]:
|
||||||
|
if self.indicated_length == 'extended':
|
||||||
|
return self.raw_umid[32:32]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
#import xml.etree.ElementTree as ET
|
#import xml.etree.ElementTree as ET
|
||||||
from lxml import etree as ET
|
from lxml import etree as ET
|
||||||
import io
|
import io
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
|
IXMLTrack = namedtuple('IXMLTrack', ['channel_index', 'interleave_index', 'name', 'function'])
|
||||||
|
|
||||||
class WavIXMLFormat:
|
class WavIXMLFormat:
|
||||||
"""
|
"""
|
||||||
@@ -27,6 +31,26 @@ class WavIXMLFormat:
|
|||||||
if e is not None:
|
if e is not None:
|
||||||
return e.text
|
return e.text
|
||||||
|
|
||||||
|
@property
|
||||||
|
def raw_xml(self):
|
||||||
|
"""
|
||||||
|
The root entity of the iXML document.
|
||||||
|
"""
|
||||||
|
return self.parsed
|
||||||
|
|
||||||
|
@property
|
||||||
|
def track_list(self):
|
||||||
|
"""
|
||||||
|
A description of each track.
|
||||||
|
:return: An Iterator
|
||||||
|
"""
|
||||||
|
for track in self.parsed.find("./TRACK_LIST").iter():
|
||||||
|
if track.tag == 'TRACK':
|
||||||
|
yield IXMLTrack(channel_index=track.xpath('string(CHANNEL_INDEX/text())'),
|
||||||
|
interleave_index=track.xpath('string(INTERLEAVE_INDEX/text())'),
|
||||||
|
name=track.xpath('string(NAME/text())'),
|
||||||
|
function=track.xpath('string(FUNCTION/text())'))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def project(self):
|
def project(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -147,9 +147,13 @@ class WavInfoReader():
|
|||||||
metadata field, and the value.
|
metadata field, and the value.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scopes = ('fmt','data')#,'bext','ixml','info')
|
scopes = ('fmt', 'data') #'bext', 'ixml', 'info')
|
||||||
|
|
||||||
for scope in scopes:
|
for scope in scopes:
|
||||||
attr = self.__getattribute__(scope)
|
attr = self.__getattribute__(scope)
|
||||||
for field in attr._fields:
|
for field in attr._fields:
|
||||||
yield scope, field, attr.__getattribute__(field)
|
yield scope, field, attr.__getattribute__(field)
|
||||||
|
|
||||||
|
bext_dict = self.bext.to_dict()
|
||||||
|
for key in bext_dict.keys():
|
||||||
|
yield 'bext', key, bext_dict[key]
|
||||||
|
|||||||
Reference in New Issue
Block a user