sample rate support

This commit is contained in:
Tom Gowan 2018-11-02 22:06:08 +11:00 committed by mitchmindtree
parent b0b0484d4b
commit 6e0eb073d1
7 changed files with 150 additions and 17 deletions

View File

@ -4,6 +4,14 @@ extern "C" ASIOError get_sample_rate(double * rate){
return ASIOGetSampleRate(reinterpret_cast<ASIOSampleRate *>(rate)); return ASIOGetSampleRate(reinterpret_cast<ASIOSampleRate *>(rate));
} }
extern "C" ASIOError set_sample_rate(double rate){
return ASIOSetSampleRate(rate);
}
extern "C" ASIOError can_sample_rate(double rate){
return ASIOCanSampleRate(rate);
}
extern AsioDrivers* asioDrivers; extern AsioDrivers* asioDrivers;
bool loadAsioDriver(char *name); bool loadAsioDriver(char *name);

View File

@ -5,6 +5,12 @@
// Helper function to wrap confusing preprocessor // Helper function to wrap confusing preprocessor
extern "C" ASIOError get_sample_rate(double * rate); extern "C" ASIOError get_sample_rate(double * rate);
// Helper function to wrap confusing preprocessor
extern "C" ASIOError set_sample_rate(double rate);
// Helper function to wrap confusing preprocessor
extern "C" ASIOError can_sample_rate(double rate);
extern "C" bool load_asio_driver(char * name); extern "C" bool load_asio_driver(char * name);
extern "C" void remove_current_driver(); extern "C" void remove_current_driver();
extern "C" long get_driver_names(char **names, long maxDrivers); extern "C" long get_driver_names(char **names, long maxDrivers);

View File

@ -167,6 +167,8 @@ fn create_bindings(cpal_asio_dir: &PathBuf) {
.whitelist_function("ASIOGetChannelInfo") .whitelist_function("ASIOGetChannelInfo")
.whitelist_function("ASIOGetBufferSize") .whitelist_function("ASIOGetBufferSize")
.whitelist_function("get_sample_rate") .whitelist_function("get_sample_rate")
.whitelist_function("set_sample_rate")
.whitelist_function("can_sample_rate")
.whitelist_function("ASIOInit") .whitelist_function("ASIOInit")
.whitelist_function("ASIOCreateBuffers") .whitelist_function("ASIOCreateBuffers")
.whitelist_function("ASIOStart") .whitelist_function("ASIOStart")

View File

@ -238,6 +238,24 @@ impl Drivers {
sample_rate sample_rate
} }
pub fn set_sample_rate(&self, sample_rate: u32) -> Result<(), AsioError>{
// Initialize memory for calls
let rate: c_double = c_double::from(sample_rate);
unsafe {
get_drivers().asio_set_sample_rate(rate)
}
}
pub fn can_sample_rate(&self, sample_rate: u32) -> bool {
// Initialize memory for calls
let rate: c_double = c_double::from(sample_rate);
unsafe {
get_drivers().asio_can_sample_rate(rate).is_ok()
}
}
pub fn get_data_type(&self) -> Result<AsioSampleType, AsioDriverError> { pub fn get_data_type(&self) -> Result<AsioSampleType, AsioDriverError> {
// Initialize memory for calls // Initialize memory for calls
@ -538,6 +556,24 @@ unsafe fn asio_get_sample_rate(&mut self, rate: &mut c_double) -> Result<(), Asi
} }
} }
unsafe fn asio_set_sample_rate(&mut self, rate: c_double) -> Result<(), AsioError> {
if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers)
} else {
let result = ai::set_sample_rate(rate);
asio_result!(result)
}
}
unsafe fn asio_can_sample_rate(&mut self, rate: c_double) -> Result<(), AsioError> {
if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers)
} else {
let result = ai::can_sample_rate(rate);
asio_result!(result)
}
}
unsafe fn asio_get_channel_info(&mut self, ci: &mut ai::ASIOChannelInfo) -> Result<(), AsioError> { unsafe fn asio_get_channel_info(&mut self, ci: &mut ai::ASIOChannelInfo) -> Result<(), AsioError> {
if let AsioState::Offline = self.state { if let AsioState::Offline = self.state {
Err(AsioError::NoDrivers) Err(AsioError::NoDrivers)

View File

@ -490,7 +490,6 @@ impl Device {
format.sample_rate = SampleRate(rate as _); format.sample_rate = SampleRate(rate as _);
supported_formats.push(SupportedFormat::from(format.clone())); supported_formats.push(SupportedFormat::from(format.clone()));
} }
Ok(supported_formats.into_iter()) Ok(supported_formats.into_iter())
} }
} }

View File

