Skip to main content

Build an IR dataset with IR collections

Use this tutorial to build a filtered impulse-response dataset for downstream audio tasks. First, you'll set up, preprocess, and save an IR collection on one machine (e.g., your laptop). Then you'll load it on a different machine (e.g., cloud compute cluster) to access audio and iterate over the collection for a downstream task.

For focused examples of individual collection operations, see the IR collections how-to. For the IR collection data model and technical details, see the reference on IR collections.


This tutorial follows these key steps:

  1. Set up and preprocess the collection
    1.1 Get the collection from a dataset
    1.2 Enrich the collection with metadata
    1.3 Filter and sample the collection
    1.4 Create a train/test split
  2. Use the collection on a different machine for a downstream task
    2.1 Preload audio
    2.2 Iterate over the collection

Set up and preprocess the collection​

In this part, you prepare the IR collection. You start from the Treble10 dataset, add direction of arrival (DOA), source-receiver distance, and acoustic parameter columns, filter and sample the rows, create a train/test split, and write the final IR collection parquet file.

Get the collection from a dataset​

Start by importing the packages used for preprocessing and loading Treble10 as an IR collection:

from pathlib import Path
import hashlib

import polars as pl

from treble_tsdk import treble
from treble_tsdk.collections.ir_info import IRInfo
from treble_tsdk.collections.distributions import Uniform

# This tutorial requires SDK credentials configured in the default location.
tsdk = treble.TSDK()
work_dir = Path("./ir_dataset_tutorial")
work_dir.mkdir(parents=True, exist_ok=True)

# Treble10 is a free dataset available through the SDK dataset library.
ir_collection = tsdk.datasets.purchase("Treble10")

print(f"IR count: {len(ir_collection)}")
print(ir_collection.columns)

Enrich the collection with metadata​

Add direction-of-arrival (DOA) metadata. The DOA column stores [azimuth, elevation] in degrees, and the scalar doa_azimuth column is used later for sampling:

def compute_doa(ir_info: IRInfo) -> list[float]:
doa_vec = ir_info.source.location - ir_info.receiver.location
doa_sph = treble.Rotation.cartesian_to_spherical(*doa_vec.to_list())
return doa_sph.to_list()[:2] # Return azimuth and elevation; discard radius.


ir_collection.add_column("doa", compute_doa)
ir_collection = ir_collection.apply(
lambda df: df.with_columns(
pl.col("doa").list.get(0).alias("doa_azimuth"),
pl.col("doa").list.get(1).alias("doa_elevation"),
)
)

Add acoustic parameters from completed simulation results:

acoustic_parameters = ["t30", "edt", "d50", "c50"]

ir_collection.enrich_with_acoustic_parameters(
acoustic_parameters=acoustic_parameters,
work_dir=work_dir / "acoustic_parameters",
)

Explore the added columns:

print(ir_collection.dataframe.select(["doa", "doa_azimuth", "source_receiver_dist", "t30", "c50"]))

Filter and sample the collection​

Filter the collection to keep IRs with a source-receiver distance between 1 and 3 m:

filtered_collection = ir_collection.filter_collection(
(pl.col("source_receiver_dist") >= 1.0) & (pl.col("source_receiver_dist") <= 3.0)
)

print(f"Filtered IR count: {len(filtered_collection)}")

Sample the filtered collection so the selected rows follow a uniform azimuth angle distribution:

sampled_collection = filtered_collection.sample_with_distribution(
column="doa_azimuth",
n_samples=1000,
distribution=Uniform(low=-180.0, high=180.0, seed=42),
)

Visualize the resulting collection:

sampled_collection.plot()

Create a train/test split​

Create a deterministic train/test split and add the corresponding metadata as a new column to the IR collection:

def split_fn(simulation) -> str:
key = simulation.id
hashed_key = int(hashlib.md5(str(key).encode("utf-8")).hexdigest(), 16)
return "train" if (hashed_key % 100) < 80 else "test"


collection.add_column("split", split_fn)

Finally, save the dataset as a parquet file. Move this file to the downstream machine or store it in shared storage:

dataset_metadata_path = work_dir / "treble10_preprocessed_metadata.parquet"
dataset_collection.write_parquet(dataset_metadata_path)

Use the collection on a different machine for a downstream task​

In this part, you work on the downstream machine. You reload the prepared IR collection file, preload the audio into a local cache, and iterate over the collection rows used by a training or evaluation pipeline.

Preload audio​

Load the prepared metadata file and create a data loader. The loader writes audio data to the cache directory so later iterations can reuse local files:

from pathlib import Path

from treble_tsdk import treble

# This step requires SDK credentials configured on the downstream machine.
tsdk = treble.TSDK()

# Replace this path with the prepared metadata file from the preprocessing machine.
dataset_metadata_path = Path(
"/Users/georggoetz/Documents/TSDK/Code/own-demo-notebooks/2026-docs-update/ir_dataset_tutorial/treble10_preprocessed_metadata.parquet"
)
cache_dir = Path("./ir_dataset_cache")

dataset_collection = tsdk.collections.ir_collection_from_file(dataset_metadata_path)
data_loader = dataset_collection.get_data_loader(
work_dir=cache_dir,
)

data_loader.preload_data(dataset_collection)
info

The metadata file and cache directory serve different purposes. The metadata file defines the collection rows and columns. The cache directory stores downloaded IR audio used by get_mono_ir(), get_spatial_ir(), get_moving_ir(), and get_device_ir().

Iterate over the collection​

Iterate over the IR collection to access azimuth angle, source-receiver distance, and mono IR audio for your downstream task:

for ir_info in dataset_collection:
# Read values and audio from collection:
mono_ir = ir_info.get_mono_ir(data_loader=data_loader)
audio = mono_ir.unpadded_data()
azi = dataset_collection[0].dataframe_row["doa_azimuth"]
dist = dataset_collection[0].dataframe_row["source_receiver_dist"]

# Replace this with your downstream task:
is_frontal = "in front" if abs(azi) < 15 else "not in front"
print(
f"Source {ir_info.source.label} is {is_frontal} of receiver {ir_info.receiver.label}:\t"
+ f"Azimuth = {azi:.02f} degrees, Source-receiver distance: {dist:.02f} m"
)
print(f"IR peak value: {max(abs(audio)):.02f}")

You now have a portable IR collection file, a local IR cache on the downstream machine, and an iteration pattern that keeps each mono IR paired with DOA, source-receiver distance, acoustic parameters, and split metadata.

See the IR collections how-to for additional filtering, sorting, device-rendering, and preprocessing examples.