AudioDataSource¶
- class AudioDataSource(path: str | Path, *, audio_dir: str | Path | None = None, num_records: int | None = None, sample_rate: int | None = None, mono: bool = False, duration: float | None = None, pad_mode: Literal['constant', 'wrap'] = 'constant', filter_fn: Callable[[Dict], bool] | None = None, on_read_error: Literal['raise', 'skip', 'warn'] = 'raise')¶
A DataSource that reads audio files based on a manifest file created by AudioWriter.
This DataSource is designed to work seamlessly with the output of AudioWriter, reading audio files and restoring their associated extras from NPZ manifests.
NPZ format provides: - Efficient binary storage with 20x+ compression vs JSON for large datasets - Fast loading without text parsing - Exact numeric type preservation - Support for both compressed and uncompressed variants
- Parameters:
path – The dataset directory an
AudioWriterproduced (itsmanifest.npzis located inside), or an explicit path to a manifest file (NPZ format)audio_dir – Optional directory containing audio files. If None, uses manifest directory
num_records – Optional limit on number of records to load
sample_rate – Optional target sample rate for resampling
mono – Whether to convert audio to mono
duration –
Optional duration to trim/pad audio to (in seconds)
When
sample_rate,monoordurationchange the audio relative to what was written, the manifest’s derived fields (lufs,lufs_windows,codes,latents) are not restored – they describe the written audio, not the altered one. Annotation fields (pitch,velocity,note_duration) are restored either way.pad_mode – Padding mode if duration is specified (“constant” or “wrap”)
filter_fn – Optional function to filter manifest entries
on_read_error – What to do when one entry’s audio file is missing or cannot be decoded.
"raise"(default) raisesAudioReadError, naming the path;"warn"drops the item (ds[i]returnsNone, which grain skips at iteration) and emits aUserWarning;"skip"drops it quietly. Seecreate_audio_dataset()for the reasoning.
Example
First write some audio with
AudioWriterso there is amanifest.npzto read back:>>> import tempfile >>> import numpy as np >>> import jax.numpy as jnp >>> from audiotree import AudioTree, AudioWriter >>> out_dir = tempfile.mkdtemp() >>> lufs = np.full((5,), -10.0, dtype=np.float32) >>> with AudioWriter(out_dir) as writer: ... _ = writer.write( ... AudioTree.create(jnp.zeros((5, 1, 44100)), 44100, lufs=lufs) ... ) >>> manifest_path = f"{out_dir}/manifest.npz"
Read straight from the NPZ manifest:
>>> source = AudioDataSource(manifest_path) >>> len(source) 5 >>> source[0].waveform.shape (1, 1, 44100)
Filter entries by extras while loading:
>>> source = AudioDataSource( ... manifest_path, ... filter_fn=lambda entry: entry.get('lufs', -float('inf')) > -20 ... )
Or point straight at the dataset directory (its
manifest.npzis located inside), the same wayTreeDataSourceis used:>>> source = AudioDataSource(out_dir)
- filter(predicate: Callable[[Dict], bool]) AudioDataSource¶
Create a new AudioDataSource keeping the entries matching
predicate.Filtering narrows the current entries, so filters compose:
source.filter(a).filter(b)keeps the entries matching both.- Parameters:
predicate – Function called with a manifest entry, returning whether to keep it
- Returns:
New AudioDataSource with the matching entries
- Raises:
ValueError – If no entry matches
- filter_by_lufs(min_lufs: float | None = None, max_lufs: float | None = None) AudioDataSource¶
Create a new AudioDataSource filtered by loudness range.
Filters entries based on the ‘lufs’ field in the manifest. Works with manifests created by AudioWriter in NPZ format.
Narrows the receiver’s entries, so this composes with any other filter already applied (including a constructor
filter_fnand anum_recordscap).- Parameters:
min_lufs – Minimum loudness in LUFS (inclusive)
max_lufs – Maximum loudness in LUFS (inclusive)
- Returns:
New AudioDataSource with filtered entries
- Raises:
ValueError – If no entry falls in the range
Example
Write four items with known per-item loudness so the manifest records a
lufsfield to filter on:>>> import tempfile >>> import numpy as np >>> import jax.numpy as jnp >>> from audiotree import AudioTree, AudioWriter >>> out_dir = tempfile.mkdtemp() >>> audio = AudioTree.create( ... jnp.zeros((4, 1, 44100)), 44100, ... lufs=np.array([-30.0, -18.0, -10.0, -25.0], dtype=np.float32), ... ) >>> with AudioWriter(out_dir) as writer: ... _ = writer.write(audio) >>> source = AudioDataSource(out_dir)
Keep only samples louder than -20 LUFS:
>>> loud_source = source.filter_by_lufs(min_lufs=-20.0) >>> len(loud_source) 2
Keep samples within a specific loudness range:
>>> mid_source = source.filter_by_lufs(min_lufs=-30.0, max_lufs=-15.0) >>> len(mid_source) 3
Filters compose, so chaining keeps only what matches both:
>>> len(loud_source.filter_by_lufs(max_lufs=-15.0)) 1
- filter_by_tag(tag_name: str, tag_value) AudioDataSource¶
Create a new AudioDataSource filtered by a specific tag value.
Narrows the receiver’s entries, so this composes with any other filter already applied (including a constructor
filter_fnand anum_recordscap).- Parameters:
tag_name – Name of the tag to filter by
tag_value – Value the tag must have
- Returns:
New AudioDataSource with filtered entries
- Raises:
ValueError – If no entry has that tag value
- get_all_entries() List[Dict]¶
Get all manifest entries.
- Returns:
List of all manifest entry dictionaries
- get_entry(index: int) Dict¶
Get the raw manifest entry for a given index.
- Parameters:
index – Index of the entry
- Returns:
Dictionary containing the manifest entry