Skip to content

Commit 1d499d9

Browse files
authored
Merge pull request #36 from iluvcapra/feature-smpl
Feature: smpl Metadata
2 parents c6f66b2 + 299f79a commit 1d499d9

File tree

6 files changed

+31
-12
lines changed

6 files changed

+31
-12
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ it is not supported, please submit an issue!
3131
and Dolby Atmos `dbmd` metadata for re-renders and mixdowns.
3232
* Wave embedded [cue markers][cues], cue marker labels, notes and timed ranges as used
3333
by Zoom, iZotope RX, etc.
34+
* Wave embedded [sampler][smpl] and sample loop metadata.
3435
* The [wav format][format] is also parsed, so you can access the basic sample rate
3536
and channel count information.
3637

3738

3839
[format]:https://wavinfo.readthedocs.io/en/latest/classes.html#wavinfo.wave_reader.WavAudioFormat
3940
[cues]:https://wavinfo.readthedocs.io/en/latest/scopes/cue.html
4041
[bext]:https://wavinfo.readthedocs.io/en/latest/scopes/bext.html
42+
[smpl]:https://wavinfo.readthedocs.io/en/latest/scopes/smpl.html
4143
[smpte_330m2011]:https://wavinfo.readthedocs.io/en/latest/scopes/bext.html#wavinfo.wave_bext_reader.WavBextReader.umid
4244
[adm]:https://wavinfo.readthedocs.io/en/latest/scopes/adm.html
4345
[ebu3285s6]:https://wavinfo.readthedocs.io/en/latest/scopes/dolby.html

docs/source/scopes/smpl.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
Sampler Metadata
3+
=================
4+
5+
Class Reference
6+
---------------
7+
8+
.. automodule:: wavinfo.wave_smpl_reader
9+
10+
.. autoclass:: wavinfo.wave_smpl_reader.WavSmplReader
11+
:members:
12+
13+
.. autoclass:: wavinfo.wave_smpl_reader.WaveSmplLoop
14+
:members:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
66

77
[tool.poetry]
88
name = "wavinfo"
9-
version = "3.0.1"
9+
version = "3.1.0"
1010
description = "Probe WAVE files for all metadata"
1111
authors = ["Jamie Hardt <jamiehardt@me.com>"]
1212
license = "MIT"
195 KB
Binary file not shown.

wavinfo/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def default(self, o):
1515
if isinstance(o, Enum):
1616
return o._name_
1717
elif isinstance(o, bytes):
18-
return b64encode(o).decode('ascii')
18+
return 'base64:' + b64encode(o).decode('ascii')
1919
else:
2020
return super().default(o)
2121

wavinfo/wave_smpl_reader.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class WaveSmplLoop(NamedTuple):
88
loop_type: int
99
start: int
1010
end: int
11-
fraction: int
11+
detune_cents: int
1212
repetition_count: int
1313

1414
def loop_type_desc(self):
@@ -30,7 +30,7 @@ def to_dict(self):
3030
'loop_type_description': self.loop_type_desc(),
3131
'start_samples': self.start,
3232
'end_samples': self.end,
33-
'fraction': self.fraction,
33+
'detune_cents': self.detune_cents,
3434
'repetition_count': self.repetition_count,
3535
}
3636

@@ -42,8 +42,8 @@ def __init__(self, smpl_data: bytes):
4242
Read sampler metadata from smpl chunk.
4343
"""
4444

45-
header_field_fmt = "<IIIIIIbbbbII"
46-
loop_field_fmt = "<IIIIII"
45+
header_field_fmt = "<IIIIiIbbbbII"
46+
loop_field_fmt = "<IIIIiI"
4747
header_size = struct.calcsize(header_field_fmt)
4848
loop_size = struct.calcsize(loop_field_fmt)
4949

@@ -65,7 +65,7 @@ def __init__(self, smpl_data: bytes):
6565
self.midi_note: int = unpacked_data[3]
6666

6767
#: The number of semitones above the MIDI note the loops tune for.
68-
self.midi_pitch_fraction_semis: int = unpacked_data[4]
68+
self.midi_pitch_detune_cents: int = unpacked_data[4]
6969

7070
#: SMPTE timecode format, one of (0, 24, 25, 29, 30)
7171
self.smpte_format: int = unpacked_data[5]
@@ -89,21 +89,24 @@ def __init__(self, smpl_data: bytes):
8989
loop_type=unpacked_loop[1],
9090
start=unpacked_loop[2],
9191
end=unpacked_loop[3],
92-
fraction=unpacked_loop[4],
92+
detune_cents=unpacked_loop[4],
9393
repetition_count=unpacked_loop[5]))
9494

9595
#: Sampler-specific user data.
96-
self.sampler_udata: bytes = smpl_data[
97-
header_size + loop_size * loop_count:
98-
header_size + loop_size * loop_count + sampler_udata_length]
96+
self.sampler_udata: bytes | None = None
97+
98+
if sampler_udata_length > 0:
99+
self.sampler_udata = smpl_data[
100+
header_size + loop_size * loop_count:
101+
header_size + loop_size * loop_count + sampler_udata_length]
99102

100103
def to_dict(self):
101104
return {
102105
'manufactuer': self.manufacturer,
103106
'product': self.product,
104107
'sample_period_ns': self.sample_period_ns,
105108
'midi_note': self.midi_note,
106-
'midi_pitch_fraction_semis': self.midi_pitch_fraction_semis,
109+
'midi_pitch_detune_cents': self.midi_pitch_detune_cents,
107110
'smpte_format': self.smpte_format,
108111
'smpte_offset': "%02i:%02i:%02i:%02i" % self.smpte_offset,
109112
'loops': [x.to_dict() for x in self.sample_loops],

0 commit comments

Comments
 (0)