mirror of
https://github.com/iluvcapra/wavinfo.git
synced 2025-12-31 17:00:41 +00:00
Tests
This commit is contained in:
92
README.md
92
README.md
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
# wavinfo
|
# wavinfo
|
||||||
|
|
||||||
|
|
||||||
The `wavinfo` package allows you to probe WAVE files and extract extended metadata, with an emphasis on
|
The `wavinfo` package allows you to probe WAVE files and extract extended metadata, with an emphasis on
|
||||||
production metadata.
|
production metadata.
|
||||||
|
|
||||||
@@ -23,4 +24,93 @@ This module is presently under construction and not sutiable for production at t
|
|||||||
|
|
||||||
[ebu]:https://tech.ebu.ch/docs/tech/tech3285.pdf
|
[ebu]:https://tech.ebu.ch/docs/tech/tech3285.pdf
|
||||||
[smpte_330m2011]:http://standards.smpte.org/content/978-1-61482-678-1/st-330-2011/SEC1.abstract
|
[smpte_330m2011]:http://standards.smpte.org/content/978-1-61482-678-1/st-330-2011/SEC1.abstract
|
||||||
[ixml]:http://www.ixml.info
|
[ixml]:http://www.ixml.infoi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Demonstration
|
||||||
|
|
||||||
|
|
||||||
|
The entry point for wavinfo is the WavInfoReader class.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
from wavinfo import WavInfoReader
|
||||||
|
|
||||||
|
path = '../tests/test_files/A101_1.WAV'
|
||||||
|
|
||||||
|
info = WavInfoReader(path)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Broadcast WAV Extension
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```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
|
||||||
|
|
||||||
|
|
||||||
|
```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
|
||||||
|
A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
104
demo.md
Normal file
104
demo.md
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
|
||||||
|
# `wavinfo` Demonstration
|
||||||
|
|
||||||
|
The entry point for wavinfo is the WavInfoReader class.
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
from wavinfo import WavInfoReader
|
||||||
|
|
||||||
|
path = '../tests/test_files/A101_1.WAV'
|
||||||
|
|
||||||
|
info = WavInfoReader(path)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
(info.fmt.sample_rate, info.fmt.channel_count, info.fmt.block_align, info.fmt.bits_per_sample)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(48000, 2, 6, 24)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Broadcast WAV Extension
|
||||||
|
|
||||||
|
|
||||||
|
```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
|
||||||
|
|
||||||
|
|
||||||
|
```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
|
||||||
|
A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
189
examples/demo.ipynb
Normal file
189
examples/demo.ipynb
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# `wavinfo` Demonstration\n",
|
||||||
|
"\n",
|
||||||
|
"The entry point for wavinfo is the WavInfoReader class."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 2,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from wavinfo import WavInfoReader\n",
|
||||||
|
"\n",
|
||||||
|
"path = '../tests/test_files/A101_1.WAV'\n",
|
||||||
|
"\n",
|
||||||
|
"info = WavInfoReader(path)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Basic WAV Data\n",
|
||||||
|
"\n",
|
||||||
|
"The length of the file in frames (interleaved samples) and bytes is available, as is the contents of the format chunk."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 4,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"(240239, 1441434)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 4,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"(info.data.frame_count, info.data.byte_count)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 7,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"(48000, 2, 6, 24)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 7,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"(info.fmt.sample_rate, info.fmt.channel_count, info.fmt.block_align, info.fmt.bits_per_sample)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## Broadcast WAV Extension"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 13,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"sSPEED=023.976-ND\r\n",
|
||||||
|
"sTAKE=1\r\n",
|
||||||
|
"sUBITS=$12311801\r\n",
|
||||||
|
"sSWVER=2.67\r\n",
|
||||||
|
"sPROJECT=BMH\r\n",
|
||||||
|
"sSCENE=A101\r\n",
|
||||||
|
"sFILENAME=A101_1.WAV\r\n",
|
||||||
|
"sTAPE=18Y12M31\r\n",
|
||||||
|
"sTRK1=MKH516 A\r\n",
|
||||||
|
"sTRK2=Boom\r\n",
|
||||||
|
"sNOTE=\r\n",
|
||||||
|
"\n",
|
||||||
|
"----------\n",
|
||||||
|
"Originator: Sound Dev: 702T S#GR1112089007\n",
|
||||||
|
"Originator Ref: USSDVGR1112089007124001008206301\n",
|
||||||
|
"Originator Date: 2018-12-31\n",
|
||||||
|
"Originator Time: 12:40:00\n",
|
||||||
|
"Time Reference: 2190940753\n",
|
||||||
|
"A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch\r\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"print(info.bext.description)\n",
|
||||||
|
"print(\"----------\")\n",
|
||||||
|
"print(\"Originator:\", info.bext.originator)\n",
|
||||||
|
"print(\"Originator Ref:\", info.bext.originator_ref)\n",
|
||||||
|
"print(\"Originator Date:\", info.bext.originator_date)\n",
|
||||||
|
"print(\"Originator Time:\", info.bext.originator_time)\n",
|
||||||
|
"print(\"Time Reference:\", info.bext.time_reference)\n",
|
||||||
|
"print(info.bext.coding_history)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"## iXML Production Recorder Metadata"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 14,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"iXML Project: BMH\n",
|
||||||
|
"iXML Scene: A101\n",
|
||||||
|
"iXML Take: 1\n",
|
||||||
|
"iXML Tape: 18Y12M31\n",
|
||||||
|
"iXML File Family Name: None\n",
|
||||||
|
"iXML File Family UID: USSDVGR1112089007124001008206300\n",
|
||||||
|
"A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch\r\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"print(\"iXML Project:\", info.ixml.project)\n",
|
||||||
|
"print(\"iXML Scene:\", info.ixml.scene)\n",
|
||||||
|
"print(\"iXML Take:\", info.ixml.take)\n",
|
||||||
|
"print(\"iXML Tape:\", info.ixml.tape)\n",
|
||||||
|
"print(\"iXML File Family Name:\", info.ixml.family_name)\n",
|
||||||
|
"print(\"iXML File Family UID:\", info.ixml.family_uid)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 2
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"WavBextFormat(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=b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00', loudness_value=0, loudness_range=0, max_true_peak=0, max_momentary_loudness=0, max_shortterm_loudness=0, coding_history='A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch\\r\\n')\n"
|
"WavBextFormat(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=b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00', loudness_value=0.0, loudness_range=0.0, max_true_peak=0.0, max_momentary_loudness=0.0, max_shortterm_loudness=0.0, coding_history='A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch\\r\\n')\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
"output_type": "stream",
|
"output_type": "stream",
|
||||||
"text": [
|
"text": [
|
||||||
"WavBextFormat(description='dUBITS=12311804\\r\\ndSCENE=A101\\r\\ndTAKE=4\\r\\ndTAPE=18Y12M31\\r\\ndFRAMERATE=23.976ND\\r\\ndSPEED=023.976-NDF\\r\\ndTRK1=MKH516 A\\r\\ndTRK2=Boom\\r\\n', originator='Sound Dev: 702T S#GR1112089007', originator_ref='aa4CKtcd13Vk', originator_date='2018-12-31', originator_time='12:40:07', time_reference=2191709524, version=0, umid=b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00', loudness_value=0, loudness_range=0, max_true_peak=0, max_momentary_loudness=0, max_shortterm_loudness=0, coding_history='A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch\\r\\n')\n"
|
"WavBextFormat(description='dUBITS=12311804\\r\\ndSCENE=A101\\r\\ndTAKE=4\\r\\ndTAPE=18Y12M31\\r\\ndFRAMERATE=23.976ND\\r\\ndSPEED=023.976-NDF\\r\\ndTRK1=MKH516 A\\r\\ndTRK2=Boom\\r\\n', originator='Sound Dev: 702T S#GR1112089007', originator_ref='aa4CKtcd13Vk', originator_date='2018-12-31', originator_time='12:40:07', time_reference=2191709524, version=0, umid=b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00', loudness_value=0.0, loudness_range=0.0, max_true_peak=0.0, max_momentary_loudness=0.0, max_shortterm_loudness=0.0, coding_history='A=PCM,F=48000,W=24,M=stereo,R=48000,T=2 Ch\\r\\n')\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
2
tests/__init__.py
Normal file
2
tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import test_wave_parsing
|
||||||
|
|
||||||
BIN
tests/test_files/A101_1_1.WAV
Normal file
BIN
tests/test_files/A101_1_1.WAV
Normal file
Binary file not shown.
BIN
tests/test_files/A101_1_2.WAV
Normal file
BIN
tests/test_files/A101_1_2.WAV
Normal file
Binary file not shown.
1
tests/test_files/WaveAgentSoundReport.csv
Normal file
1
tests/test_files/WaveAgentSoundReport.csv
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SOUND REPORT
|
||||||
|
Can't render this file because it contains an unexpected character in line 1 and column 53.
|
99
tests/test_wave_parsing.py
Normal file
99
tests/test_wave_parsing.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import os.path
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
import wavinfo
|
||||||
|
|
||||||
|
FFPROBE='/usr/local/bin/ffprobe'
|
||||||
|
|
||||||
|
|
||||||
|
def ffprobe(path):
|
||||||
|
|
||||||
|
arguments = [ FFPROBE , "-of", "json" , "-show_format", "-show_streams", path ]
|
||||||
|
|
||||||
|
process = subprocess.run(arguments, stdin=None, capture_output=True)
|
||||||
|
|
||||||
|
if process.returncode == 0:
|
||||||
|
return json.loads(process.stdout)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class TestWaveInfo(TestCase):
|
||||||
|
|
||||||
|
|
||||||
|
def all_files(self):
|
||||||
|
for dirpath, dirnames, filenames in os.walk('tests/test_files'):
|
||||||
|
for filename in filenames:
|
||||||
|
name, ext = os.path.splitext(filename)
|
||||||
|
if ext == '.wav':
|
||||||
|
yield os.path.join(dirpath, filename)
|
||||||
|
|
||||||
|
|
||||||
|
def test_sanity(self):
|
||||||
|
for wav_file in self.all_files():
|
||||||
|
info = wavinfo.WavInfoReader(wav_file)
|
||||||
|
self.assertTrue(info is not None)
|
||||||
|
|
||||||
|
def test_fmt_against_ffprobe(self):
|
||||||
|
for wav_file in self.all_files():
|
||||||
|
info = wavinfo.WavInfoReader(wav_file)
|
||||||
|
ffprobe_info = ffprobe(wav_file)
|
||||||
|
|
||||||
|
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.bits_per_sample, int(ffprobe_info['streams'][0]['bits_per_raw_sample']) )
|
||||||
|
|
||||||
|
if info.fmt.audio_format == 1:
|
||||||
|
self.assertTrue(ffprobe_info['streams'][0]['codec_name'].startswith('pcm') )
|
||||||
|
byte_rate = int(ffprobe_info['streams'][0]['sample_rate']) \
|
||||||
|
* ffprobe_info['streams'][0]['channels'] \
|
||||||
|
* int(ffprobe_info['streams'][0]['bits_per_raw_sample']) / 8
|
||||||
|
self.assertEqual( info.fmt.byte_rate , byte_rate )
|
||||||
|
|
||||||
|
def test_data_against_ffprobe(self):
|
||||||
|
for wav_file in self.all_files():
|
||||||
|
info = wavinfo.WavInfoReader(wav_file)
|
||||||
|
ffprobe_info = ffprobe(wav_file)
|
||||||
|
|
||||||
|
self.assertEqual( info.data.frame_count, int(ffprobe_info['streams'][0]['duration_ts'] ))
|
||||||
|
|
||||||
|
def test_bext_against_ffprobe(self):
|
||||||
|
for wav_file in self.all_files():
|
||||||
|
info = wavinfo.WavInfoReader(wav_file)
|
||||||
|
ffprobe_info = ffprobe(wav_file)
|
||||||
|
|
||||||
|
self.assertEqual( info.bext.description, ffprobe_info['format']['tags']['comment'] )
|
||||||
|
self.assertEqual( info.bext.originator, ffprobe_info['format']['tags']['encoded_by'] )
|
||||||
|
self.assertEqual( info.bext.originator_ref, ffprobe_info['format']['tags']['originator_reference'] )
|
||||||
|
|
||||||
|
# these don't always reflect the bext info
|
||||||
|
#self.assertEqual( info.bext.originator_date, ffprobe_info['format']['tags']['date'] )
|
||||||
|
#self.assertEqual( info.bext.originator_time, ffprobe_info['format']['tags']['creation_time'] )
|
||||||
|
self.assertEqual( info.bext.time_reference, int(ffprobe_info['format']['tags']['time_reference']) )
|
||||||
|
self.assertEqual( info.bext.coding_history, ffprobe_info['format']['tags']['coding_history'] )
|
||||||
|
|
||||||
|
def test_ixml(self):
|
||||||
|
expected = {'A101_4.WAV': {'project' : 'BMH', 'scene': 'A101', 'take': '4',
|
||||||
|
'tape': '18Y12M31', 'family_uid': 'USSDVGR1112089007124015008231000'},
|
||||||
|
'A101_3.WAV': {'project' : 'BMH', 'scene': 'A101', 'take': '3',
|
||||||
|
'tape': '18Y12M31', 'family_uid': 'USSDVGR1112089007124014008228300'},
|
||||||
|
'A101_2.WAV': {'project' : 'BMH', 'scene': 'A101', 'take': '2',
|
||||||
|
'tape': '18Y12M31', 'family_uid': 'USSDVGR1112089007124004008218600'},
|
||||||
|
'A101_1.WAV': {'project' : 'BMH', 'scene': 'A101', 'take': '1',
|
||||||
|
'tape': '18Y12M31', 'family_uid': 'USSDVGR1112089007124001008206300'},
|
||||||
|
}
|
||||||
|
|
||||||
|
for wav_file in self.all_files():
|
||||||
|
basename = os.path.basename(wav_file)
|
||||||
|
if basename in expected:
|
||||||
|
info = wavinfo.WavInfoReader(wav_file)
|
||||||
|
e = expected[basename]
|
||||||
|
|
||||||
|
self.assertEqual( e['project'], info.ixml.project )
|
||||||
|
self.assertEqual( e['scene'], info.ixml.scene )
|
||||||
|
self.assertEqual( e['take'], info.ixml.take )
|
||||||
|
self.assertEqual( e['tape'], info.ixml.tape )
|
||||||
|
self.assertEqual( e['family_uid'], info.ixml.family_uid )
|
||||||
@@ -9,8 +9,8 @@ WavDataDescriptor = namedtuple('WavDataDescriptor','byte_count frame_count')
|
|||||||
|
|
||||||
WavInfoFormat = namedtuple("WavInfoFormat",'audio_format channel_count sample_rate byte_rate block_align bits_per_sample')
|
WavInfoFormat = namedtuple("WavInfoFormat",'audio_format channel_count sample_rate byte_rate block_align bits_per_sample')
|
||||||
|
|
||||||
WavBextFormat = namedtuple("WavBextFormat",'description originator originator_ref ' +
|
WavBextFormat = namedtuple("WavBextFormat",'description originator originator_ref ' +
|
||||||
'originator_date originator_time time_reference version umid ' +
|
'originator_date originator_time time_reference version umid ' +
|
||||||
'loudness_value loudness_range max_true_peak max_momentary_loudness max_shortterm_loudness ' +
|
'loudness_value loudness_range max_true_peak max_momentary_loudness max_shortterm_loudness ' +
|
||||||
'coding_history')
|
'coding_history')
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@ class WavInfoReader():
|
|||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
chunks = parse_chunk(f)
|
chunks = parse_chunk(f)
|
||||||
|
|
||||||
self.main_list = chunks.children
|
self.main_list = chunks.children
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
|
|
||||||
self.fmt = self._get_format(f)
|
self.fmt = self._get_format(f)
|
||||||
self.bext = self._get_bext(f)
|
self.bext = self._get_bext(f)
|
||||||
self.ixml = self._get_ixml(f)
|
self.ixml = self._get_ixml(f)
|
||||||
@@ -53,14 +53,14 @@ class WavInfoReader():
|
|||||||
def _describe_data(self,f):
|
def _describe_data(self,f):
|
||||||
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 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))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _get_format(self,f):
|
def _get_format(self,f):
|
||||||
fmt_data = self._find_chunk_data(b'fmt ',f)
|
fmt_data = self._find_chunk_data(b'fmt ',f)
|
||||||
|
|
||||||
# The format chunk is
|
# The format chunk is
|
||||||
# audio_format U16
|
# audio_format U16
|
||||||
# channel_count U16
|
# channel_count U16
|
||||||
@@ -70,9 +70,9 @@ class WavInfoReader():
|
|||||||
# bits_per_sampl U16
|
# bits_per_sampl U16
|
||||||
packstring = "<HHIIHH"
|
packstring = "<HHIIHH"
|
||||||
rest_starts = struct.calcsize(packstring)
|
rest_starts = struct.calcsize(packstring)
|
||||||
|
|
||||||
unpacked = struct.unpack(packstring, fmt_data[:rest_starts])
|
unpacked = struct.unpack(packstring, fmt_data[:rest_starts])
|
||||||
|
|
||||||
#0x0001 WAVE_FORMAT_PCM PCM
|
#0x0001 WAVE_FORMAT_PCM PCM
|
||||||
#0x0003 WAVE_FORMAT_IEEE_FLOAT IEEE float
|
#0x0003 WAVE_FORMAT_IEEE_FLOAT IEEE float
|
||||||
#0x0006 WAVE_FORMAT_ALAW 8-bit ITU-T G.711 A-law
|
#0x0006 WAVE_FORMAT_ALAW 8-bit ITU-T G.711 A-law
|
||||||
@@ -91,7 +91,7 @@ class WavInfoReader():
|
|||||||
|
|
||||||
bext_data = self._find_chunk_data(b'bext',f,default_none=True)
|
bext_data = self._find_chunk_data(b'bext',f,default_none=True)
|
||||||
|
|
||||||
# description[256]
|
# description[256]
|
||||||
# originator[32]
|
# originator[32]
|
||||||
# originatorref[32]
|
# originatorref[32]
|
||||||
# originatordate[10] "YYYY:MM:DD"
|
# originatordate[10] "YYYY:MM:DD"
|
||||||
@@ -100,7 +100,7 @@ class WavInfoReader():
|
|||||||
# hightimeref U32
|
# hightimeref U32
|
||||||
# version U16
|
# version U16
|
||||||
# umid[64]
|
# umid[64]
|
||||||
#
|
#
|
||||||
# EBU 3285 fields
|
# EBU 3285 fields
|
||||||
# loudnessvalue S16 (in LUFS*100)
|
# loudnessvalue S16 (in LUFS*100)
|
||||||
# loudnessrange S16 (in LUFS*100)
|
# loudnessrange S16 (in LUFS*100)
|
||||||
@@ -113,10 +113,10 @@ class WavInfoReader():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
packstring = "<256s"+ "32s" + "32s" + "10s" + "8s" + "QH" + "64s" + "hhhhh" + "180s"
|
packstring = "<256s"+ "32s" + "32s" + "10s" + "8s" + "QH" + "64s" + "hhhhh" + "180s"
|
||||||
|
|
||||||
rest_starts = struct.calcsize(packstring)
|
rest_starts = struct.calcsize(packstring)
|
||||||
unpacked = struct.unpack(packstring, bext_data[:rest_starts])
|
unpacked = struct.unpack(packstring, bext_data[:rest_starts])
|
||||||
|
|
||||||
return WavBextFormat(description=unpacked[0].decode('ascii').rstrip('\0'),
|
return WavBextFormat(description=unpacked[0].decode('ascii').rstrip('\0'),
|
||||||
originator = unpacked[1].decode('ascii').rstrip('\0'),
|
originator = unpacked[1].decode('ascii').rstrip('\0'),
|
||||||
originator_ref = unpacked[2].decode('ascii').rstrip('\0'),
|
originator_ref = unpacked[2].decode('ascii').rstrip('\0'),
|
||||||
@@ -125,11 +125,11 @@ class WavInfoReader():
|
|||||||
time_reference = unpacked[5],
|
time_reference = unpacked[5],
|
||||||
version = unpacked[6],
|
version = unpacked[6],
|
||||||
umid = unpacked[7],
|
umid = unpacked[7],
|
||||||
loudness_value = unpacked[8],
|
loudness_value = unpacked[8] / 100.0,
|
||||||
loudness_range = unpacked[9],
|
loudness_range = unpacked[9] / 100.0,
|
||||||
max_true_peak = unpacked[10],
|
max_true_peak = unpacked[10] / 100.0,
|
||||||
max_momentary_loudness = unpacked[11],
|
max_momentary_loudness = unpacked[11] / 100.0,
|
||||||
max_shortterm_loudness = unpacked[12],
|
max_shortterm_loudness = unpacked[12] / 100.0,
|
||||||
coding_history = bext_data[rest_starts:].decode('ascii').rstrip('\0')
|
coding_history = bext_data[rest_starts:].decode('ascii').rstrip('\0')
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -141,8 +141,8 @@ class WavInfoReader():
|
|||||||
|
|
||||||
ixml_string = ixml_data.decode('utf-8')
|
ixml_string = ixml_data.decode('utf-8')
|
||||||
return WavIXMLFormat(ixml_string)
|
return WavIXMLFormat(ixml_string)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user