Rendering a scene collection
A SceneCollection stores audio scenes that must be rendered before the mixed audio and clean speech target are available for downstream use.
Rendering consists of two stages: device IR computation followed by audio convolution.
Device IR computation combines spatial IRs with device transfer functions (DTFs) to produce device-specific IRs.
Device IR computation can run either locally or on the cloud — see Remote device rendering below.
Audio convolution is always performed locally.
The examples on this page assume you already have a SceneCollection.
See Scene collection for instructions on building one.
Basic examples
Remote device rendering and filtering
If your scene collection uses a device and/or filters in the scene listener device specs, call start_remote_optimization() before rendering.
This dispatches the device IR computation to the cloud and optionally shows a live progress view.
Once remote processing is complete, device IRs are downloaded and cached on disk on demand — each scene fetches its IRs from the cache on first use, downloading any that aren't present yet.
scene_collection.start_remote_optimization()
Before triggering remote processing, call print_remote_optimization_token_cost() to print the quoted token cost:
scene_collection.print_remote_optimization_token_cost()
start_remote_optimization() returns as soon as the tasks have been submitted and the progress view closes.
To block until processing is complete, call wait_for_remote_optimization_processing() after it:
# show_progress=False suppresses the live view so the call returns immediately after submission.
scene_collection.start_remote_optimization(show_progress=False)
# Block the current thread until all remote device IR jobs have finished.
scene_collection.wait_for_remote_optimization_processing()
When you render individual scenes by iterating over the collection and calling audio_scene.render() directly, and start_remote_optimization() hasn't been called yet, device IR computation runs locally instead.
Local computation downloads the spatial IRs and DTFs, combines them into device IRs on your machine, and caches the results to disk.
Use remote rendering when the number of scenes in the Scene Collection is large and a device has been set.
You only need to call start_remote_optimization() once per collection.
Subsequent renders reuse the cached device IRs automatically. SceneCollection.render()/render_to_disk() both call it for you if it hasn't been triggered yet, so you can omit the explicit call when using either method.
Rendering a large collection to disk
render_to_disk() is the recommended way to render a SceneCollection: it writes .wav files directly to disk and keeps only file paths and metrics in the dataframe, so rendering doesn't have to hold every scene's audio in memory at once.
output_path = Path("rendered_dataset")
render_settings = {
"output_path": output_path,
"sampling_rate": 32000,
"render_mode": sc.RenderMode.AUTO,
"target_selection": sc.TargetSelection.group_position("conversation", 0),
"target_render_mode": sc.TargetRenderMode.WET_MONO,
}
scene_collection.render_to_disk(
**render_settings,
calculate_snr=True,
)
This writes rendered_dataset/noisy/{scene.id}.wav, rendered_dataset/clean/{scene.id}.wav (since target_selection is set), and, with output_separated_tracks=True, rendered_dataset/separated/{scene.id}_{i}.wav.
Retrieve the result with get_renders_from_disk(), passing the same output_path:
You can call render_to_disk() multiple times with different settings — each call adds a separate entry to the collection's dataframe, keyed by its output_path.
This lets several renders of the same collection coexist (different sampling rates, render modes, or target selections, for example).
Pass the matching output_path to get_renders_from_disk() to pick up the render you want:
scene_collection.render_to_disk(**render_settings)
render_settings_mono = {**render_settings, "output_path": Path("rendered_dataset_mono"), "render_mode": sc.RenderMode.MONO}
scene_collection.render_to_disk(**render_settings_mono)
# Pick up a specific render by its output_path.
scene_id = scene_collection[0].id
result = scene_collection.get_renders_from_disk(scene_id, output_path=render_settings_mono["output_path"])
Re-running render_to_disk() with the same output_path but a different render recipe overwrites the existing .wav files under that path.
A different output_path accumulates as a separate entry, so the same collection can be rendered to disk multiple times under different folders or settings.
Rendered audio retrieval (disk render)
get_renders_from_disk() reads the .wav files back from disk and returns the actual audio tracks, un-normalized:
result = scene_collection.get_renders_from_disk(scene_id, output_path=output_path)
mixed, target = result["noisy"], result["clean"]
Call plot() or playback() on the returned signal to inspect or listen to it:
mixed.plot()
mixed.playback()
Deriving extra data during a disk render
Pass collate_fn to render_to_disk() to pull anything else out of the collection while it renders — metadata, transcripts, or derived signals — instead of a second pass over the collection afterward.
It's called once per scene, right after that scene's files are written, with the scene's dataframe row, the output directory, the render entry just written, the actual AudioScene, and any collate_fn_kwargs you pass through.
The live AudioScene still has its full track_map available.
The following example writes each scene's transcript alongside its rendered files:
def write_transcripts(row, output_dir, entry, scene, **_):
"""Write the transcript of the first track of the conversation group."""
transcript_path = output_dir / "transcripts"
transcript_path.mkdir(parents=True, exist_ok=True)
lines = scene.transcript(target_selection=sc.TargetSelection.group_position("conversation", 0))
(transcript_path / f"{scene.id}.txt").write_text("\n".join(lines))
scene_collection.render_to_disk(
**render_settings,
collate_fn=write_transcripts,
)
Separated track output
Pass output_separated_tracks=True to render_to_disk() to store each source track as an individual rendered signal alongside the mixed output.
scene_collection.render_to_disk(
**render_settings,
output_separated_tracks=True,
)
result = scene_collection.get_renders_from_disk(scene_id, output_path=output_path)
track = result["separated_tracks"][0] # ConvolvedAudioSignal
track.plot()
track.playback()
SNR calculation
Passing calculate_snr=True to render_to_disk() (as in the example above) computes the signal-to-noise ratio for each rendered scene and stores it alongside the audio results.
target_selection is required when using this option.
Retrieve it via get_renders_from_disk(), under the "snr" key:
result = scene_collection.get_renders_from_disk(scene_id, output_path=output_path)
snr = result["snr"] # one value per output channel, in dB
The SNR is measured using the ITU-T P.56 active-speech level method. An activity mask is derived from the rendered target signal and applied to both the target and the combined noise before computing the ratio, so only intervals where the target is active contribute to the result. All non-target source tracks plus any device noise are treated as noise.
Preloading IR data ahead of a render
render_to_disk() already fetches and caches the IRs it needs before rendering.
Call preload_rendered_irs() explicitly when you want that step to happen on its own:
scene_collection.preload_rendered_irs()
Advanced examples
In-memory collection rendering
For small collections that fit comfortably in memory, SceneCollection.render() renders every scene and stores the results in the collection's internal dataframe, instead of writing them to disk like render_to_disk().
Prefer render_to_disk() for anything beyond a quick, small-scale check — it keeps only file paths and metrics in the dataframe, so rendering doesn't have to hold every scene's audio in memory at once.
Pass sampling_rate, render_mode, target_selection, and target_render_mode to control the output.
As with render_to_disk() (see above), you can call render() multiple times with different parameters — each set of results is stored under its own entry and identified by those parameters.
render_settings = {
"sampling_rate": 32000,
"render_mode": sc.RenderMode.AUTO,
"target_selection": sc.TargetSelection.group_position("conversation", 0),
"target_render_mode": sc.TargetRenderMode.WET_MONO,
}
# Results are keyed by these parameters, so multiple render() calls can coexist.
scene_collection.render(**render_settings)
To store a second set of renders with different settings alongside the first:
render_settings_mono = {
"sampling_rate": 16000,
"render_mode": sc.RenderMode.MONO,
"target_selection": sc.TargetSelection.group_position("conversation", 0),
"target_render_mode": sc.TargetRenderMode.WET_MONO,
}
scene_collection.render(**render_settings_mono)
Rendered audio retrieval (in-memory render)
After calling render(), retrieve the audio for any scene by its id using get_render().
Pass the same render parameters you used when rendering to select the correct entry.
# Pass the same render_settings used during render() to select the correct result entry.
rendered = scene_collection.get_render(scene_id, **render_settings)
mixed = rendered["noisy"] # AudioSignal — all sources mixed and convolved
target = rendered["clean"] # AudioSignal — target signal only
When the collection has only one set of renders, no render parameters are required:
rendered = scene_collection.get_render(scene_id)
Scene-by-scene rendering
You can iterate over the collection and render each AudioScene individually.
Use this approach when you need to process each scene immediately rather than batching all renders upfront.
render_target() accepts a TargetSelection as target_selection.
When the selection resolves to several tracks, each track is rendered individually and the results are summed.
See Target selection for all selection options, including selection by tag:
for audio_scene in scene_collection:
mixed = audio_scene.render(
sampling_rate=32000,
render_mode=sc.RenderMode.DEVICE,
)
target = audio_scene.render_target(
target_selection=sc.TargetSelection.group_position("conversation", 0),
sampling_rate=32000,
render_mode=sc.TargetRenderMode.WET_MONO,
)
# mixed and target are ConvolvedAudioSignal objects — write to disk or pass to a model here
When iterating scene-by-scene and start_remote_optimization() hasn't been called, device IR computation runs locally.
Call start_remote_optimization() before the loop to use cloud-side computation instead.
Render modes
Mixed audio render modes
RenderMode controls how source tracks are convolved when rendering the mixed audio signal:
RenderMode.AUTO— usesDEVICEif a device is configured, otherwise falls back toMONORenderMode.MONO— mono room impulse response, single-channel outputRenderMode.SPATIAL— full spatial IR, multichannel outputRenderMode.DEVICE— device-filtered IR, output channels match the device arrayRenderMode.NONE— no convolution, dry (anechoic) output
Target render modes
TargetRenderMode controls how the clean target signal is rendered:
TargetRenderMode.DRY_MONO— anechoic, monoTargetRenderMode.FREE_FIELD_DEVICE— device-filtered, no room acousticsTargetRenderMode.WET_MONO— room-convolved, monoTargetRenderMode.WET_DEVICE— room-convolved, device-filtered
In addition to the TargetRenderMode values above, WetMonoWindowed and WetDeviceWindowed window the room-convolved target to crop late reverberation.