mirror of
https://github.com/iluvcapra/wavinfo.git
synced 2025-12-31 17:00:41 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fcc9787f6 | ||
|
|
52ea6fdb60 | ||
|
|
c26942db04 | ||
|
|
12eff79e5f | ||
|
|
d9e3e8deee | ||
|
|
c17fb242e3 | ||
|
|
64f3a640e3 | ||
|
|
5d4f97f6cc | ||
|
|
f9e5f28f7d | ||
|
|
3e6c485eb9 | ||
|
|
436bbe1686 | ||
|
|
ddb4d5cdca | ||
|
|
cec8165919 | ||
|
|
73a5034e02 | ||
|
|
9a46db4ae5 | ||
|
|
ccca30e234 | ||
|
|
c367acc185 |
35
README.md
35
README.md
@@ -5,23 +5,24 @@
|
|||||||
|
|
||||||
The `wavinfo` package allows you to probe WAVE and [RF64/WAVE files][eburf64] and extract extended metadata, with an emphasis on film, video and professional music production metadata.
|
The `wavinfo` package allows you to probe WAVE and [RF64/WAVE files][eburf64] and extract extended metadata, with an emphasis on film, video and professional music production metadata.
|
||||||
|
|
||||||
|
|
||||||
|
## Metadata Support
|
||||||
|
|
||||||
`wavinfo` reads:
|
`wavinfo` reads:
|
||||||
|
|
||||||
* __Broadcast-WAVE__ metadata<sup>[1][ebu]</sup>, including embedded program
|
* [__Broadcast-WAVE__][ebu] metadata, including embedded program
|
||||||
loudness and coding history, if extant. This also includes the SMPTE UMID<sup>[2][smpte_330m2011]</sup>.
|
loudness and coding history and [__SMPTE UMID__][smpte_330m2011].
|
||||||
* ADM track metadata<sup>[3][adm]</sup>, including channel, pack formats, object and content names.
|
* [__ADM__][adm] track metadata, including channel, pack formats, object and content names.
|
||||||
* __iXML__ production recorder metadata<sup>[4][ixml]</sup>, including project, scene, and take tags, recorder notes
|
* [__iXML__][ixml] production recorder metadata, including project, scene, and take tags, recorder notes
|
||||||
and file family information.
|
and file family information.
|
||||||
* Most of the common __RIFF INFO__<sup>[5][info-tags]</sup> metadata fields.
|
* Most of the common [__RIFF INFO__][info-tags] metadata fields.
|
||||||
* The __wav format__ is also parsed, so you can access the basic sample rate and channel count
|
* The __wav format__ is also parsed, so you can access the basic sample rate and channel count
|
||||||
information.
|
information.
|
||||||
|
|
||||||
|
|
||||||
In progress:
|
In progress:
|
||||||
* [Dolby RMU][dolby] metadata and [EBU Tech 3285 Supplement 6][ebu3285s6].
|
* [__Dolby RMU__][dolby] metadata and [EBU Tech 3285 Supplement 6][ebu3285s6].
|
||||||
* iXML `STEINBERG` sound library attributes.
|
|
||||||
* __NetMix__ library attributes.
|
|
||||||
* Pro Tools __embedded regions__.
|
* Pro Tools __embedded regions__.
|
||||||
|
* iXML `STEINBERG` sound library attributes.
|
||||||
|
|
||||||
[dolby]:https://developer.dolby.com/globalassets/documentation/technology/dolby_atmos_master_adm_profile_v1.0.pdf
|
[dolby]:https://developer.dolby.com/globalassets/documentation/technology/dolby_atmos_master_adm_profile_v1.0.pdf
|
||||||
[ebu]:https://tech.ebu.ch/docs/tech/tech3285.pdf
|
[ebu]:https://tech.ebu.ch/docs/tech/tech3285.pdf
|
||||||
@@ -32,7 +33,7 @@ In progress:
|
|||||||
[eburf64]:https://tech.ebu.ch/docs/tech/tech3306v1_1.pdf
|
[eburf64]:https://tech.ebu.ch/docs/tech/tech3306v1_1.pdf
|
||||||
[info-tags]:https://exiftool.org/TagNames/RIFF.html#Info
|
[info-tags]:https://exiftool.org/TagNames/RIFF.html#Info
|
||||||
|
|
||||||
## Demonstration
|
## How To Use
|
||||||
|
|
||||||
The entry point for wavinfo is the WavInfoReader class.
|
The entry point for wavinfo is the WavInfoReader class.
|
||||||
|
|
||||||
@@ -42,6 +43,9 @@ from wavinfo import WavInfoReader
|
|||||||
path = '../tests/test_files/A101_1.WAV'
|
path = '../tests/test_files/A101_1.WAV'
|
||||||
|
|
||||||
info = WavInfoReader(path)
|
info = WavInfoReader(path)
|
||||||
|
|
||||||
|
adm_metadata = info.adm
|
||||||
|
ixml_metadata = info.ixml
|
||||||
```
|
```
|
||||||
|
|
||||||
The package also installs a shell command:
|
The package also installs a shell command:
|
||||||
@@ -50,17 +54,6 @@ The package also installs a shell command:
|
|||||||
$ wavinfo test_files/A101_1.WAV
|
$ wavinfo test_files/A101_1.WAV
|
||||||
```
|
```
|
||||||
|
|
||||||
### Basic WAV Data
|
|
||||||
|
|
||||||
The length of the file in frames (interleaved samples) and bytes is available, as is the contents of the format chunk.
|
|
||||||
|
|
||||||
```python
|
|
||||||
(info.data.frame_count, info.data.byte_count)
|
|
||||||
>>> (240239, 1441434)
|
|
||||||
(info.fmt.sample_rate, info.fmt.channel_count, info.fmt.block_align, info.fmt.bits_per_sample)
|
|
||||||
>>> (48000, 2, 6, 24)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Other Resources
|
## Other Resources
|
||||||
|
|
||||||
* For other file formats and ID3 decoding, look at [audio-metadata](https://github.com/thebigmunch/audio-metadata).
|
* For other file formats and ID3 decoding, look at [audio-metadata](https://github.com/thebigmunch/audio-metadata).
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ sys.path.insert(0, os.path.abspath('../..'))
|
|||||||
sys.path.insert(0, os.path.abspath("../../.."))
|
sys.path.insert(0, os.path.abspath("../../.."))
|
||||||
print(sys.path)
|
print(sys.path)
|
||||||
|
|
||||||
|
import wavinfo
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
@@ -26,9 +27,9 @@ copyright = u'2022, Jamie Hardt'
|
|||||||
author = u'Jamie Hardt'
|
author = u'Jamie Hardt'
|
||||||
|
|
||||||
# The short X.Y version
|
# The short X.Y version
|
||||||
version = u''
|
version = wavinfo.__version__
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = u'v1.7.1'
|
release = wavinfo.__version__
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ Welcome to wavinfo's documentation!
|
|||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Notes
|
:caption: Notes
|
||||||
|
|
||||||
|
quickstart
|
||||||
metadata_scopes/adm.rst
|
metadata_scopes/adm.rst
|
||||||
metadata_scopes/bext.rst
|
metadata_scopes/bext.rst
|
||||||
metadata_scopes/info.rst
|
metadata_scopes/info.rst
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ Result:
|
|||||||
Class Reference
|
Class Reference
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
.. module:: wavinfo
|
|
||||||
|
|
||||||
.. autoclass:: wavinfo.wave_bext_reader.WavBextReader
|
.. autoclass:: wavinfo.wave_bext_reader.WavBextReader
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ music library software.
|
|||||||
Class Reference
|
Class Reference
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
.. module:: wavinfo
|
|
||||||
|
|
||||||
.. autoclass:: wavinfo.wave_info_reader.WavInfoChunkReader
|
.. autoclass:: wavinfo.wave_info_reader.WavInfoChunkReader
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ Result:
|
|||||||
Class Reference
|
Class Reference
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
.. module:: wavinfo
|
|
||||||
|
|
||||||
.. autoclass:: wavinfo.wave_ixml_reader.WavIXMLFormat
|
.. autoclass:: wavinfo.wave_ixml_reader.WavIXMLFormat
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|||||||
12
docs/source/quickstart.rst
Normal file
12
docs/source/quickstart.rst
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
ptulsconv Quickstart
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: Using wavinfo
|
||||||
|
|
||||||
|
import wavinfo
|
||||||
|
|
||||||
|
path = 'path/to/your/wave/audio.wav'
|
||||||
|
|
||||||
|
info = wavinfo.WavInfoReader(path)
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
"""
|
|
||||||
Wavinfo
|
|
||||||
"""
|
|
||||||
__version__ = '2.0.0'
|
|
||||||
__author__ = 'Jamie Hardt <jamiehardt@gmail.com>'
|
|
||||||
__license__ = "MIT"
|
|
||||||
2
setup.py
2
setup.py
@@ -1,5 +1,5 @@
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
from metadata import __author__, __license__, __version__
|
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()
|
||||||
|
|||||||
BIN
tests/test_files/problems/DinerAmbience VAL085101-glued.wav
Normal file
BIN
tests/test_files/problems/DinerAmbience VAL085101-glued.wav
Normal file
Binary file not shown.
@@ -21,12 +21,12 @@ class TestWaveInfo(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(info.fmt.channel_count, ffprobe_info['streams'][0]['channels'])
|
self.assertEqual(info.fmt.channel_count, ffprobe_info['streams'][0]['channels'])
|
||||||
self.assertEqual(info.fmt.sample_rate, int(ffprobe_info['streams'][0]['sample_rate']))
|
self.assertEqual(info.fmt.sample_rate, int(ffprobe_info['streams'][0]['sample_rate']))
|
||||||
self.assertEqual(info.fmt.bits_per_sample, int(ffprobe_info['streams'][0]['bits_per_raw_sample']))
|
self.assertEqual(info.fmt.bits_per_sample, int(ffprobe_info['streams'][0]['bits_per_sample']))
|
||||||
|
|
||||||
if info.fmt.audio_format == 1:
|
if info.fmt.audio_format == 1:
|
||||||
self.assertTrue(ffprobe_info['streams'][0]['codec_name'].startswith('pcm'))
|
self.assertTrue(ffprobe_info['streams'][0]['codec_name'].startswith('pcm'))
|
||||||
streams = ffprobe_info['streams'][0]
|
streams = ffprobe_info['streams'][0]
|
||||||
byte_rate = int(streams['sample_rate']) * streams['channels'] * int(streams['bits_per_raw_sample']) / 8
|
byte_rate = int(streams['sample_rate']) * streams['channels'] * int(streams['bits_per_sample']) / 8
|
||||||
self.assertEqual(info.fmt.byte_rate, byte_rate)
|
self.assertEqual(info.fmt.byte_rate, byte_rate)
|
||||||
|
|
||||||
def test_data_against_ffprobe(self):
|
def test_data_against_ffprobe(self):
|
||||||
|
|||||||
@@ -7,3 +7,6 @@ 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
|
from .riff_parser import WavInfoEOFError
|
||||||
|
|
||||||
|
__version__ = '2.0.1'
|
||||||
|
__author__ = 'Jamie Hardt <jamiehardt@gmail.com>'
|
||||||
|
__license__ = "MIT"
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class WavInfoReader:
|
|||||||
The text encoding to use when decoding the string
|
The text encoding to use when decoding the string
|
||||||
fields of the Broadcast-WAV extension. Per EBU 3285 this is ASCII
|
fields of the Broadcast-WAV extension. Per EBU 3285 this is ASCII
|
||||||
but this parameter is available to you if you encounter a weirdo.
|
but this parameter is available to you if you encounter a weirdo.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.info_encoding = info_encoding
|
self.info_encoding = info_encoding
|
||||||
@@ -89,7 +91,7 @@ class WavInfoReader:
|
|||||||
return chunk_descriptor.read_data(from_stream) if chunk_descriptor else None
|
return chunk_descriptor.read_data(from_stream) if chunk_descriptor else None
|
||||||
|
|
||||||
def _describe_data(self):
|
def _describe_data(self):
|
||||||
data_chunk = next(c for c in self.main_list if c.ident == b'data')
|
data_chunk = next(c for c in self.main_list if type(c) is ChunkDescriptor and c.ident == b'data')
|
||||||
|
|
||||||
return WavDataDescriptor(byte_count=data_chunk.length,
|
return WavDataDescriptor(byte_count=data_chunk.length,
|
||||||
frame_count=int(data_chunk.length / self.fmt.block_align))
|
frame_count=int(data_chunk.length / self.fmt.block_align))
|
||||||
@@ -149,9 +151,11 @@ class WavInfoReader:
|
|||||||
|
|
||||||
:yields: a string, the :scope: of the metadatum, the string :name: of the
|
:yields: a string, the :scope: of the metadatum, the string :name: of the
|
||||||
metadata field, and the value.
|
metadata field, and the value.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
scopes = ('fmt', 'data', 'ixml', 'bext', 'info')
|
scopes = ('fmt', 'data', 'ixml', 'bext', 'info', 'adm')
|
||||||
|
|
||||||
for scope in scopes:
|
for scope in scopes:
|
||||||
if scope in ['fmt', 'data']:
|
if scope in ['fmt', 'data']:
|
||||||
@@ -159,20 +163,10 @@ class WavInfoReader:
|
|||||||
for field in attr._fields:
|
for field in attr._fields:
|
||||||
yield scope, field, attr.__getattribute__(field)
|
yield scope, field, attr.__getattribute__(field)
|
||||||
|
|
||||||
if scope in ['bext']:
|
else:
|
||||||
bext_dict = self.bext.to_dict() if self.bext else {}
|
dict = self.__getattribute__(scope).to_dict() if self.__getattribute__(scope) else {}
|
||||||
for key in bext_dict.keys():
|
for key in dict.keys():
|
||||||
yield 'bext', key, bext_dict[key]
|
yield scope, key, dict[key]
|
||||||
|
|
||||||
if scope in ['info']:
|
|
||||||
info_dict = self.info.to_dict() if self.info else {}
|
|
||||||
for key in info_dict.keys():
|
|
||||||
yield 'info', key, info_dict[key]
|
|
||||||
|
|
||||||
if scope in ['ixml']:
|
|
||||||
ixml_dict = self.ixml.to_dict() if self.ixml else {}
|
|
||||||
for key in ixml_dict.keys():
|
|
||||||
yield 'ixml', key, ixml_dict[key]
|
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user