Merge pull request #36 from iluvcapra/feature-smpl

Feature: smpl Metadata
This commit is contained in:
Jamie Hardt
2024-11-25 11:09:43 -08:00
committed by GitHub
6 changed files with 31 additions and 12 deletions

View File

@@ -31,6 +31,7 @@ it is not supported, please submit an issue!
and Dolby Atmos `dbmd` metadata for re-renders and mixdowns. and Dolby Atmos `dbmd` metadata for re-renders and mixdowns.
* Wave embedded [cue markers][cues], cue marker labels, notes and timed ranges as used * Wave embedded [cue markers][cues], cue marker labels, notes and timed ranges as used
by Zoom, iZotope RX, etc. by Zoom, iZotope RX, etc.
* Wave embedded [sampler][smpl] and sample loop metadata.
* The [wav format][format] is also parsed, so you can access the basic sample rate * The [wav format][format] is also parsed, so you can access the basic sample rate
and channel count information. and channel count information.
@@ -38,6 +39,7 @@ it is not supported, please submit an issue!
[format]:https://wavinfo.readthedocs.io/en/latest/classes.html#wavinfo.wave_reader.WavAudioFormat [format]:https://wavinfo.readthedocs.io/en/latest/classes.html#wavinfo.wave_reader.WavAudioFormat
[cues]:https://wavinfo.readthedocs.io/en/latest/scopes/cue.html [cues]:https://wavinfo.readthedocs.io/en/latest/scopes/cue.html
[bext]:https://wavinfo.readthedocs.io/en/latest/scopes/bext.html [bext]:https://wavinfo.readthedocs.io/en/latest/scopes/bext.html
[smpl]:https://wavinfo.readthedocs.io/en/latest/scopes/smpl.html
[smpte_330m2011]:https://wavinfo.readthedocs.io/en/latest/scopes/bext.html#wavinfo.wave_bext_reader.WavBextReader.umid [smpte_330m2011]:https://wavinfo.readthedocs.io/en/latest/scopes/bext.html#wavinfo.wave_bext_reader.WavBextReader.umid
[adm]:https://wavinfo.readthedocs.io/en/latest/scopes/adm.html [adm]:https://wavinfo.readthedocs.io/en/latest/scopes/adm.html
[ebu3285s6]:https://wavinfo.readthedocs.io/en/latest/scopes/dolby.html [ebu3285s6]:https://wavinfo.readthedocs.io/en/latest/scopes/dolby.html

View File

@@ -0,0 +1,14 @@
Sampler Metadata
=================
Class Reference
---------------
.. automodule:: wavinfo.wave_smpl_reader
.. autoclass:: wavinfo.wave_smpl_reader.WavSmplReader
:members:
.. autoclass:: wavinfo.wave_smpl_reader.WaveSmplLoop
:members:

View File

@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry] [tool.poetry]
name = "wavinfo" name = "wavinfo"
version = "3.0.1" version = "3.1.0"
description = "Probe WAVE files for all metadata" description = "Probe WAVE files for all metadata"
authors = ["Jamie Hardt <jamiehardt@me.com>"] authors = ["Jamie Hardt <jamiehardt@me.com>"]
license = "MIT" license = "MIT"

Binary file not shown.

View File

@@ -15,7 +15,7 @@ class MyJSONEncoder(json.JSONEncoder):
if isinstance(o, Enum): if isinstance(o, Enum):
return o._name_ return o._name_
elif isinstance(o, bytes): elif isinstance(o, bytes):
return b64encode(o).decode('ascii') return 'base64:' + b64encode(o).decode('ascii')
else: else:
return super().default(o) return super().default(o)

View File

@@ -8,7 +8,7 @@ class WaveSmplLoop(NamedTuple):
loop_type: int loop_type: int
start: int start: int
end: int end: int
fraction: int detune_cents: int
repetition_count: int repetition_count: int
def loop_type_desc(self): def loop_type_desc(self):
@@ -30,7 +30,7 @@ class WaveSmplLoop(NamedTuple):
'loop_type_description': self.loop_type_desc(), 'loop_type_description': self.loop_type_desc(),
'start_samples': self.start, 'start_samples': self.start,
'end_samples': self.end, 'end_samples': self.end,
'fraction': self.fraction, 'detune_cents': self.detune_cents,
'repetition_count': self.repetition_count, 'repetition_count': self.repetition_count,
} }
@@ -42,8 +42,8 @@ class WavSmplReader:
Read sampler metadata from smpl chunk. Read sampler metadata from smpl chunk.
""" """
header_field_fmt = "<IIIIIIbbbbII" header_field_fmt = "<IIIIiIbbbbII"
loop_field_fmt = "<IIIIII" loop_field_fmt = "<IIIIiI"
header_size = struct.calcsize(header_field_fmt) header_size = struct.calcsize(header_field_fmt)
loop_size = struct.calcsize(loop_field_fmt) loop_size = struct.calcsize(loop_field_fmt)
@@ -65,7 +65,7 @@ class WavSmplReader:
self.midi_note: int = unpacked_data[3] self.midi_note: int = unpacked_data[3]
#: The number of semitones above the MIDI note the loops tune for. #: The number of semitones above the MIDI note the loops tune for.
self.midi_pitch_fraction_semis: int = unpacked_data[4] self.midi_pitch_detune_cents: int = unpacked_data[4]
#: SMPTE timecode format, one of (0, 24, 25, 29, 30) #: SMPTE timecode format, one of (0, 24, 25, 29, 30)
self.smpte_format: int = unpacked_data[5] self.smpte_format: int = unpacked_data[5]
@@ -89,11 +89,14 @@ class WavSmplReader:
loop_type=unpacked_loop[1], loop_type=unpacked_loop[1],
start=unpacked_loop[2], start=unpacked_loop[2],
end=unpacked_loop[3], end=unpacked_loop[3],
fraction=unpacked_loop[4], detune_cents=unpacked_loop[4],
repetition_count=unpacked_loop[5])) repetition_count=unpacked_loop[5]))
#: Sampler-specific user data. #: Sampler-specific user data.
self.sampler_udata: bytes = smpl_data[ self.sampler_udata: bytes | None = None
if sampler_udata_length > 0:
self.sampler_udata = smpl_data[
header_size + loop_size * loop_count: header_size + loop_size * loop_count:
header_size + loop_size * loop_count + sampler_udata_length] header_size + loop_size * loop_count + sampler_udata_length]
@@ -103,7 +106,7 @@ class WavSmplReader:
'product': self.product, 'product': self.product,
'sample_period_ns': self.sample_period_ns, 'sample_period_ns': self.sample_period_ns,
'midi_note': self.midi_note, 'midi_note': self.midi_note,
'midi_pitch_fraction_semis': self.midi_pitch_fraction_semis, 'midi_pitch_detune_cents': self.midi_pitch_detune_cents,
'smpte_format': self.smpte_format, 'smpte_format': self.smpte_format,
'smpte_offset': "%02i:%02i:%02i:%02i" % self.smpte_offset, 'smpte_offset': "%02i:%02i:%02i:%02i" % self.smpte_offset,
'loops': [x.to_dict() for x in self.sample_loops], 'loops': [x.to_dict() for x in self.sample_loops],