Skip to main content

Audio

Struct Audio 

Source
pub struct Audio { /* private fields */ }
Expand description

The audio system. Reach it through ctx.audio.

Implementations§

Source§

impl Audio

Source

pub fn silent(sound_dir: impl Into<PathBuf>) -> Self

An audio system without an output device. Everything works, nothing is heard.

Source

pub fn sample_rate(&self) -> u32

Sample rate of the output device in Hz.

Source

pub fn has_output(&self) -> bool

Whether an output device is open (false means sounds are silently ignored).

Source

pub fn set_sound_dir(&mut self, dir: impl Into<PathBuf>)

Changes the directory searched for sounds. See the module docs.

Source

pub fn preload(&mut self, name: &str) -> Result<()>

Loads a sound into the cache now, so the first play has no delay.

Source

pub fn preload_dir(&mut self, dir: impl AsRef<Path>) -> Result<usize>

Loads every sound file found in dir (not recursive). Names are the file paths relative to the sound directory without extension, e.g. "step". Returns how many files were loaded.

Source

pub fn register(&mut self, name: impl Into<String>, data: impl Into<SoundData>)

Registers sound data generated in code (or a Synth) under a name, so it can be played like a file. Registering the same name again replaces the sound.

ctx.audio.register("beep", SoundData::tone(880.0, 0.1, 0.5));
ctx.audio.play("beep");
Examples found in repository?
examples/bell_catcher.rs (line 24)
22fn main() -> audio_code::Result<()> {
23    Engine::new("Bell Catcher").run_with(|ctx| {
24        ctx.audio.register("bell", Synth::sine(1568.0).with(Synth::sine(3136.0).volume(0.3)).length(0.35).decay(9.0).volume(0.6));
25        ctx.audio.register("step", Synth::noise().length(0.05).decay(80.0).volume(0.5));
26        ctx.audio.register("edge", Synth::sine(110.0).length(0.12).decay(30.0).volume(0.8));
27        ctx.audio.register(
28            "catch",
29            Synth::sine(784.0).length(0.08).then(Synth::sine(1047.0).length(0.08)).then(Synth::sine(1568.0).length(0.2).decay(12.0)).volume(0.4),
30        );
31        ctx.audio.register("miss", Synth::saw(220.0).sweep_to(80.0).length(0.5).decay(5.0).volume(0.35));
32
33        // Restore the speech settings from the last session.
34        if let Some(voice) = ctx.storage.get::<String>("voice") {
35            ctx.speech.set_voice(voice);
36        }
37        let rate: f32 = ctx.storage.get_or("rate", 50.0);
38        if rate != 50.0 {
39            ctx.speech.set_rate(rate / 100.0);
40        }
41        Title
42    })
43}
More examples
Hide additional examples
examples/beacon.rs (line 33)
32    fn new(ctx: &mut Context) -> Self {
33        ctx.audio.register("step", Synth::noise().length(0.08).decay(60.0));
34        ctx.audio.register("wall", Synth::sine(90.0).length(0.15).decay(25.0).volume(0.8));
35        ctx.audio.register("beacon", Synth::sine(1320.0).length(0.08).then(Synth::silence(0.04)).then(Synth::sine(1320.0).length(0.08)).then(Synth::silence(0.8)));
36        ctx.audio.register("found", Synth::sine(440.0).sweep_to(1200.0).length(0.6).decay(4.0).volume(0.3));
37
38        // Every character is a 2 meter tile: walls, the player start and three beacons.
39        let level = ctx.physics.build_level(MAP, 2.0);
40        let player = ctx.physics.add(Body::kinematic(level.point('P').unwrap()).with_sphere(0.4).with_tag("player"));
41        ctx.follow(player); // the ears go wherever the player goes and face where it faces
42
43        let mut beacons = Vec::new();
44        for spot in level.points('B') {
45            let beacon = ctx.physics.add(Body::sensor(spot).with_sphere(0.8).with_tag("beacon"));
46            ctx.loop_on(beacon, "beacon"); // stops by itself when the beacon is removed
47            beacons.push(beacon);
48        }
49        Self { player, beacons, clock: Timer::new() }
50    }
examples/flight.rs (line 32)
31    fn new(ctx: &mut Context) -> Self {
32        ctx.audio.register("bird", Synth::sine(2200.0).sweep_to(3200.0).length(0.07).then(Synth::silence(0.05)).then(Synth::sine(2600.0).length(0.06)).then(Synth::silence(0.7)));
33        ctx.audio.register("hum", Synth::saw(70.0).length(1.0).volume(0.08));
34        ctx.audio.register("bump", Synth::sine(80.0).length(0.15).decay(25.0).volume(0.8));
35        ctx.audio.register("found", Synth::triangle(660.0).sweep_to(1320.0).length(0.4).decay(6.0).volume(0.3));
36
37        // A hall of 40 x 40 meters and 16 meters high: floor, roof and four walls.
38        // A box is given by its center and its full width (x), depth (y) and height (z).
39        let physics = &mut ctx.physics;
40        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(40.0, 40.0, 1.0).with_tag("floor"));
41        physics.add(Body::fixed(vec3(0.0, 0.0, 16.5)).with_box(40.0, 40.0, 1.0).with_tag("roof"));
42        physics.add(Body::fixed(vec3(0.0, 20.5, 8.0)).with_box(40.0, 1.0, 16.0).with_tag("north wall"));
43        physics.add(Body::fixed(vec3(0.0, -20.5, 8.0)).with_box(40.0, 1.0, 16.0).with_tag("south wall"));
44        physics.add(Body::fixed(vec3(20.5, 0.0, 8.0)).with_box(1.0, 40.0, 16.0).with_tag("east wall"));
45        physics.add(Body::fixed(vec3(-20.5, 0.0, 8.0)).with_box(1.0, 40.0, 16.0).with_tag("west wall"));
46
47        // The drone starts in the middle, 2 meters above the floor. No gravity: it hovers.
48        let drone = ctx.physics.add(Body::kinematic(vec3(0.0, 0.0, 2.0)).with_sphere(0.5).with_tag("drone"));
49        ctx.follow(drone); // the ears use the drone's position, facing and pitch
50        ctx.audio.play_looped("hum");
51
52        for spot in BIRDS {
53            let bird = ctx.physics.add(Body::sensor(spot).with_sphere(1.0).with_tag("bird"));
54            ctx.loop_on(bird, "bird");
55        }
56        Self { drone, birds: BIRDS.len(), blocked: false }
57    }
examples/driving.rs (line 33)
29    fn new(ctx: &mut Context) -> Self {
30        // Engine: a low buzz that loops without clicks (whole cycles, no fades).
31        let engine = Synth::saw(55.0).length(1.0).attack(0.0).release(0.0).volume(0.25)
32            .with(Synth::square(110.0).length(1.0).attack(0.0).release(0.0).volume(0.08));
33        ctx.audio.register("engine", engine);
34        ctx.audio.register("horn", Synth::square(415.0).length(0.5).volume(0.25).with(Synth::square(523.0).length(0.5).volume(0.2)));
35        ctx.audio.register("shift", Synth::noise().length(0.04).decay(90.0).volume(0.3));
36        ctx.audio.register("crash", Synth::noise().length(0.4).decay(10.0).volume(0.8).with(Synth::sine(80.0).sweep_to(30.0).length(0.3)));
37        ctx.audio.register("start", Synth::saw(40.0).sweep_to(55.0).length(0.6).volume(0.3));
38        ctx.audio.register("garage", Synth::sine(880.0).length(0.15).decay(10.0));
39
40        let level = ctx.physics.build_level(MAP, 6.0);
41        let sounds = |v: Vehicle| {
42            v.with_engine_sound("engine")
43                .with_horn_sound("horn")
44                .with_shift_sound("shift")
45                .with_crash_sound("crash")
46                .with_start_sound("start")
47        };
48        let car_start = level.point('P').unwrap_or(Vec3::ZERO);
49        let car = ctx.physics.add(Body::car(car_start).with_facing(90.0).with_vehicle(sounds(Vehicle::car().with_fuel(5.0, 8.0))));
50        let truck_start = level.point('T').unwrap_or(Vec3::ZERO);
51        let truck = ctx.physics.add(
52            Body::truck(truck_start).with_facing(90.0).with_vehicle(sounds(Vehicle::truck()).engine_off()),
53        );
54        let garage_at = level.point('G').unwrap_or(Vec3::ZERO);
55        let garage = ctx.physics.add(Body::sensor(garage_at).with_box(6.0, 6.0, 3.0).with_tag("garage"));
56        ctx.loop_on(garage, "garage");
57        ctx.follow(car);
58        Self { car, truck, driving: car, garage }
59    }
examples/platformer.rs (line 21)
19    fn new(ctx: &mut Context) -> Self {
20        let audio = &mut ctx.audio;
21        audio.register("jump", Synth::square(300.0).sweep_to(700.0).length(0.15).decay(10.0).volume(0.2));
22        audio.register("land", Synth::noise().length(0.1).decay(40.0).with(Synth::sine(90.0).length(0.1).decay(30.0)));
23        audio.register("coin", Synth::triangle(988.0).length(0.08).then(Synth::triangle(1319.0).length(0.17).decay(15.0)));
24        audio.register("run", Synth::noise().length(0.06).decay(60.0).volume(0.3).then(Synth::silence(0.09)));
25        audio.register("lift", Synth::saw(55.0).length(1.0).volume(0.1));
26        audio.register("push", Synth::noise().length(0.3).volume(0.15));
27
28        let physics = &mut ctx.physics;
29        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(200.0, 4.0, 1.0).with_tag("ground"));
30        for (x, z) in [(8.0, 2.0), (14.0, 4.0), (20.0, 2.5)] {
31            physics.add(Body::fixed(vec3(x, 0.0, z)).with_box(4.0, 4.0, 0.5).with_tag("platform"));
32        }
33        // A lift going up and down at x = 40, and a heavy crate at x = 30.
34        let lift = physics.add(Body::kinematic(vec3(40.0, 0.0, 0.0)).with_box(3.0, 4.0, 0.5).with_tag("lift"));
35        physics.add(Body::dynamic(vec3(30.0, 0.0, 0.5)).with_box(1.0, 1.0, 1.0).with_mass(5.0).with_tag("crate"));
36        // Facing north, so x maps to left and right in the ears.
37        let player = physics
38            .add(Body::dynamic(vec3(0.0, 0.0, 1.0)).with_box(0.8, 0.8, 1.6).with_max_speed(25.0).with_tag("player"));
39
40        ctx.follow(player);
41        ctx.loop_on(lift, "lift");
42        // Coins are sensors with a looping sound, so you can hear where they are.
43        for (x, z) in [(5.0, 1.0), (14.0, 5.5), (25.0, 1.0), (40.0, 8.0)] {
44            let coin = ctx.physics.add(Body::sensor(vec3(x, 0.0, z)).with_sphere(0.6).with_tag("coin"));
45            ctx.loop_on(coin, "coin").set_volume(0.3);
46        }
47
48        let run_sound = ctx.audio.sound("run").looped().volume(0.0).play();
49        Self { player, lift, run_sound, score: 0 }
50    }
Source

