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

24 Bit Sample Support #531

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/beep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ 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(),
cpal::SampleFormat::I24 => unimplemented!(),
}
}

Expand Down
12 changes: 10 additions & 2 deletions examples/record_wav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ fn main() -> Result<(), anyhow::Error> {
move |data, _: &_| write_input_data::<i16, i16>(data, &writer_2),
err_fn,
)?,
cpal::SampleFormat::I24 => unimplemented!(),
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 @@ -70,9 +76,11 @@ 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,
cpal::SampleFormat::I24 => unimplemented!()
}
}

Expand Down
12 changes: 8 additions & 4 deletions src/host/alsa/mod.rs
Original file line number Diff line number Diff line change
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); 5] = [
//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,16 @@ fn set_hw_params_from_format<'a>(
match sample_format {
SampleFormat::I16 => alsa::pcm::Format::S16BE,
SampleFormat::U16 => alsa::pcm::Format::U16BE,
SampleFormat::I24 => alsa::pcm::Format::S24BE,
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::I24 => alsa::pcm::Format::S24LE,
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
Original file line number Diff line number Diff line change
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
52 changes: 45 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@
//! 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),
//! SampleFormat::I24 => unimplemented!(),
//! }.unwrap();
//!
//! fn write_silence<T: Sample>(data: &mut [T], _: &cpal::OutputCallbackInfo) {
Expand Down Expand Up @@ -157,7 +159,7 @@ pub use platform::{
available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream,
SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS,
};
pub use samples_formats::{Sample, SampleFormat};
pub use samples_formats::{Sample, SampleFormat, Unpacked24};
use std::convert::TryInto;
use std::ops::{Div, Mul};
use std::time::Duration;
Expand Down Expand Up @@ -603,14 +605,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, I24};

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

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

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

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 +707,20 @@ 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::I24,
},
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 +731,35 @@ 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::I24);
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::I32);
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::U16);
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].sample_format(), SampleFormat::I16);
assert_eq!(formats[4].min_sample_rate(), SampleRate(1));
assert_eq!(formats[4].max_sample_rate(), SampleRate(96000));
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(22050));
assert_eq!(formats[5].channels(), 2);

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

impl From<SupportedStreamConfig> for StreamConfig {
Expand Down
Loading