Skip to content

Commit

Permalink
32 bit sample format support
Browse files Browse the repository at this point in the history
  • Loading branch information
julientregoat committed Jan 11, 2021
1 parent 1d72ae7 commit 1f7b8cb
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 39 deletions.
1 change: 1 addition & 0 deletions examples/beep.rs
Expand Up @@ -85,6 +85,7 @@ fn main() -> anyhow::Result<()> {
match config.sample_format() {
cpal::SampleFormat::F32 => run::<f32>(&device, &config.into()),
cpal::SampleFormat::I16 => run::<i16>(&device, &config.into()),
cpal::SampleFormat::I32 => run::<i32>(&device, &config.into()),
cpal::SampleFormat::U16 => run::<u16>(&device, &config.into()),
}
}
Expand Down
10 changes: 8 additions & 2 deletions examples/record_wav.rs
Expand Up @@ -121,6 +121,11 @@ fn main() -> Result<(), anyhow::Error> {
move |data, _: &_| write_input_data::<i16, i16>(data, &writer_2),
err_fn,
)?,
cpal::SampleFormat::I32 => device.build_input_stream(
&config.into(),
move |data, _: &_| write_input_data::<i32, i32>(data, &writer_2),
err_fn,
)?,
cpal::SampleFormat::U16 => device.build_input_stream(
&config.into(),
move |data, _: &_| write_input_data::<u16, i16>(data, &writer_2),
Expand All @@ -140,8 +145,9 @@ fn main() -> Result<(), anyhow::Error> {

fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat {
match format {
cpal::SampleFormat::U16 => hound::SampleFormat::Int,
cpal::SampleFormat::I16 => hound::SampleFormat::Int,
cpal::SampleFormat::U16 | cpal::SampleFormat::I16 | cpal::SampleFormat::I32 => {
hound::SampleFormat::Int
}
cpal::SampleFormat::F32 => hound::SampleFormat::Float,
}
}
Expand Down
1 change: 1 addition & 0 deletions examples/wasm-beep/src/lib.rs
Expand Up @@ -36,6 +36,7 @@ pub fn beep() -> Handle {

Handle(match config.sample_format() {
cpal::SampleFormat::F32 => run::<f32>(&device, &config.into()),
cpal::SampleFormat::I32 => run::<i32>(&device, &config.into()),
cpal::SampleFormat::I16 => run::<i16>(&device, &config.into()),
cpal::SampleFormat::U16 => run::<u16>(&device, &config.into()),
})
Expand Down
10 changes: 6 additions & 4 deletions src/host/alsa/mod.rs
Expand Up @@ -321,18 +321,18 @@ impl Device {
let hw_params = alsa::pcm::HwParams::any(&handle)?;

// TODO: check endianess
const FORMATS: [(SampleFormat, alsa::pcm::Format); 3] = [
const FORMATS: [(SampleFormat, alsa::pcm::Format); 4] = [
//SND_PCM_FORMAT_S8,
//SND_PCM_FORMAT_U8,
(SampleFormat::I16, alsa::pcm::Format::S16LE),
//SND_PCM_FORMAT_S16_BE,
(SampleFormat::U16, alsa::pcm::Format::U16LE),
//SND_PCM_FORMAT_U16_BE,
//SND_PCM_FORMAT_S24_LE,
// (SampleFormat::I24, alsa::pcm::Format::S24LE),
//SND_PCM_FORMAT_S24_BE,
//SND_PCM_FORMAT_U24_LE,
//SND_PCM_FORMAT_U24_BE,
//SND_PCM_FORMAT_S32_LE,
(SampleFormat::I32, alsa::pcm::Format::S32LE),
//SND_PCM_FORMAT_S32_BE,
//SND_PCM_FORMAT_U32_LE,
//SND_PCM_FORMAT_U32_BE,
Expand All @@ -348,7 +348,7 @@ impl Device {
//SND_PCM_FORMAT_MPEG,
//SND_PCM_FORMAT_GSM,
//SND_PCM_FORMAT_SPECIAL,
//SND_PCM_FORMAT_S24_3LE,
//(SampleFormat::I24, alsa::pcm::Format::S243LE),
//SND_PCM_FORMAT_S24_3BE,
//SND_PCM_FORMAT_U24_3LE,
//SND_PCM_FORMAT_U24_3BE,
Expand Down Expand Up @@ -975,12 +975,14 @@ fn set_hw_params_from_format<'a>(
match sample_format {
SampleFormat::I16 => alsa::pcm::Format::S16BE,
SampleFormat::U16 => alsa::pcm::Format::U16BE,
SampleFormat::I32 => alsa::pcm::Format::S32BE,
SampleFormat::F32 => alsa::pcm::Format::FloatBE,
}
} else {
match sample_format {
SampleFormat::I16 => alsa::pcm::Format::S16LE,
SampleFormat::U16 => alsa::pcm::Format::U16LE,
SampleFormat::I32 => alsa::pcm::Format::S32LE,
SampleFormat::F32 => alsa::pcm::Format::FloatLE,
}
};
Expand Down
9 changes: 2 additions & 7 deletions src/host/asio/device.rs
Expand Up @@ -213,13 +213,8 @@ pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option<SampleFormat
sys::AsioSampleType::ASIOSTInt16LSB => SampleFormat::I16,
sys::AsioSampleType::ASIOSTFloat32MSB => SampleFormat::F32,
sys::AsioSampleType::ASIOSTFloat32LSB => SampleFormat::F32,
// NOTE: While ASIO does not support these formats directly, the stream callback created by
// CPAL supports converting back and forth between the following. This is because many ASIO
// drivers only support `Int32` formats, while CPAL does not support this format at all. We
// allow for this implicit conversion temporarily until CPAL gets support for an `I32`
// format.
sys::AsioSampleType::ASIOSTInt32MSB => SampleFormat::I16,
sys::AsioSampleType::ASIOSTInt32LSB => SampleFormat::I16,
sys::AsioSampleType::ASIOSTInt32MSB => SampleFormat::I32,
sys::AsioSampleType::ASIOSTInt32LSB => SampleFormat::I32,
_ => return None,
};
Some(fmt)
Expand Down
12 changes: 6 additions & 6 deletions src/host/asio/stream.rs
Expand Up @@ -181,7 +181,7 @@ impl Device {
// TODO: Add support for the following sample formats to CPAL and simplify the
// `process_output_callback` function above by removing the unnecessary sample
// conversion function.
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => {
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I32) => {
process_input_callback::<i32, i16, _, _>(
&mut data_callback,
&mut interleaved,
Expand All @@ -191,7 +191,7 @@ impl Device {
from_le,
);
}
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => {
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I32) => {
process_input_callback::<i32, i16, _, _>(
&mut data_callback,
&mut interleaved,
Expand Down Expand Up @@ -404,8 +404,8 @@ impl Device {
// TODO: Add support for the following sample formats to CPAL and simplify the
// `process_output_callback` function above by removing the unnecessary sample
// conversion function.
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => {
process_output_callback::<i16, i32, _, _>(
(SampleFormat::I32, &sys::AsioSampleType::ASIOSTInt32LSB) => {
process_output_callback::<i32, i32, _, _>(
&mut data_callback,
&mut interleaved,
silence,
Expand All @@ -415,8 +415,8 @@ impl Device {
to_le,
);
}
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => {
process_output_callback::<i16, i32, _, _>(
(SampleFormat::I32, &sys::AsioSampleType::ASIOSTInt32MSB) => {
process_output_callback::<i32, i32, _, _>(
&mut data_callback,
&mut interleaved,
silence,
Expand Down
4 changes: 4 additions & 0 deletions src/host/oboe/mod.rs
Expand Up @@ -397,6 +397,10 @@ impl DeviceTrait for Device {
description: "U16 format is not supported on Android.".to_owned(),
}
.into()),
SampleFormat::I32 => Err(BackendSpecificError {
description: "I32 format is not supported on Android.".to_owned(),
}
.into()),
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/host/wasapi/device.rs
Expand Up @@ -300,7 +300,7 @@ unsafe fn format_from_waveformatex_ptr(
a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 && a.Data4 == b.Data4
}
let sample_format = match (
(*waveformatex_ptr).wBitsPerSample,
(*waveformatex_ptr).wBitsPerSample, // 8 or 16 for integers, 32 for floats
(*waveformatex_ptr).wFormatTag,
) {
(16, mmreg::WAVE_FORMAT_PCM) => SampleFormat::I16,
Expand Down
32 changes: 26 additions & 6 deletions src/lib.rs
Expand Up @@ -99,6 +99,7 @@
//! let stream = match sample_format {
//! SampleFormat::F32 => device.build_output_stream(&config, write_silence::<f32>, err_fn),
//! SampleFormat::I16 => device.build_output_stream(&config, write_silence::<i16>, err_fn),
//! SampleFormat::I32 => device.build_output_stream(&config, write_silence::<i32>, err_fn),
//! SampleFormat::U16 => device.build_output_stream(&config, write_silence::<u16>, err_fn),
//! }.unwrap();
//!
Expand Down Expand Up @@ -603,14 +604,16 @@ impl SupportedStreamConfigRange {
/// - f32
/// - i16
/// - u16
/// - i32
/// - i24
///
/// **Sample rate**:
///
/// - 44100 (cd quality)
/// - Max sample rate
pub fn cmp_default_heuristics(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::Ordering::Equal;
use SampleFormat::{F32, I16, U16};
use SampleFormat::{F32, I16, I32, U16};

let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2));
if cmp_stereo != Equal {
Expand Down Expand Up @@ -642,6 +645,11 @@ impl SupportedStreamConfigRange {
return cmp_u16;
}

let cmp_i32 = (self.sample_format == I32).cmp(&(other.sample_format == I32));
if cmp_i32 != Equal {
return cmp_i32;
}

const HZ_44100: SampleRate = SampleRate(44_100);
let r44100_in_self = self.min_sample_rate <= HZ_44100 && HZ_44100 <= self.max_sample_rate;
let r44100_in_other =
Expand Down Expand Up @@ -693,6 +701,13 @@ fn test_cmp_default_heuristics() {
max_sample_rate: SampleRate(22050),
sample_format: SampleFormat::F32,
},
SupportedStreamConfigRange {
buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
channels: 2,
min_sample_rate: SampleRate(1),
max_sample_rate: SampleRate(96000),
sample_format: SampleFormat::I32,
},
];

formats.sort_by(|a, b| a.cmp_default_heuristics(b));
Expand All @@ -703,25 +718,30 @@ fn test_cmp_default_heuristics() {
assert_eq!(formats[0].max_sample_rate(), SampleRate(96000));
assert_eq!(formats[0].channels(), 1);

assert_eq!(formats[1].sample_format(), SampleFormat::U16);
assert_eq!(formats[1].sample_format(), SampleFormat::I32);
assert_eq!(formats[1].min_sample_rate(), SampleRate(1));
assert_eq!(formats[1].max_sample_rate(), SampleRate(96000));
assert_eq!(formats[1].channels(), 2);

assert_eq!(formats[2].sample_format(), SampleFormat::I16);
assert_eq!(formats[2].sample_format(), SampleFormat::U16);
assert_eq!(formats[2].min_sample_rate(), SampleRate(1));
assert_eq!(formats[2].max_sample_rate(), SampleRate(96000));
assert_eq!(formats[2].channels(), 2);

assert_eq!(formats[3].sample_format(), SampleFormat::F32);
assert_eq!(formats[3].sample_format(), SampleFormat::I16);
assert_eq!(formats[3].min_sample_rate(), SampleRate(1));
assert_eq!(formats[3].max_sample_rate(), SampleRate(22050));
assert_eq!(formats[3].max_sample_rate(), SampleRate(96000));
assert_eq!(formats[3].channels(), 2);

assert_eq!(formats[4].sample_format(), SampleFormat::F32);
assert_eq!(formats[4].min_sample_rate(), SampleRate(1));
assert_eq!(formats[4].max_sample_rate(), SampleRate(96000));
assert_eq!(formats[4].max_sample_rate(), SampleRate(22050));
assert_eq!(formats[4].channels(), 2);

assert_eq!(formats[5].sample_format(), SampleFormat::F32);
assert_eq!(formats[5].min_sample_rate(), SampleRate(1));
assert_eq!(formats[5].max_sample_rate(), SampleRate(96000));
assert_eq!(formats[5].channels(), 2);
}

impl From<SupportedStreamConfig> for StreamConfig {
Expand Down

0 comments on commit 1f7b8cb

Please sign in to comment.