pub fn is_loaded(&self, name: &str) -> bool

Whether a sound with this name is loaded or registered.

Source

pub fn unload(&mut self, name: &str)

Removes a sound from the cache to free memory. Playing sounds are not affected.

Source

pub fn duration(&mut self, name: &str) -> Option<f32>

Length of a sound in seconds (loads it if needed).

Source

pub fn sound(&mut self, name: &str) -> SoundBuilder<'_>

Starts building a sound with full control over its settings. Finish with SoundBuilder::play.

Examples found in repository?
examples/bell_catcher.rs (line 183)
179    fn ring(&mut self, ctx: &mut Context) {
180        // 0.0 when the bell starts falling, 1.0 when it lands.
181        let progress = 1.0 - self.left / self.fall;
182        let position = self.bell_position();
183        ctx.audio.sound("bell").at(position).pitch(1.0 + progress * 0.6).play();
184        self.next_ring = 0.12 + 0.45 * (1.0 - progress);
185    }
186
187    fn land(&mut self, ctx: &mut Context) {
188        if self.lane == self.bell_lane {
189            self.score += 1;
190            self.fall = (self.fall * 0.93).max(FASTEST_FALL);
191            ctx.play("catch");
192            if self.score.is_multiple_of(5) {
193                ctx.speak(format!("{} bells!", self.score));
194            }
195        } else {
196            self.lives -= 1;
197            ctx.play("miss");
198            match self.lives {
199                0 => return self.game_over(ctx),
200                1 => ctx.speak("Missed! Last life."),
201                n => ctx.speak(format!("Missed! {n} lives left.")),
202            }
203        }
204        // A short break before the next bell.
205        self.left = -0.8;
206    }
207
208    fn game_over(&mut self, ctx: &mut Context) {
209        self.over = true;
210        let best: u32 = ctx.storage.get_or("best", 0);
211        let text = if self.score > best {
212            ctx.storage.set("best", self.score);
213            format!("Game over. {} bells, a new best score!", self.score)
214        } else {
215            format!("Game over. {} bells. Your best is {best}.", self.score)
216        };
217        ctx.ui.message("over", text);
218    }
219}
220
221impl Scene for Game {
222    fn start(&mut self, ctx: &mut Context) {
223        ctx.speak("Catch the bells! Left and right to move.");
224        // Give the player a moment to listen to the first bell.
225        self.left = -1.5;
226    }
227
228    fn update(&mut self, ctx: &mut Context) {
229        if self.over {
230            if ctx.ui.closed("over") {
231                ctx.switch_scene(Title);
232            }
233            return;
234        }
235        if ctx.pressed(Key::Escape) {
236            ctx.switch_scene(Title);
237            return;
238        }
239        if ctx.pressed(Key::S) {
240            ctx.speak(format!("{} bells, {} lives", self.score, self.lives));
241        }
242        for (key, step) in [(Key::Left, -1), (Key::Right, 1)] {
243            if ctx.input.repeating(key, 0.3, 0.15) {
244                let lane = (self.lane + step).clamp(-LANES, LANES);
245                if lane == self.lane {
246                    ctx.play("edge");
247                } else {
248                    self.lane = lane;
249                    ctx.audio.sound("step").at(vec3(step as f32 * 0.5, 0.0, 0.0)).random_pitch(0.9, 1.1).play();
250                }
251            }
252        }
253
254        let dt = ctx.time.delta();
255        if self.left < 0.0 {
256            self.left += dt;
257            if self.left >= 0.0 {
258                self.drop_bell();
259            }
260            return;
261        }
262        self.left -= dt;
263        self.next_ring -= dt;
264        if self.left <= 0.0 {
265            self.land(ctx);
266        } else if self.next_ring <= 0.0 {
267            self.ring(ctx);
268        }
269    }
More examples
Hide additional examples
examples/beacon.rs (line 75)
58    fn update(&mut self, ctx: &mut Context) {
59        if ctx.pressed(Key::Escape) {
60            ctx.push_scene(Pause);
61        }
62        if ctx.pressed(Key::Left) || ctx.pressed(Key::Right) {
63            let degrees = if ctx.pressed(Key::Left) { -45.0 } else { 45.0 };
64            let facing = ctx.physics.turn(self.player, degrees);
65            ctx.speak(math::direction_name(facing));
66        }
67
68        let forward = ctx.input.repeating(Key::Up, 0.35, 0.35);
69        let backward = ctx.input.repeating(Key::Down, 0.35, 0.35);
70        if forward || backward {
71            let step = if forward { 1.0 } else { -1.0 };
72            if ctx.physics.move_forward(self.player, step).blocked {
73                ctx.play("wall");
74            } else {
75                ctx.audio.sound("step").random_pitch(0.85, 1.15).play();
76            }
77        }
78
79        if ctx.pressed(Key::C) {
80            let position = ctx.physics.position(self.player);
81            match ctx.physics.nearest_with_tag(position, "beacon") {
82                Some(beacon) => {
83                    let text = ctx.physics.describe(self.player, beacon);
84                    ctx.speak(text);
85                }
86                None => ctx.speak("No beacons left"),
87            }
88        }
89        if ctx.pressed(Key::F) {
90            let facing = ctx.physics.facing(self.player);
91            ctx.speak(format!("Facing {}", math::direction_name(facing)));
92        }
93
94        if let Some(beacon) = ctx.physics.entered_tag(self.player, "beacon") {
95            ctx.physics.remove(beacon);
96            self.beacons.retain(|b| *b != beacon);
97            ctx.play("found");
98            if self.beacons.is_empty() {
99                self.finish(ctx);
100            } else {
101                ctx.speak(format!("Beacon found! {} left.", self.beacons.len()));
102            }
103        }
104        if ctx.ui.closed("done") {
105            ctx.quit();
106        }
107    }
examples/platformer.rs (line 48)
19    fn new(ctx: &mut Context) -> Self {
20        let audio = &mut ctx.audio;
21        audio.register("jump", Synth::square(300.0).sweep_to(700.0).length(0.15).decay(10.0).volume(0.2));
22        audio.register("land", Synth::noise().length(0.1).decay(40.0).with(Synth::sine(90.0).length(0.1).decay(30.0)));
23        audio.register("coin", Synth::triangle(988.0).length(0.08).then(Synth::triangle(1319.0).length(0.17).decay(15.0)));
24        audio.register("run", Synth::noise().length(0.06).decay(60.0).volume(0.3).then(Synth::silence(0.09)));
25        audio.register("lift", Synth::saw(55.0).length(1.0).volume(0.1));
26        audio.register("push", Synth::noise().length(0.3).volume(0.15));
27
28        let physics = &mut ctx.physics;
29        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(200.0, 4.0, 1.0).with_tag("ground"));
30        for (x, z) in [(8.0, 2.0), (14.0, 4.0), (20.0, 2.5)] {
31            physics.add(Body::fixed(vec3(x, 0.0, z)).with_box(4.0, 4.0, 0.5).with_tag("platform"));
32        }
33        // A lift going up and down at x = 40, and a heavy crate at x = 30.
34        let lift = physics.add(Body::kinematic(vec3(40.0, 0.0, 0.0)).with_box(3.0, 4.0, 0.5).with_tag("lift"));
35        physics.add(Body::dynamic(vec3(30.0, 0.0, 0.5)).with_box(1.0, 1.0, 1.0).with_mass(5.0).with_tag("crate"));
36        // Facing north, so x maps to left and right in the ears.
37        let player = physics
38            .add(Body::dynamic(vec3(0.0, 0.0, 1.0)).with_box(0.8, 0.8, 1.6).with_max_speed(25.0).with_tag("player"));
39
40        ctx.follow(player);
41        ctx.loop_on(lift, "lift");
42        // Coins are sensors with a looping sound, so you can hear where they are.
43        for (x, z) in [(5.0, 1.0), (14.0, 5.5), (25.0, 1.0), (40.0, 8.0)] {
44            let coin = ctx.physics.add(Body::sensor(vec3(x, 0.0, z)).with_sphere(0.6).with_tag("coin"));
45            ctx.loop_on(coin, "coin").set_volume(0.3);
46        }
47
48        let run_sound = ctx.audio.sound("run").looped().volume(0.0).play();
49        Self { player, lift, run_sound, score: 0 }
50    }
51}
52
53impl Scene for Platformer {
54    fn start(&mut self, ctx: &mut Context) {
55        ctx.speak("Collect the coins. Left and right to run, space to jump, S for score, escape to quit.");
56    }
57
58    fn update(&mut self, ctx: &mut Context) {
59        if ctx.pressed(Key::Escape) {
60            ctx.quit();
61        }
62
63        // The lift goes up for 4 seconds and down for 4 seconds.
64        let lift_up = (ctx.time.elapsed() % 8.0) < 4.0;
65        ctx.physics.set_velocity(self.lift, vec3(0.0, 0.0, if lift_up { 2.0 } else { -2.0 }));
66
67        let run = ctx.input.axis(Key::Left, Key::Right);
68        let mut velocity = ctx.physics.velocity(self.player);
69        velocity.x = run * 6.0;
70        ctx.physics.set_velocity(self.player, velocity);
71        if ctx.pressed(Key::Space) && ctx.physics.jump(self.player, 2.5) {
72            ctx.play("jump");
73        }
74        let running = ctx.physics.on_ground(self.player) && run != 0.0;
75        self.run_sound.set_volume(if running { 0.6 } else { 0.0 });
76
77        if let Some(speed) = ctx.physics.landed(self.player) {
78            ctx.audio.sound("land").volume((speed / 12.0).clamp(0.2, 1.0)).play();
79            if speed > 15.0 {
80                ctx.speak("Ouch!");
81            }
82        }
83        if let Some((other, _)) = ctx.physics.collided(self.player)
84            && ctx.physics.tag(other) == "crate"
85        {
86            ctx.play_on(other, "push");
87        }
88        if let Some(coin) = ctx.physics.entered_tag(self.player, "coin") {
89            ctx.physics.remove(coin); // its looping sound stops by itself
90            self.score += 1;
91            ctx.audio.sound("coin").pitch(1.5).play();
92            ctx.speak(format!("{} of {COINS} coins", self.score));
93            if self.score == COINS {
94                ctx.ui.message("won", "You collected every coin! Press enter to quit.");
95            }
96        }
97        if ctx.ui.closed("won") {
98            ctx.quit();
99        }
100
101        if ctx.pressed(Key::S) {
102            let x = ctx.physics.position(self.player).x;
103            ctx.speak(format!("Score {}, position {x:.0}", self.score));
104        }
105    }
Source

pub fn play(&mut self, name: &str) -> Sound

Plays a sound once, centered. Returns a handle you may keep or ignore.

Source

pub fn play_at(&mut self, name: &str, position: Vec3) -> Sound

Plays a sound once at a position in the world.

Source

pub fn play_looped(&mut self, name: &str) -> Sound

Plays a sound in an endless loop. Stop it with Sound::stop.

Examples found in repository?
examples/flight.rs (line 50)
31    fn new(ctx: &mut Context) -> Self {
32        ctx.audio.register("bird", Synth::sine(2200.0).sweep_to(3200.0).length(0.07).then(Synth::silence(0.05)).then(Synth::sine(2600.0).length(0.06)).then(Synth::silence(0.7)));
33        ctx.audio.register("hum", Synth::saw(70.0).length(1.0).volume(0.08));
34        ctx.audio.register("bump", Synth::sine(80.0).length(0.15).decay(25.0).volume(0.8));
35        ctx.audio.register("found", Synth::triangle(660.0).sweep_to(1320.0).length(0.4).decay(6.0).volume(0.3));
36
37        // A hall of 40 x 40 meters and 16 meters high: floor, roof and four walls.
38        // A box is given by its center and its full width (x), depth (y) and height (z).
39        let physics = &mut ctx.physics;
40        physics.add(Body::fixed(vec3(0.0, 0.0, -0.5)).with_box(40.0, 40.0, 1.0).with_tag("floor"));
41        physics.add(Body::fixed(vec3(0.0, 0.0, 16.5)).with_box(40.0, 40.0, 1.0).with_tag("roof"));
42        physics.add(Body::fixed(vec3(0.0, 20.5, 8.0)).with_box(40.0, 1.0, 16.0).with_tag("north wall"));
43        physics.add(Body::fixed(vec3(0.0, -20.5, 8.0)).with_box(40.0, 1.0, 16.0).with_tag("south wall"));
44        physics.add(Body::fixed(vec3(20.5, 0.0, 8.0)).with_box(1.0, 40.0, 16.0).with_tag("east wall"));
45        physics.add(Body::fixed(vec3(-20.5, 0.0, 8.0)).with_box(1.0, 40.0, 16.0).with_tag("west wall"));
46
47        // The drone starts in the middle, 2 meters above the floor. No gravity: it hovers.
48        let drone = ctx.physics.add(Body::kinematic(vec3(0.0, 0.0, 2.0)).with_sphere(0.5).with_tag("drone"));
49        ctx.follow(drone); // the ears use the drone's position, facing and pitch
50        ctx.audio.play_looped("hum");
51
52        for spot in BIRDS {
53            let bird = ctx.physics.add(Body::sensor(spot).with_sphere(1.0).with_tag("bird"));
54            ctx.loop_on(bird, "bird");
55        }
56        Self { drone, birds: BIRDS.len(), blocked: false }
57    }
Source

pub fn play_looped_at(&mut self, name: &str, position: Vec3) -> Sound

Plays a looping sound at a position in the world.

Source

pub fn play_random(&mut self, names: &[&str]) -> Sound

Plays one of several sounds picked at random, e.g. footstep variations.

Source

pub fn play_random_at(&mut self, names: &[&str], position: Vec3) -> Sound

Plays one of several sounds picked at random at a position.

Source

pub fn play_tone(&mut self, frequency: f32, seconds: f32) -> Sound

Plays a generated sine beep of frequency hertz (e.g. 880.0) for seconds. Great for prototyping before you have sound files.

Examples found in repository?
examples/hello.rs (line 18)
14    fn update(&mut self, ctx: &mut Context) {
15        if ctx.input.pressed(Key::Space) {
16            // A pitch that rises over time, so every beep sounds different.
17            let pitch = 300.0 + (ctx.time.elapsed() as f32 * 50.0) % 900.0;
18            ctx.audio.play_tone(pitch.round(), 0.15);
19        }
20        if ctx.input.pressed(Key::F1) {
21            ctx.speech.repeat_last();
22        }
23        if ctx.input.pressed(Key::Escape) {
24            ctx.quit();
25        }
26    }
More examples
Hide additional examples
examples/clicker.rs (line 20)
8fn main() -> audio_code::Result<()> {
9    let mut score = 0;
10
11    audio_code::run("Clicker", move |ctx| {
12        if ctx.once("intro") {
13            let best: u32 = ctx.storage.get_or("best", 0);
14            ctx.speak(format!("Press space as often as you can in ten seconds. Your best is {best}. Go!"));
15            ctx.time.after("end", 10.0);
16        }
17
18        if ctx.time.is_scheduled("end") && ctx.pressed(Key::Space) {
19            score += 1;
20            ctx.audio.play_tone(300.0 + score as f32 * 15.0, 0.05);
21        }
22
23        if ctx.time.fired("end") {
24            let best: u32 = ctx.storage.get_or("best", 0);
25            if score > best {
26                ctx.storage.set("best", score);
27                ctx.ui.message("result", format!("{score} presses. New record!"));
28            } else {
29                ctx.ui.message("result", format!("{score} presses. Your best is {best}."));
30            }
31        }
32
33        if ctx.ui.closed("result") || ctx.pressed(Key::Escape) {
34            ctx.quit();
35        }
36    })
37}
Source

pub fn play_music(&mut self, name: &str) -> Sound

Plays background music in a loop. The file is decoded in the background so the game does not stutter. Any previous music fades out while the new one fades in. Calling it again with the music that is already playing does nothing.

Source

pub fn play_music_with_fade(&mut self, name: &str, fade: f32) -> Sound

Like Audio::play_music with a custom cross fade time in seconds.

Source

pub fn stop_music(&mut self, fade: f32)

Fades out the current music over fade seconds.

Source

pub fn music(&self) -> Option<&Sound>

Handle of the current music, if any.

Source

pub fn music_name(&self) -> Option<&str>

Name of the current music, as passed to Audio::play_music.

Source

pub fn set_listener(&mut self, position: Vec3, yaw: f32)

Places the listener (the player’s ears) in the world. yaw is the compass direction the player faces (0 = north, 90 = east). Call this whenever the player moves or turns. The listener looks level with the horizon; use Audio::set_listener_3d to also look up or down.

Source

pub fn set_listener_3d(&mut self, position: Vec3, yaw: f32, pitch: f32)

Like Audio::set_listener, plus pitch: how far the player looks up (positive, 90 = straight up) or down (negative, -90 = straight down), in degrees. For flying, swimming or space games.

Source

pub fn listener_position(&self) -> Vec3

Current listener position.

Source

pub fn listener_yaw(&self) -> f32

Current listener facing in compass degrees.

Source

pub fn listener_pitch(&self) -> f32

Current listener pitch in degrees (0 = level, 90 = straight up).

Source

pub fn set_default_distance(&mut self, min: f32, max: f32)

Default audible range for positioned sounds in meters: full volume up to min, silent from max on. The default is 1.0 and 30.0.

Source

pub fn set_default_rolloff(&mut self, rolloff: f32)

Default rolloff for positioned sounds: how fast the volume drops between min and max distance. 1.0 is physically accurate (twice the distance, half the volume), lower values keep distant sounds louder, 0.0 keeps full volume until shortly before max. Default is 0.7, which is at 32 % volume at 5 m and 18 % at 10 m.

Source

pub fn set_master_volume(&mut self, volume: f32)

Sets the overall volume as a linear factor: 1.0 = unchanged (default), 0.5 = half, 0.0 = silent. Higher values amplify and may distort.

Examples found in repository?
examples/menus.rs (line 76)
50    fn on_ui(&mut self, event: &UiEvent, ctx: &mut Context) {
51        match event {
52            UiEvent::Activated { menu, item } => match (menu.as_str(), item.as_str()) {
53                ("main", "play") => {
54                    let s = ctx.get::<Settings>().expect("settings exist");
55                    let text = format!("Good luck, {}! (this is where your game would start)", s.name);
56                    ctx.ui.message("intro", text);
57                }
58                ("main", "options") => {
59                    let menu = options_menu(ctx.get::<Settings>().expect("settings exist"));
60                    ctx.ui.open(menu);
61                }
62                ("main", "name") => ctx.ui.open(TextInput::new("name", "Enter your name").max_length(20).required()),
63                ("main", "quit") => ctx.ui.open(Confirm::new("quit", "Do you really want to quit?").default_no()),
64                ("options", "back") => ctx.ui.close(),
65                _ => {}
66            },
67            UiEvent::Changed { menu, item, value } if menu == "options" => {
68                let s = ctx.get_mut::<Settings>().expect("settings exist");
69                match item.as_str() {
70                    "volume" => s.volume = value.as_f32() / 100.0,
71                    "difficulty" => s.difficulty = value.as_index(),
72                    "name" => s.name = value.as_str().to_string(),
73                    _ => {}
74                }
75                let volume = s.volume;
76                ctx.audio.set_master_volume(volume);
77            }
78            UiEvent::TextSubmitted { id, text } if id == "name" => {
79                ctx.get_mut::<Settings>().expect("settings exist").name = text.clone();
80                ctx.speech.speak_queued(format!("Hello, {text}"));
81            }
82            UiEvent::Confirmed { id, yes: true } if id == "quit" => ctx.quit(),
83            _ => {}
84        }
85    }
Source

pub fn master_volume(&self) -> f32

Overall volume.

Source

pub fn set_group_volume(&mut self, group: Group, volume: f32)

Sets the volume of one group (sound effects, music or UI) as a linear factor, 1.0 = unchanged (default). It is multiplied with each sound’s own volume and the master volume.

Source

pub fn group_volume(&self, group: Group) -> f32

Volume of one group.

Source

pub fn set_music_volume(&mut self, volume: f32)

Shortcut for set_group_volume(Group::Music, volume).

Source

pub fn set_sound_volume(&mut self, volume: f32)

Shortcut for set_group_volume(Group::Sound, volume).

Source

pub fn stop_all(&mut self)

Stops every sound immediately (including music).

Source

pub fn stop_group(&mut self, group: Group, fade: f32)

Fades out all sounds of a group over fade seconds and stops them.

Trait Implementations§

Source§

impl Debug for Audio

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for Audio

§

impl !UnwindSafe for Audio

§

impl Freeze for Audio

§

impl Send for Audio

§

impl Sync for Audio

§

impl Unpin for Audio

§

impl UnsafeUnpin for Audio

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more