Skip to content

Commit

Permalink
Intial support for 24 bits sample format (24bit stored on 4 bytes)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
abique committed Sep 24, 2024
1 parent 6ecfec4 commit 49ef872
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 22 deletions.
4 changes: 4 additions & 0 deletions examples/synth_tones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,14 @@ where
match config.sample_format() {
cpal::SampleFormat::I8 => make_stream::<i8>(&device, &config.into()),
cpal::SampleFormat::I16 => make_stream::<i16>(&device, &config.into()),
cpal::SampleFormat::I24 => make_stream::<i32>(&device, &config.into()),
// cpal::SampleFormat::I24_3 => make_stream::<[i8; 3]>(&device, &config.into()),
cpal::SampleFormat::I32 => make_stream::<i32>(&device, &config.into()),
cpal::SampleFormat::I64 => make_stream::<i64>(&device, &config.into()),
cpal::SampleFormat::U8 => make_stream::<u8>(&device, &config.into()),
cpal::SampleFormat::U16 => make_stream::<u16>(&device, &config.into()),
cpal::SampleFormat::U24 => make_stream::<u32>(&device, &config.into()),
// cpal::SampleFormat::U24_3 => make_stream::<[i8; 3]>(&device, &config.into()),
cpal::SampleFormat::U32 => make_stream::<u32>(&device, &config.into()),
cpal::SampleFormat::U64 => make_stream::<u64>(&device, &config.into()),
cpal::SampleFormat::F32 => make_stream::<f32>(&device, &config.into()),
Expand Down
21 changes: 13 additions & 8 deletions src/host/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,16 +323,17 @@ 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),
//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,
(SampleFormat::U24, alsa::pcm::Format::U24LE),
//SND_PCM_FORMAT_U24_BE,
(SampleFormat::I32, alsa::pcm::Format::S32LE),
//SND_PCM_FORMAT_S32_BE,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
36 changes: 33 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
43 changes: 32 additions & 11 deletions src/samples_formats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,

Expand All @@ -70,7 +76,8 @@ impl SampleFormat {
match *self {
SampleFormat::I8 | SampleFormat::U8 => mem::size_of::<i8>(),
SampleFormat::I16 | SampleFormat::U16 => mem::size_of::<i16>(),
// SampleFormat::I24 | SampleFormat::U24 => 3,
// SampleFormat::I24_3 | SampleFormat::U24_3 => 3,
SampleFormat::I24 | SampleFormat::U24 => 4,
SampleFormat::I32 | SampleFormat::U32 => mem::size_of::<i32>(),
// SampleFormat::I48 | SampleFormat::U48 => 6,
SampleFormat::I64 | SampleFormat::U64 => mem::size_of::<i64>(),
Expand All @@ -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
)
}

Expand All @@ -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
)
}

Expand All @@ -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",
Expand All @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit 49ef872

Please sign in to comment.