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 133 134 135 136 137 138 139
use std::sync::Arc;
use crate::{NextSample, Sound};
/// A Sound that stores all samples on the heap.
///
/// The heap samples can be shared between multiple MemorySounds that can be
/// played simultaneously. Optionally the sound can repeat forever.
#[derive(Clone)]
pub struct MemorySound {
samples: Arc<Vec<i16>>,
channel_count: u16,
sample_rate: u32,
next_sample: usize,
should_loop: bool,
}
/// A [MetadataChanged][NextSample::MetadataChanged] was returned while reading
/// into a [MemorySound] which is not currently supported.
#[derive(Debug)]
pub struct UnsupportedMetadataChangeError {}
impl std::fmt::Display for UnsupportedMetadataChangeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"unsupported MetadataChanged encountered when consuming Sound"
)
}
}
impl std::error::Error for UnsupportedMetadataChangeError {}
impl MemorySound {
/// Create a MemorySound be consuming another Sound and storing the samples
/// until it returns `Finished` or `Paused`.
///
/// If an Error is encountered it is returned and any already obtained
/// samples are lost.
///
/// It is not currently supported for the the originating sample to change
/// its metadata (i.e. channel count or sample rate). If it does an
/// IoError of ErrorKind::Other with a UnsupportedMetadataChangeError is
/// returned.
pub fn from_sound(mut orig: impl Sound) -> Result<Self, crate::Error> {
let channel_count = orig.channel_count();
let sample_rate = orig.sample_rate();
let mut samples = Vec::new();
loop {
let sample = orig.next_sample()?;
match sample {
crate::NextSample::Sample(s) => {
samples.push(s);
}
crate::NextSample::MetadataChanged => {
if orig.channel_count() != channel_count || orig.sample_rate() != sample_rate {
return Err(crate::Error::IoError(std::io::Error::other(
UnsupportedMetadataChangeError {},
)));
}
// Sometimes we see a MetadataChanged from a sound just to
// ensure that channels stay in sync. Lets ensure that here
// by ensuring that the next sample after MetadataChanged is
// for the first channel.
let channel_idx = samples.len() % channel_count as usize;
if channel_idx != 0 {
let outputs_to_stay_in_sync = channel_count as usize - channel_idx;
// This should be rare so lets just output 0 for the filler samples.
samples.extend(std::iter::repeat(0).take(outputs_to_stay_in_sync));
}
}
crate::NextSample::Paused | crate::NextSample::Finished => break,
}
}
Ok(MemorySound {
samples: Arc::new(samples),
channel_count,
sample_rate,
next_sample: 0,
should_loop: false,
})
}
/// Create memory sound from the raw data of samples.
///
/// Samples should be in the same order as they will be returned from the
/// next_samples function (e.g. interleaved by channel).
pub fn from_samples(
samples: Arc<Vec<i16>>,
channel_count: u16,
sample_rate: u32,
) -> MemorySound {
MemorySound {
samples,
channel_count,
sample_rate,
next_sample: 0,
should_loop: false,
}
}
/// Instead of finishing after playing all samples, start back at the
/// beginning and continue forever.
pub fn set_looping(&mut self, should_loop: bool) {
self.should_loop = should_loop;
}
}
impl Sound for MemorySound {
fn channel_count(&self) -> u16 {
self.channel_count
}
fn sample_rate(&self) -> u32 {
self.sample_rate
}
fn next_sample(&mut self) -> Result<NextSample, crate::Error> {
if let Some(sample) = self.samples.get(self.next_sample) {
self.next_sample += 1;
Ok(NextSample::Sample(*sample))
} else if self.should_loop && !self.samples.is_empty() {
self.next_sample = 0;
self.next_sample()
} else {
Ok(NextSample::Finished)
}
}
fn on_start_of_batch(&mut self) {}
}
#[cfg(test)]
#[path = "./tests/memory_sound.rs"]
mod tests;