Merge pull request #57 from tomaka/remove-conversion
Remove the conversion system
This commit is contained in:
commit
659aeba42d
|
@ -1,3 +0,0 @@
|
||||||
## License
|
|
||||||
|
|
||||||
The `music.ogg` file in this directory is under cc-by-sa.
|
|
|
@ -4,17 +4,33 @@ fn main() {
|
||||||
let mut channel = cpal::Voice::new();
|
let mut channel = cpal::Voice::new();
|
||||||
|
|
||||||
// Produce a sinusoid of maximum amplitude.
|
// Produce a sinusoid of maximum amplitude.
|
||||||
let max = std::u16::MAX as f32;
|
|
||||||
let mut data_source = (0u64..).map(|t| t as f32 * 0.03)
|
let mut data_source = (0u64..).map(|t| t as f32 * 0.03)
|
||||||
.map(|t| ((t.sin() * 0.5 + 0.5) * max) as u16);
|
.map(|t| t.sin());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
{
|
match channel.append_data(32768) {
|
||||||
let mut buffer = channel.append_data(1, cpal::SamplesRate(44100), 32768);
|
cpal::UnknownTypeBuffer::U16(mut buffer) => {
|
||||||
|
for (sample, value) in buffer.chunks_mut(2).zip(&mut data_source) {
|
||||||
|
let value = ((value * 0.5 + 0.5) * std::u16::MAX as f32) as u16;
|
||||||
|
sample[0] = value;
|
||||||
|
sample[1] = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
for (sample, value) in buffer.iter_mut().zip(&mut data_source) {
|
cpal::UnknownTypeBuffer::I16(mut buffer) => {
|
||||||
*sample = value;
|
for (sample, value) in buffer.chunks_mut(2).zip(&mut data_source) {
|
||||||
}
|
let value = (value * std::i16::MAX as f32) as i16;
|
||||||
|
sample[0] = value;
|
||||||
|
sample[1] = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cpal::UnknownTypeBuffer::F32(mut buffer) => {
|
||||||
|
for (sample, value) in buffer.chunks_mut(2).zip(&mut data_source) {
|
||||||
|
sample[0] = value;
|
||||||
|
sample[1] = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.play();
|
channel.play();
|
||||||
|
|
Binary file not shown.
|
@ -1,47 +0,0 @@
|
||||||
extern crate cpal;
|
|
||||||
extern crate vorbis;
|
|
||||||
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut channel = cpal::Voice::new();
|
|
||||||
channel.play();
|
|
||||||
|
|
||||||
let mut decoder = vorbis::Decoder::new(Cursor::new(&include_bytes!("music.ogg")[..]))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
'main: for packet in decoder.packets() {
|
|
||||||
let packet = packet.unwrap();
|
|
||||||
let vorbis::Packet { channels, rate, data, .. } = packet;
|
|
||||||
|
|
||||||
let mut data = &data[..];
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if data.len() == 0 {
|
|
||||||
continue 'main;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut buffer = channel.append_data(channels, cpal::SamplesRate(rate as u32),
|
|
||||||
data.len());
|
|
||||||
let mut buffer = buffer.iter_mut();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let next_sample = match data.get(0) {
|
|
||||||
Some(s) => *s,
|
|
||||||
None => continue 'main
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(output) = buffer.next() {
|
|
||||||
*output = next_sample;
|
|
||||||
data = &data[1..];
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,136 +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 samples_formats::Sample;
|
|
||||||
|
|
||||||
/// 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 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 {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
for element in input.chunks(channels as usize * (from / to) as usize) {
|
|
||||||
for i in (0 .. channels) {
|
|
||||||
result.push(element[i as usize]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
let mut previous: Option<Vec<T>> = None;
|
|
||||||
for element in input.chunks(channels as usize) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
previous = Some(element.to_vec());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts between a certain number of channels.
|
|
||||||
///
|
|
||||||
/// If the target number is inferior to the source number, additional channels are removed.
|
|
||||||
///
|
|
||||||
/// If the target number is superior to the source number, the value of channel `N` is equal
|
|
||||||
/// to the value of channel `N % source_channels`.
|
|
||||||
///
|
|
||||||
/// ## Panic
|
|
||||||
///
|
|
||||||
/// 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>
|
|
||||||
where T: Sample
|
|
||||||
{
|
|
||||||
assert!(from != 0);
|
|
||||||
assert!(to != 0);
|
|
||||||
assert!(input.len() % from as usize == 0);
|
|
||||||
|
|
||||||
let mut result = Vec::new();
|
|
||||||
|
|
||||||
for element in input.chunks(from as usize) {
|
|
||||||
// copying the common channels
|
|
||||||
for i in (0 .. ::std::cmp::min(from, to)) {
|
|
||||||
result.push(element[i as usize]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// adding extra ones
|
|
||||||
if to > from {
|
|
||||||
for i in (0 .. to - from) {
|
|
||||||
result.push(element[i as usize % element.len()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::convert_channels;
|
|
||||||
use super::convert_samples_rate;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_channels() {
|
|
||||||
let result = convert_channels(&[1u16, 2, 3, 1, 2, 3], 3, 2);
|
|
||||||
assert_eq!(result, [1, 2, 1, 2]);
|
|
||||||
|
|
||||||
let result = convert_channels(&[1u16, 2, 3, 4, 1, 2, 3, 4], 4, 1);
|
|
||||||
assert_eq!(result, [1, 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_channels() {
|
|
||||||
let result = convert_channels(&[1u16, 2, 1, 2], 2, 3);
|
|
||||||
assert_eq!(result, [1, 2, 1, 1, 2, 1]);
|
|
||||||
|
|
||||||
let result = convert_channels(&[1u16, 2, 1, 2], 2, 4);
|
|
||||||
assert_eq!(result, [1, 2, 1, 2, 1, 2, 1, 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
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, [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]);
|
|
||||||
}
|
|
||||||
}
|
|
214
src/lib.rs
214
src/lib.rs
|
@ -7,21 +7,10 @@ In order to play a sound, first you need to create a `Voice`.
|
||||||
let mut voice = cpal::Voice::new();
|
let mut voice = cpal::Voice::new();
|
||||||
```
|
```
|
||||||
|
|
||||||
Then you must send raw samples to it by calling `append_data`.
|
Then you must send raw samples to it by calling `append_data`. You must take the number of channels
|
||||||
This function takes three parameters: the number of channels, the number of samples
|
and samples rate into account when writing the data.
|
||||||
that must be played per second, and the number of samples that you have available.
|
|
||||||
|
|
||||||
You can then fill the buffer with the data.
|
TODO: add example
|
||||||
|
|
||||||
```no_run
|
|
||||||
# let mut voice = cpal::Voice::new();
|
|
||||||
let mut buffer: cpal::Buffer<f32> = voice.append_data(2, cpal::SamplesRate(44100), 1024);
|
|
||||||
|
|
||||||
// filling the buffer with 0s
|
|
||||||
for e in buffer.iter_mut() {
|
|
||||||
*e = 0.0f32;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important**: the `append_data` function can return a buffer shorter than what you requested.
|
**Important**: the `append_data` function can return a buffer shorter than what you requested.
|
||||||
This is the case if the device doesn't have enough space available. **It happens very often**,
|
This is the case if the device doesn't have enough space available. **It happens very often**,
|
||||||
|
@ -38,21 +27,11 @@ The audio device of the user will read the buffer that you sent, and play it. If
|
||||||
reaches the end of the data, it will stop playing. You must continuously fill the buffer by
|
reaches the end of the data, it will stop playing. You must continuously fill the buffer by
|
||||||
calling `append_data` repeatedly if you don't want the audio to stop playing.
|
calling `append_data` repeatedly if you don't want the audio to stop playing.
|
||||||
|
|
||||||
# Native format
|
|
||||||
|
|
||||||
Each `Voice` is bound to a specific number of channels, samples rate, and samples format.
|
|
||||||
If you call `append_data` with values different than these, then cpal will automatically perform
|
|
||||||
a conversion on your data.
|
|
||||||
|
|
||||||
If you have the possibility, you should try to match the format of the voice.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub use samples_formats::{SampleFormat, Sample};
|
pub use samples_formats::{SampleFormat, Sample};
|
||||||
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
mod conversions;
|
|
||||||
mod samples_formats;
|
mod samples_formats;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
@ -71,20 +50,6 @@ mod cpal_impl;
|
||||||
#[path="null/mod.rs"]
|
#[path="null/mod.rs"]
|
||||||
mod cpal_impl;
|
mod cpal_impl;
|
||||||
|
|
||||||
/// Controls a sound output. A typical application has one `Voice` for each sound
|
|
||||||
/// it wants to output.
|
|
||||||
///
|
|
||||||
/// A voice must be periodically filled with new data by calling `append_data`, or the sound
|
|
||||||
/// will stop playing.
|
|
||||||
///
|
|
||||||
/// Each `Voice` is bound to a specific number of channels, samples rate, and samples format,
|
|
||||||
/// which can be retreived by calling `get_channels`, `get_samples_rate` and `get_samples_format`.
|
|
||||||
/// If you call `append_data` with values different than these, then cpal will automatically
|
|
||||||
/// perform a conversion on your data.
|
|
||||||
///
|
|
||||||
/// If you have the possibility, you should try to match the format of the voice.
|
|
||||||
pub struct Voice(cpal_impl::Voice);
|
|
||||||
|
|
||||||
/// Number of channels.
|
/// Number of channels.
|
||||||
pub type ChannelsCount = u16;
|
pub type ChannelsCount = u16;
|
||||||
|
|
||||||
|
@ -100,21 +65,34 @@ pub struct SamplesRate(pub u32);
|
||||||
pub struct Buffer<'a, T: 'a> where T: Sample {
|
pub struct Buffer<'a, T: 'a> where T: Sample {
|
||||||
// also contains something, taken by `Drop`
|
// also contains something, taken by `Drop`
|
||||||
target: Option<cpal_impl::Buffer<'a, T>>,
|
target: Option<cpal_impl::Buffer<'a, T>>,
|
||||||
|
|
||||||
// if this is non-none, then the data will be written to `conversion.intermediate_buffer`
|
|
||||||
// instead of `target`, and the conversion will be done in buffer's destructor
|
|
||||||
conversion: Option<RequiredConversion<T>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RequiredConversion<T> {
|
/// This is the struct that is provided to you by cpal when you want to write samples to a buffer.
|
||||||
intermediate_buffer: Vec<T>,
|
///
|
||||||
from_sample_rate: SamplesRate,
|
/// Since the type of data is only known at runtime, you have to fill the right buffer.
|
||||||
to_sample_rate: SamplesRate,
|
pub enum UnknownTypeBuffer<'a> {
|
||||||
to_format: SampleFormat,
|
/// Samples whose format is `u16`.
|
||||||
from_channels: ChannelsCount,
|
U16(Buffer<'a, u16>),
|
||||||
to_channels: ChannelsCount,
|
/// Samples whose format is `i16`.
|
||||||
|
I16(Buffer<'a, i16>),
|
||||||
|
/// Samples whose format is `f32`.
|
||||||
|
F32(Buffer<'a, f32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Controls a sound output. A typical application has one `Voice` for each sound
|
||||||
|
/// it wants to output.
|
||||||
|
///
|
||||||
|
/// A voice must be periodically filled with new data by calling `append_data`, or the sound
|
||||||
|
/// will stop playing.
|
||||||
|
///
|
||||||
|
/// Each `Voice` is bound to a specific number of channels, samples rate, and samples format,
|
||||||
|
/// which can be retreived by calling `get_channels`, `get_samples_rate` and `get_samples_format`.
|
||||||
|
/// If you call `append_data` with values different than these, then cpal will automatically
|
||||||
|
/// perform a conversion on your data.
|
||||||
|
///
|
||||||
|
/// If you have the possibility, you should try to match the format of the voice.
|
||||||
|
pub struct Voice(cpal_impl::Voice);
|
||||||
|
|
||||||
impl Voice {
|
impl Voice {
|
||||||
/// Builds a new channel.
|
/// Builds a new channel.
|
||||||
pub fn new() -> Voice {
|
pub fn new() -> Voice {
|
||||||
|
@ -148,11 +126,11 @@ impl Voice {
|
||||||
|
|
||||||
/// Adds some PCM data to the voice's buffer.
|
/// Adds some PCM data to the voice's buffer.
|
||||||
///
|
///
|
||||||
/// This function returns a `Buffer` object that must be filled with the audio data.
|
/// This function indirectly returns a `Buffer` object that must be filled with the audio data.
|
||||||
/// The size of the buffer being returned depends on the current state of the backend
|
/// The size of the buffer being returned depends on the current state of the backend
|
||||||
/// and can't be known in advance. However it is never greater than `max_elements`.
|
/// and can't be known in advance. However it is never greater than `max_samples`.
|
||||||
///
|
///
|
||||||
/// You must fill the buffer *entirely*, so do not set `max_elements` to a value greater
|
/// You must fill the buffer *entirely*, so do not set `max_samples` to a value greater
|
||||||
/// than the amount of data available to you.
|
/// than the amount of data available to you.
|
||||||
///
|
///
|
||||||
/// Channels are interleaved. For example if you have two channels, you must write
|
/// Channels are interleaved. For example if you have two channels, you must write
|
||||||
|
@ -160,65 +138,23 @@ impl Voice {
|
||||||
/// then the second sample of the first channel, then the second sample of the second
|
/// then the second sample of the first channel, then the second sample of the second
|
||||||
/// channel, etc.
|
/// channel, etc.
|
||||||
///
|
///
|
||||||
/// ## Parameters
|
|
||||||
///
|
|
||||||
/// * `channels`: number of channels (1 for mono, 2 for stereo, etc.)
|
|
||||||
/// * `samples_rate`: number of samples that must be played by second for each channel
|
|
||||||
/// * `max_elements`: maximum size of the returned buffer
|
|
||||||
///
|
|
||||||
/// ## Panic
|
/// ## Panic
|
||||||
///
|
///
|
||||||
/// Panics if `max_elements` is 0 or is not a multiple of `channels`.
|
/// Panics if `max_samples` is 0.
|
||||||
///
|
///
|
||||||
pub fn append_data<'a, T>(&'a mut self, channels: ChannelsCount,
|
pub fn append_data(&mut self, max_samples: usize) -> UnknownTypeBuffer {
|
||||||
samples_rate: SamplesRate, max_elements: usize)
|
assert!(max_samples != 0);
|
||||||
-> Buffer<'a, T> where T: Sample + Clone
|
|
||||||
{
|
|
||||||
assert!(max_elements != 0);
|
|
||||||
assert!(max_elements % channels as usize == 0);
|
|
||||||
|
|
||||||
let target_samples_rate = self.0.get_samples_rate();
|
match self.get_samples_format() {
|
||||||
let target_channels = self.0.get_channels();
|
SampleFormat::U16 => UnknownTypeBuffer::U16(Buffer {
|
||||||
|
target: Some(self.0.append_data(max_samples))
|
||||||
let source_samples_format = Sample::get_format(None::<T>);
|
}),
|
||||||
let target_samples_format = self.0.get_samples_format();
|
SampleFormat::I16 => UnknownTypeBuffer::I16(Buffer {
|
||||||
|
target: Some(self.0.append_data(max_samples))
|
||||||
// if we need to convert the incoming data
|
}),
|
||||||
if samples_rate != target_samples_rate || channels != target_channels ||
|
SampleFormat::F32 => UnknownTypeBuffer::F32(Buffer {
|
||||||
source_samples_format != target_samples_format
|
target: Some(self.0.append_data(max_samples))
|
||||||
{
|
}),
|
||||||
let max_elements = max_elements * target_channels as usize / channels as usize;
|
|
||||||
let max_elements = max_elements * target_samples_rate.0 as usize /
|
|
||||||
samples_rate.0 as usize;
|
|
||||||
|
|
||||||
let mut target_buffer = self.0.append_data(max_elements);
|
|
||||||
|
|
||||||
// computing the length of the intermediary buffer
|
|
||||||
let intermediate_buffer_length = target_buffer.get_buffer().len();
|
|
||||||
let intermediate_buffer_length = intermediate_buffer_length * channels as usize /
|
|
||||||
target_channels as usize;
|
|
||||||
let intermediate_buffer_length = intermediate_buffer_length * samples_rate.0 as usize /
|
|
||||||
target_samples_rate.0 as usize;
|
|
||||||
let intermediate_buffer = std::iter::repeat(unsafe { std::mem::uninitialized() })
|
|
||||||
.take(intermediate_buffer_length).collect();
|
|
||||||
|
|
||||||
Buffer {
|
|
||||||
target: Some(target_buffer),
|
|
||||||
conversion: Some(RequiredConversion {
|
|
||||||
intermediate_buffer: intermediate_buffer,
|
|
||||||
from_sample_rate: samples_rate,
|
|
||||||
to_sample_rate: target_samples_rate,
|
|
||||||
to_format: target_samples_format,
|
|
||||||
from_channels: channels,
|
|
||||||
to_channels: target_channels,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Buffer {
|
|
||||||
target: Some(self.0.append_data(max_elements)),
|
|
||||||
conversion: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,74 +188,12 @@ impl<'a, T> Deref for Buffer<'a, T> where T: Sample {
|
||||||
|
|
||||||
impl<'a, T> DerefMut for Buffer<'a, T> where T: Sample {
|
impl<'a, T> DerefMut for Buffer<'a, T> where T: Sample {
|
||||||
fn deref_mut(&mut self) -> &mut [T] {
|
fn deref_mut(&mut self) -> &mut [T] {
|
||||||
if let Some(ref mut conversion) = self.conversion {
|
self.target.as_mut().unwrap().get_buffer()
|
||||||
&mut conversion.intermediate_buffer
|
|
||||||
} else {
|
|
||||||
self.target.as_mut().unwrap().get_buffer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Drop for Buffer<'a, T> where T: Sample {
|
impl<'a, T> Drop for Buffer<'a, T> where T: Sample {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(conversion) = self.conversion.take() {
|
|
||||||
let buffer = conversion.intermediate_buffer;
|
|
||||||
|
|
||||||
let buffer = if conversion.from_channels != conversion.to_channels {
|
|
||||||
conversions::convert_channels(&buffer, conversion.from_channels,
|
|
||||||
conversion.to_channels)
|
|
||||||
} else {
|
|
||||||
buffer
|
|
||||||
};
|
|
||||||
|
|
||||||
let buffer = if conversion.from_sample_rate != conversion.to_sample_rate {
|
|
||||||
conversions::convert_samples_rate(&buffer, conversion.from_sample_rate,
|
|
||||||
conversion.to_sample_rate,
|
|
||||||
conversion.to_channels)
|
|
||||||
} else {
|
|
||||||
buffer
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = self.target.as_mut().unwrap().get_buffer();
|
|
||||||
assert!(buffer.len() == output.len(), "Buffers length mismatch: {} vs {}", buffer.len(), output.len());
|
|
||||||
|
|
||||||
macro_rules! write_to_buf(
|
|
||||||
($buf:expr, $output:expr, $ty:ty) => ({
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
let output: &mut [$ty] = unsafe { std::mem::transmute($output) };
|
|
||||||
|
|
||||||
match $buf {
|
|
||||||
Cow::Borrowed(buf) => {
|
|
||||||
for (i, o) in buf.iter().zip(output.iter_mut()) {
|
|
||||||
*o = *i;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Cow::Owned(buf) => {
|
|
||||||
for (i, o) in buf.into_iter().zip(output.iter_mut()) {
|
|
||||||
*o = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
match conversion.to_format {
|
|
||||||
SampleFormat::I16 => {
|
|
||||||
let buffer = Sample::to_vec_i16(&buffer);
|
|
||||||
write_to_buf!(buffer, output, i16);
|
|
||||||
},
|
|
||||||
SampleFormat::U16 => {
|
|
||||||
let buffer = Sample::to_vec_u16(&buffer);
|
|
||||||
write_to_buf!(buffer, output, u16);
|
|
||||||
},
|
|
||||||
SampleFormat::F32 => {
|
|
||||||
let buffer = Sample::to_vec_f32(&buffer);
|
|
||||||
write_to_buf!(buffer, output, f32);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.target.take().unwrap().finish();
|
self.target.take().unwrap().finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
/// Format that each sample has.
|
/// Format that each sample has.
|
||||||
|
@ -24,171 +23,25 @@ impl SampleFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for containers that contain PCM data.
|
/// Trait for containers that contain PCM data.
|
||||||
pub trait Sample: Copy + Clone {
|
pub unsafe trait Sample: Copy + Clone {
|
||||||
fn get_format(Option<Self>) -> SampleFormat;
|
/// Returns the `SampleFormat` corresponding to this data type.
|
||||||
|
fn get_format() -> 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`.
|
|
||||||
fn to_vec_u16(&[Self]) -> Cow<[u16]>;
|
|
||||||
/// Turns the data into samples of type `F32`.
|
|
||||||
fn to_vec_f32(&[Self]) -> Cow<[f32]>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sample for u16 {
|
unsafe impl Sample for u16 {
|
||||||
fn get_format(_: Option<u16>) -> SampleFormat {
|
fn get_format() -> SampleFormat {
|
||||||
SampleFormat::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 {
|
|
||||||
(value - 32768) as i16
|
|
||||||
} else {
|
|
||||||
(value as i16) - 32767 - 1
|
|
||||||
}
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec_u16(input: &[u16]) -> Cow<[u16]> {
|
|
||||||
Cow::Borrowed(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec_f32(input: &[u16]) -> Cow<[f32]> {
|
|
||||||
Cow::Owned(Sample::to_vec_f32(&Sample::to_vec_i16(input)).to_vec())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sample for i16 {
|
unsafe impl Sample for i16 {
|
||||||
fn get_format(_: Option<i16>) -> SampleFormat {
|
fn get_format() -> SampleFormat {
|
||||||
SampleFormat::I16
|
SampleFormat::I16
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interpolate(self, other: i16) -> i16 {
|
|
||||||
(self + other) / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec_i16(input: &[i16]) -> Cow<[i16]> {
|
|
||||||
Cow::Borrowed(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec_u16(input: &[i16]) -> Cow<[u16]> {
|
|
||||||
Cow::Owned(input.iter().map(|&value| {
|
|
||||||
if value < 0 {
|
|
||||||
(value + 32767 + 1) as u16
|
|
||||||
} else {
|
|
||||||
(value as u16) + 32768
|
|
||||||
}
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec_f32(input: &[i16]) -> Cow<[f32]> {
|
|
||||||
Cow::Owned(input.iter().map(|&value| {
|
|
||||||
if value > 0 {
|
|
||||||
value as f32 / 32767.0
|
|
||||||
} else {
|
|
||||||
value as f32 / 32768.0
|
|
||||||
}
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sample for f32 {
|
unsafe impl Sample for f32 {
|
||||||
fn get_format(_: Option<f32>) -> SampleFormat {
|
fn get_format() -> SampleFormat {
|
||||||
SampleFormat::F32
|
SampleFormat::F32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interpolate(self, other: f32) -> f32 {
|
|
||||||
(self + other) / 2.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec_i16(input: &[f32]) -> Cow<[i16]> {
|
|
||||||
Cow::Owned(input.iter().map(|&value| {
|
|
||||||
if value >= 0.0 {
|
|
||||||
(value * 32767.0) as i16
|
|
||||||
} else {
|
|
||||||
(value * 32768.0) as i16
|
|
||||||
}
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec_u16(input: &[f32]) -> Cow<[u16]> {
|
|
||||||
Cow::Owned(input.iter().map(|&value| {
|
|
||||||
if value >= 0.0 {
|
|
||||||
((value * 32767.0) + 32768.0) as u16
|
|
||||||
} else {
|
|
||||||
((value * 32768.0) + 32768.0) as u16
|
|
||||||
}
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec_f32(input: &[f32]) -> Cow<[f32]> {
|
|
||||||
Cow::Borrowed(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::Sample;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i16_to_i16() {
|
|
||||||
let out = Sample::to_vec_i16(&[0i16, -467, 32767, -32768]).into_owned();
|
|
||||||
assert_eq!(out, vec![0, -467, 32767, -32768]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i16_to_u16() {
|
|
||||||
let out = Sample::to_vec_u16(&[0i16, -16384, 32767, -32768]).into_owned();
|
|
||||||
assert_eq!(out, vec![32768, 16384, 65535, 0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i16_to_f32() {
|
|
||||||
let out = Sample::to_vec_f32(&[0i16, -16384, 32767, -32768]).into_owned();
|
|
||||||
assert_eq!(out, vec![0.0, -0.5, 1.0, -1.0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn u16_to_i16() {
|
|
||||||
let out = Sample::to_vec_i16(&[32768u16, 16384, 65535, 0]).into_owned();
|
|
||||||
assert_eq!(out, vec![0, -16384, 32767, -32768]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn u16_to_u16() {
|
|
||||||
let out = Sample::to_vec_u16(&[0u16, 467, 32767, 65535]).into_owned();
|
|
||||||
assert_eq!(out, vec![0, 467, 32767, 65535]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn u16_to_f32() {
|
|
||||||
let out = Sample::to_vec_f32(&[0u16, 32768, 65535]).into_owned();
|
|
||||||
assert_eq!(out, vec![-1.0, 0.0, 1.0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn f32_to_i16() {
|
|
||||||
let out = Sample::to_vec_i16(&[0.0f32, -0.5, 1.0, -1.0]).into_owned();
|
|
||||||
assert_eq!(out, vec![0, -16384, 32767, -32768]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn f32_to_u16() {
|
|
||||||
let out = Sample::to_vec_u16(&[-1.0f32, 0.0, 1.0]).into_owned();
|
|
||||||
assert_eq!(out, vec![0, 32768, 65535]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn f32_to_f32() {
|
|
||||||
let out = Sample::to_vec_f32(&[0.1f32, -0.7, 1.0]).into_owned();
|
|
||||||
assert_eq!(out, vec![0.1, -0.7, 1.0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ extern crate libc;
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
extern crate ole32;
|
extern crate ole32;
|
||||||
|
|
||||||
use std::{slice, mem, ptr};
|
use std::{cmp, slice, mem, ptr};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
// TODO: determine if should be NoSend or not
|
// TODO: determine if should be NoSend or not
|
||||||
|
@ -62,9 +62,9 @@ impl Voice {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let frames_available = ::std::cmp::min(frames_available,
|
let frames_available = cmp::min(frames_available,
|
||||||
max_elements as u32 * mem::size_of::<T>() as u32 /
|
max_elements as u32 * mem::size_of::<T>() as u32 /
|
||||||
self.bytes_per_frame as u32);
|
self.bytes_per_frame as u32);
|
||||||
assert!(frames_available != 0);
|
assert!(frames_available != 0);
|
||||||
|
|
||||||
// loading buffer
|
// loading buffer
|
||||||
|
@ -147,11 +147,11 @@ impl<'a, T> Buffer<'a, T> {
|
||||||
fn init() -> Result<Voice, String> {
|
fn init() -> Result<Voice, String> {
|
||||||
// FIXME: release everything
|
// FIXME: release everything
|
||||||
unsafe {
|
unsafe {
|
||||||
try!(check_result(ole32::CoInitializeEx(::std::ptr::null_mut(), 0)));
|
try!(check_result(ole32::CoInitializeEx(ptr::null_mut(), 0)));
|
||||||
|
|
||||||
// building the devices enumerator object
|
// building the devices enumerator object
|
||||||
let enumerator = {
|
let enumerator = {
|
||||||
let mut enumerator: *mut winapi::IMMDeviceEnumerator = ::std::mem::uninitialized();
|
let mut enumerator: *mut winapi::IMMDeviceEnumerator = mem::uninitialized();
|
||||||
|
|
||||||
let hresult = ole32::CoCreateInstance(&winapi::CLSID_MMDeviceEnumerator,
|
let hresult = ole32::CoCreateInstance(&winapi::CLSID_MMDeviceEnumerator,
|
||||||
ptr::null_mut(), winapi::CLSCTX_ALL,
|
ptr::null_mut(), winapi::CLSCTX_ALL,
|
||||||
|
|
Loading…
Reference in New Issue