Datasets
This page documents the torchrir.datasets helpers for CMU ARCTIC and
LibriSpeech, including accepted options, expected directory structures, and
error handling for invalid inputs.
For external corpora involving moving sources or microphone arrays, see Related Dynamic Speech and Acoustic Datasets. Those datasets are research references and are not currently built-in integrations.
Scope
Covered APIs:
torchrir.datasets.CmuArcticDatasettorchrir.datasets.LibriSpeechDatasettorchrir.datasets.load_dataset_sourcestorchrir.datasets.collate_dataset_itemstorchrir.datasets.DynamicCmuArcticBuildConfigtorchrir.datasets.build_dynamic_cmu_arctic
Quick start (local data, no download)
from pathlib import Path
from torchrir.datasets import CmuArcticDataset, LibriSpeechDataset
cmu = CmuArcticDataset(Path("datasets/cmu_arctic"), speaker="bdl", download=False)
libri = LibriSpeechDataset(
Path("datasets/librispeech"),
subset="train-clean-100",
speaker="103",
download=False,
)
When download=False, dataset loaders never start a network download. They may
finish recovery of an interrupted local publication before checking readiness,
then raise an error if the requested tree is missing or incomplete. download
must be an actual bool; truthy integers and strings are rejected.
Secure filesystem access for these corpus loaders and the dynamic builder is
implemented only on Linux and macOS. It requires POSIX directory-descriptor
walking plus atomic no-replace and exchange renames. An unsupported platform or
missing descriptor primitive raises NotImplementedError before local dataset
I/O; a filesystem that refuses the required atomic rename fails at that rename
instead of using a weaker fallback.
CMU ARCTIC
Accepted options
CmuArcticDataset(root, speaker="bdl", download=False)
| Option | Accepted values | Default | Notes |
|---|---|---|---|
root |
Path or path-like |
required | Dataset root directory managed by the caller. |
speaker |
One of aew, ahw, aup, awb, axb, bdl, clb, eey, fem, gka, jmk, ksp, ljm, lnh, rms, rxr, slp, slt |
"bdl" |
Valid IDs are defined in VALID_SPEAKERS. |
download |
True or False |
False |
If True, download/extract missing archive for the selected speaker. |
Enumerate every supported speaker, or only speakers installed below a root, with:
from torchrir.datasets import cmu_arctic_speakers
print(cmu_arctic_speakers())
print(cmu_arctic_speakers(Path("datasets/cmu_arctic")))
Expected local structure
For root = datasets/cmu_arctic and speaker = bdl:
datasets/cmu_arctic/
ARCTIC/
cmu_us_bdl_arctic/
etc/txt.done.data
wav/*.wav
A speaker tree is usable only when every non-empty line in txt.done.data
parses with the CMU ARCTIC transcript syntax and contains a canonical
arctic_<letter><four-digits> ID. At least one of those IDs must have a
same-named WAV file; audio is not required for every otherwise valid transcript
entry. This same predicate drives construction, cmu_arctic_speakers(root),
and the download fast path. The speaker dataset root, intermediate etc/wav
directories, and final transcript/audio paths must not be symlinks. Each path
component is opened relative to an already-open directory descriptor with
O_NOFOLLOW; transcripts and audio are parsed or decoded through that same
final file descriptor instead of reopening a validated pathname.
Invalid input handling
| Condition | Behavior |
|---|---|
speaker is not a string |
Raises TypeError. |
String speaker not in supported set |
Raises ValueError (unsupported speaker: ...). |
Dataset tree missing, partial, or without a canonical transcript/audio pair and download=False |
Raises FileNotFoundError with guidance to use download=True. |
Usable tree and download=True |
Returns without checking an archive or accessing the network. |
| HTTP 408/429/5xx, transport, malformed/mismatched response length, or digest failure during download | Uses HTTPS, verifies the pinned per-speaker SHA-256, and retries once by default before propagating the error. |
| Declared or streamed body exceeds the 64 GiB safety limit | Raises ValueError without retrying. |
| Local filesystem, extraction, validation, or publication failure | Propagates immediately without a download retry or re-downloading an already verified archive. |
| Cached archive path is a symlink or another non-regular entry | A symlink is replaced only through a verified atomic exchange without following its target; any other non-regular cache entry is rejected. |
| Corrupt/unsafe or excessive archive (path traversal, link/special-file entry, too many members, or declared size over a bound) | Raises ValueError before any member is extracted. |
Malformed or path-like utterance_id |
Raises ValueError before filesystem access. |
load_audio(utterance_id) for missing or non-regular audio |
Raises FileNotFoundError before SoundFile is called. |
LibriSpeech
Accepted options
LibriSpeechDataset(root, subset="train-clean-100", speaker=None, download=False)
| Option | Accepted values | Default | Notes |
|---|---|---|---|
root |
Path or path-like |
required | Dataset root directory managed by the caller. |
subset |
One of dev-clean, dev-other, test-clean, test-other, train-clean-100, train-clean-360, train-other-500 |
"train-clean-100" |
Valid values are defined in VALID_SUBSETS. |
speaker |
None or a numeric speaker ID string (for example, "103") |
None |
If set, loader is restricted to that speaker only. |
download |
True or False |
False |
If True, download/extract missing subset archive. |
Expected local structure
For root = datasets/librispeech and subset = train-clean-100:
datasets/librispeech/
LibriSpeech/
train-clean-100/
<speaker_id>/
<chapter_id>/
<utt_id>.flac
<speaker_id>-<chapter_id>.trans.txt
The loader scans regular *.trans.txt files directly below non-symlink numeric
chapter directories of non-symlink numeric speaker trees. A speaker tree is
usable only when every non-empty entry in every transcript found by that scan
contains a canonical speaker-chapter-utterance ID whose speaker and chapter
components agree with its directories. At least one such entry must have a
same-named regular FLAC; audio is not required for every otherwise valid
transcript entry. Unrelated transcript-like files outside this canonical tree
and unusable stray speaker trees are ignored. The same predicate drives
speaker/subset discovery, construction, and the download fast path. The subset
root, numeric speaker/chapter directories, and final transcript/audio paths must
not be symlinks. Each final entry must be regular and is consumed through the
same descriptor obtained by a component-wise O_NOFOLLOW walk.
Invalid input handling
| Condition | Behavior |
|---|---|
subset is not a string, or speaker is neither None nor a string |
Raises TypeError. |
String subset not in supported set, or string speaker is empty/non-numeric |
Raises ValueError. |
Subset tree missing, partial, or without a canonical transcript/audio pair and download=False |
Raises FileNotFoundError with guidance to use download=True. |
speaker provided but its directory is missing or incomplete |
Raises FileNotFoundError (speaker directory is missing or incomplete: ...). |
Usable subset and download=True |
Returns without checking an archive or accessing the network. With speaker=..., this fast path requires that specific speaker to be usable; another ready speaker does not mask a partial requested tree. |
load_audio(utterance_id) with malformed ID (not numeric spk-chapter-utt) |
Raises ValueError before filesystem access. |
utterance_id belongs to a different configured speaker |
Raises ValueError. |
| Transcript ID conflicts with its speaker/chapter directory | Raises ValueError while discovering sentences. |
load_audio(utterance_id) for missing or non-regular audio |
Raises FileNotFoundError before SoundFile is called. |
| HTTP 408/429/5xx, transport, malformed/mismatched response length, or digest failure during download | Verifies the pinned subset MD5 and retries once by default before propagating the error. |
| Declared or streamed body exceeds the 64 GiB safety limit | Raises ValueError without retrying. |
| Local filesystem, extraction, validation, or publication failure | Propagates immediately without a download retry or re-downloading an already verified archive. |
| Cached archive path is a symlink or another non-regular entry | A symlink is replaced only through a verified atomic exchange without following its target; any other non-regular cache entry is rejected. |
| Corrupt/unsafe or excessive archive (path traversal, link/special-file entry, too many members, or declared size over a bound) | Raises ValueError before any member is extracted. |
Shared archive, concurrency, and recovery contract
CMU ARCTIC and LibriSpeech use the same verified transfer implementation:
- Dataset filesystem operations require Linux or macOS with POSIX
dir_fd,O_DIRECTORY, andO_NOFOLLOW, plus Linuxrenameat2or macOSrenameatx_npno-replace/exchange support. The implementation raisesNotImplementedErrorrather than weakening the contract on unsupported platforms or filesystems. - Each connection and response-body read has a 60-second I/O timeout. A
six-hour total transfer deadline starts before connection setup, and its
remaining time is applied to each connection/read timeout. The deadline
bounds network progress; hashing, filesystem scheduling, and cleanup are not
hard real-time operations. The response is streamed into an exclusively
created
archive.partinside a fresh marker-owned sibling workspace; no shared partial-download path is reused or followed. - A transfer may contain at most 64 GiB. If supplied,
Content-Lengthmust be a valid non-negative integer no greater than that limit. The body is still read through EOF: a short or overlong response is rejected, as is a body withoutContent-Lengththat crosses the byte limit. The pinned digest must always match before publication. - The default single retry is limited to HTTP 408, HTTP 429, HTTP 5xx, connection/response-body transport failures, malformed response length, and declared-length/digest integrity failures. The 64 GiB safety limit, other HTTP statuses, and local filesystem errors are not retried. Archive opening, extraction, readiness validation, and publication are also outside the retry operation, so a verified cached archive remains available when one of those local steps fails.
- An absent archive destination is published with an atomic no-replace rename. An existing regular file or symlink is replaced only by an atomic exchange/swap after its device, inode, and type have been snapshotted. The displaced entry must match that snapshot; otherwise the exchange is reversed and the racing third-party entry is retained. Symlink targets are never followed, and other non-regular destination types are rejected.
- Local transcript, audio, and archive files are opened by walking every path
component from an already-open root directory descriptor with
O_NOFOLLOW. Parsing, SoundFile decoding, hashing, and tar parsing consume that same final file descriptor. A pathname cannot be swapped between validation and use. - Every member is prevalidated before extraction. The allowlist contains only regular files and directories below the staging root; traversal, links, and special files are rejected. Declared content is bounded to 1,000,000 members, 8 GiB for one file, and 512 GiB in total. Archive payloads are extracted below a child directory so they cannot overwrite the workspace ownership marker.
Dataset repair has two lock scopes. A CMU speaker lock or LibriSpeech subset
lock is acquired before stale-workspace cleanup and the readiness check. While
holding it, the loader removes only empty or marker-verified workspaces reserved
for that archive/target and, only if repair is still necessary, performs cache
inspection, download, extraction, staged validation, and publication. Unsafe,
symlinked, or unowned matching paths are retained and reported. Cleanup failure
is a warning and does not replace the primary operation result. Lock acquisition
has a strict 1800-second default deadline, checked before and after non-blocking
flock; the acquired path is then revalidated against the descriptor to prevent
split-brain locking. The operating system releases the advisory lock if a
process exits. Persistent hidden regular lock files are not deleted after each
writer.
Staged publication has a separate sibling lock for each final target. That lock is held only while recovering a prior transaction or committing the staged tree: it covers the final target/type and create-only checks, the old-target backup rename, the staged-tree rename, immediate rollback, and transaction cleanup. Every target/backup/staged rename uses the platform's atomic no-replace primitive, so an entry that races into a previously absent name is never overwritten.
Replacing an existing target records its previous tree in a deterministic
sibling transaction directory. A fsynced schema-versioned manifest records the
device/inode identities of the staged and previous trees plus the
initialized, backed_up, or published phase. A normal publication failure
attempts immediate identity-checked rollback. If an atomic rename completed but
then reported an error, the implementation identifies the actual outcome
before deciding whether to return, roll back, or retain the transaction.
After interruption, the next loader or build restores a missing target only from the manifest-recorded previous identity, and cleans state only when the target is one of the recorded identities. A third-party target, changed backup, unsafe symlink, malformed manifest, or unknown transaction entry stops recovery without deleting the retained backup. Only recognized owned manifest temporary files may be reclaimed automatically.
The dynamic builder adds a broader per-output build lock. It acquires that lock
before transaction recovery, the overwrite/existence check, marker-owned
stale build-workspace cleanup, corpus loading, or scene generation. Builds for
the same target are serialized for their complete expensive operation; the
target-specific publication lock remains a narrower nested lock for the final
transaction. Builds for different output targets remain independent.
Shared utility behavior
Dataset interfaces and records
BaseDataset is an abstract torch.utils.data.Dataset contract. Concrete
subclasses must implement speaker discovery, sentence discovery, audio loading,
and attribution; an incomplete subclass cannot be instantiated.
CmuArcticSentence, LibriSpeechSentence, and DatasetAttribution are frozen,
slotted, keyword-only records. Sentence construction validates canonical corpus
identifiers and, for LibriSpeech, agreement among utterance, speaker, and
chapter IDs. Attribution construction validates non-empty fields and an actual
boolean attribution flag.
load_dataset_sources
load_dataset_sources(*, dataset_factory, speakers, num_sources, duration_s,
rng) builds fixed-duration signals by sampling an explicit speaker catalog and
concatenating utterances. All arguments are keyword-only. The factory receives
only selected string IDs; None is not used as a hidden catalog sentinel.
Accepted options and key constraints:
| Parameter | Accepted values | Invalid handling |
|---|---|---|
dataset_factory |
Callable from one speaker ID string to a BaseDataset |
Raises TypeError if non-callable or if its result is not a BaseDataset; runtime error if a selected dataset has no sentences. |
speakers |
Non-empty sequence of unique, non-empty speaker ID strings | Raises TypeError/ValueError; use cmu_arctic_speakers(root) or a LibriSpeech catalog's list_speakers() for local data. |
num_sources |
Positive non-boolean int, and must be <= unique available speakers |
Raises TypeError/ValueError for invalid values. |
duration_s |
Finite positive real number | Raises TypeError/ValueError; target length is ceil(duration_s * sample_rate). |
rng |
random.Random instance |
Raises TypeError otherwise. |
Additional runtime checks:
- Every loaded utterance must be a non-empty, finite, mono floating-point Tensor with a positive integer sample rate.
- Raises
ValueErrorif sampled utterances/speakers mix sample rates, dtypes, or devices.
DatasetItem and collate_dataset_items
DatasetItem is a frozen, slotted, keyword-only record. audio must be a
non-empty finite mono floating-point Tensor, sample_rate a positive integer,
and utterance_id a non-empty string. The optional metadata field is an
explicit pass-through payload. Because Tensor contents remain shallow-mutable,
collation calls DatasetItem.validate() again at the consumer boundary.
Sample rates accept non-boolean Python or NumPy integer scalars and are
normalized to Python int; the supported range is 1..2**31-1. The same
range is used by corpus audio validation and dynamic build results.
collate_dataset_items(items, pad_value=0.0, keep_metadata=False) builds a
padded, frozen CollateBatch for DataLoader. Identifier/text/speaker
sequences and retained item metadata are normalized to tuples.
| Condition | Behavior |
|---|---|
Empty items |
Raises ValueError (collate_dataset_items received an empty batch). |
Invalid or mutated DatasetItem |
Revalidation raises before padding. |
| Mixed sample rates, dtypes, or devices in a batch | Raises ValueError. |
Non-finite pad_value |
Raises ValueError. |
| Valid mixed lengths | Pads to max_len with pad_value. |
keep_metadata=True |
Preserves one explicit metadata payload per item as a tuple. |
Interaction with example scripts
The library loaders follow strict download behavior: download=False means
no network access.
examples/build_dynamic_dataset.py preserves the same strict policy:
- If a dataset is missing or incomplete and
--downloadis not set, the script raisesFileNotFoundErrorwithout network access. - Passing
--downloadexplicitly authorizes fetching missing archives. --datasetis argparse-constrained tocmu_arcticorlibrispeech.--subsetis validated later byLibriSpeechDataset(ValueErroron unsupported values).
Use --dataset-dir to point at a fully prepared local dataset tree when you
need predictable offline execution.
Attribution and redistribution
For licensing and redistribution guidance, see
THIRD_PARTY_DATASETS.md.
Dynamic CMU ARCTIC builder (oobss-compatible)
build_dynamic_cmu_arctic(config) generates dynamic scenes in the file format
expected by the oobss loader type torchrir_dynamic. This describes the
published artifact layout; oobss is not required to build the dataset.
The builder accepts one DynamicCmuArcticBuildConfig and returns a
DynamicDatasetBuildResult containing the output root, sample rate,
microphone count, scene count, and scene paths. Builds are staged in a sibling
temporary directory. The target directory is created or replaced only after
every scene has completed successfully, so a failed build leaves an existing
dataset unchanged.
Configuration construction performs all checks that depend only on requested
values. This includes positive counts and durations, source and moving-source
counts, trajectory length, motion-ratio ordering, moving-speed bounds, room
and source-margin geometry, Sabine T60 feasibility, simulation image-grid
dimension, and whether the octahedral microphone array and source radius fit
around a fixed or randomized microphone center. It also
rejects equal or nested cmu_root and dataset_root paths in either direction,
preventing an overwrite from deleting the source corpus. Invalid requests
therefore fail before a staging directory or dataset loader is created.
Filesystem existence, available utterances, per-utterance audio validity and
sample-rate consistency are checked when the build runs because they depend on
external state. Once the sample rate is known, the builder additionally
requires trajectory_steps <= ceil(duration_sec * sample_rate), so every frame
has a distinct integer start sample.
The frozen config owns an immutable snapshot of its inputs. cmu_root and
dataset_root are normalized to Path; speakers, room_size, mic_center,
and source_margin are copied to tuples. Later mutation of lists supplied by a
caller cannot change an existing build request.
The builder acquires .<target>.torchrir-build.lock before recovery and the
initial overwrite check. While holding it, the builder removes only
marker-owned stale .<target>.torchrir-build.* workspaces, writes the complete
output below a new managed sibling workspace, and invokes the shared
publication transaction only after every scene succeeds. Thus two builds for
the same target do not duplicate expensive generation; the final commit still
uses the nested publication lock and repeats its target/create-only checks. See
Shared archive, concurrency, and recovery contract
for the exact lock and rollback scope.
Python API
from pathlib import Path
import torch
from torchrir.config import SimulationConfig
from torchrir.datasets import (
DynamicCmuArcticBuildConfig,
build_dynamic_cmu_arctic,
)
config = DynamicCmuArcticBuildConfig(
cmu_root=Path("datasets/cmu_arctic"),
dataset_root=Path("outputs/cmu_arctic_torchrir_dynamic_dataset"),
n_scenes=2,
simulation=SimulationConfig(
max_order=6,
nsample=4096,
device="auto",
dtype=torch.float32,
),
overwrite=True,
)
result = build_dynamic_cmu_arctic(config)
print(result.dataset_root)
print(result.scene_dirs)
Default output parameters include:
n_sources=3trajectory_steps=256simulation=SimulationConfig(max_order=6, nsample=4096, dtype=torch.float32)- moving speed range
0.3-0.8m/s - motion profile:
0-35%static,35-65%moving,65-100%static
The simulation object is the sole owner of RIR algorithm settings. The
builder creates FrameSchedule.uniform(...) in exact integer samples and uses
those same starts for trajectory evaluation, scene simulation, convolution,
and metadata; it does not construct a second endpoint-inclusive time grid.
The requested duration is mapped to ceil(duration_sec * sample_rate) samples.
The resulting sample-aligned duration drives motion and is recorded beside the
requested duration in metadata. Before writing 32-bit floating-point WAV,
every reverberant stem and their summed mixture are checked together and, when
necessary, multiplied by one common scale capped at 0.99. Relative stem gains
and additivity are therefore preserved without clipping or independent PCM
quantization.
CLI usage
Module entrypoint:
python -m torchrir.datasets.dynamic_cmu_arctic \
--cmu-root datasets/cmu_arctic \
--dataset-root outputs/cmu_arctic_torchrir_dynamic_dataset \
--n-scenes 10 \
--save-layout-mp4 \
--save-layout-mp4-3d \
--overwrite-dataset
Console script entrypoint:
torchrir-build-dynamic-cmu-arctic \
--cmu-root datasets/cmu_arctic \
--dataset-root outputs/cmu_arctic_torchrir_dynamic_dataset \
--n-scenes 10 \
--layout-video-fps 12 \
--overwrite-dataset
Useful video flags:
--no-save-layout-mp4: disable MP4 rendering--no-save-layout-mp4-3d: skiproom_layout_3d.mp4--layout-video-fps <float>: override frame rate--layout-video-no-audio: disable mixture-audio mux into MP4--no-save-layout-images: disable static layout images--no-save-layout-images-3d: skiproom_layout_3d.png--no-annotate-source-indices: disable source index labels (S0,S1, ...)
Simulation flags map into the same SimulationConfig contract used by the
Python API: --max-order, --rir-samples, --device, and --dtype. Both the
CLI and Python simulation API support float32 and float64; lower-precision
ISM geometry is rejected before execution.
Output structure
PNG files are present when layout images are enabled. MP4 files are produced
only when requested and a system ffmpeg is available; audio muxing also
requires the audio extra. Otherwise the builder records/skips those optional
artifacts without changing the required scene data.
<dataset-root>/
scene_0000/
mixture.wav
source_00.wav
source_01.wav
...
metadata.json
source_info.json
room_layout_2d.png # optional
room_layout_3d.png # optional
room_layout_2d.mp4 # optional
room_layout_3d.mp4 # optional
scene_0001/
...
Each metadata.json uses torchrir.scene schema version 1. It stores the
integer frame_schedule.starts_samples, emission-time convolution semantics,
and compact RIR/signal sample counts; it does not duplicate starts in seconds
or serialize a complete RIR time-axis array.