@ -44,7 +44,19 @@ impl Device {
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, pub fn supported_input_formats(&self) -> Result<SupportedInputFormats,
FormatsEnumerationError> { FormatsEnumerationError> {
match self.default_input_format() { match self.default_input_format() {
Ok(f) => Ok(vec![SupportedFormat::from(f)].into_iter()), Ok(f) => {
let supported_formats: Vec<SupportedFormat> = [44100, 48000]
.into_iter()
.filter(|rate| self.drivers.can_sample_rate(**rate as u32) )
.map(|rate| {
let mut format = f.clone();
format.sample_rate = SampleRate(*rate);
SupportedFormat::from(format)
})
.collect();
//Ok(vec![SupportedFormat::from(f)].into_iter())
Ok(supported_formats.into_iter())
},
Err(_) => Err(FormatsEnumerationError::DeviceNotAvailable), Err(_) => Err(FormatsEnumerationError::DeviceNotAvailable),
} }
} }
@ -52,7 +64,19 @@ impl Device {
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats,
FormatsEnumerationError> { FormatsEnumerationError> {
match self.default_output_format() { match self.default_output_format() {
Ok(f) => Ok(vec![SupportedFormat::from(f)].into_iter()), Ok(f) => {
let supported_formats: Vec<SupportedFormat> = [44100, 48000]
.into_iter()
.filter(|rate| self.drivers.can_sample_rate(**rate as u32) )
.map(|rate| {
let mut format = f.clone();
format.sample_rate = SampleRate(*rate);
SupportedFormat::from(format)
})
.collect();
//Ok(vec![SupportedFormat::from(f)].into_iter())
Ok(supported_formats.into_iter())
},
Err(_) => Err(FormatsEnumerationError::DeviceNotAvailable), Err(_) => Err(FormatsEnumerationError::DeviceNotAvailable),
} }
} }

View File

@ -75,8 +75,15 @@ impl EventLoop {
/// Create a new CPAL Input Stream /// Create a new CPAL Input Stream
/// If there is no ASIO Input Stream /// If there is no ASIO Input Stream
/// it will be created /// it will be created
fn get_input_stream(&self, drivers: &sys::Drivers, num_channels: usize) -> Result<usize, CreationError> { fn get_input_stream(&self, drivers: &sys::Drivers, num_channels: usize, sample_rate: u32) -> Result<usize, CreationError> {
let ref mut streams = *self.asio_streams.lock().unwrap(); let ref mut streams = *self.asio_streams.lock().unwrap();
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 {
panic!("This sample rate {:?} is not supported", sample_rate);
}
}
match streams.input { match streams.input {
Some(ref input) => Ok(input.buffer_size as usize), Some(ref input) => Ok(input.buffer_size as usize),
None => { None => {
@ -98,8 +105,15 @@ impl EventLoop {
} }
} }
fn get_output_stream(&self, drivers: &sys::Drivers, num_channels: usize) -> Result<usize, CreationError> { fn get_output_stream(&self, drivers: &sys::Drivers, num_channels: usize, sample_rate: u32) -> Result<usize, CreationError> {
let ref mut streams = *self.asio_streams.lock().unwrap(); let ref mut streams = *self.asio_streams.lock().unwrap();
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 {
panic!("This sample rate {:?} is not supported", sample_rate);
}
}
match streams.output { match streams.output {
Some(ref output) => Ok(output.buffer_size as usize), Some(ref output) => Ok(output.buffer_size as usize),
None => { None => {
@ -132,7 +146,8 @@ impl EventLoop {
} = device; } = device;
let num_channels = format.channels.clone(); let num_channels = format.channels.clone();
let stream_type = drivers.get_data_type().expect("Couldn't load data type"); let stream_type = drivers.get_data_type().expect("Couldn't load data type");
self.get_input_stream(&drivers, num_channels as usize).map(|stream_buffer_size| { let sample_rate = format.sample_rate.0;
self.get_input_stream(&drivers, num_channels as usize, sample_rate).map(|stream_buffer_size| {
let cpal_num_samples = stream_buffer_size * num_channels as usize; let cpal_num_samples = stream_buffer_size * num_channels as usize;
let count = self.stream_count.load(Ordering::SeqCst); let count = self.stream_count.load(Ordering::SeqCst);
self.stream_count.store(count + 1, Ordering::SeqCst); self.stream_count.store(count + 1, Ordering::SeqCst);
@ -197,6 +212,26 @@ impl EventLoop {
// Theres only a single callback because theres only one event loop // Theres only a single callback because theres only one event loop
match callbacks.first_mut() { match callbacks.first_mut() {
Some(callback) => { Some(callback) => {
macro_rules! convert_sample {
($AsioTypeIdent:ident,
u16,
$SampleTypeIdent:ident,
$Sample:expr
) => {
((*$Sample as f64 + $AsioTypeIdent::MAX as f64) /
(::std::u16::MAX as f64 /
::std::AsioTypeIdent::MAX as f64)) as u16
};
($AsioTypeIdent:ident,
$SampleType:ty,
$SampleTypeIdent:ident,
$Sample:expr
) => {
(*$Sample as i64 *
::std::$SampleTypeIdent::MAX as i64 /
::std::$AsioTypeIdent::MAX as i64) as $SampleType
};
};
macro_rules! try_callback { macro_rules! try_callback {
($SampleFormat:ident, ($SampleFormat:ident,
$SampleType:ty, $SampleType:ty,
@ -224,10 +259,11 @@ impl EventLoop {
buff_ptr, buff_ptr,
asio_stream.buffer_size as usize); asio_stream.buffer_size as usize);
for asio_s in asio_buffer.iter(){ for asio_s in asio_buffer.iter(){
channel.push( $ConvertEndian((*asio_s as i64 * channel.push( $ConvertEndian(convert_sample!(
::std::$SampleTypeIdent::MAX as i64 / $AsioTypeIdent,
::std::$AsioTypeIdent::MAX as i64) as $SampleType, $SampleType,
$Endianness)); $SampleTypeIdent,
asio_s), $Endianness));
} }
} }
@ -325,7 +361,8 @@ pub fn build_output_stream(
} = device; } = device;
let num_channels = format.channels.clone(); let num_channels = format.channels.clone();
let stream_type = drivers.get_data_type().expect("Couldn't load data type"); let stream_type = drivers.get_data_type().expect("Couldn't load data type");
self.get_output_stream(&drivers, num_channels as usize).map(|stream_buffer_size| { let sample_rate = format.sample_rate.0;
self.get_output_stream(&drivers, num_channels as usize, sample_rate).map(|stream_buffer_size| {
let cpal_num_samples = stream_buffer_size * num_channels as usize; let cpal_num_samples = stream_buffer_size * num_channels as usize;
let count = self.stream_count.load(Ordering::SeqCst); let count = self.stream_count.load(Ordering::SeqCst);
self.stream_count.store(count + 1, Ordering::SeqCst); self.stream_count.store(count + 1, Ordering::SeqCst);
@ -336,7 +373,6 @@ pub fn build_output_stream(
let channel_len = cpal_num_samples let channel_len = cpal_num_samples
/ num_channels as usize; / num_channels as usize;
let mut re_buffers = match format.data_type{ let mut re_buffers = match format.data_type{
SampleFormat::I16 => { SampleFormat::I16 => {
Buffers{ Buffers{
@ -386,6 +422,27 @@ pub fn build_output_stream(
// Number of samples needed total // Number of samples needed total
let mut callbacks = callbacks.lock().unwrap(); let mut callbacks = callbacks.lock().unwrap();
macro_rules! convert_sample {
($AsioTypeIdent:ident,
$AsioType:ty,
u16,
$Sample:expr
) => {
((*$Sample as i64 *
::std::$AsioTypeIdent::MAX as i64 /
::std::u16::MAX as i64) - $AsioTypeIdent::MAX as i64) as $AsioType
};
($AsioTypeIdent:ident,
$AsioType:ty,
$SampleTypeIdent:ident,
$Sample:expr
) => {
(*$Sample as i64 *
::std::$AsioTypeIdent::MAX as i64 /
::std::$SampleTypeIdent::MAX as i64) as $AsioType
};
};
// Theres only a single callback because theres only one event loop // Theres only a single callback because theres only one event loop
match callbacks.first_mut() { match callbacks.first_mut() {
Some(callback) => { Some(callback) => {
@ -446,7 +503,6 @@ pub fn build_output_stream(
// For each channel write the cpal data to // For each channel write the cpal data to
// the asio buffer // the asio buffer
// TODO need to check for Endian
for (i, channel) in my_buffers.channel.iter().enumerate(){ for (i, channel) in my_buffers.channel.iter().enumerate(){
let buff_ptr = asio_stream let buff_ptr = asio_stream
.buffer_infos[i] .buffer_infos[i]
@ -458,9 +514,12 @@ pub fn build_output_stream(
for (asio_s, cpal_s) in asio_buffer.iter_mut() for (asio_s, cpal_s) in asio_buffer.iter_mut()
.zip(channel){ .zip(channel){
if silence { *asio_s = 0.0 as $AsioType; } if silence { *asio_s = 0.0 as $AsioType; }
*asio_s += $ConvertEndian((*cpal_s as i64 * *asio_s += $ConvertEndian(convert_sample!(
::std::$AsioTypeIdent::MAX as i64 / $AsioTypeIdent,
::std::$SampleTypeIdent::MAX as i64) as $AsioType, $AsioType,
$SampleTypeIdent,
cpal_s
),
$Endianness); $Endianness);
} }
@ -468,7 +527,6 @@ pub fn build_output_stream(
}; };
} }
// Generic over types // Generic over types
// TODO check for endianess
match stream_type { match stream_type {
sys::AsioSampleType::ASIOSTInt32LSB => { sys::AsioSampleType::ASIOSTInt32LSB => {
try_callback!(I16, i16, i16, i32, i32, try_callback!(I16, i16, i16, i32, i32,