use super::*; /// A sound sample. #[derive(Default, Debug)] pub struct Sample { pub name: String, pub start: usize, pub end: usize, pub channels: Vec>, pub rate: Option, } impl Sample { pub fn from_edn <'e> (dir: &str, args: &[Edn<'e>]) -> Usually<(Option, Arc>)> { let mut name = String::new(); let mut file = String::new(); let mut midi = None; let mut start = 0usize; edn!(edn in args { Edn::Map(map) => { if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { name = String::from(*n); } if let Some(Edn::Str(f)) = map.get(&Edn::Key(":file")) { file = String::from(*f); } if let Some(Edn::Int(i)) = map.get(&Edn::Key(":start")) { start = *i as usize; } if let Some(Edn::Int(m)) = map.get(&Edn::Key(":midi")) { midi = Some(u7::from(*m as u8)); } }, _ => panic!("unexpected in sample {name}"), }); let (end, data) = read_sample_data(&format!("{dir}/{file}"))?; Ok((midi, Arc::new(RwLock::new(Self::new(&name, start, end, data))))) } pub fn new (name: &str, start: usize, end: usize, channels: Vec>) -> Self { Self { name: name.to_string(), start, end, channels, rate: None } } pub fn play (sample: &Arc>, after: usize, velocity: &u7) -> Voice { Voice { sample: sample.clone(), after, position: sample.read().unwrap().start, velocity: velocity.as_int() as f32 / 127.0, } } } /// Load sample from WAV and assign to MIDI note. #[macro_export] macro_rules! sample { ($note:expr, $name:expr, $src:expr) => {{ let (end, data) = read_sample_data($src)?; ( u7::from_int_lossy($note).into(), Sample::new($name, 0, end, data).into() ) }}; } /// Read WAV from file pub fn read_sample_data (src: &str) -> Usually<(usize, Vec>)> { let mut channels: Vec> = vec![]; for channel in wavers::Wav::from_path(src)?.channels() { channels.push(channel); } let mut end = 0; let mut data: Vec> = vec![]; for samples in channels.iter() { let channel = Vec::from(samples.as_ref()); end = end.max(channel.len()); data.push(channel); } Ok((end, data)) }