use {Step, Velocity, MIN_STEP, MAX_STEP};
use audio::Audio;
use pitch;
#[derive(Clone, Debug, PartialEq)]
pub struct Map<A> {
pub pairs: Vec<SampleOverRange<A>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Sample<A> {
pub base_hz: pitch::Hz,
pub base_vel: Velocity,
pub audio: A,
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct StepVelRange {
pub step: Range<Step>,
pub vel: Range<Velocity>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SampleOverRange<A> {
pub range: StepVelRange,
pub sample: Sample<A>,
}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Range<T> {
pub min: T,
pub max: T,
}
impl Range<Step> {
pub fn is_over(&self, step: Step) -> bool {
self.min <= step && step <= self.max
}
}
impl Range<Velocity> {
pub fn is_over(&self, vel: Velocity) -> bool {
self.min <= vel && vel <= self.max
}
}
impl<A> Sample<A> {
pub fn new(base_hz: pitch::Hz, base_vel: Velocity, audio: A) -> Self {
Sample {
base_hz: base_hz,
base_vel: base_vel,
audio: audio,
}
}
pub fn map_audio<F, B>(self, map: F) -> Sample<B>
where F: FnOnce(A) -> B,
{
let Sample { base_hz, base_vel, audio } = self;
Sample {
base_hz: base_hz,
base_vel: base_vel,
audio: map(audio),
}
}
}
impl<A> Map<A>
where A: Audio,
{
pub fn empty() -> Self {
Map { pairs: vec![] }
}
pub fn from_sequential_steps<I>(mappings: I) -> Self
where I: IntoIterator<Item=(Step, Velocity, Sample<A>)>,
{
let (mut last_step, mut last_vel) = (0, 1.0);
let pairs = mappings.into_iter().map(|(step, vel, sample)| {
let range = StepVelRange {
step: Range { min: last_step, max: step },
vel: Range { min: last_vel, max: vel },
};
last_step = step;
last_vel = vel;
SampleOverRange { range: range, sample: sample }
}).collect();
Map { pairs: pairs }
}
pub fn from_single_sample(sample: Sample<A>) -> Self {
let range = StepVelRange {
step: Range { min: MIN_STEP, max: MAX_STEP },
vel: Range { min: 0.0, max: 1.0 },
};
let pairs = vec![SampleOverRange { range: range, sample: sample }];
Map { pairs: pairs }
}
pub fn insert(&mut self, range: StepVelRange, sample: Sample<A>) {
for i in 0..self.pairs.len() {
if self.pairs[i].range > range {
self.pairs.insert(i, SampleOverRange { range: range, sample: sample });
return;
}
}
self.pairs.push(SampleOverRange { range: range, sample: sample });
}
pub fn sample(&self, hz: pitch::Hz, vel: Velocity) -> Option<Sample<A>> {
let step = hz.step().round() as Step;
for &SampleOverRange { ref range, ref sample } in &self.pairs {
if range.step.is_over(step) && range.vel.is_over(vel) {
return Some(sample.clone());
}
}
None
}
}
#[cfg(feature="wav")]
pub mod wav {
use audio;
use map;
use pitch;
use sample;
use std;
pub type Sample<F> = super::Sample<std::sync::Arc<audio::wav::Audio<F>>>;
impl<F> Sample<F>
where F: sample::Frame,
F::Sample: sample::Duplex<f64> + sample::Duplex<i32>,
Box<[F::Sample]>: sample::ToBoxedFrameSlice<F>,
{
pub fn from_wav_file<P>(path: P, target_sample_hz: f64) -> Result<Self, audio::wav::Error>
where P: AsRef<std::path::Path>,
{
let path = path.as_ref();
const DEFAULT_LETTER_OCTAVE: pitch::LetterOctave = pitch::LetterOctave(pitch::Letter::C, 1);
let base_letter_octave = read_base_letter_octave(path).unwrap_or(DEFAULT_LETTER_OCTAVE);
let base_hz = base_letter_octave.to_hz();
let base_vel = 1.0;
let audio = std::sync::Arc::new(try!(audio::wav::Audio::from_file(path, target_sample_hz)));
Ok(map::Sample::new(base_hz, base_vel, audio))
}
}
fn read_base_letter_octave(path: &std::path::Path) -> Option<pitch::LetterOctave> {
use pitch::Letter::*;
use std::ascii::AsciiExt;
let s = path.to_str().map_or("".into(), |s| s.to_ascii_lowercase());
let contains_letter = |letter: &str| -> Option<pitch::LetterOctave> {
for i in -8i8..24 {
let pattern = format!("{}{}", letter, i);
if s.contains(&pattern) {
let letter = match letter {
"c" => C,
"c#" | "csh" => Csh,
"d" => D,
"d#" | "dsh" => Dsh,
"e" => E,
"f" => F,
"f#" | "fsh" => Fsh,
"g" => G,
"g#" | "gsh" => Gsh,
"a" => A,
"a#" | "ash" => Ash,
"b" => B,
_ => unreachable!(),
};
return Some(pitch::LetterOctave(letter, i as pitch::Octave));
}
}
None
};
let list = [
"c", "c#", "csh", "d", "d#", "dsh", "e", "f", "f#", "fsh", "g", "g#", "gsh",
"a", "a#", "ash", "b",
];
for letter in &list[..] {
if let Some(letter_octave) = contains_letter(letter) {
return Some(letter_octave);
}
}
None
}
}