From 49ef872f2409bf011bdd79ab5edd4986b4f6d73a Mon Sep 17 00:00:00 2001 From: Alexandre Bique Date: Sun, 22 Sep 2024 11:29:55 +0200 Subject: [PATCH] Intial support for 24 bits sample format (24bit stored on 4 bytes) There are two common ways of storing 24 bits samples: - on 4 bytes (called 24, most common) - on 3 bytes (called 24_3) With the current code, 3 bytes seems difficult to implement and would require a larger code refactoring. That being said, having at least '24' helps to work with hardware that only offer it. --- examples/synth_tones.rs | 4 ++++ src/host/alsa/mod.rs | 21 ++++++++++++-------- src/lib.rs | 36 +++++++++++++++++++++++++++++++--- src/samples_formats.rs | 43 ++++++++++++++++++++++++++++++----------- 4 files changed, 82 insertions(+), 22 deletions(-) diff --git a/examples/synth_tones.rs b/examples/synth_tones.rs index c83f816d9..3430253dd 100644 --- a/examples/synth_tones.rs +++ b/examples/synth_tones.rs @@ -98,10 +98,14 @@ where match config.sample_format() { cpal::SampleFormat::I8 => make_stream::(&device, &config.into()), cpal::SampleFormat::I16 => make_stream::(&device, &config.into()), + cpal::SampleFormat::I24 => make_stream::(&device, &config.into()), + // cpal::SampleFormat::I24_3 => make_stream::<[i8; 3]>(&device, &config.into()), cpal::SampleFormat::I32 => make_stream::(&device, &config.into()), cpal::SampleFormat::I64 => make_stream::(&device, &config.into()), cpal::SampleFormat::U8 => make_stream::(&device, &config.into()), cpal::SampleFormat::U16 => make_stream::(&device, &config.into()), + cpal::SampleFormat::U24 => make_stream::(&device, &config.into()), + // cpal::SampleFormat::U24_3 => make_stream::<[i8; 3]>(&device, &config.into()), cpal::SampleFormat::U32 => make_stream::(&device, &config.into()), cpal::SampleFormat::U64 => make_stream::(&device, &config.into()), cpal::SampleFormat::F32 => make_stream::(&device, &config.into()), diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index 2085c20b7..2bd662f12 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -323,7 +323,7 @@ impl Device { let hw_params = alsa::pcm::HwParams::any(handle)?; // TODO: check endianness - const FORMATS: [(SampleFormat, alsa::pcm::Format); 8] = [ + const FORMATS: [(SampleFormat, alsa::pcm::Format); 10] = [ (SampleFormat::I8, alsa::pcm::Format::S8), (SampleFormat::U8, alsa::pcm::Format::U8), (SampleFormat::I16, alsa::pcm::Format::S16LE), @@ -331,8 +331,9 @@ impl Device { (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, + (SampleFormat::U24, alsa::pcm::Format::U24LE), //SND_PCM_FORMAT_U24_BE, (SampleFormat::I32, alsa::pcm::Format::S32LE), //SND_PCM_FORMAT_S32_BE, @@ -350,9 +351,9 @@ impl Device { //SND_PCM_FORMAT_MPEG, //SND_PCM_FORMAT_GSM, //SND_PCM_FORMAT_SPECIAL, - //SND_PCM_FORMAT_S24_3LE, + //(SampleFormat::I24_3, alsa::pcm::Format::S243LE), //SND_PCM_FORMAT_S24_3BE, - //SND_PCM_FORMAT_U24_3LE, + //(SampleFormat::U24_3, alsa::pcm::Format::U243LE), //SND_PCM_FORMAT_U24_3BE, //SND_PCM_FORMAT_S20_3LE, //SND_PCM_FORMAT_S20_3BE, @@ -1009,13 +1010,15 @@ fn set_hw_params_from_format( match sample_format { SampleFormat::I8 => alsa::pcm::Format::S8, SampleFormat::I16 => alsa::pcm::Format::S16BE, - // SampleFormat::I24 => alsa::pcm::Format::S24BE, + SampleFormat::I24 => alsa::pcm::Format::S24BE, + // SampleFormat::I24_3 => alsa::pcm::Format::S243BE, SampleFormat::I32 => alsa::pcm::Format::S32BE, // SampleFormat::I48 => alsa::pcm::Format::S48BE, // SampleFormat::I64 => alsa::pcm::Format::S64BE, SampleFormat::U8 => alsa::pcm::Format::U8, SampleFormat::U16 => alsa::pcm::Format::U16BE, - // SampleFormat::U24 => alsa::pcm::Format::U24BE, + SampleFormat::U24 => alsa::pcm::Format::U24BE, + // SampleFormat::U24_3 => alsa::pcm::Format::U243BE, SampleFormat::U32 => alsa::pcm::Format::U32BE, // SampleFormat::U48 => alsa::pcm::Format::U48BE, // SampleFormat::U64 => alsa::pcm::Format::U64BE, @@ -1034,13 +1037,15 @@ fn set_hw_params_from_format( match sample_format { SampleFormat::I8 => alsa::pcm::Format::S8, SampleFormat::I16 => alsa::pcm::Format::S16LE, - // SampleFormat::I24 => alsa::pcm::Format::S24LE, + SampleFormat::I24 => alsa::pcm::Format::S24LE, + // SampleFormat::I24_3 => alsa::pcm::Format::S243LE, SampleFormat::I32 => alsa::pcm::Format::S32LE, // SampleFormat::I48 => alsa::pcm::Format::S48LE, // SampleFormat::I64 => alsa::pcm::Format::S64LE, SampleFormat::U8 => alsa::pcm::Format::U8, SampleFormat::U16 => alsa::pcm::Format::U16LE, - // SampleFormat::U24 => alsa::pcm::Format::U24LE, + SampleFormat::U24 => alsa::pcm::Format::U24LE, + // SampleFormat::U24_3 => alsa::pcm::Format::U243LE, SampleFormat::U32 => alsa::pcm::Format::U32LE, // SampleFormat::U48 => alsa::pcm::Format::U48LE, // SampleFormat::U64 => alsa::pcm::Format::U64LE, diff --git a/src/lib.rs b/src/lib.rs index 74405a4a2..c4061f508 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ //! ``` //! //! Before we can create a stream, we must decide what the configuration of the audio stream is -//! going to be. +//! going to be. //! You can query all the supported configurations with the //! [`supported_input_configs()`] and [`supported_output_configs()`] methods. //! These produce a list of [`SupportedStreamConfigRange`] structs which can later be turned into @@ -225,7 +225,7 @@ pub type FrameCount = u32; /// behavior of the given host. Note, the default buffer size may be surprisingly /// large, leading to latency issues. If low latency is desired, [`Fixed(FrameCount)`] /// should be used in accordance with the [`SupportedBufferSize`] range produced by -/// the [`SupportedStreamConfig`] API. +/// the [`SupportedStreamConfig`] API. /// /// [`Default`]: BufferSize::Default /// [`Fixed(FrameCount)`]: BufferSize::Fixed @@ -695,7 +695,7 @@ impl SupportedStreamConfigRange { /// - 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, I24, I32, U16, U24, U32}; let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2)); if cmp_stereo != Equal { @@ -717,6 +717,36 @@ impl SupportedStreamConfigRange { return cmp_f32; } + let cmp_i32 = (self.sample_format == I32).cmp(&(other.sample_format == I32)); + if cmp_i32 != Equal { + return cmp_i32; + } + + let cmp_u32 = (self.sample_format == U32).cmp(&(other.sample_format == U32)); + if cmp_u32 != Equal { + return cmp_u32; + } + + let cmp_i24 = (self.sample_format == I24).cmp(&(other.sample_format == I24)); + if cmp_i24 != Equal { + return cmp_i24; + } + + let cmp_u24 = (self.sample_format == U24).cmp(&(other.sample_format == U24)); + if cmp_u24 != Equal { + return cmp_u24; + } + + // let cmp_i24_3 = (self.sample_format == I24_3).cmp(&(other.sample_format == I24_3)); + // if cmp_i24_3 != Equal { + // return cmp_i24_3; + // } + + // let cmp_u24_3 = (self.sample_format == U24_3).cmp(&(other.sample_format == U24_3)); + // if cmp_u24_3 != Equal { + // return cmp_u24_3; + // } + let cmp_i16 = (self.sample_format == I16).cmp(&(other.sample_format == I16)); if cmp_i16 != Equal { return cmp_i16; diff --git a/src/samples_formats.rs b/src/samples_formats.rs index bf2197f91..72eee4203 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -29,12 +29,15 @@ pub enum SampleFormat { /// `i16` with a valid range of `i16::MIN..=i16::MAX` with `0` being the origin. I16, + /// `I24` with a valid range of '-(1 << 23)..(1 << 23)' with `0` being the origin + I24, + // /// `I24` with a valid range of '-(1 << 23)..(1 << 23)' with `0` being the origin - // I24, + // I24_3, /// `i32` with a valid range of `i32::MIN..=i32::MAX` with `0` being the origin. I32, - // /// `I24` with a valid range of '-(1 << 47)..(1 << 47)' with `0` being the origin + // /// `I48` with a valid range of '-(1 << 47)..(1 << 47)' with `0` being the origin // I48, /// `i64` with a valid range of `i64::MIN..=i64::MAX` with `0` being the origin. I64, @@ -45,8 +48,11 @@ pub enum SampleFormat { /// `u16` with a valid range of `u16::MIN..=u16::MAX` with `1 << 15 == 32768` being the origin. U16, - // /// `U24` with a valid range of '0..16777216' with `1 << 23 == 8388608` being the origin - // U24, + /// `U24` with a valid range of '0..16777216' with `1 << 23 == 8388608` being the origin + U24, + + // /// `U24_3` with a valid range of '0..16777216' with `1 << 23 == 8388608` being the origin + // U24_3, /// `u32` with a valid range of `u32::MIN..=u32::MAX` with `1 << 31` being the origin. U32, @@ -70,7 +76,8 @@ impl SampleFormat { match *self { SampleFormat::I8 | SampleFormat::U8 => mem::size_of::(), SampleFormat::I16 | SampleFormat::U16 => mem::size_of::(), - // SampleFormat::I24 | SampleFormat::U24 => 3, + // SampleFormat::I24_3 | SampleFormat::U24_3 => 3, + SampleFormat::I24 | SampleFormat::U24 => 4, SampleFormat::I32 | SampleFormat::U32 => mem::size_of::(), // SampleFormat::I48 | SampleFormat::U48 => 6, SampleFormat::I64 | SampleFormat::U64 => mem::size_of::(), @@ -85,7 +92,11 @@ impl SampleFormat { //matches!(*self, SampleFormat::I8 | SampleFormat::I16 | SampleFormat::I24 | SampleFormat::I32 | SampleFormat::I48 | SampleFormat::I64) matches!( *self, - SampleFormat::I8 | SampleFormat::I16 | SampleFormat::I32 | SampleFormat::I64 + SampleFormat::I8 + | SampleFormat::I16 + | SampleFormat::I24 + | SampleFormat::I32 + | SampleFormat::I64 ) } @@ -95,7 +106,11 @@ impl SampleFormat { //matches!(*self, SampleFormat::U8 | SampleFormat::U16 | SampleFormat::U24 | SampleFormat::U32 | SampleFormat::U48 | SampleFormat::U64) matches!( *self, - SampleFormat::U8 | SampleFormat::U16 | SampleFormat::U32 | SampleFormat::U64 + SampleFormat::U8 + | SampleFormat::U16 + | SampleFormat::U24 + | SampleFormat::U32 + | SampleFormat::U64 ) } @@ -111,13 +126,15 @@ impl Display for SampleFormat { match *self { SampleFormat::I8 => "i8", SampleFormat::I16 => "i16", - // SampleFormat::I24 => "i24", + SampleFormat::I24 => "i24", + // SampleFormat::I24_3 => "i24_3", SampleFormat::I32 => "i32", // SampleFormat::I48 => "i48", SampleFormat::I64 => "i64", SampleFormat::U8 => "u8", SampleFormat::U16 => "u16", - // SampleFormat::U24 => "u24", + SampleFormat::U24 => "u24", + // SampleFormat::U24_3 => "u24_3", SampleFormat::U32 => "u32", // SampleFormat::U48 => "u48", SampleFormat::U64 => "u64", @@ -140,7 +157,9 @@ impl SizedSample for i16 { const FORMAT: SampleFormat = SampleFormat::I16; } -// impl SizedSample for I24 { const FORMAT: SampleFormat = SampleFormat::I24; } +impl SizedSample for I24 { + const FORMAT: SampleFormat = SampleFormat::I24; +} impl SizedSample for i32 { const FORMAT: SampleFormat = SampleFormat::I32; @@ -160,7 +179,9 @@ impl SizedSample for u16 { const FORMAT: SampleFormat = SampleFormat::U16; } -// impl SizedSample for U24 { const FORMAT: SampleFormat = SampleFormat::U24; } +impl SizedSample for U24 { + const FORMAT: SampleFormat = SampleFormat::U24; +} impl SizedSample for u32 { const FORMAT: SampleFormat = SampleFormat::U32;