diff --git a/src/conversions.rs b/src/conversions.rs index 2a5c77b..2dea5bc 100644 --- a/src/conversions.rs +++ b/src/conversions.rs @@ -4,34 +4,48 @@ This module contains function that will convert from one PCM format to another. This includes conversion between samples formats, channels or sample rates. */ -pub fn convert_samples_rate(input: &[T], from: ::SamplesRate, to: ::SamplesRate) -> Vec - where T: Copy +use samples_formats::Sample; + +/// Converts between samples rates while preserving the pitch. +pub fn convert_samples_rate(input: &[T], from: ::SamplesRate, to: ::SamplesRate, + channels: ::ChannelsCount) -> Vec + where T: Sample { let from = from.0; let to = to.0; // if `from` is a multiple of `to` (for example `from` is 44100 and `to` is 22050), // then we simply skip some samples - if from % to == 0 { + if from % to == 0 { let mut result = Vec::new(); - for element in input.chunks((from / to) as uint) { - result.push(element[0]); + for element in input.chunks(channels as uint * (from / to) as uint) { + for i in range(0, channels) { + result.push(element[i as uint]); + } } return result; } - // if `to` is a multiple of `from` (for example `to` is 44100 and `from` is 22050) - // TODO: dumb algorithm - // FIXME: doesn't take channels into account - if to % from == 0 { + // if `to` is twice `from` (for example `to` is 44100 and `from` is 22050) + // TODO: more generic + if to == from * 2 { let mut result = Vec::new(); - for element in input.windows(2) { - for _ in range(0, (to / from) as uint) { - result.push(element[0]); + let mut previous: Option> = None; + for element in input.chunks(channels as uint) { + if let Some(previous) = previous.take() { + for (prev, curr) in previous.into_iter().zip(element.iter()) { + result.push(prev.interpolate(*curr)); + } + for curr in element.iter() { + result.push(*curr); + } + } else { + for e in element.iter() { + result.push(*e); + } } - } - for _ in range(0, (to / from) as uint) { - result.push(*input.last().unwrap()); + + previous = Some(element.to_vec()); } return result; } @@ -50,7 +64,7 @@ pub fn convert_samples_rate(input: &[T], from: ::SamplesRate, to: ::SamplesRa /// /// Panics if `from` is 0, `to` is 0, or if the data length is not a multiple of `from`. pub fn convert_channels(input: &[T], from: ::ChannelsCount, to: ::ChannelsCount) -> Vec - where T: Copy + where T: Sample { assert!(from != 0); assert!(to != 0); @@ -78,6 +92,7 @@ pub fn convert_channels(input: &[T], from: ::ChannelsCount, to: ::ChannelsCou #[cfg(test)] mod test { use super::convert_channels; + use super::convert_samples_rate; #[test] fn remove_channels() { @@ -102,4 +117,20 @@ mod test { fn convert_channels_wrong_data_len() { convert_channels(&[1u16, 2, 3], 2, 1); } + + #[test] + fn half_samples_rate() { + let result = convert_samples_rate(&[1u16, 16, 2, 17, 3, 18, 4, 19], + ::SamplesRate(44100), ::SamplesRate(22050), 2); + + assert_eq!(result.as_slice(), [1, 16, 3, 18]); + } + + #[test] + fn double_samples_rate() { + let result = convert_samples_rate(&[2u16, 16, 4, 18, 6, 20, 8, 22], + ::SamplesRate(22050), ::SamplesRate(44100), 2); + + assert_eq!(result.as_slice(), [2, 16, 3, 17, 4, 18, 5, 19, 6, 20, 7, 21, 8, 22]); + } } diff --git a/src/lib.rs b/src/lib.rs index b760ea6..802c342 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -245,7 +245,8 @@ impl<'a, T> Drop for Buffer<'a, T> where T: Sample { let buffer = if conversion.from_sample_rate != conversion.to_sample_rate { conversions::convert_samples_rate(buffer.as_slice(), conversion.from_sample_rate, - conversion.to_sample_rate) + conversion.to_sample_rate, + conversion.to_channels) } else { buffer }; diff --git a/src/samples_formats.rs b/src/samples_formats.rs index dd4c6f1..9319186 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -25,9 +25,12 @@ impl SampleFormat { /// Trait for containers that contain PCM data. #[unstable = "Will be rewritten with associated types"] -pub trait Sample: Copy { +pub trait Sample: Copy + Clone { fn get_format(Option) -> SampleFormat; + /// Returns `(self + other) / 2`. + fn interpolate(self, other: Self) -> Self; + /// Turns the data into samples of type `I16`. fn to_vec_i16(&[Self]) -> Cow, [i16]>; /// Turns the data into samples of type `U16`. @@ -41,6 +44,10 @@ impl Sample for u16 { SampleFormat::U16 } + fn interpolate(self, other: u16) -> u16 { + (self + other) / 2 + } + fn to_vec_i16(input: &[u16]) -> Cow, [i16]> { Cow::Owned(input.iter().map(|&value| { if value >= 32768 { @@ -65,6 +72,10 @@ impl Sample for i16 { SampleFormat::I16 } + fn interpolate(self, other: i16) -> i16 { + (self + other) / 2 + } + fn to_vec_i16(input: &[i16]) -> Cow, [i16]> { Cow::Borrowed(input) } @@ -95,6 +106,10 @@ impl Sample for f32 { SampleFormat::F32 } + fn interpolate(self, other: f32) -> f32 { + (self + other) / 2.0 + } + fn to_vec_i16(input: &[f32]) -> Cow, [i16]> { unimplemented!() }