| Header | Link |
|---|---|
| Purpose | Purpose |
| Use Cases | Use Cases |
| Example | Example |
| Reference | Reference |
Perro audio plays music and sound effects, with optional spatialization that models distance falloff, occlusion, reflection, portals, and effect zones. It handles simple centered one-shots, positional 2D/3D sounds, loops that follow a moving node, buses for grouped mixing, and a full MIDI layer using a built-in synth or .sf2 soundfonts. Reach for it for anything from a UI click to a doppler engine loop tracking a car across a level.
- UI and feedback one-shots:
audio_play!(ctx.res, Audio::new("res://ui/click.wav"))plays centered with no spatial work. - Positional impacts and pickups:
Audio2D::new/Audio3D::newwith arange, routed to a bus viaaudio_play!(ctx.res, audio_bus!("sfx"), sound). - Loops that follow a node:
ctx.run.Audio().play_attached_bus(...)withSpatialAudioOptionsso an engine or torch loop moves with its owner. - Grouped mixer control: buses (
audio_bus!("music"),audio_bus!("sfx")) carry volume, speed, and pause so a pause menu can duck effects while music continues. - Directional emitters:
AudioDirection::DirectionalorBidirectionalfor a car horn, PA speaker, or one-way soundscape. - Interactive and procedural music: MIDI via
midi_play!,midi_start!/midi_release!for held notes, usingMidiSound::BuiltInor a loaded.sf2(midi_load_soundfont!). - Environmental acoustics: audio-material fields on physics bodies plus
AudioPortal2D/AudioPortal3Dand cameraaudio_optionsfor occlusion and reverb.
Use centered one-shots for UI and non-spatial feedback, attached playback for a moving emitter, and buses/zones for mix policy shared by many sounds. Keep the sound asset choice in typed state when instances vary; keep a literal path only for a truly global effect. Signals fit loose gameplay events such as damage; direct playback fits one script that owns the effect.
Play a positional 3D impact on the "sfx" bus at a world point:
let hit = Audio3D::new(
"res://audio/impact.wav",
Vector3::new(0.0, 1.0, -4.0),
80.0,
);
let _ = audio_play!(ctx.res, audio_bus!("sfx"), hit);Attach a looping engine sound to a moving vehicle node so it tracks position and applies falloff:
let audio = RuntimeAudio {
source: "res://audio/engine_loop.ogg",
looped: true,
volume: 0.8,
effects: AudioEffects {
low_pass: 0.05,
reverb_send: 0.1,
..AudioEffects::new()
},
from_start: 0.0,
from_end: 0.0,
};
let spatial = SpatialAudioOptions {
range: 80.0,
audio_layer: BitMask::ALL,
enable_propagation: true,
direction_2d: AudioDirection::Omni,
direction_3d: AudioDirection::Omni,
};
let _ = ctx.run.Audio().play_attached_bus(audio_bus!("ambience"), audio, vehicle_node, spatial);Perro audio has three layers:
- script API:
ctx.res.Audio()andctx.run.Audio() - mic API:
ctx.res.Mic()for recorded clips - runtime propagation: listener, occlusion, reflection, portals, effect zones
- playback backend:
perro_pawdio
Use API docs for script calls:
perro_pawdio is the audio backend crate.
Path:
perro_source/audio_stack/perro_pawdio
Public pieces:
AudioController: command sender and audio thread ownerBarkPlayer: rodio output stream, spatial sinks, cache, busesAudioPlaybackRequest: playback command dataSpatialAudioParams: live spatial playback update dataAudioPan,AudioEq,AudioCompression: shared playback controlsAudio2D,Audio3D,AudioListener2D,AudioListener3D: simple backend spatial types
Scripts do not call BarkPlayer directly.
Script calls go through runtime/resource APIs.
Those APIs enqueue commands into AudioController.
AudioController spawns the perro_pawdio_audio thread.
That thread owns BarkPlayer and handles load/play/stop/bus/spatial commands.
Runtime audio source paths use normal project asset paths:
res://...wavres://...oggres://...mp3res://...flacres://...aacres://...m4ares://...midres://...midires://...sf2
Dynamic runtime loads read audio files and let rodio decode them.
MIDI files and soundfonts are read through the same asset path system.
Static builds pack audio into .pawdio blobs.
.pawdio is not an authored source format.
It is a static pipeline container for embedded audio bytes.
Static pipeline behavior:
- scan audio, MIDI, and soundfont files in
res/ - preserve original
res://...lookup path - write embedded files under
embedded/audios/ - emit
static/audios.rslookup code - choose zlib payload only when smaller than raw bytes
.pawdio v1 layout:
- magic:
PAWDIO - version:
1 - flags:
FLAG_ZLIBor0 - raw length
- payload: raw source bytes or zlib-compressed source bytes
At runtime, perro_pawdio unwraps .pawdio back into original audio bytes.
Then rodio decodes those bytes like a normal source.
MIDI files and .sf2 soundfonts are embedded as raw bytes and keep their original extension.
All playback uses an AudioPlaybackRequest.
Core fields:
- source path
- optional bus id
- loop flag
- volume
- speed
- pan
- effect values
- trim start/end
- playback id for spatial updates
Plain Audio plays centered unless wrapped in PannedAudio.
Audio2D and Audio3D become spatial requests before backend playback.
Attached runtime audio follows node transforms before backend playback.
Current backend rule:
- one active sink per source path
- new play of same source stops previous playback for that source
Audio buses group playback controls.
Bus state:
- volume
- speed
- paused flag
Final volume:
- with bus:
master_volume * bus_volume * audio.volume - without bus:
master_volume * audio.volume
Final speed:
- with bus:
bus_speed * audio.effects.speed - without bus:
audio.effects.speed
effects.speed changes playback rate and pitch.
perro_pawdio caches source bytes by source path.
Cache commands:
- load: cache as unreserved
- reserve: cache as reserved
- drop: remove cached source
Reserved sources stay cached until explicit drop. Unreserved sources can be evicted after use.
Unreserved eviction:
- when duration known:
max(audio_length * 2.0, 250ms) - when duration unknown:
1s - never evict while source has active playback
- cache soft limit:
128 MiB
MIDI lives under ctx.res.Audio().midi().
It supports:
- live one-shot notes
- held notes with explicit release
.midand.midifile playback- built-in procedural instruments
.sf2soundfont instruments- bus volume, bus speed, pause, resume, and stop
- point 2D/3D propagation
- node-attached propagation through
ctx.run.Audio().midi()
Main types:
Note: MIDI key wrapper with constants fromNote::C0throughNote::C8MidiChannel:0..15,MidiProgram: GM patch0..127MidiSound:BuiltInorSoundFont(soundfont_id)MidiNoteOptions: velocity, sustain, channel, program, sound, bus, volume, panMidiSong: source path, sound, bus, volume, loop flagMidiNoteHandle: handle returned by held notes
Notes:
Note::from_midi(key)accepts raw MIDI keys.play_noteusesMidiNoteOptions.sustainfor automatic note-off.start_noteignores sustain and keeps the note alive until release.release_note(handle)stops held notes.- channel + program pick the instrument lane.
- channel 9 is the standard drum lane.
- bus speed changes MIDI playback rate.
MidiSound chooses the synthesizer.
program chooses the instrument slot inside that synthesizer.
MidiSound::BuiltIn:
- uses Perro's procedural synth
- needs no asset file
- uses
programas a GM-style category hint - maps program ranges to simple wave types
- does not use sampled instruments
- good for quick tones, prototyping, and light effects
Built-in program wave map:
- piano/chromatic/default: sine
- organ + synth lead: square
- guitar + bass + brass + reed + pipe: saw
- strings + ensemble: triangle
- synth pad + synth fx: sine
- percussive + sound fx: noise
MidiSound::SoundFont(soundfont_id):
- loads an
.sf2bank - uses
programto pick a patch inside that bank - uses
channelto hold current program state - uses channel 9 for drums by MIDI convention
- sound quality depends on the
.sf2
So the soundfont is not one instrument.
It is a bank of many instruments.
program::Piano::AcousticGrand means patch 0 in that bank.
program::Brass::Trumpet means patch 56 in that bank.
If the .sf2 has weak or missing patches, output follows that file.
Built-in MIDI:
let music = audio_bus!("music");
let lead = MidiNoteOptions {
velocity: 112,
sustain: std::time::Duration::from_millis(180),
program: program::SynthLead::Square,
volume: 0.8,
..MidiNoteOptions::default()
};
let _ = midi_play!(ctx.res, music, Note::C4, lead);
let _ = ctx.res.Audio().midi().play_note(Note::E4, lead);
if let Some(handle) = midi_start!(ctx.res, Note::G4, lead) {
let _ = midi_release!(ctx.res, handle);
}
let song = MidiSong::new("res://music/theme.mid").looped();
let _ = midi_play!(ctx.res, song);Soundfont MIDI:
let font = "res://soundfonts/game.sf2";
let font_id = midi_load_soundfont!(ctx.res, font);
let piano = MidiNoteOptions {
sound: MidiSound::SoundFont(font_id),
program: program::Piano::AcousticGrand,
sustain: std::time::Duration::from_millis(350),
..MidiNoteOptions::default()
};
let _ = midi_play!(ctx.res, Note::C4, piano);
let _ = midi_play!(ctx.res, Note::E4, piano);
let _ = midi_play!(ctx.res, Note::G4, piano);
let sf2_song = MidiSong::new("res://music/theme.mid")
.with_sound(MidiSound::SoundFont(font_id))
.looped();
let _ = ctx.res.Audio().midi().play_file(sf2_song);Soundfont rules:
- source must be a project asset path, usually
res://soundfonts/name.sf2 midi_load_soundfont!loads the bank and returnsSoundFontID- same source returns same
SoundFontID - notes and files require a loaded soundfont id
- one live-note soundfont mixer is shared per soundfont, bus, and pan
MidiSound::BuiltIndoes not require.sf2
Positional MIDI:
let font_id = midi_load_soundfont!(ctx.res, "res://soundfonts/game.sf2");
let opts = MidiNoteOptions {
sound: MidiSound::SoundFont(font_id),
program: program::Brass::Trumpet,
..MidiNoteOptions::default()
};
let _ = midi_play_at!(
ctx.res,
Note::C5,
Vector2::new(128.0, 64.0),
512.0,
opts
);
let _ = midi_play_at!(
ctx.res,
MidiSong::new("res://music/sting.mid"),
Vector3::new(0.0, 2.0, -6.0),
40.0
);Attached MIDI:
let spatial = SpatialAudioOptions {
range: 40.0,
audio_layer: BitMask::ALL,
enable_propagation: true,
direction_2d: AudioDirection::Omni,
direction_3d: AudioDirection::Omni,
};
let opts = MidiNoteOptions {
program: program::Guitar::Clean,
..MidiNoteOptions::default()
};
let _ = ctx.run.Audio().midi().play_note_attached(Note::A3, node, opts, spatial);
let held = ctx.run.Audio().midi().start_note_attached(Note::E3, node, opts, spatial);
if let Some(handle) = held {
let _ = ctx.run.Audio().midi().release_note(handle);
}
let song = MidiSong::new("res://music/loop.mid").looped();
let _ = ctx.run.Audio().midi().play_file_attached(song, node, spatial);
let _ = ctx.run.Audio().midi().stop_attached(node, "res://music/loop.mid");Programs follow General MIDI patch numbers.
Use MidiProgram::new(n) for a raw value.
Use program::Group::Name for named values.
| Num | Helper | GM name |
|---|---|---|
| 0 | program::Piano::AcousticGrand |
Acoustic Grand Piano |
| 1 | program::Piano::BrightAcoustic |
Bright Acoustic Piano |
| 2 | program::Piano::ElectricGrand |
Electric Grand Piano |
| 3 | program::Piano::HonkyTonk |
Honky-tonk Piano |
| 4 | program::Piano::Electric1 |
Electric Piano 1 |
| 5 | program::Piano::Electric2 |
Electric Piano 2 |
| 6 | program::Piano::Harpsichord |
Harpsichord |
| 7 | program::Piano::Clavinet |
Clavinet |
| 8 | program::Chromatic::Celesta |
Celesta |
| 9 | program::Chromatic::Glockenspiel |
Glockenspiel |
| 10 | program::Chromatic::MusicBox |
Music Box |
| 11 | program::Chromatic::Vibraphone |
Vibraphone |
| 12 | program::Chromatic::Marimba |
Marimba |
| 13 | program::Chromatic::Xylophone |
Xylophone |
| 14 | program::Chromatic::TubularBells |
Tubular Bells |
| 15 | program::Chromatic::Dulcimer |
Dulcimer |
| 16 | program::Organ::Drawbar |
Drawbar Organ |
| 17 | program::Organ::Percussive |
Percussive Organ |
| 18 | program::Organ::Rock |
Rock Organ |
| 19 | program::Organ::Church |
Church Organ |
| 20 | program::Organ::Reed |
Reed Organ |
| 21 | program::Organ::Accordion |
Accordion |
| 22 | program::Organ::Harmonica |
Harmonica |
| 23 | program::Organ::TangoAccordion |
Tango Accordion |
| 24 | program::Guitar::Nylon |
Acoustic Guitar (nylon) |
| 25 | program::Guitar::Steel |
Acoustic Guitar (steel) |
| 26 | program::Guitar::Jazz |
Electric Guitar (jazz) |
| 27 | program::Guitar::Clean |
Electric Guitar (clean) |
| 28 | program::Guitar::Muted |
Electric Guitar (muted) |
| 29 | program::Guitar::Overdriven |
Overdriven Guitar |
| 30 | program::Guitar::Distortion |
Distortion Guitar |
| 31 | program::Guitar::Harmonics |
Guitar Harmonics |
| 32 | program::Bass::Acoustic |
Acoustic Bass |
| 33 | program::Bass::Finger |
Electric Bass (finger) |
| 34 | program::Bass::Pick |
Electric Bass (pick) |
| 35 | program::Bass::Fretless |
Fretless Bass |
| 36 | program::Bass::Slap1 |
Slap Bass 1 |
| 37 | program::Bass::Slap2 |
Slap Bass 2 |
| 38 | program::Bass::Synth1 |
Synth Bass 1 |
| 39 | program::Bass::Synth2 |
Synth Bass 2 |
| 40 | program::Strings::Violin |
Violin |
| 41 | program::Strings::Viola |
Viola |
| 42 | program::Strings::Cello |
Cello |
| 43 | program::Strings::Contrabass |
Contrabass |
| 44 | program::Strings::Tremolo |
Tremolo Strings |
| 45 | program::Strings::Pizzicato |
Pizzicato Strings |
| 46 | program::Strings::Harp |
Orchestral Harp |
| 47 | program::Strings::Timpani |
Timpani |
| 48 | program::Ensemble::String1 |
String Ensemble 1 |
| 49 | program::Ensemble::String2 |
String Ensemble 2 |
| 50 | program::Ensemble::SynthStrings1 |
Synth Strings 1 |
| 51 | program::Ensemble::SynthStrings2 |
Synth Strings 2 |
| 52 | program::Ensemble::ChoirAahs |
Choir Aahs |
| 53 | program::Ensemble::VoiceOohs |
Voice Oohs |
| 54 | program::Ensemble::SynthVoice |
Synth Voice |
| 55 | program::Ensemble::OrchestraHit |
Orchestra Hit |
| 56 | program::Brass::Trumpet |
Trumpet |
| 57 | program::Brass::Trombone |
Trombone |
| 58 | program::Brass::Tuba |
Tuba |
| 59 | program::Brass::MutedTrumpet |
Muted Trumpet |
| 60 | program::Brass::FrenchHorn |
French Horn |
| 61 | program::Brass::BrassSection |
Brass Section |
| 62 | program::Brass::SynthBrass1 |
Synth Brass 1 |
| 63 | program::Brass::SynthBrass2 |
Synth Brass 2 |
| 64 | program::Reed::SopranoSax |
Soprano Sax |
| 65 | program::Reed::AltoSax |
Alto Sax |
| 66 | program::Reed::TenorSax |
Tenor Sax |
| 67 | program::Reed::BaritoneSax |
Baritone Sax |
| 68 | program::Reed::Oboe |
Oboe |
| 69 | program::Reed::EnglishHorn |
English Horn |
| 70 | program::Reed::Bassoon |
Bassoon |
| 71 | program::Reed::Clarinet |
Clarinet |
| 72 | program::Pipe::Piccolo |
Piccolo |
| 73 | program::Pipe::Flute |
Flute |
| 74 | program::Pipe::Recorder |
Recorder |
| 75 | program::Pipe::PanFlute |
Pan Flute |
| 76 | program::Pipe::BlownBottle |
Blown Bottle |
| 77 | program::Pipe::Shakuhachi |
Shakuhachi |
| 78 | program::Pipe::Whistle |
Whistle |
| 79 | program::Pipe::Ocarina |
Ocarina |
| 80 | program::SynthLead::Square |
Lead 1 (square) |
| 81 | program::SynthLead::Saw |
Lead 2 (sawtooth) |
| 82 | program::SynthLead::Calliope |
Lead 3 (calliope) |
| 83 | program::SynthLead::Chiff |
Lead 4 (chiff) |
| 84 | program::SynthLead::Charang |
Lead 5 (charang) |
| 85 | program::SynthLead::Voice |
Lead 6 (voice) |
| 86 | program::SynthLead::Fifths |
Lead 7 (fifths) |
| 87 | program::SynthLead::BassLead |
Lead 8 (bass + lead) |
| 88 | program::SynthPad::NewAge |
Pad 1 (new age) |
| 89 | program::SynthPad::Warm |
Pad 2 (warm) |
| 90 | program::SynthPad::Polysynth |
Pad 3 (polysynth) |
| 91 | program::SynthPad::Choir |
Pad 4 (choir) |
| 92 | program::SynthPad::Bowed |
Pad 5 (bowed) |
| 93 | program::SynthPad::Metallic |
Pad 6 (metallic) |
| 94 | program::SynthPad::Halo |
Pad 7 (halo) |
| 95 | program::SynthPad::Sweep |
Pad 8 (sweep) |
| 96 | program::SynthFx::Rain |
FX 1 (rain) |
| 97 | program::SynthFx::Soundtrack |
FX 2 (soundtrack) |
| 98 | program::SynthFx::Crystal |
FX 3 (crystal) |
| 99 | program::SynthFx::Atmosphere |
FX 4 (atmosphere) |
| 100 | program::SynthFx::Brightness |
FX 5 (brightness) |
| 101 | program::SynthFx::Goblins |
FX 6 (goblins) |
| 102 | program::SynthFx::Echoes |
FX 7 (echoes) |
| 103 | program::SynthFx::SciFi |
FX 8 (sci-fi) |
| 104 | program::World::Sitar |
Sitar |
| 105 | program::World::Banjo |
Banjo |
| 106 | program::World::Shamisen |
Shamisen |
| 107 | program::World::Koto |
Koto |
| 108 | program::World::Kalimba |
Kalimba |
| 109 | program::World::Bagpipe |
Bagpipe |
| 110 | program::World::Fiddle |
Fiddle |
| 111 | program::World::Shanai |
Shanai |
| 112 | program::Percussive::TinkleBell |
Tinkle Bell |
| 113 | program::Percussive::Agogo |
Agogo |
| 114 | program::Percussive::SteelDrums |
Steel Drums |
| 115 | program::Percussive::Woodblock |
Woodblock |
| 116 | program::Percussive::TaikoDrum |
Taiko Drum |
| 117 | program::Percussive::MelodicTom |
Melodic Tom |
| 118 | program::Percussive::SynthDrum |
Synth Drum |
| 119 | program::Percussive::ReverseCymbal |
Reverse Cymbal |
| 120 | program::SoundFx::GuitarFretNoise |
Guitar Fret Noise |
| 121 | program::SoundFx::BreathNoise |
Breath Noise |
| 122 | program::SoundFx::Seashore |
Seashore |
| 123 | program::SoundFx::BirdTweet |
Bird Tweet |
| 124 | program::SoundFx::TelephoneRing |
Telephone Ring |
| 125 | program::SoundFx::Helicopter |
Helicopter |
| 126 | program::SoundFx::Applause |
Applause |
| 127 | program::SoundFx::Gunshot |
Gunshot |
Drum kit helpers use normal GM drum-kit program values.
Use them with MidiChannel::DRUMS when target synth or soundfont supports drum kits.
| Num | Helper | Kit |
|---|---|---|
| 0 | program::DrumKit::Standard |
Standard |
| 8 | program::DrumKit::Room |
Room |
| 16 | program::DrumKit::Power |
Power |
| 24 | program::DrumKit::Electronic |
Electronic |
| 25 | program::DrumKit::Analog |
Analog |
| 32 | program::DrumKit::Jazz |
Jazz |
| 40 | program::DrumKit::Brush |
Brush |
| 48 | program::DrumKit::Orchestra |
Orchestra |
| 56 | program::DrumKit::Sfx |
SFX |
Spatial audio starts in script API, then moves through runtime propagation.
Entrypoints:
ctx.res.Audio()for pointAudio2DandAudio3Dctx.run.Audio()for node-attached runtime audio
Use point audio for impacts, pickups, doors, switches, and other one-shot sounds at a fixed position.
Use attached audio for loops or held notes that follow a scene node.
Use audio_play!(res, audio_2d_or_3d) for master point playback.
Use audio_play!(res, bus, audio_2d_or_3d) for bus point playback.
Listener source:
- active
Camera2Dfor 2D - active
Camera3Dfor 3D
Runtime propagation inputs:
- source position and range
- audio direction mode
- listener transform
- listener
audio_optionson active camera - audio material fields on physics nodes
AudioMask2DAudioMask3DAudioPortal2DandAudioPortal3D- audio effect zones
Propagation output becomes SpatialAudioParams.
The runtime sends those params to perro_pawdio.
perro_pawdio applies pan and volume to the sink.
It also applies low-pass, EQ, compression, echo, reverb send, reflection, and occlusion in DSP.
Spatial mapping rules:
- pan is direction-based: unit direction to the perceived source, near-faded so close fly-bys sweep instead of flipping channels
- volume uses squared linear falloff: half range = quarter volume, zero at range
- occluded sources probe extra rays around the source ("sound wave cloud"); open sides soften occlusion, restore leaked volume, and pull the perceived position toward the open edge
- spatial MIDI notes play on dedicated per-note sinks so pan, occlusion, and low-pass track each note
- params smooth between propagation ticks to avoid stepping
- strongly above/below sources get subtle extra low-pass as an elevation cue
Propagation runs only while active positional or attached spatial sounds exist. If no active spatial sounds exist, no audio ray work runs that frame.
Active Camera2D and Camera3D can set audio_options.
Listener options apply after audio zones and before explicit sound effects.
audio_mask ignores matching emitted audio_layer.
Default audio_mask = [] ignores nothing, so listener effects apply to all emitted audio layers.
Scene example:
[Camera3D]
audio_options = {
audio_mask = [1, 3],
effects = [
{ reverb_send: 0.4, echo: 0.1, dampening: 0.2 }
]
}
[/Camera3D]
Shorthand fields also work on cameras:
[Camera2D]
audio_mask = [2]
reverb_send = 0.6
echo = 0.2
dampening = 0.3
[/Camera2D]
Spatial audio direction lives in SpatialAudioOptions.
Point audio direction lives on Audio2D and Audio3D.
Default-style setup uses AudioDirection::Omni.
Omni ignores direction and radiates the same in every direction.
Direction modes:
AudioDirection::Omni- emits outward in all directionsAudioDirection::Directional(forward): strongest towardforward.AudioDirection::InverseDirectional(forward): strongest oppositeforward.AudioDirection::Bidirectional(forward): strongest towardforwardand-forward.
Point audio:
Audio2D::new(source, position, range)Audio3D::new(source, position, range)
Attached audio:
SpatialAudioOptionssets node-attached range, audio layer, propagation, and direction.direction_2dtakesAudioDirection<Vector2>.direction_3dtakesAudioDirection<Vector3>.- struct fields set attached/node direction.
- direction vectors are normalized by the runtime.
- attached 2D directional audio uses node forward from node rotation.
- attached 3D directional audio uses node forward from node rotation.
- explicit direction is fallback for point use and non-attached use.
Point 2D example:
let hit = Audio2D {
audio: Audio::new("res://audio/hit.wav"),
position: Vector2::new(128.0, 64.0),
range: 512.0,
audio_layer: BitMask::ALL,
enable_propagation: true,
direction: None,
};
let _ = audio_play!(ctx.res, audio_bus!("sfx"), hit);Point 3D directional example:
let horn = Audio3D::new(
"res://audio/horn.wav",
Vector3::new(0.0, 1.0, -4.0),
80.0,
);
let horn = Audio3D {
direction: Some(AudioDirection::Bidirectional(Vector3::new(0.0, 0.0, -1.0))),
..horn
};
let _ = audio_play!(ctx.res, audio_bus!("sfx"), horn);Attached loop example:
let audio = RuntimeAudio {
source: "res://audio/engine_loop.ogg",
looped: true,
volume: 0.8,
effects: AudioEffects {
low_pass: 0.05,
reverb_send: 0.1,
..AudioEffects::new()
},
from_start: 0.0,
from_end: 0.0,
};
let spatial = SpatialAudioOptions {
range: 80.0,
audio_layer: BitMask::ALL,
enable_propagation: true,
direction_2d: AudioDirection::Omni,
direction_3d: AudioDirection::Omni,
};
let _ = ctx.run.Audio().play_attached_bus(audio_bus!("ambience"), audio, vehicle_node, spatial);Physics bodies and areas participate in audio propagation when audio_interaction is set.
Use audio_interaction = none to turn it off.
AudioInteraction fields:
audio_interactionabsorptionreflectiontransmissiondiffusionlow_pass_strengththickness_multiplieraudio_mask- Uses
BitMask; seedocs/scripting/bitmask.md.
- Uses
Use audio masks, effect zones, and portals for invisible or non-physical audio geometry.
Emitted sounds use audio_layer; audio geometry uses audio_mask to decide which layers to ignore.
Default audio_mask = [] means audio geometry affects all emitted audio layers.
Audio portals are one-way links. Add reverse links when sound should travel both directions.
Portal behavior:
- collision-shape children form portal input surfaces
- target portal gives exit transform
- hit point and ray direction move through target transform
- ray continues tracing after exit
- portal hops stop at cycle guard
- ray cannot immediately re-enter portal it just exited
[audio]
listener_max_distance = 500.0
propagation_tick_hz = 20
energy_cutoff = 0.02
debug_rays = false
# Ray propagation. Plain key sets 2D + 3D; `_2d` / `_3d` suffix tunes one path.
max_bounces = 4
max_ray_distance = 500.0
rays_per_tick_2d = 64
rays_per_tick_3d = 128