Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

32 bit Sample Format support #420

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/android.rs
Expand Up @@ -18,6 +18,7 @@ fn main() {
match config.sample_format() {
cpal::SampleFormat::F32 => run::<f32>(&device, &config.into()).unwrap(),
cpal::SampleFormat::I16 => run::<i16>(&device, &config.into()).unwrap(),
cpal::SampleFormat::I32 => run::<i32>(&device, &config.into()).unwrap(),
cpal::SampleFormat::U16 => run::<u16>(&device, &config.into()).unwrap(),
}
}
Expand Down
1 change: 1 addition & 0 deletions examples/beep.rs
Expand Up @@ -86,6 +86,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 @@ -122,6 +122,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 @@ -141,8 +146,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 @@ -312,18 +312,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 @@ -339,7 +339,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 @@ -958,12 +958,14 @@ fn set_hw_params_from_format(
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 @@ -209,13 +209,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
18 changes: 9 additions & 9 deletions src/host/asio/stream.rs
Expand Up @@ -181,8 +181,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.
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => {
process_input_callback::<i32, i16, _, _>(
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I32) => {
process_input_callback::<i32, i32, _, _>(
&mut data_callback,
&mut interleaved,
asio_stream,
Expand All @@ -191,8 +191,8 @@ impl Device {
from_le,
);
}
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => {
process_input_callback::<i32, i16, _, _>(
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I32) => {
process_input_callback::<i32, i32, _, _>(
&mut data_callback,
&mut interleaved,
asio_stream,
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 Expand Up @@ -675,7 +675,7 @@ fn check_config(
}
// unsigned formats are not supported by asio
match sample_format {
SampleFormat::I16 | SampleFormat::F32 => (),
SampleFormat::I16 | SampleFormat::I32 | SampleFormat::F32 => (),
SampleFormat::U16 => return Err(BuildStreamError::StreamConfigNotSupported),
}
if *channels > num_asio_channels {
Expand Down
8 changes: 8 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 Expand Up @@ -466,6 +470,10 @@ impl DeviceTrait for Device {
.into())
}
}
SampleFormat::I32 => Err(BackendSpecificError {
description: "I32 format is not supported on Android.".to_owned(),
}
.into()),
SampleFormat::U16 => Err(BackendSpecificError {
description: "U16 format is not supported on Android.".to_owned(),
}
Expand Down
8 changes: 4 additions & 4 deletions 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 Expand Up @@ -1228,7 +1228,7 @@ fn config_to_waveformatextensible(
let format_tag = match sample_format {
SampleFormat::I16 => mmreg::WAVE_FORMAT_PCM,
SampleFormat::F32 => mmreg::WAVE_FORMAT_EXTENSIBLE,
SampleFormat::U16 => return None,
SampleFormat::U16 | SampleFormat::I32 => return None,
};
let channels = config.channels as WORD;
let sample_rate = config.sample_rate.0 as DWORD;
Expand All @@ -1243,7 +1243,7 @@ fn config_to_waveformatextensible(
let ex_size = mem::size_of::<mmreg::WAVEFORMATEX>();
(extensible_size - ex_size) as WORD
}
SampleFormat::U16 => return None,
SampleFormat::U16 | SampleFormat::I32 => return None,
};
let waveformatex = mmreg::WAVEFORMATEX {
wFormatTag: format_tag,
Expand All @@ -1263,7 +1263,7 @@ fn config_to_waveformatextensible(
let sub_format = match sample_format {
SampleFormat::I16 => ksmedia::KSDATAFORMAT_SUBTYPE_PCM,
SampleFormat::F32 => ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
SampleFormat::U16 => return None,
SampleFormat::U16 | SampleFormat::I32 => return None,
};
let waveformatextensible = mmreg::WAVEFORMATEXTENSIBLE {
Format: waveformatex,
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