2018-10-12 05:54:26 +00:00
|
|
|
extern crate asio_sys as sys;
|
2018-11-01 06:58:50 +00:00
|
|
|
extern crate num_traits;
|
2018-10-12 05:54:26 +00:00
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
use self::num_traits::PrimInt;
|
|
|
|
use super::asio_utils as au;
|
|
|
|
use super::Device;
|
2018-10-12 05:54:26 +00:00
|
|
|
use std;
|
2018-11-05 01:41:15 +00:00
|
|
|
use std::mem;
|
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
use std::sync::{Arc, Mutex};
|
2018-11-07 07:38:33 +00:00
|
|
|
use std::thread;
|
|
|
|
use std::time::Duration;
|
2018-10-12 05:54:26 +00:00
|
|
|
use CreationError;
|
2018-11-05 01:41:15 +00:00
|
|
|
use Format;
|
|
|
|
use SampleFormat;
|
2018-10-12 05:54:26 +00:00
|
|
|
use StreamData;
|
|
|
|
use UnknownTypeInputBuffer;
|
2018-10-30 05:50:20 +00:00
|
|
|
use UnknownTypeOutputBuffer;
|
2018-10-12 05:54:26 +00:00
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Controls all streams
|
2018-10-12 05:54:26 +00:00
|
|
|
pub struct EventLoop {
|
2018-11-05 01:41:15 +00:00
|
|
|
/// The input and output ASIO streams
|
2018-10-29 11:57:42 +00:00
|
|
|
asio_streams: Arc<Mutex<sys::AsioStreams>>,
|
2018-11-05 01:41:15 +00:00
|
|
|
/// List of all CPAL streams
|
2018-10-30 03:27:50 +00:00
|
|
|
cpal_streams: Arc<Mutex<Vec<Option<Stream>>>>,
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Total stream count
|
2018-10-24 06:34:31 +00:00
|
|
|
stream_count: AtomicUsize,
|
2018-11-05 01:41:15 +00:00
|
|
|
/// The CPAL callback that the user gives to fill the buffers.
|
2018-11-07 07:38:33 +00:00
|
|
|
callbacks: Arc<Mutex<Option<&'static mut (FnMut(StreamId, StreamData) + Send)>>>,
|
2018-10-12 05:54:26 +00:00
|
|
|
}
|
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Id for each stream.
|
|
|
|
/// Created depending on the number they are created.
|
|
|
|
/// Starting at one! not zero.
|
2018-10-12 05:54:26 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct StreamId(usize);
|
|
|
|
|
|
|
|
pub struct InputBuffer<'a, T: 'a> {
|
|
|
|
buffer: &'a [T],
|
|
|
|
}
|
|
|
|
pub struct OutputBuffer<'a, T: 'a> {
|
|
|
|
buffer: &'a mut [T],
|
|
|
|
}
|
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// CPAL stream.
|
|
|
|
/// This decouples the many cpal streams
|
|
|
|
/// from the single input and single output
|
|
|
|
/// ASIO streams.
|
|
|
|
/// Each stream can be playing or paused.
|
|
|
|
struct Stream {
|
2018-10-30 05:50:20 +00:00
|
|
|
playing: bool,
|
2018-10-30 03:27:50 +00:00
|
|
|
}
|
|
|
|
|
2018-10-12 12:38:55 +00:00
|
|
|
#[derive(Default)]
|
2018-11-05 01:41:15 +00:00
|
|
|
struct I16Buffer {
|
2018-10-12 12:38:55 +00:00
|
|
|
cpal: Vec<i16>,
|
|
|
|
channel: Vec<Vec<i16>>,
|
|
|
|
}
|
2018-11-07 01:57:22 +00:00
|
|
|
|
2018-10-12 12:38:55 +00:00
|
|
|
#[derive(Default)]
|
2018-11-05 01:41:15 +00:00
|
|
|
struct F32Buffer {
|
2018-10-12 12:38:55 +00:00
|
|
|
cpal: Vec<f32>,
|
|
|
|
channel: Vec<Vec<f32>>,
|
|
|
|
}
|
|
|
|
struct Buffers {
|
|
|
|
i16_buff: I16Buffer,
|
2018-11-06 01:33:07 +00:00
|
|
|
//u16_buff: U16Buffer,
|
2018-10-12 12:38:55 +00:00
|
|
|
f32_buff: F32Buffer,
|
|
|
|
}
|
|
|
|
|
2018-11-01 06:58:50 +00:00
|
|
|
enum Endian {
|
|
|
|
Little,
|
|
|
|
Big,
|
|
|
|
}
|
|
|
|
|
2018-10-12 05:54:26 +00:00
|
|
|
impl EventLoop {
|
|
|
|
pub fn new() -> EventLoop {
|
|
|
|
EventLoop {
|
2018-11-05 01:41:15 +00:00
|
|
|
asio_streams: Arc::new(Mutex::new(sys::AsioStreams {
|
|
|
|
input: None,
|
|
|
|
output: None,
|
|
|
|
})),
|
2018-10-30 03:27:50 +00:00
|
|
|
cpal_streams: Arc::new(Mutex::new(Vec::new())),
|
2018-11-05 01:41:15 +00:00
|
|
|
// This is why the Id's count from one not zero
|
|
|
|
// because at this point there is no streams
|
2018-10-24 06:34:31 +00:00
|
|
|
stream_count: AtomicUsize::new(0),
|
2018-11-07 07:38:33 +00:00
|
|
|
callbacks: Arc::new(Mutex::new(None)),
|
2018-10-12 05:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-07 09:42:00 +00:00
|
|
|
fn check_format(
|
2018-11-07 07:38:33 +00:00
|
|
|
&self,
|
|
|
|
drivers: &sys::Drivers,
|
|
|
|
format: &Format,
|
2018-11-07 09:42:00 +00:00
|
|
|
num_asio_channels: u16,
|
|
|
|
) -> Result<(), CreationError> {
|
2018-11-04 10:23:24 +00:00
|
|
|
let Format {
|
|
|
|
channels,
|
|
|
|
sample_rate,
|
2018-11-07 09:42:00 +00:00
|
|
|
data_type,
|
2018-11-04 10:23:24 +00:00
|
|
|
} = format;
|
2018-11-05 01:41:15 +00:00
|
|
|
// Try and set the sample rate to what the user selected.
|
2018-11-04 10:23:24 +00:00
|
|
|
let sample_rate = sample_rate.0;
|
2018-11-05 01:41:15 +00:00
|
|
|
if sample_rate != drivers.get_sample_rate().rate {
|
|
|
|
if drivers.can_sample_rate(sample_rate) {
|
|
|
|
drivers
|
|
|
|
.set_sample_rate(sample_rate)
|
|
|
|
.expect("Unsupported sample rate");
|
|
|
|
} else {
|
2018-11-07 08:26:39 +00:00
|
|
|
return Err(CreationError::FormatNotSupported);
|
2018-10-29 11:57:42 +00:00
|
|
|
}
|
2018-11-05 01:41:15 +00:00
|
|
|
}
|
2018-11-07 09:42:00 +00:00
|
|
|
// unsigned formats are not supported by asio
|
|
|
|
match data_type {
|
|
|
|
SampleFormat::I16 | SampleFormat::F32 => (),
|
|
|
|
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
|
|
|
}
|
|
|
|
if *channels > num_asio_channels {
|
|
|
|
return Err(CreationError::FormatNotSupported);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a new CPAL Input Stream.
|
|
|
|
/// If there is no ASIO Input Stream
|
|
|
|
/// it will be created.
|
|
|
|
fn get_input_stream(
|
|
|
|
&self,
|
|
|
|
drivers: &sys::Drivers,
|
|
|
|
format: &Format,
|
|
|
|
device: &Device,
|
|
|
|
) -> Result<usize, CreationError> {
|
|
|
|
match device.default_input_format() {
|
|
|
|
Ok(f) => {
|
|
|
|
let num_asio_channels = f.channels;
|
|
|
|
self.check_format(drivers, format, num_asio_channels)
|
|
|
|
},
|
|
|
|
Err(_) => Err(CreationError::FormatNotSupported),
|
|
|
|
}?;
|
|
|
|
let num_channels = format.channels as usize;
|
|
|
|
let ref mut streams = *self.asio_streams.lock().unwrap();
|
2018-11-05 01:41:15 +00:00
|
|
|
// Either create a stream if thers none or had back the
|
|
|
|
// size of the current one.
|
|
|
|
match streams.input {
|
|
|
|
Some(ref input) => Ok(input.buffer_size as usize),
|
|
|
|
None => {
|
|
|
|
let output = streams.output.take();
|
|
|
|
drivers
|
|
|
|
.prepare_input_stream(output, num_channels)
|
|
|
|
.map(|new_streams| {
|
|
|
|
let bs = match new_streams.input {
|
|
|
|
Some(ref inp) => inp.buffer_size as usize,
|
|
|
|
None => unreachable!(),
|
|
|
|
};
|
|
|
|
*streams = new_streams;
|
|
|
|
bs
|
|
|
|
}).map_err(|ref e| {
|
|
|
|
println!("Error preparing stream: {}", e);
|
|
|
|
CreationError::DeviceNotAvailable
|
|
|
|
})
|
2018-11-07 07:38:33 +00:00
|
|
|
}
|
2018-11-05 01:41:15 +00:00
|
|
|
}
|
2018-10-29 11:57:42 +00:00
|
|
|
}
|
2018-11-05 01:41:15 +00:00
|
|
|
|
|
|
|
/// Create a new CPAL Output Stream.
|
|
|
|
/// If there is no ASIO Output Stream
|
|
|
|
/// it will be created.
|
|
|
|
fn get_output_stream(
|
2018-11-07 07:38:33 +00:00
|
|
|
&self,
|
|
|
|
drivers: &sys::Drivers,
|
|
|
|
format: &Format,
|
2018-11-07 09:42:00 +00:00
|
|
|
device: &Device,
|
2018-11-05 01:41:15 +00:00
|
|
|
) -> Result<usize, CreationError> {
|
2018-11-07 09:42:00 +00:00
|
|
|
match device.default_output_format() {
|
|
|
|
Ok(f) => {
|
|
|
|
let num_asio_channels = f.channels;
|
|
|
|
self.check_format(drivers, format, num_asio_channels)
|
|
|
|
},
|
|
|
|
Err(_) => Err(CreationError::FormatNotSupported),
|
|
|
|
}?;
|
|
|
|
let num_channels = format.channels as usize;
|
2018-11-05 01:41:15 +00:00
|
|
|
let ref mut streams = *self.asio_streams.lock().unwrap();
|
|
|
|
// Either create a stream if thers none or had back the
|
|
|
|
// size of the current one.
|
|
|
|
match streams.output {
|
|
|
|
Some(ref output) => Ok(output.buffer_size as usize),
|
|
|
|
None => {
|
|
|
|
let input = streams.input.take();
|
|
|
|
drivers
|
|
|
|
.prepare_output_stream(input, num_channels)
|
|
|
|
.map(|new_streams| {
|
|
|
|
let bs = match new_streams.output {
|
|
|
|
Some(ref out) => out.buffer_size as usize,
|
|
|
|
None => unreachable!(),
|
|
|
|
};
|
|
|
|
*streams = new_streams;
|
|
|
|
bs
|
|
|
|
}).map_err(|ref e| {
|
|
|
|
println!("Error preparing stream: {}", e);
|
|
|
|
CreationError::DeviceNotAvailable
|
|
|
|
})
|
2018-11-07 07:38:33 +00:00
|
|
|
}
|
2018-11-05 01:41:15 +00:00
|
|
|
}
|
2018-10-29 11:57:42 +00:00
|
|
|
}
|
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Builds a new cpal input stream
|
2018-10-12 05:54:26 +00:00
|
|
|
pub fn build_input_stream(
|
2018-11-07 07:38:33 +00:00
|
|
|
&self,
|
|
|
|
device: &Device,
|
|
|
|
format: &Format,
|
2018-11-05 01:41:15 +00:00
|
|
|
) -> Result<StreamId, CreationError> {
|
|
|
|
let Device { drivers, .. } = device;
|
2018-10-19 03:05:55 +00:00
|
|
|
let num_channels = format.channels.clone();
|
2018-10-16 05:48:40 +00:00
|
|
|
let stream_type = drivers.get_data_type().expect("Couldn't load data type");
|
2018-11-07 09:42:00 +00:00
|
|
|
let input_stream = self.get_input_stream(&drivers, format, device);
|
2018-11-07 08:26:39 +00:00
|
|
|
input_stream.map(|stream_buffer_size| {
|
|
|
|
let cpal_num_samples = stream_buffer_size * num_channels as usize;
|
|
|
|
let count = self.stream_count.fetch_add(1, Ordering::SeqCst);
|
|
|
|
let asio_streams = self.asio_streams.clone();
|
|
|
|
let cpal_streams = self.cpal_streams.clone();
|
|
|
|
let callbacks = self.callbacks.clone();
|
|
|
|
|
|
|
|
let channel_len = cpal_num_samples / num_channels as usize;
|
|
|
|
|
|
|
|
// Create buffers depending on data type
|
|
|
|
// TODO the naming of cpal and channel is confusing.
|
|
|
|
// change it to:
|
|
|
|
// cpal -> interleaved
|
|
|
|
// channels -> per_channel
|
|
|
|
let mut buffers = match format.data_type {
|
|
|
|
SampleFormat::I16 => Buffers {
|
|
|
|
i16_buff: I16Buffer {
|
|
|
|
cpal: vec![0 as i16; cpal_num_samples],
|
|
|
|
channel: (0..num_channels)
|
|
|
|
.map(|_| Vec::with_capacity(channel_len))
|
|
|
|
.collect(),
|
2018-11-05 01:41:15 +00:00
|
|
|
},
|
2018-11-07 08:26:39 +00:00
|
|
|
f32_buff: F32Buffer::default(),
|
|
|
|
},
|
|
|
|
SampleFormat::F32 => Buffers {
|
|
|
|
i16_buff: I16Buffer::default(),
|
|
|
|
f32_buff: F32Buffer {
|
|
|
|
cpal: vec![0 as f32; cpal_num_samples],
|
|
|
|
channel: (0..num_channels)
|
|
|
|
.map(|_| Vec::with_capacity(channel_len))
|
|
|
|
.collect(),
|
2018-11-05 01:41:15 +00:00
|
|
|
},
|
2018-11-07 08:26:39 +00:00
|
|
|
},
|
|
|
|
_ => unimplemented!(),
|
|
|
|
};
|
2018-10-12 05:54:26 +00:00
|
|
|
|
2018-11-07 08:26:39 +00:00
|
|
|
// Set the input callback.
|
|
|
|
// This is most performance critical part of the ASIO bindings.
|
|
|
|
sys::set_callback(move |index| unsafe {
|
|
|
|
// if not playing return early
|
|
|
|
{
|
2018-11-07 09:02:27 +00:00
|
|
|
if let Some(s) = cpal_streams.lock().unwrap().get(count) {
|
2018-11-07 08:26:39 +00:00
|
|
|
if let Some(s) = s {
|
|
|
|
if !s.playing {
|
|
|
|
return ();
|
2018-11-05 01:41:15 +00:00
|
|
|
}
|
|
|
|
}
|
2018-10-30 05:50:20 +00:00
|
|
|
}
|
2018-11-07 08:26:39 +00:00
|
|
|
}
|
|
|
|
// Get the stream
|
|
|
|
let stream_lock = asio_streams.lock().unwrap();
|
|
|
|
let ref asio_stream = match stream_lock.input {
|
|
|
|
Some(ref asio_stream) => asio_stream,
|
|
|
|
None => return (),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Get the callback
|
|
|
|
let mut callbacks = callbacks.lock().unwrap();
|
|
|
|
|
|
|
|
// Theres only a single callback because theres only one event loop
|
|
|
|
let callback = match callbacks.as_mut() {
|
|
|
|
Some(callback) => callback,
|
|
|
|
None => return (),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Macro to convert sample from ASIO to CPAL type
|
|
|
|
macro_rules! convert_sample {
|
|
|
|
// floats types required different conversion
|
2018-11-07 10:54:27 +00:00
|
|
|
(f32,
|
|
|
|
f32,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample
|
|
|
|
};
|
|
|
|
(f64,
|
|
|
|
f64,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample
|
|
|
|
};
|
|
|
|
(f64,
|
|
|
|
f32,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample as f32
|
|
|
|
};
|
|
|
|
(f32,
|
|
|
|
f64,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample as f64
|
|
|
|
};
|
2018-11-07 08:26:39 +00:00
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
f32,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
2018-11-07 10:54:27 +00:00
|
|
|
(*$Sample as f64 / ::std::$AsioTypeIdent::MAX as f64) as f32
|
2018-11-07 08:26:39 +00:00
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
f64,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
2018-11-07 10:54:27 +00:00
|
|
|
*$Sample as f64 / ::std::$AsioTypeIdent::MAX as f64
|
|
|
|
};
|
|
|
|
(f32,
|
|
|
|
$SampleType:ty,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
(*$Sample as f64 * ::std::$SampleTypeIdent::MAX as f64) as $SampleType
|
|
|
|
};
|
|
|
|
(f64,
|
|
|
|
$SampleType:ty,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
(*$Sample as f64 * ::std::$SampleTypeIdent::MAX as f64) as $SampleType
|
2018-11-07 08:26:39 +00:00
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
$SampleType:ty,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
(*$Sample as i64 * ::std::$SampleTypeIdent::MAX as i64
|
|
|
|
/ ::std::$AsioTypeIdent::MAX as i64) as $SampleType
|
|
|
|
};
|
|
|
|
};
|
|
|
|
// This creates gets the buffer and interleaves it.
|
|
|
|
// It allows it to be done based on the sample type.
|
|
|
|
macro_rules! try_callback {
|
|
|
|
($SampleFormat:ident,
|
|
|
|
$SampleType:ty,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$AsioType:ty,
|
|
|
|
$AsioTypeIdent:ident,
|
|
|
|
$Buffers:expr,
|
|
|
|
$BuffersType:ty,
|
|
|
|
$BuffersTypeIdent:ident,
|
|
|
|
$Endianness:expr,
|
|
|
|
$ConvertEndian:expr
|
|
|
|
) => {
|
|
|
|
// For each channel write the asio buffer to
|
|
|
|
// the cpal buffer
|
|
|
|
|
|
|
|
for (i, channel) in $Buffers.channel.iter_mut().enumerate() {
|
|
|
|
let buff_ptr = asio_stream.buffer_infos[i].buffers[index as usize]
|
|
|
|
as *mut $AsioType;
|
|
|
|
let asio_buffer: &'static [$AsioType] = std::slice::from_raw_parts(
|
|
|
|
buff_ptr,
|
|
|
|
asio_stream.buffer_size as usize,
|
|
|
|
);
|
|
|
|
for asio_s in asio_buffer.iter() {
|
|
|
|
channel.push($ConvertEndian(
|
|
|
|
convert_sample!(
|
|
|
|
$AsioTypeIdent,
|
|
|
|
$SampleType,
|
|
|
|
$SampleTypeIdent,
|
|
|
|
asio_s
|
|
|
|
),
|
|
|
|
$Endianness,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// interleave all the channels
|
|
|
|
{
|
|
|
|
let $BuffersTypeIdent {
|
|
|
|
cpal: ref mut c_buffer,
|
|
|
|
channel: ref mut channels,
|
|
|
|
} = $Buffers;
|
|
|
|
au::interleave(&channels, c_buffer);
|
|
|
|
// Clear the per channel buffers
|
|
|
|
for c in channels.iter_mut() {
|
|
|
|
c.clear();
|
2018-11-07 07:38:33 +00:00
|
|
|
}
|
2018-10-12 05:54:26 +00:00
|
|
|
}
|
2018-11-07 08:26:39 +00:00
|
|
|
|
|
|
|
// Wrap the buffer in the CPAL type
|
|
|
|
let buff = InputBuffer {
|
|
|
|
buffer: &mut $Buffers.cpal,
|
|
|
|
};
|
|
|
|
// Call the users callback with the buffer
|
|
|
|
callback(
|
|
|
|
StreamId(count),
|
|
|
|
StreamData::Input {
|
|
|
|
buffer: UnknownTypeInputBuffer::$SampleFormat(::InputBuffer {
|
|
|
|
buffer: Some(super::super::InputBuffer::Asio(buff)),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
// Call the right buffer handler depending on types
|
|
|
|
match stream_type {
|
|
|
|
sys::AsioSampleType::ASIOSTInt32LSB => {
|
|
|
|
try_callback!(
|
|
|
|
I16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i32,
|
|
|
|
i32,
|
|
|
|
buffers.i16_buff,
|
|
|
|
I16Buffer,
|
|
|
|
I16Buffer,
|
|
|
|
Endian::Little,
|
2018-11-08 07:18:08 +00:00
|
|
|
convert_endian_from
|
2018-11-07 08:26:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTInt16LSB => {
|
|
|
|
try_callback!(
|
|
|
|
I16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
buffers.i16_buff,
|
|
|
|
I16Buffer,
|
|
|
|
I16Buffer,
|
|
|
|
Endian::Little,
|
2018-11-08 07:18:08 +00:00
|
|
|
convert_endian_from
|
2018-11-07 08:26:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTInt32MSB => {
|
|
|
|
try_callback!(
|
|
|
|
I16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i32,
|
|
|
|
i32,
|
|
|
|
buffers.i16_buff,
|
|
|
|
I16Buffer,
|
|
|
|
I16Buffer,
|
|
|
|
Endian::Big,
|
2018-11-08 07:18:08 +00:00
|
|
|
convert_endian_from
|
2018-11-07 08:26:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTInt16MSB => {
|
|
|
|
try_callback!(
|
|
|
|
I16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
buffers.i16_buff,
|
|
|
|
I16Buffer,
|
|
|
|
I16Buffer,
|
|
|
|
Endian::Big,
|
2018-11-08 07:18:08 +00:00
|
|
|
convert_endian_from
|
2018-11-07 08:26:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTFloat32LSB => {
|
|
|
|
try_callback!(
|
|
|
|
F32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
buffers.f32_buff,
|
|
|
|
F32Buffer,
|
|
|
|
F32Buffer,
|
|
|
|
Endian::Little,
|
|
|
|
|a, _| a
|
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTFloat64LSB => {
|
|
|
|
try_callback!(
|
|
|
|
F32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f64,
|
|
|
|
f64,
|
|
|
|
buffers.f32_buff,
|
|
|
|
F32Buffer,
|
|
|
|
F32Buffer,
|
|
|
|
Endian::Little,
|
|
|
|
|a, _| a
|
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTFloat32MSB => {
|
|
|
|
try_callback!(
|
|
|
|
F32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
buffers.f32_buff,
|
|
|
|
F32Buffer,
|
|
|
|
F32Buffer,
|
|
|
|
Endian::Big,
|
|
|
|
|a, _| a
|
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTFloat64MSB => {
|
|
|
|
try_callback!(
|
|
|
|
F32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f64,
|
|
|
|
f64,
|
|
|
|
buffers.f32_buff,
|
|
|
|
F32Buffer,
|
|
|
|
F32Buffer,
|
|
|
|
Endian::Big,
|
|
|
|
|a, _| a
|
|
|
|
);
|
2018-10-12 05:54:26 +00:00
|
|
|
}
|
2018-11-07 08:26:39 +00:00
|
|
|
_ => println!("unsupported format {:?}", stream_type),
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// Create stream and set to paused
|
|
|
|
self.cpal_streams
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.push(Some(Stream { playing: false }));
|
|
|
|
StreamId(count)
|
|
|
|
})
|
2018-10-12 05:54:26 +00:00
|
|
|
}
|
2018-10-29 11:57:42 +00:00
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Create the an output cpal stream.
|
|
|
|
pub fn build_output_stream(
|
2018-11-07 07:38:33 +00:00
|
|
|
&self,
|
|
|
|
device: &Device,
|
|
|
|
format: &Format,
|
2018-10-29 11:57:42 +00:00
|
|
|
) -> Result<StreamId, CreationError> {
|
2018-11-05 01:41:15 +00:00
|
|
|
let Device { drivers, .. } = device;
|
|
|
|
let num_channels = format.channels.clone();
|
|
|
|
let stream_type = drivers.get_data_type().expect("Couldn't load data type");
|
2018-11-07 09:42:00 +00:00
|
|
|
let output_stream = self.get_output_stream(&drivers, format, device);
|
2018-11-07 08:26:39 +00:00
|
|
|
output_stream.map(|stream_buffer_size| {
|
|
|
|
let cpal_num_samples = stream_buffer_size * num_channels as usize;
|
|
|
|
let count = self.stream_count.fetch_add(1, Ordering::SeqCst);
|
|
|
|
let asio_streams = self.asio_streams.clone();
|
|
|
|
let cpal_streams = self.cpal_streams.clone();
|
|
|
|
let callbacks = self.callbacks.clone();
|
|
|
|
let channel_len = cpal_num_samples / num_channels as usize;
|
|
|
|
|
|
|
|
// Create buffers depending on data type
|
|
|
|
let mut re_buffers = match format.data_type {
|
|
|
|
SampleFormat::I16 => Buffers {
|
|
|
|
i16_buff: I16Buffer {
|
|
|
|
cpal: vec![0 as i16; cpal_num_samples],
|
|
|
|
channel: (0..num_channels)
|
|
|
|
.map(|_| Vec::with_capacity(channel_len))
|
|
|
|
.collect(),
|
2018-11-05 01:41:15 +00:00
|
|
|
},
|
2018-11-07 08:26:39 +00:00
|
|
|
f32_buff: F32Buffer::default(),
|
|
|
|
},
|
|
|
|
SampleFormat::F32 => Buffers {
|
|
|
|
i16_buff: I16Buffer::default(),
|
|
|
|
f32_buff: F32Buffer {
|
|
|
|
cpal: vec![0 as f32; cpal_num_samples],
|
|
|
|
channel: (0..num_channels)
|
|
|
|
.map(|_| Vec::with_capacity(channel_len))
|
|
|
|
.collect(),
|
2018-11-05 01:41:15 +00:00
|
|
|
},
|
2018-11-07 08:26:39 +00:00
|
|
|
},
|
|
|
|
_ => unimplemented!(),
|
|
|
|
};
|
2018-10-29 11:57:42 +00:00
|
|
|
|
2018-11-07 08:26:39 +00:00
|
|
|
sys::set_callback(move |index| unsafe {
|
|
|
|
// if not playing return early
|
|
|
|
{
|
2018-11-07 09:02:27 +00:00
|
|
|
if let Some(s) = cpal_streams.lock().unwrap().get(count) {
|
2018-11-07 08:26:39 +00:00
|
|
|
if let Some(s) = s {
|
|
|
|
if !s.playing {
|
|
|
|
return ();
|
2018-11-05 01:41:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-07 08:26:39 +00:00
|
|
|
}
|
|
|
|
// Get the stream
|
|
|
|
let stream_lock = asio_streams.lock().unwrap();
|
|
|
|
let ref asio_stream = match stream_lock.output {
|
|
|
|
Some(ref asio_stream) => asio_stream,
|
|
|
|
None => return (),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Get the callback
|
|
|
|
let mut callbacks = callbacks.lock().unwrap();
|
|
|
|
|
|
|
|
// Theres only a single callback because theres only one event loop
|
|
|
|
let callback = match callbacks.as_mut() {
|
|
|
|
Some(callback) => callback,
|
|
|
|
None => return (),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Convert sample depending on the sample type
|
|
|
|
macro_rules! convert_sample {
|
2018-11-07 10:54:27 +00:00
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
f64,
|
|
|
|
f64,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample
|
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample
|
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
f64,
|
|
|
|
f32,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample as f64
|
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
f32,
|
|
|
|
f64,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample as f32
|
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
$AsioType:ty,
|
|
|
|
f32,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
(*$Sample as f64 * ::std::$AsioTypeIdent::MAX as f64) as $AsioType
|
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
$AsioType:ty,
|
|
|
|
f64,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
(*$Sample as f64 * ::std::$AsioTypeIdent::MAX as f64) as $AsioType
|
|
|
|
};
|
2018-11-07 08:26:39 +00:00
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
f32,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
(*$Sample as f64 / ::std::$SampleTypeIdent::MAX as f64) as f32
|
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
f64,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
*$Sample as f64 / ::std::$SampleTypeIdent::MAX as f64
|
|
|
|
};
|
|
|
|
($AsioTypeIdent:ident,
|
|
|
|
$AsioType:ty,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$Sample:expr
|
|
|
|
) => {
|
|
|
|
(*$Sample as i64 * ::std::$AsioTypeIdent::MAX as i64
|
|
|
|
/ ::std::$SampleTypeIdent::MAX as i64) as $AsioType
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
macro_rules! try_callback {
|
|
|
|
($SampleFormat:ident,
|
|
|
|
$SampleType:ty,
|
|
|
|
$SampleTypeIdent:ident,
|
|
|
|
$AsioType:ty,
|
|
|
|
$AsioTypeIdent:ident,
|
|
|
|
$Buffers:expr,
|
|
|
|
$BuffersType:ty,
|
|
|
|
$BuffersTypeIdent:ident,
|
|
|
|
$Endianness:expr,
|
|
|
|
$ConvertEndian:expr
|
|
|
|
) => {
|
|
|
|
let mut my_buffers = $Buffers;
|
|
|
|
{
|
|
|
|
// Wrap the cpal buffer
|
|
|
|
let buff = OutputBuffer {
|
|
|
|
buffer: &mut my_buffers.cpal,
|
2018-11-05 01:41:15 +00:00
|
|
|
};
|
2018-11-07 08:26:39 +00:00
|
|
|
// call the callback to fill the buffer with
|
|
|
|
// users data
|
|
|
|
callback(
|
|
|
|
StreamId(count),
|
|
|
|
StreamData::Output {
|
|
|
|
buffer: UnknownTypeOutputBuffer::$SampleFormat(
|
|
|
|
::OutputBuffer {
|
|
|
|
target: Some(super::super::OutputBuffer::Asio(
|
|
|
|
buff,
|
|
|
|
)),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// Deinter all the channels
|
|
|
|
{
|
|
|
|
let $BuffersTypeIdent {
|
|
|
|
cpal: ref mut c_buffer,
|
|
|
|
channel: ref mut channels,
|
|
|
|
} = my_buffers;
|
|
|
|
au::deinterleave(&c_buffer[..], channels);
|
|
|
|
}
|
2018-11-02 11:06:08 +00:00
|
|
|
|
2018-11-07 10:54:27 +00:00
|
|
|
// Silence the buffer that is about to be used.
|
|
|
|
// This checks if any other callbacks have already
|
|
|
|
// silenced this buffer. If not it will silence it
|
|
|
|
// and set the opposite buffer half to unsilenced.
|
2018-11-07 08:26:39 +00:00
|
|
|
let silence = match index {
|
|
|
|
0 => {
|
|
|
|
if !sys::SILENCE_FIRST.load(Ordering::SeqCst) {
|
|
|
|
sys::SILENCE_FIRST.store(true, Ordering::SeqCst);
|
|
|
|
sys::SILENCE_SECOND.store(false, Ordering::SeqCst);
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
1 => {
|
|
|
|
if !sys::SILENCE_SECOND.load(Ordering::SeqCst) {
|
|
|
|
sys::SILENCE_SECOND.store(true, Ordering::SeqCst);
|
|
|
|
sys::SILENCE_FIRST.store(false, Ordering::SeqCst);
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
2018-11-05 01:41:15 +00:00
|
|
|
}
|
2018-11-07 08:26:39 +00:00
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
// For each channel write the cpal data to
|
|
|
|
// the asio buffer
|
|
|
|
for (i, channel) in my_buffers.channel.iter().enumerate() {
|
|
|
|
let buff_ptr = asio_stream.buffer_infos[i].buffers
|
|
|
|
[index as usize] as *mut $AsioType;
|
|
|
|
let asio_buffer: &'static mut [$AsioType] =
|
|
|
|
std::slice::from_raw_parts_mut(
|
|
|
|
buff_ptr,
|
|
|
|
asio_stream.buffer_size as usize,
|
|
|
|
);
|
|
|
|
for (asio_s, cpal_s) in asio_buffer.iter_mut().zip(channel) {
|
|
|
|
if silence {
|
|
|
|
*asio_s = 0.0 as $AsioType;
|
2018-10-29 11:57:42 +00:00
|
|
|
}
|
2018-11-07 08:26:39 +00:00
|
|
|
*asio_s += $ConvertEndian(
|
|
|
|
convert_sample!(
|
|
|
|
$AsioTypeIdent,
|
|
|
|
$AsioType,
|
|
|
|
$SampleTypeIdent,
|
|
|
|
cpal_s
|
|
|
|
),
|
|
|
|
$Endianness,
|
|
|
|
);
|
2018-11-07 07:38:33 +00:00
|
|
|
}
|
2018-10-29 11:57:42 +00:00
|
|
|
}
|
2018-11-07 08:26:39 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
// Choose the buffer conversions based on the sample types
|
|
|
|
match stream_type {
|
|
|
|
sys::AsioSampleType::ASIOSTInt32LSB => {
|
|
|
|
try_callback!(
|
|
|
|
I16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i32,
|
|
|
|
i32,
|
|
|
|
&mut re_buffers.i16_buff,
|
|
|
|
I16Buffer,
|
|
|
|
I16Buffer,
|
|
|
|
Endian::Little,
|
2018-11-08 07:18:08 +00:00
|
|
|
convert_endian_to
|
2018-11-07 08:26:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTInt16LSB => {
|
|
|
|
try_callback!(
|
|
|
|
I16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
&mut re_buffers.i16_buff,
|
|
|
|
I16Buffer,
|
|
|
|
I16Buffer,
|
|
|
|
Endian::Little,
|
2018-11-08 07:18:08 +00:00
|
|
|
convert_endian_to
|
2018-11-07 08:26:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTInt32MSB => {
|
|
|
|
try_callback!(
|
|
|
|
I16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i32,
|
|
|
|
i32,
|
|
|
|
&mut re_buffers.i16_buff,
|
|
|
|
I16Buffer,
|
|
|
|
I16Buffer,
|
|
|
|
Endian::Big,
|
2018-11-08 07:18:08 +00:00
|
|
|
convert_endian_to
|
2018-11-07 08:26:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTInt16MSB => {
|
|
|
|
try_callback!(
|
|
|
|
I16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
i16,
|
|
|
|
&mut re_buffers.i16_buff,
|
|
|
|
I16Buffer,
|
|
|
|
I16Buffer,
|
|
|
|
Endian::Big,
|
2018-11-08 07:18:08 +00:00
|
|
|
convert_endian_to
|
2018-11-07 08:26:39 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTFloat32LSB => {
|
|
|
|
try_callback!(
|
|
|
|
F32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
&mut re_buffers.f32_buff,
|
|
|
|
F32Buffer,
|
|
|
|
F32Buffer,
|
|
|
|
Endian::Little,
|
|
|
|
|a, _| a
|
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTFloat64LSB => {
|
|
|
|
try_callback!(
|
|
|
|
F32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f64,
|
|
|
|
f64,
|
|
|
|
&mut re_buffers.f32_buff,
|
|
|
|
F32Buffer,
|
|
|
|
F32Buffer,
|
|
|
|
Endian::Little,
|
|
|
|
|a, _| a
|
|
|
|
);
|
|
|
|
}
|
|
|
|
sys::AsioSampleType::ASIOSTFloat32MSB => {
|
|
|
|
try_callback!(
|
|
|
|
F32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
&mut re_buffers.f32_buff,
|
|
|
|
F32Buffer,
|
|
|
|
F32Buffer,
|
|
|
|
Endian::Big,
|
|
|
|
|a, _| a
|
|
|
|
);
|
2018-10-29 11:57:42 +00:00
|
|
|
}
|
2018-11-07 08:26:39 +00:00
|
|
|
sys::AsioSampleType::ASIOSTFloat64MSB => {
|
|
|
|
try_callback!(
|
|
|
|
F32,
|
|
|
|
f32,
|
|
|
|
f32,
|
|
|
|
f64,
|
|
|
|
f64,
|
|
|
|
&mut re_buffers.f32_buff,
|
|
|
|
F32Buffer,
|
|
|
|
F32Buffer,
|
|
|
|
Endian::Big,
|
|
|
|
|a, _| a
|
|
|
|
);
|
|
|
|
}
|
|
|
|
_ => println!("unsupported format {:?}", stream_type),
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// Create the stream paused
|
|
|
|
self.cpal_streams
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.push(Some(Stream { playing: false }));
|
|
|
|
// Give the ID based on the stream count
|
|
|
|
StreamId(count)
|
|
|
|
})
|
2018-10-30 05:50:20 +00:00
|
|
|
}
|
2018-10-12 05:54:26 +00:00
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Play the cpal stream for the given ID.
|
|
|
|
/// Also play The ASIO streams if they are not already.
|
|
|
|
pub fn play_stream(&self, stream_id: StreamId) {
|
|
|
|
let mut streams = self.cpal_streams.lock().unwrap();
|
|
|
|
if let Some(s) = streams.get_mut(stream_id.0).expect("Bad play stream index") {
|
|
|
|
s.playing = true;
|
|
|
|
}
|
|
|
|
// Calling play when already playing is a no-op
|
|
|
|
sys::play();
|
2018-10-30 05:50:20 +00:00
|
|
|
}
|
2018-11-05 01:41:15 +00:00
|
|
|
|
|
|
|
/// Pause the cpal stream for the given ID.
|
|
|
|
/// Pause the ASIO streams if there are no CPAL streams palying.
|
|
|
|
pub fn pause_stream(&self, stream_id: StreamId) {
|
|
|
|
let mut streams = self.cpal_streams.lock().unwrap();
|
|
|
|
if let Some(s) = streams
|
|
|
|
.get_mut(stream_id.0)
|
|
|
|
.expect("Bad pause stream index")
|
|
|
|
{
|
|
|
|
s.playing = false;
|
|
|
|
}
|
|
|
|
let any_playing = streams
|
|
|
|
.iter()
|
2018-11-07 03:59:38 +00:00
|
|
|
.any(|s| if let Some(s) = s { s.playing } else { false });
|
|
|
|
if any_playing {
|
2018-11-05 01:41:15 +00:00
|
|
|
sys::stop();
|
|
|
|
}
|
2018-10-30 05:50:20 +00:00
|
|
|
}
|
2018-10-29 11:57:42 +00:00
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Destroy the cpal stream based on the ID.
|
|
|
|
pub fn destroy_stream(&self, stream_id: StreamId) {
|
|
|
|
let mut streams = self.cpal_streams.lock().unwrap();
|
|
|
|
streams.get_mut(stream_id.0).take();
|
2018-10-12 05:54:26 +00:00
|
|
|
}
|
2018-10-30 03:27:50 +00:00
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Run the cpal callbacks
|
|
|
|
pub fn run<F>(&self, mut callback: F) -> !
|
|
|
|
where
|
|
|
|
F: FnMut(StreamId, StreamData) + Send,
|
|
|
|
{
|
|
|
|
let callback: &mut (FnMut(StreamId, StreamData) + Send) = &mut callback;
|
2018-11-07 07:38:33 +00:00
|
|
|
// Transmute needed to convince the compiler that the callback has a static lifetime
|
2018-11-07 08:26:39 +00:00
|
|
|
*self.callbacks.lock().unwrap() = Some(unsafe { mem::transmute(callback) });
|
2018-11-05 01:41:15 +00:00
|
|
|
loop {
|
2018-11-07 03:59:38 +00:00
|
|
|
// A sleep here to prevent the loop being
|
2018-11-05 01:41:15 +00:00
|
|
|
// removed in --release
|
2018-11-07 03:59:38 +00:00
|
|
|
thread::sleep(Duration::new(1u64, 0u32));
|
2018-11-05 01:41:15 +00:00
|
|
|
}
|
2018-10-12 05:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Clean up if event loop is dropped.
|
|
|
|
/// Currently event loop is never dropped.
|
2018-10-30 03:27:50 +00:00
|
|
|
impl Drop for EventLoop {
|
|
|
|
fn drop(&mut self) {
|
2018-11-07 08:26:39 +00:00
|
|
|
*self.asio_streams.lock().unwrap() = sys::AsioStreams {
|
|
|
|
output: None,
|
|
|
|
input: None,
|
|
|
|
};
|
2018-10-30 03:27:50 +00:00
|
|
|
sys::clean_up();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-12 05:54:26 +00:00
|
|
|
impl<'a, T> InputBuffer<'a, T> {
|
|
|
|
pub fn buffer(&self) -> &[T] {
|
|
|
|
&self.buffer
|
|
|
|
}
|
2018-11-05 01:41:15 +00:00
|
|
|
pub fn finish(self) {}
|
2018-10-12 05:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T> OutputBuffer<'a, T> {
|
|
|
|
pub fn buffer(&mut self) -> &mut [T] {
|
|
|
|
&mut self.buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.buffer.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn finish(self) {}
|
|
|
|
}
|
2018-11-01 06:58:50 +00:00
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Helper function to convert to system endianness
|
2018-11-01 06:58:50 +00:00
|
|
|
fn convert_endian_to<T: PrimInt>(sample: T, endian: Endian) -> T {
|
|
|
|
match endian {
|
|
|
|
Endian::Big => sample.to_be(),
|
|
|
|
Endian::Little => sample.to_le(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-05 01:41:15 +00:00
|
|
|
/// Helper function to convert from system endianness
|
2018-11-01 06:58:50 +00:00
|
|
|
fn convert_endian_from<T: PrimInt>(sample: T, endian: Endian) -> T {
|
|
|
|
match endian {
|
|
|
|
Endian::Big => T::from_be(sample),
|
|
|
|
Endian::Little => T::from_le(sample),
|
|
|
|
}
|
2018-11-05 01:41:15 +00:00
|
|
|
}
|