Cleanup and add tests for convert_samples_rate
This commit is contained in:
parent
4239fae948
commit
aa83f64443
|
@ -4,8 +4,12 @@ This module contains function that will convert from one PCM format to another.
|
||||||
This includes conversion between samples formats, channels or sample rates.
|
This includes conversion between samples formats, channels or sample rates.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
pub fn convert_samples_rate<T>(input: &[T], from: ::SamplesRate, to: ::SamplesRate) -> Vec<T>
|
use samples_formats::Sample;
|
||||||
where T: Copy
|
|
||||||
|
/// Converts between samples rates while preserving the pitch.
|
||||||
|
pub fn convert_samples_rate<T>(input: &[T], from: ::SamplesRate, to: ::SamplesRate,
|
||||||
|
channels: ::ChannelsCount) -> Vec<T>
|
||||||
|
where T: Sample
|
||||||
{
|
{
|
||||||
let from = from.0;
|
let from = from.0;
|
||||||
let to = to.0;
|
let to = to.0;
|
||||||
|
@ -14,24 +18,34 @@ pub fn convert_samples_rate<T>(input: &[T], from: ::SamplesRate, to: ::SamplesRa
|
||||||
// then we simply skip some samples
|
// then we simply skip some samples
|
||||||
if from % to == 0 {
|
if from % to == 0 {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for element in input.chunks((from / to) as uint) {
|
for element in input.chunks(channels as uint * (from / to) as uint) {
|
||||||
result.push(element[0]);
|
for i in range(0, channels) {
|
||||||
|
result.push(element[i as uint]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if `to` is a multiple of `from` (for example `to` is 44100 and `from` is 22050)
|
// if `to` is twice `from` (for example `to` is 44100 and `from` is 22050)
|
||||||
// TODO: dumb algorithm
|
// TODO: more generic
|
||||||
// FIXME: doesn't take channels into account
|
if to == from * 2 {
|
||||||
if to % from == 0 {
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for element in input.windows(2) {
|
let mut previous: Option<Vec<T>> = None;
|
||||||
for _ in range(0, (to / from) as uint) {
|
for element in input.chunks(channels as uint) {
|
||||||
result.push(element[0]);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -50,7 +64,7 @@ pub fn convert_samples_rate<T>(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`.
|
/// Panics if `from` is 0, `to` is 0, or if the data length is not a multiple of `from`.
|
||||||
pub fn convert_channels<T>(input: &[T], from: ::ChannelsCount, to: ::ChannelsCount) -> Vec<T>
|
pub fn convert_channels<T>(input: &[T], from: ::ChannelsCount, to: ::ChannelsCount) -> Vec<T>
|
||||||
where T: Copy
|
where T: Sample
|
||||||
{
|
{
|
||||||
assert!(from != 0);
|
assert!(from != 0);
|
||||||
assert!(to != 0);
|
assert!(to != 0);
|
||||||
|
@ -78,6 +92,7 @@ pub fn convert_channels<T>(input: &[T], from: ::ChannelsCount, to: ::ChannelsCou
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::convert_channels;
|
use super::convert_channels;
|
||||||
|
use super::convert_samples_rate;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_channels() {
|
fn remove_channels() {
|
||||||
|
@ -102,4 +117,20 @@ mod test {
|
||||||
fn convert_channels_wrong_data_len() {
|
fn convert_channels_wrong_data_len() {
|
||||||
convert_channels(&[1u16, 2, 3], 2, 1);
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
let buffer = if conversion.from_sample_rate != conversion.to_sample_rate {
|
||||||
conversions::convert_samples_rate(buffer.as_slice(), conversion.from_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 {
|
} else {
|
||||||
buffer
|
buffer
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,9 +25,12 @@ impl SampleFormat {
|
||||||
|
|
||||||
/// Trait for containers that contain PCM data.
|
/// Trait for containers that contain PCM data.
|
||||||
#[unstable = "Will be rewritten with associated types"]
|
#[unstable = "Will be rewritten with associated types"]
|
||||||
pub trait Sample: Copy {
|
pub trait Sample: Copy + Clone {
|
||||||
fn get_format(Option<Self>) -> SampleFormat;
|
fn get_format(Option<Self>) -> SampleFormat;
|
||||||
|
|
||||||
|
/// Returns `(self + other) / 2`.
|
||||||
|
fn interpolate(self, other: Self) -> Self;
|
||||||
|
|
||||||
/// Turns the data into samples of type `I16`.
|
/// Turns the data into samples of type `I16`.
|
||||||
fn to_vec_i16(&[Self]) -> Cow<Vec<i16>, [i16]>;
|
fn to_vec_i16(&[Self]) -> Cow<Vec<i16>, [i16]>;
|
||||||
/// Turns the data into samples of type `U16`.
|
/// Turns the data into samples of type `U16`.
|
||||||
|
@ -41,6 +44,10 @@ impl Sample for u16 {
|
||||||
SampleFormat::U16
|
SampleFormat::U16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn interpolate(self, other: u16) -> u16 {
|
||||||
|
(self + other) / 2
|
||||||
|
}
|
||||||
|
|
||||||
fn to_vec_i16(input: &[u16]) -> Cow<Vec<i16>, [i16]> {
|
fn to_vec_i16(input: &[u16]) -> Cow<Vec<i16>, [i16]> {
|
||||||
Cow::Owned(input.iter().map(|&value| {
|
Cow::Owned(input.iter().map(|&value| {
|
||||||
if value >= 32768 {
|
if value >= 32768 {
|
||||||
|
@ -65,6 +72,10 @@ impl Sample for i16 {
|
||||||
SampleFormat::I16
|
SampleFormat::I16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn interpolate(self, other: i16) -> i16 {
|
||||||
|
(self + other) / 2
|
||||||
|
}
|
||||||
|
|
||||||
fn to_vec_i16(input: &[i16]) -> Cow<Vec<i16>, [i16]> {
|
fn to_vec_i16(input: &[i16]) -> Cow<Vec<i16>, [i16]> {
|
||||||
Cow::Borrowed(input)
|
Cow::Borrowed(input)
|
||||||
}
|
}
|
||||||
|
@ -95,6 +106,10 @@ impl Sample for f32 {
|
||||||
SampleFormat::F32
|
SampleFormat::F32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn interpolate(self, other: f32) -> f32 {
|
||||||
|
(self + other) / 2.0
|
||||||
|
}
|
||||||
|
|
||||||
fn to_vec_i16(input: &[f32]) -> Cow<Vec<i16>, [i16]> {
|
fn to_vec_i16(input: &[f32]) -> Cow<Vec<i16>, [i16]> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue