mirror of
https://github.com/RustAudio/rodio
synced 2024-11-10 14:14:21 +00:00
Reorganize the conversions module
This commit is contained in:
parent
803c9ac192
commit
cefae3aa51
11 changed files with 534 additions and 521 deletions
38
src/conversions/amplifier.rs
Normal file
38
src/conversions/amplifier.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use conversions::Sample;
|
||||||
|
|
||||||
|
pub struct AmplifierIterator<I> where I: Iterator {
|
||||||
|
input: I,
|
||||||
|
amplication: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> AmplifierIterator<I> where I: Iterator {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(input: I, amplication: f32) -> AmplifierIterator<I> {
|
||||||
|
AmplifierIterator {
|
||||||
|
input: input,
|
||||||
|
amplication: amplication,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_amplification(&mut self, new_value: f32) {
|
||||||
|
self.amplication = new_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Iterator for AmplifierIterator<I> where I: Iterator, I::Item: Sample {
|
||||||
|
type Item = I::Item;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<I::Item> {
|
||||||
|
self.input.next().map(|value| value.amplify(self.amplication))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.input.size_hint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ExactSizeIterator for AmplifierIterator<I>
|
||||||
|
where I: ExactSizeIterator, I::Item: Sample {}
|
104
src/conversions/channels.rs
Normal file
104
src/conversions/channels.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
use cpal;
|
||||||
|
|
||||||
|
/// Iterator that converts from a certain channels count to another.
|
||||||
|
pub struct ChannelsCountConverter<I> where I: Iterator {
|
||||||
|
input: I,
|
||||||
|
from: cpal::ChannelsCount,
|
||||||
|
to: cpal::ChannelsCount,
|
||||||
|
sample_repeat: Option<I::Item>,
|
||||||
|
next_output_sample_pos: cpal::ChannelsCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ChannelsCountConverter<I> where I: Iterator {
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// Panicks if `from` or `to` are equal to 0.
|
||||||
|
///
|
||||||
|
#[inline]
|
||||||
|
pub fn new(input: I, from: cpal::ChannelsCount, to: cpal::ChannelsCount)
|
||||||
|
-> ChannelsCountConverter<I>
|
||||||
|
{
|
||||||
|
assert!(from >= 1);
|
||||||
|
assert!(to >= 1);
|
||||||
|
|
||||||
|
ChannelsCountConverter {
|
||||||
|
input: input,
|
||||||
|
from: from,
|
||||||
|
to: to,
|
||||||
|
sample_repeat: None,
|
||||||
|
next_output_sample_pos: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Iterator for ChannelsCountConverter<I> where I: Iterator, I::Item: Clone {
|
||||||
|
type Item = I::Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<I::Item> {
|
||||||
|
let result = if self.next_output_sample_pos == self.from - 1 {
|
||||||
|
let value = self.input.next();
|
||||||
|
self.sample_repeat = value.clone();
|
||||||
|
value
|
||||||
|
} else if self.next_output_sample_pos < self.from {
|
||||||
|
self.input.next()
|
||||||
|
} else {
|
||||||
|
self.sample_repeat.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.next_output_sample_pos += 1;
|
||||||
|
|
||||||
|
if self.next_output_sample_pos == self.to {
|
||||||
|
self.next_output_sample_pos -= self.to;
|
||||||
|
|
||||||
|
if self.from > self.to {
|
||||||
|
for _ in (self.to .. self.from) {
|
||||||
|
self.input.next(); // discarding extra input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let (min, max) = self.input.size_hint();
|
||||||
|
|
||||||
|
let min = (min / self.from as usize) * self.to as usize + self.next_output_sample_pos as usize;
|
||||||
|
let max = max.map(|max| (max / self.from as usize) * self.to as usize + self.next_output_sample_pos as usize);
|
||||||
|
|
||||||
|
(min, max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ExactSizeIterator for ChannelsCountConverter<I>
|
||||||
|
where I: ExactSizeIterator, I::Item: Clone {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::ChannelsCountConverter;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remove_channels() {
|
||||||
|
let input = vec![1u16, 2, 3, 1, 2, 3];
|
||||||
|
let output = ChannelsCountConverter::new(input.into_iter(), 3, 2).collect::<Vec<_>>();
|
||||||
|
assert_eq!(output, [1, 2, 1, 2]);
|
||||||
|
|
||||||
|
let input = vec![1u16, 2, 3, 4, 1, 2, 3, 4];
|
||||||
|
let output = ChannelsCountConverter::new(input.into_iter(), 4, 1).collect::<Vec<_>>();
|
||||||
|
assert_eq!(output, [1, 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_channels() {
|
||||||
|
let input = vec![1u16, 2, 1, 2];
|
||||||
|
let output = ChannelsCountConverter::new(input.into_iter(), 2, 3).collect::<Vec<_>>();
|
||||||
|
assert_eq!(output, [1, 2, 2, 1, 2, 2]);
|
||||||
|
|
||||||
|
let input = vec![1u16, 2, 1, 2];
|
||||||
|
let output = ChannelsCountConverter::new(input.into_iter(), 2, 4).collect::<Vec<_>>();
|
||||||
|
assert_eq!(output, [1, 2, 2, 2, 1, 2, 2, 2]);
|
||||||
|
}
|
||||||
|
}
|
45
src/conversions/mod.rs
Normal file
45
src/conversions/mod.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*!
|
||||||
|
This module contains function that will convert from one PCM format to another.
|
||||||
|
|
||||||
|
This includes conversion between samples formats, channels or sample rates.
|
||||||
|
|
||||||
|
*/
|
||||||
|
use std::iter;
|
||||||
|
use cpal::UnknownTypeBuffer;
|
||||||
|
|
||||||
|
pub use self::sample::Sample;
|
||||||
|
pub use self::channels::ChannelsCountConverter;
|
||||||
|
pub use self::samples_rate::SamplesRateConverter;
|
||||||
|
pub use self::amplifier::AmplifierIterator;
|
||||||
|
|
||||||
|
mod amplifier;
|
||||||
|
mod channels;
|
||||||
|
mod sample;
|
||||||
|
mod samples_rate;
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn convert_and_write<I, S>(samples: I, output: &mut UnknownTypeBuffer)
|
||||||
|
where I: Iterator<Item=S>, S: Sample
|
||||||
|
{
|
||||||
|
let samples = samples.chain(iter::repeat(Sample::zero_value()));
|
||||||
|
|
||||||
|
match output {
|
||||||
|
&mut UnknownTypeBuffer::U16(ref mut buffer) => {
|
||||||
|
for (i, o) in samples.zip(buffer.iter_mut()) {
|
||||||
|
*o = i.to_u16();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
&mut UnknownTypeBuffer::I16(ref mut buffer) => {
|
||||||
|
for (i, o) in samples.zip(buffer.iter_mut()) {
|
||||||
|
*o = i.to_i16();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
&mut UnknownTypeBuffer::F32(ref mut buffer) => {
|
||||||
|
for (i, o) in samples.zip(buffer.iter_mut()) {
|
||||||
|
*o = i.to_f32();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
199
src/conversions/sample.rs
Normal file
199
src/conversions/sample.rs
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
use cpal;
|
||||||
|
|
||||||
|
/// Trait for containers that contain PCM data.
|
||||||
|
pub trait Sample: cpal::Sample {
|
||||||
|
fn lerp(first: Self, second: Self, numerator: u32, denominator: u32) -> Self;
|
||||||
|
fn amplify(self, value: f32) -> Self;
|
||||||
|
|
||||||
|
fn zero_value() -> Self;
|
||||||
|
|
||||||
|
fn to_i16(&self) -> i16;
|
||||||
|
fn to_u16(&self) -> u16;
|
||||||
|
fn to_f32(&self) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sample for u16 {
|
||||||
|
#[inline]
|
||||||
|
fn lerp(first: u16, second: u16, numerator: u32, denominator: u32) -> u16 {
|
||||||
|
(first as u32 + (second as u32 - first as u32) * numerator / denominator) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn amplify(self, value: f32) -> u16 {
|
||||||
|
((self as f32) * value) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn zero_value() -> u16 {
|
||||||
|
32768
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_i16(&self) -> i16 {
|
||||||
|
if *self >= 32768 {
|
||||||
|
(*self - 32768) as i16
|
||||||
|
} else {
|
||||||
|
(*self as i16) - 32767 - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_u16(&self) -> u16 {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_f32(&self) -> f32 {
|
||||||
|
self.to_i16().to_f32()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sample for i16 {
|
||||||
|
#[inline]
|
||||||
|
fn lerp(first: i16, second: i16, numerator: u32, denominator: u32) -> i16 {
|
||||||
|
(first as i32 + (second as i32 - first as i32) * numerator as i32 / denominator as i32) as i16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn amplify(self, value: f32) -> i16 {
|
||||||
|
((self as f32) * value) as i16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn zero_value() -> i16 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_i16(&self) -> i16 {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_u16(&self) -> u16 {
|
||||||
|
if *self < 0 {
|
||||||
|
(*self - ::std::i16::MIN) as u16
|
||||||
|
} else {
|
||||||
|
(*self as u16) + 32768
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_f32(&self) -> f32 {
|
||||||
|
if *self < 0 {
|
||||||
|
*self as f32 / -(::std::i16::MIN as f32)
|
||||||
|
} else {
|
||||||
|
*self as f32 / ::std::i16::MAX as f32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sample for f32 {
|
||||||
|
#[inline]
|
||||||
|
fn lerp(first: f32, second: f32, numerator: u32, denominator: u32) -> f32 {
|
||||||
|
first + (second - first) * numerator as f32 / denominator as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn amplify(self, value: f32) -> f32 {
|
||||||
|
self * value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn zero_value() -> f32 {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_i16(&self) -> i16 {
|
||||||
|
if *self >= 0.0 {
|
||||||
|
(*self * ::std::i16::MAX as f32) as i16
|
||||||
|
} else {
|
||||||
|
(-*self * ::std::i16::MIN as f32) as i16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_u16(&self) -> u16 {
|
||||||
|
(((*self + 1.0) * 0.5) * ::std::u16::MAX as f32).round() as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_f32(&self) -> f32 {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::Sample;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i16_to_i16() {
|
||||||
|
assert_eq!(0i16.to_i16(), 0);
|
||||||
|
assert_eq!((-467i16).to_i16(), -467);
|
||||||
|
assert_eq!(32767i16.to_i16(), 32767);
|
||||||
|
assert_eq!((-32768i16).to_i16(), -32768);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i16_to_u16() {
|
||||||
|
assert_eq!(0i16.to_u16(), 32768);
|
||||||
|
assert_eq!((-16384i16).to_u16(), 16384);
|
||||||
|
assert_eq!(32767i16.to_u16(), 65535);
|
||||||
|
assert_eq!((-32768i16).to_u16(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i16_to_f32() {
|
||||||
|
assert_eq!(0i16.to_f32(), 0.0);
|
||||||
|
assert_eq!((-16384i16).to_f32(), -0.5);
|
||||||
|
assert_eq!(32767i16.to_f32(), 1.0);
|
||||||
|
assert_eq!((-32768i16).to_f32(), -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn u16_to_i16() {
|
||||||
|
assert_eq!(32768u16.to_i16(), 0);
|
||||||
|
assert_eq!(16384u16.to_i16(), -16384);
|
||||||
|
assert_eq!(65535u16.to_i16(), 32767);
|
||||||
|
assert_eq!(0u16.to_i16(), -32768);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn u16_to_u16() {
|
||||||
|
assert_eq!(0u16.to_u16(), 0);
|
||||||
|
assert_eq!(467u16.to_u16(), 467);
|
||||||
|
assert_eq!(32767u16.to_u16(), 32767);
|
||||||
|
assert_eq!(65535u16.to_u16(), 65535);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn u16_to_f32() {
|
||||||
|
assert_eq!(0u16.to_f32(), -1.0);
|
||||||
|
assert_eq!(32768u16.to_f32(), 0.0);
|
||||||
|
assert_eq!(65535u16.to_f32(), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f32_to_i16() {
|
||||||
|
assert_eq!(0.0f32.to_i16(), 0);
|
||||||
|
assert_eq!((-0.5f32).to_i16(), ::std::i16::MIN / 2);
|
||||||
|
assert_eq!(1.0f32.to_i16(), ::std::i16::MAX);
|
||||||
|
assert_eq!((-1.0f32).to_i16(), ::std::i16::MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f32_to_u16() {
|
||||||
|
assert_eq!((-1.0f32).to_u16(), 0);
|
||||||
|
assert_eq!(0.0f32.to_u16(), 32768);
|
||||||
|
assert_eq!(1.0f32.to_u16(), 65535);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f32_to_f32() {
|
||||||
|
assert_eq!(0.1f32.to_f32(), 0.1);
|
||||||
|
assert_eq!((-0.7f32).to_f32(), -0.7);
|
||||||
|
assert_eq!(1.0f32.to_f32(), 1.0);
|
||||||
|
}
|
||||||
|
}
|
145
src/conversions/samples_rate.rs
Normal file
145
src/conversions/samples_rate.rs
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
use cpal;
|
||||||
|
use conversions::Sample;
|
||||||
|
|
||||||
|
/// Iterator that converts from a certain samples rate to another.
|
||||||
|
pub struct SamplesRateConverter<I> where I: Iterator {
|
||||||
|
/// The iterator that gives us samples.
|
||||||
|
input: I,
|
||||||
|
/// We convert chunks of `from` samples into chunks of `to` samples.
|
||||||
|
from: u32,
|
||||||
|
/// We convert chunks of `from` samples into chunks of `to` samples.
|
||||||
|
to: u32,
|
||||||
|
/// One sample extracted from `input`.
|
||||||
|
current_sample: Option<I::Item>,
|
||||||
|
/// Position of `current_sample` modulo `from`.
|
||||||
|
current_sample_pos_in_chunk: u32,
|
||||||
|
/// The sample right after `current_sample`, extracted from `input`.
|
||||||
|
next_sample: Option<I::Item>,
|
||||||
|
/// The position of the next sample that the iterator should return, modulo `to`.
|
||||||
|
/// This counter is incremented (modulo `to`) every time the iterator is called.
|
||||||
|
next_output_sample_pos_in_chunk: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> SamplesRateConverter<I> where I: Iterator {
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Panic
|
||||||
|
///
|
||||||
|
/// Panicks if `from` or `to` are equal to 0.
|
||||||
|
///
|
||||||
|
#[inline]
|
||||||
|
pub fn new(mut input: I, from: cpal::SamplesRate, to: cpal::SamplesRate)
|
||||||
|
-> SamplesRateConverter<I>
|
||||||
|
{
|
||||||
|
let from = from.0;
|
||||||
|
let to = to.0;
|
||||||
|
|
||||||
|
assert!(from >= 1);
|
||||||
|
assert!(to >= 1);
|
||||||
|
|
||||||
|
// finding greatest common divisor
|
||||||
|
let gcd = {
|
||||||
|
#[inline]
|
||||||
|
fn gcd(a: u32, b: u32) -> u32 {
|
||||||
|
if b == 0 {
|
||||||
|
a
|
||||||
|
} else {
|
||||||
|
gcd(b, a % b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gcd(from, to)
|
||||||
|
};
|
||||||
|
|
||||||
|
let first_sample = input.next();
|
||||||
|
let second_sample = input.next();
|
||||||
|
|
||||||
|
SamplesRateConverter {
|
||||||
|
input: input,
|
||||||
|
from: from / gcd,
|
||||||
|
to: to / gcd,
|
||||||
|
current_sample_pos_in_chunk: 0,
|
||||||
|
next_output_sample_pos_in_chunk: 0,
|
||||||
|
current_sample: first_sample,
|
||||||
|
next_sample: second_sample,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Iterator for SamplesRateConverter<I> where I: Iterator, I::Item: Sample + Clone {
|
||||||
|
type Item = I::Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<I::Item> {
|
||||||
|
// The sample we are going to return from this function will be a linear interpolation
|
||||||
|
// between `self.current_sample` and `self.next_sample`.
|
||||||
|
|
||||||
|
// Finding the position of the first sample of the linear interpolation.
|
||||||
|
let req_left_sample = (self.from * self.next_output_sample_pos_in_chunk / self.to) %
|
||||||
|
self.from;
|
||||||
|
|
||||||
|
// Advancing `self.current_sample`, `self.next_sample` and
|
||||||
|
// `self.current_sample_pos_in_chunk` until the latter variable matches `req_left_sample`.
|
||||||
|
while self.current_sample_pos_in_chunk != req_left_sample {
|
||||||
|
self.current_sample_pos_in_chunk += 1;
|
||||||
|
self.current_sample_pos_in_chunk %= self.from;
|
||||||
|
self.current_sample = self.next_sample;
|
||||||
|
self.next_sample = self.input.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doing the linear interpolation. We handle a possible end of stream here.
|
||||||
|
let result = match (self.current_sample, self.next_sample) {
|
||||||
|
(Some(ref cur), Some(ref next)) => {
|
||||||
|
let numerator = (self.from * self.next_output_sample_pos_in_chunk) % self.to;
|
||||||
|
Sample::lerp(cur.clone(), next.clone(), numerator, self.to)
|
||||||
|
},
|
||||||
|
|
||||||
|
(Some(ref cur), None) if self.next_output_sample_pos_in_chunk == 0 => {
|
||||||
|
cur.clone()
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Incrementing the counter for the next iteration.
|
||||||
|
self.next_output_sample_pos_in_chunk += 1;
|
||||||
|
self.next_output_sample_pos_in_chunk %= self.to;
|
||||||
|
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let (min, max) = self.input.size_hint();
|
||||||
|
|
||||||
|
// TODO: inexact?
|
||||||
|
let min = (min / self.from as usize) * self.to as usize;
|
||||||
|
let max = max.map(|max| (max / self.from as usize) * self.to as usize);
|
||||||
|
|
||||||
|
(min, max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ExactSizeIterator for SamplesRateConverter<I>
|
||||||
|
where I: ExactSizeIterator, I::Item: Sample + Clone {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
/*use super::SamplesRateConverter;
|
||||||
|
|
||||||
|
|
||||||
|
#[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, [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, [2, 16, 3, 17, 4, 18, 5, 19, 6, 20, 7, 21, 8, 22]);
|
||||||
|
}*/
|
||||||
|
}
|
|
@ -1,513 +0,0 @@
|
||||||
/*!
|
|
||||||
This module contains function that will convert from one PCM format to another.
|
|
||||||
|
|
||||||
This includes conversion between samples formats, channels or sample rates.
|
|
||||||
|
|
||||||
*/
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::cmp;
|
|
||||||
use std::mem;
|
|
||||||
use std::iter;
|
|
||||||
|
|
||||||
use cpal;
|
|
||||||
use cpal::UnknownTypeBuffer;
|
|
||||||
use cpal::SampleFormat;
|
|
||||||
|
|
||||||
///
|
|
||||||
pub fn convert_and_write<I, S>(mut samples: I, output: &mut UnknownTypeBuffer)
|
|
||||||
where I: Iterator<Item=S>, S: Sample
|
|
||||||
{
|
|
||||||
let samples = samples.chain(iter::repeat(Sample::zero_value()));
|
|
||||||
|
|
||||||
match output {
|
|
||||||
&mut UnknownTypeBuffer::U16(ref mut buffer) => {
|
|
||||||
for (i, o) in samples.zip(buffer.iter_mut()) {
|
|
||||||
*o = i.to_u16();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
&mut UnknownTypeBuffer::I16(ref mut buffer) => {
|
|
||||||
for (i, o) in samples.zip(buffer.iter_mut()) {
|
|
||||||
*o = i.to_i16();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
&mut UnknownTypeBuffer::F32(ref mut buffer) => {
|
|
||||||
for (i, o) in samples.zip(buffer.iter_mut()) {
|
|
||||||
*o = i.to_f32();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for containers that contain PCM data.
|
|
||||||
pub trait Sample: cpal::Sample {
|
|
||||||
fn lerp(first: Self, second: Self, numerator: u32, denominator: u32) -> Self;
|
|
||||||
fn amplify(self, value: f32) -> Self;
|
|
||||||
|
|
||||||
fn zero_value() -> Self;
|
|
||||||
|
|
||||||
fn to_i16(&self) -> i16;
|
|
||||||
fn to_u16(&self) -> u16;
|
|
||||||
fn to_f32(&self) -> f32;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sample for u16 {
|
|
||||||
#[inline]
|
|
||||||
fn lerp(first: u16, second: u16, numerator: u32, denominator: u32) -> u16 {
|
|
||||||
(first as u32 + (second as u32 - first as u32) * numerator / denominator) as u16
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn amplify(self, value: f32) -> u16 {
|
|
||||||
((self as f32) * value) as u16
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn zero_value() -> u16 {
|
|
||||||
32768
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_i16(&self) -> i16 {
|
|
||||||
if *self >= 32768 {
|
|
||||||
(*self - 32768) as i16
|
|
||||||
} else {
|
|
||||||
(*self as i16) - 32767 - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_u16(&self) -> u16 {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_f32(&self) -> f32 {
|
|
||||||
self.to_i16().to_f32()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sample for i16 {
|
|
||||||
#[inline]
|
|
||||||
fn lerp(first: i16, second: i16, numerator: u32, denominator: u32) -> i16 {
|
|
||||||
(first as i32 + (second as i32 - first as i32) * numerator as i32 / denominator as i32) as i16
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn amplify(self, value: f32) -> i16 {
|
|
||||||
((self as f32) * value) as i16
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn zero_value() -> i16 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_i16(&self) -> i16 {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_u16(&self) -> u16 {
|
|
||||||
if *self < 0 {
|
|
||||||
(*self - ::std::i16::MIN) as u16
|
|
||||||
} else {
|
|
||||||
(*self as u16) + 32768
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_f32(&self) -> f32 {
|
|
||||||
if *self < 0 {
|
|
||||||
*self as f32 / -(::std::i16::MIN as f32)
|
|
||||||
} else {
|
|
||||||
*self as f32 / ::std::i16::MAX as f32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sample for f32 {
|
|
||||||
#[inline]
|
|
||||||
fn lerp(first: f32, second: f32, numerator: u32, denominator: u32) -> f32 {
|
|
||||||
first + (second - first) * numerator as f32 / denominator as f32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn amplify(self, value: f32) -> f32 {
|
|
||||||
self * value
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn zero_value() -> f32 {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_i16(&self) -> i16 {
|
|
||||||
if *self >= 0.0 {
|
|
||||||
(*self * ::std::i16::MAX as f32) as i16
|
|
||||||
} else {
|
|
||||||
(-*self * ::std::i16::MIN as f32) as i16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_u16(&self) -> u16 {
|
|
||||||
(((*self + 1.0) * 0.5) * ::std::u16::MAX as f32).round() as u16
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_f32(&self) -> f32 {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator that converts from a certain channels count to another.
|
|
||||||
pub struct ChannelsCountConverter<I> where I: Iterator {
|
|
||||||
input: I,
|
|
||||||
from: cpal::ChannelsCount,
|
|
||||||
to: cpal::ChannelsCount,
|
|
||||||
sample_repeat: Option<I::Item>,
|
|
||||||
next_output_sample_pos: cpal::ChannelsCount,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> ChannelsCountConverter<I> where I: Iterator {
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// # Panic
|
|
||||||
///
|
|
||||||
/// Panicks if `from` or `to` are equal to 0.
|
|
||||||
///
|
|
||||||
#[inline]
|
|
||||||
pub fn new(input: I, from: cpal::ChannelsCount, to: cpal::ChannelsCount)
|
|
||||||
-> ChannelsCountConverter<I>
|
|
||||||
{
|
|
||||||
assert!(from >= 1);
|
|
||||||
assert!(to >= 1);
|
|
||||||
|
|
||||||
ChannelsCountConverter {
|
|
||||||
input: input,
|
|
||||||
from: from,
|
|
||||||
to: to,
|
|
||||||
sample_repeat: None,
|
|
||||||
next_output_sample_pos: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for ChannelsCountConverter<I> where I: Iterator, I::Item: Clone {
|
|
||||||
type Item = I::Item;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<I::Item> {
|
|
||||||
let result = if self.next_output_sample_pos == self.from - 1 {
|
|
||||||
let value = self.input.next();
|
|
||||||
self.sample_repeat = value.clone();
|
|
||||||
value
|
|
||||||
} else if self.next_output_sample_pos < self.from {
|
|
||||||
self.input.next()
|
|
||||||
} else {
|
|
||||||
self.sample_repeat.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.next_output_sample_pos += 1;
|
|
||||||
|
|
||||||
if self.next_output_sample_pos == self.to {
|
|
||||||
self.next_output_sample_pos -= self.to;
|
|
||||||
|
|
||||||
if self.from > self.to {
|
|
||||||
for _ in (self.to .. self.from) {
|
|
||||||
self.input.next(); // discarding extra input
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
let (min, max) = self.input.size_hint();
|
|
||||||
|
|
||||||
let min = (min / self.from as usize) * self.to as usize + self.next_output_sample_pos as usize;
|
|
||||||
let max = max.map(|max| (max / self.from as usize) * self.to as usize + self.next_output_sample_pos as usize);
|
|
||||||
|
|
||||||
(min, max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> ExactSizeIterator for ChannelsCountConverter<I>
|
|
||||||
where I: ExactSizeIterator, I::Item: Clone {}
|
|
||||||
|
|
||||||
/// Iterator that converts from a certain samples rate to another.
|
|
||||||
pub struct SamplesRateConverter<I> where I: Iterator {
|
|
||||||
/// The iterator that gives us samples.
|
|
||||||
input: I,
|
|
||||||
/// We convert chunks of `from` samples into chunks of `to` samples.
|
|
||||||
from: u32,
|
|
||||||
/// We convert chunks of `from` samples into chunks of `to` samples.
|
|
||||||
to: u32,
|
|
||||||
/// One sample extracted from `input`.
|
|
||||||
current_sample: Option<I::Item>,
|
|
||||||
/// Position of `current_sample` modulo `from`.
|
|
||||||
current_sample_pos_in_chunk: u32,
|
|
||||||
/// The sample right after `current_sample`, extracted from `input`.
|
|
||||||
next_sample: Option<I::Item>,
|
|
||||||
/// The position of the next sample that the iterator should return, modulo `to`.
|
|
||||||
/// This counter is incremented (modulo `to`) every time the iterator is called.
|
|
||||||
next_output_sample_pos_in_chunk: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> SamplesRateConverter<I> where I: Iterator {
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// # Panic
|
|
||||||
///
|
|
||||||
/// Panicks if `from` or `to` are equal to 0.
|
|
||||||
///
|
|
||||||
#[inline]
|
|
||||||
pub fn new(mut input: I, from: cpal::SamplesRate, to: cpal::SamplesRate)
|
|
||||||
-> SamplesRateConverter<I>
|
|
||||||
{
|
|
||||||
let from = from.0;
|
|
||||||
let to = to.0;
|
|
||||||
|
|
||||||
assert!(from >= 1);
|
|
||||||
assert!(to >= 1);
|
|
||||||
|
|
||||||
// finding greatest common divisor
|
|
||||||
let gcd = {
|
|
||||||
#[inline]
|
|
||||||
fn gcd(a: u32, b: u32) -> u32 {
|
|
||||||
if b == 0 {
|
|
||||||
a
|
|
||||||
} else {
|
|
||||||
gcd(b, a % b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gcd(from, to)
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_sample = input.next();
|
|
||||||
let second_sample = input.next();
|
|
||||||
|
|
||||||
SamplesRateConverter {
|
|
||||||
input: input,
|
|
||||||
from: from / gcd,
|
|
||||||
to: to / gcd,
|
|
||||||
current_sample_pos_in_chunk: 0,
|
|
||||||
next_output_sample_pos_in_chunk: 0,
|
|
||||||
current_sample: first_sample,
|
|
||||||
next_sample: second_sample,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for SamplesRateConverter<I> where I: Iterator, I::Item: Sample + Clone {
|
|
||||||
type Item = I::Item;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<I::Item> {
|
|
||||||
// The sample we are going to return from this function will be a linear interpolation
|
|
||||||
// between `self.current_sample` and `self.next_sample`.
|
|
||||||
|
|
||||||
// Finding the position of the first sample of the linear interpolation.
|
|
||||||
let req_left_sample = (self.from * self.next_output_sample_pos_in_chunk / self.to) %
|
|
||||||
self.from;
|
|
||||||
|
|
||||||
// Advancing `self.current_sample`, `self.next_sample` and
|
|
||||||
// `self.current_sample_pos_in_chunk` until the latter variable matches `req_left_sample`.
|
|
||||||
while self.current_sample_pos_in_chunk != req_left_sample {
|
|
||||||
self.current_sample_pos_in_chunk += 1;
|
|
||||||
self.current_sample_pos_in_chunk %= self.from;
|
|
||||||
self.current_sample = self.next_sample;
|
|
||||||
self.next_sample = self.input.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doing the linear interpolation. We handle a possible end of stream here.
|
|
||||||
let result = match (self.current_sample, self.next_sample) {
|
|
||||||
(Some(ref cur), Some(ref next)) => {
|
|
||||||
let numerator = (self.from * self.next_output_sample_pos_in_chunk) % self.to;
|
|
||||||
Sample::lerp(cur.clone(), next.clone(), numerator, self.to)
|
|
||||||
},
|
|
||||||
|
|
||||||
(Some(ref cur), None) if self.next_output_sample_pos_in_chunk == 0 => {
|
|
||||||
cur.clone()
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Incrementing the counter for the next iteration.
|
|
||||||
self.next_output_sample_pos_in_chunk += 1;
|
|
||||||
self.next_output_sample_pos_in_chunk %= self.to;
|
|
||||||
|
|
||||||
Some(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
let (min, max) = self.input.size_hint();
|
|
||||||
|
|
||||||
// TODO: inexact?
|
|
||||||
let min = (min / self.from as usize) * self.to as usize;
|
|
||||||
let max = max.map(|max| (max / self.from as usize) * self.to as usize);
|
|
||||||
|
|
||||||
(min, max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> ExactSizeIterator for SamplesRateConverter<I>
|
|
||||||
where I: ExactSizeIterator, I::Item: Sample + Clone {}
|
|
||||||
|
|
||||||
pub struct AmplifierIterator<I> where I: Iterator {
|
|
||||||
input: I,
|
|
||||||
amplication: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> AmplifierIterator<I> where I: Iterator {
|
|
||||||
#[inline]
|
|
||||||
pub fn new(input: I, amplication: f32) -> AmplifierIterator<I> {
|
|
||||||
AmplifierIterator {
|
|
||||||
input: input,
|
|
||||||
amplication: amplication,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_amplification(&mut self, new_value: f32) {
|
|
||||||
self.amplication = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Iterator for AmplifierIterator<I> where I: Iterator, I::Item: Sample {
|
|
||||||
type Item = I::Item;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<I::Item> {
|
|
||||||
self.input.next().map(|value| value.amplify(self.amplication))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
self.input.size_hint()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> ExactSizeIterator for AmplifierIterator<I>
|
|
||||||
where I: ExactSizeIterator, I::Item: Sample {}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::Sample;
|
|
||||||
use super::ChannelsCountConverter;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_channels() {
|
|
||||||
let input = vec![1u16, 2, 3, 1, 2, 3];
|
|
||||||
let output = ChannelsCountConverter::new(input.into_iter(), 3, 2).collect::<Vec<_>>();
|
|
||||||
assert_eq!(output, [1, 2, 1, 2]);
|
|
||||||
|
|
||||||
let input = vec![1u16, 2, 3, 4, 1, 2, 3, 4];
|
|
||||||
let output = ChannelsCountConverter::new(input.into_iter(), 4, 1).collect::<Vec<_>>();
|
|
||||||
assert_eq!(output, [1, 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_channels() {
|
|
||||||
let input = vec![1u16, 2, 1, 2];
|
|
||||||
let output = ChannelsCountConverter::new(input.into_iter(), 2, 3).collect::<Vec<_>>();
|
|
||||||
assert_eq!(output, [1, 2, 2, 1, 2, 2]);
|
|
||||||
|
|
||||||
let input = vec![1u16, 2, 1, 2];
|
|
||||||
let output = ChannelsCountConverter::new(input.into_iter(), 2, 4).collect::<Vec<_>>();
|
|
||||||
assert_eq!(output, [1, 2, 2, 2, 1, 2, 2, 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[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, [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, [2, 16, 3, 17, 4, 18, 5, 19, 6, 20, 7, 21, 8, 22]);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i16_to_i16() {
|
|
||||||
assert_eq!(0i16.to_i16(), 0);
|
|
||||||
assert_eq!((-467i16).to_i16(), -467);
|
|
||||||
assert_eq!(32767i16.to_i16(), 32767);
|
|
||||||
assert_eq!((-32768i16).to_i16(), -32768);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i16_to_u16() {
|
|
||||||
assert_eq!(0i16.to_u16(), 32768);
|
|
||||||
assert_eq!((-16384i16).to_u16(), 16384);
|
|
||||||
assert_eq!(32767i16.to_u16(), 65535);
|
|
||||||
assert_eq!((-32768i16).to_u16(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i16_to_f32() {
|
|
||||||
assert_eq!(0i16.to_f32(), 0.0);
|
|
||||||
assert_eq!((-16384i16).to_f32(), -0.5);
|
|
||||||
assert_eq!(32767i16.to_f32(), 1.0);
|
|
||||||
assert_eq!((-32768i16).to_f32(), -1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn u16_to_i16() {
|
|
||||||
assert_eq!(32768u16.to_i16(), 0);
|
|
||||||
assert_eq!(16384u16.to_i16(), -16384);
|
|
||||||
assert_eq!(65535u16.to_i16(), 32767);
|
|
||||||
assert_eq!(0u16.to_i16(), -32768);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn u16_to_u16() {
|
|
||||||
assert_eq!(0u16.to_u16(), 0);
|
|
||||||
assert_eq!(467u16.to_u16(), 467);
|
|
||||||
assert_eq!(32767u16.to_u16(), 32767);
|
|
||||||
assert_eq!(65535u16.to_u16(), 65535);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn u16_to_f32() {
|
|
||||||
assert_eq!(0u16.to_f32(), -1.0);
|
|
||||||
assert_eq!(32768u16.to_f32(), 0.0);
|
|
||||||
assert_eq!(65535u16.to_f32(), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn f32_to_i16() {
|
|
||||||
assert_eq!(0.0f32.to_i16(), 0);
|
|
||||||
assert_eq!((-0.5f32).to_i16(), ::std::i16::MIN / 2);
|
|
||||||
assert_eq!(1.0f32.to_i16(), ::std::i16::MAX);
|
|
||||||
assert_eq!((-1.0f32).to_i16(), ::std::i16::MIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn f32_to_u16() {
|
|
||||||
assert_eq!((-1.0f32).to_u16(), 0);
|
|
||||||
assert_eq!(0.0f32.to_u16(), 32768);
|
|
||||||
assert_eq!(1.0f32.to_u16(), 65535);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn f32_to_f32() {
|
|
||||||
assert_eq!(0.1f32.to_f32(), 0.1);
|
|
||||||
assert_eq!((-0.7f32).to_f32(), -0.7);
|
|
||||||
assert_eq!(1.0f32.to_f32(), 1.0);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,6 @@
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
use cpal::Endpoint;
|
use cpal::Endpoint;
|
||||||
use cpal::Voice;
|
|
||||||
|
|
||||||
mod conversions;
|
|
||||||
mod vorbis;
|
mod vorbis;
|
||||||
mod wav;
|
mod wav;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
use std::mem;
|
|
||||||
use super::Decoder;
|
use super::Decoder;
|
||||||
use super::conversions;
|
use conversions;
|
||||||
|
|
||||||
use cpal::{self, Endpoint, Voice};
|
use cpal::{self, Endpoint, Voice};
|
||||||
use vorbis;
|
use vorbis;
|
||||||
|
|
|
@ -2,11 +2,10 @@ use std::io::{Read, Seek, SeekFrom};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use super::Decoder;
|
use super::Decoder;
|
||||||
use super::conversions;
|
use conversions;
|
||||||
|
|
||||||
use cpal::{self, Endpoint, Voice};
|
use cpal::{self, Endpoint, Voice};
|
||||||
use hound::WavReader;
|
use hound::WavReader;
|
||||||
use hound::WavSpec;
|
|
||||||
|
|
||||||
pub struct WavDecoder {
|
pub struct WavDecoder {
|
||||||
reader: conversions::AmplifierIterator<Box<Iterator<Item=i16> + Send>>,
|
reader: conversions::AmplifierIterator<Box<Iterator<Item=i16> + Send>>,
|
||||||
|
|
|
@ -6,7 +6,6 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use cpal::Endpoint;
|
use cpal::Endpoint;
|
||||||
use cpal::Voice;
|
|
||||||
use decoder;
|
use decoder;
|
||||||
use decoder::Decoder;
|
use decoder::Decoder;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub use cpal::{Endpoint, get_endpoints_list, get_default_endpoint};
|
||||||
|
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
|
mod conversions;
|
||||||
mod decoder;
|
mod decoder;
|
||||||
mod engine;
|
mod engine;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue