1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
use crate::error::GameUtilError;
use crate::timing::Timing;
use audio_engine::{AudioEngine, Sound, WavDecoder};
use std::io::Cursor;
/// Sound effect (although it can also be used for music)
/// You must call [SoundEffect::update] or [SoundEffect::update_secs] with accurate values and often otherwise playback may stutter or jump
///
/// # Usage
///
/// ```
///# use audio_engine::AudioEngine;
///# use simple_game_utils::sound_effect::NewSoundEffect;
///# use simple_game_utils::timing::Timing;
///# let some_sound_bytes = [0,0,0,0,0,0];
///# let mut timing = Timing::new(240);
///# let duration = 1.0;
/// //this must live as long as `sound` but there's no lifetimes to enforce this
/// let mut engine = AudioEngine::new().unwrap();
/// let mut sound = engine.load_from_bytes(&some_sound_bytes, duration).unwrap();
/// sound.play();
/// loop {
/// timing.update();
/// sound.update(&timing);
/// }
/// ```
pub struct SoundEffect {
//Sound data
sound: Sound,
//If sound is currently playing
is_playing: bool,
//Length in seconds
duration: f64,
// used to prevent bugs
next_play_in: f64,
//If sound automatically loops
loops: bool,
}
pub trait NewSoundEffect {
fn load_from_bytes(
&self,
bytes: &'static [u8],
duration: f64,
) -> Result<SoundEffect, GameUtilError>;
}
impl NewSoundEffect for AudioEngine {
fn load_from_bytes(
&self,
bytes: &'static [u8],
duration: f64,
) -> Result<SoundEffect, GameUtilError> {
let decoder =
WavDecoder::new(Cursor::new(bytes)).map_err(GameUtilError::SoundEffectInvalid)?;
let sound = self
.new_sound(decoder)
.map_err(GameUtilError::SoundEffectInit)?;
Ok(SoundEffect::new(sound, duration))
}
}
impl SoundEffect {
pub fn new(sound: Sound, duration: f64) -> Self {
Self {
sound,
is_playing: false,
duration,
next_play_in: 0.0,
loops: false,
}
}
/// Play sound effect, won't do anything if sound effect is already playing
pub fn play(&mut self) {
if !self.is_playing {
self.sound.play();
self.is_playing = true;
self.next_play_in = self.duration;
}
}
/// Reset playback position and stop playback
pub fn reset(&mut self) {
self.sound.stop();
self.is_playing = false;
self.next_play_in = 0.0;
}
/// Set if the sound loops automatically
pub fn set_loop(&mut self, loops: bool) {
self.loops = loops;
self.sound.set_loop(loops)
}
pub fn set_volume(&mut self, volume: f32) {
self.sound.set_volume(volume);
}
/// Returns true if calling [SoundEffect::play] will do anything
pub fn can_play(&self) -> bool {
!self.is_playing && self.next_play_in < 0.0
}
/// Allows the sound to continue playing
pub fn update(&mut self, timing: &Timing) {
self.update_secs(timing.fixed_time_step)
}
/// Allows the sound to continue playing
pub fn update_secs(&mut self, delta: f64) {
if !self.loops && self.is_playing && self.next_play_in < 0.0 {
self.reset();
}
self.next_play_in -= delta;
}
/// If the sound is currently playing
pub fn is_playing(&self) -> bool {
self.is_playing
}
/// Length in seconds
pub fn duration(&self) -> f64 {
self.duration
}
/// If sound will automatically loop
pub fn loops(&self) -> bool {
self.loops
}
}