diff --git a/examples/basic.rs b/examples/basic.rs index c342322..f385970 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -6,7 +6,8 @@ fn main() { let endpoint = rodio::get_default_endpoint().unwrap(); let file = std::fs::File::open("examples/beep.wav").unwrap(); - let beep1 = rodio::play_once(&endpoint, BufReader::new(file)); + let mut beep1 = rodio::play_once(&endpoint, BufReader::new(file)); + beep1.set_volume(0.2); std::thread::sleep_ms(1000); diff --git a/src/decoder/conversions.rs b/src/decoder/conversions.rs index 1e7a0a2..1716478 100644 --- a/src/decoder/conversions.rs +++ b/src/decoder/conversions.rs @@ -43,6 +43,7 @@ pub fn convert_and_write(mut samples: I, output: &mut UnknownTypeBuffer) /// Trait for containers that contain PCM data. pub trait Sample: cpal::Sample { fn lerp(first: Self, second: Self, numerator: u32, denominator: u32) -> Self; + fn amplify(self, value: f32) -> Self; fn zero_value() -> Self; @@ -57,6 +58,11 @@ impl Sample for u16 { (first as u32 + (second as u32 - first as u32) * numerator / denominator) as u16 } + #[inline] + fn amplify(self, value: f32) -> u16 { + ((self as f32) * value) as u16 + } + #[inline] fn zero_value() -> u16 { 32768 @@ -88,6 +94,11 @@ impl Sample for i16 { (first as i32 + (second as i32 - first as i32) * numerator as i32 / denominator as i32) as i16 } + #[inline] + fn amplify(self, value: f32) -> i16 { + ((self as f32) * value) as i16 + } + #[inline] fn zero_value() -> i16 { 0 @@ -123,6 +134,11 @@ impl Sample for f32 { first + (second - first) * numerator as f32 / denominator as f32 } + #[inline] + fn amplify(self, value: f32) -> f32 { + self * value + } + #[inline] fn zero_value() -> f32 { 0.0 @@ -345,6 +361,43 @@ impl Iterator for SamplesRateConverter where I: Iterator, I::Item: Sample impl ExactSizeIterator for SamplesRateConverter where I: ExactSizeIterator, I::Item: Sample + Clone {} +pub struct AmplifierIterator where I: Iterator { + input: I, + amplication: f32, +} + +impl AmplifierIterator where I: Iterator { + #[inline] + pub fn new(input: I, amplication: f32) -> AmplifierIterator { + AmplifierIterator { + input: input, + amplication: amplication, + } + } + + #[inline] + pub fn set_amplification(&mut self, new_value: f32) { + self.amplication = new_value; + } +} + +impl Iterator for AmplifierIterator where I: Iterator, I::Item: Sample { + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + self.input.next().map(|value| value.amplify(self.amplication)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.input.size_hint() + } +} + +impl ExactSizeIterator for AmplifierIterator + where I: ExactSizeIterator, I::Item: Sample {} + #[cfg(test)] mod test { use super::Sample; diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index f9c4c05..fc030e6 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -12,6 +12,9 @@ pub trait Decoder { /// Appends data to the voice. Returns the number of nanoseconds after which new data will need /// to have been submitted. fn write(&mut self) -> u64; + + /// Changes the volume of the sound. + fn set_volume(&mut self, f32); } /// Builds a new `Decoder` from a data stream by determining the correct format. diff --git a/src/decoder/vorbis.rs b/src/decoder/vorbis.rs index 961f4de..419e0b5 100644 --- a/src/decoder/vorbis.rs +++ b/src/decoder/vorbis.rs @@ -63,4 +63,8 @@ impl Decoder for VorbisDecoder { len as u64 * 1000000000 / self.voice.get_samples_rate().0 as u64 } + + fn set_volume(&mut self, _: f32) { + unimplemented!(); + } } diff --git a/src/decoder/wav.rs b/src/decoder/wav.rs index 7fd7fe4..4101d2a 100644 --- a/src/decoder/wav.rs +++ b/src/decoder/wav.rs @@ -1,4 +1,5 @@ use std::io::{Read, Seek, SeekFrom}; +use std::cmp; use std::cmp::Ordering; use super::Decoder; use super::conversions; @@ -8,7 +9,7 @@ use hound::WavReader; use hound::WavSpec; pub struct WavDecoder { - reader: Box + Send>, + reader: conversions::AmplifierIterator + Send>>, voice: Voice, } @@ -59,7 +60,7 @@ impl WavDecoder { voice.get_samples_rate()); Ok(WavDecoder { - reader: Box::new(reader), + reader: conversions::AmplifierIterator::new(Box::new(reader), 1.0), voice: voice, }) } @@ -108,6 +109,8 @@ fn is_wave(mut data: R) -> bool where R: Read + Seek { impl Decoder for WavDecoder { fn write(&mut self) -> u64 { let (min, _) = self.reader.size_hint(); + let min = cmp::min(min, 10240); // using a minimal value so that filters get applied + // quickly if min == 0 { // finished @@ -127,4 +130,8 @@ impl Decoder for WavDecoder { duration } + + fn set_volume(&mut self, value: f32) { + self.reader.set_amplification(value); + } } diff --git a/src/engine.rs b/src/engine.rs index 2ff0ad5..600785e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -60,6 +60,12 @@ pub struct Handle<'a> { } impl<'a> Handle<'a> { + #[inline] + pub fn set_volume(&mut self, value: f32) { + let commands = self.engine.commands.lock().unwrap(); + commands.send(Command::SetVolume(self.id, value)).unwrap(); + } + #[inline] pub fn stop(self) { let commands = self.engine.commands.lock().unwrap(); @@ -70,6 +76,7 @@ impl<'a> Handle<'a> { pub enum Command { Play(usize, Box), Stop(usize), + SetVolume(usize, f32), } fn background(rx: Receiver) { @@ -86,6 +93,14 @@ fn background(rx: Receiver) { Command::Stop(id) => { sounds.retain(|&(id2, _)| id2 != id) }, + + Command::SetVolume(id, volume) => { + if let Some(&mut (_, ref mut d)) = sounds.iter_mut() + .find(|&&mut (i, _)| i == id) + { + d.set_volume(volume); + } + }, } } diff --git a/src/lib.rs b/src/lib.rs index 98dd3f6..6e175e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,15 @@ lazy_static! { pub struct Handle(engine::Handle<'static>); impl Handle { + /// Changes the volume of the sound. + /// + /// The value `1.0` is the "normal" volume (unfiltered input). Any value other than 1.0 will + /// multiply each sample by this value. + #[inline] + pub fn set_volume(&mut self, value: f32) { + self.0.set_volume(value); + } + /// Stops the sound. #[inline] pub fn stop(self) {