Skip to content

Commit 4af1794

Browse files
authored
Merge pull request #796 from RustAudio/feat/24-bit-output
feat: support I24 output sample format
2 parents 47dab16 + c4c6de2 commit 4af1794

File tree

6 files changed

+57
-120
lines changed

6 files changed

+57
-120
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Adds a new method on source: record which collects all samples into a
2121
SamplesBuffer.
2222
- Adds `wav_to_writer` which writes a `Source` to a writer.
23+
- Added supported for `I24` output (24-bit samples on 4 bytes storage).
2324

2425
### Fixed
2526
- docs.rs will now document all features, including those that are optional.

src/math.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ mod test {
139139
/// - Practical audio range (-60dB to +40dB): max errors ~1x ε
140140
/// - Extended range (-100dB to +100dB): max errors ~2.3x ε
141141
/// - Extreme edge cases beyond ±100dB have larger errors but are rarely used
142-
142+
///
143143
/// Based on [Wikipedia's Decibel article].
144144
///
145145
/// [Wikipedia's Decibel article]: https://web.archive.org/web/20230810185300/https://en.wikipedia.org/wiki/Decibel

src/microphone.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ mod builder;
111111
mod config;
112112
pub use builder::MicrophoneBuilder;
113113
pub use config::InputConfig;
114+
use cpal::I24;
114115
use cpal::{
115116
traits::{DeviceTrait, HostTrait, StreamTrait},
116117
Device,
@@ -265,10 +266,13 @@ impl Microphone {
265266
F64, f64;
266267
I8, i8;
267268
I16, i16;
269+
I24, I24;
268270
I32, i32;
269271
I64, i64;
270272
U8, u8;
271273
U16, u16;
274+
// TODO: uncomment when https://github.com/RustAudio/cpal/pull/1011 is merged
275+
// U24, cpal::U24;
272276
U32, u32;
273277
U64, u64
274278
)

src/mixer.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,9 @@ impl MixerSource {
177177
let mut pending = self.input.0.pending_sources.lock().unwrap(); // TODO: relax ordering?
178178

179179
for source in pending.drain(..) {
180-
let in_step = self.sample_count % source.channels().get() as usize == 0;
180+
let in_step = self
181+
.sample_count
182+
.is_multiple_of(source.channels().get() as usize);
181183

182184
if in_step {
183185
self.current_sources.push(source);

src/source/linear_ramp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ where
8888
factor = self.start_gain * (1.0f32 - p) + self.end_gain * p;
8989
}
9090

91-
if self.sample_idx % (self.channels().get() as u64) == 0 {
91+
if self.sample_idx.is_multiple_of(self.channels().get() as u64) {
9292
self.elapsed_ns += 1000000000.0 / (self.input.sample_rate().get() as f32);
9393
}
9494

src/stream.rs

Lines changed: 47 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
//! There is also a convenience function `play` for using that output mixer to
77
//! play a single sound.
88
use crate::common::{assert_error_traits, ChannelCount, SampleRate};
9-
use crate::decoder;
109
use crate::math::nz;
11-
use crate::mixer::{mixer, Mixer, MixerSource};
10+
use crate::mixer::{mixer, Mixer};
1211
use crate::sink::Sink;
12+
use crate::{decoder, Source};
1313
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
14-
use cpal::{BufferSize, Sample, SampleFormat, StreamConfig};
14+
use cpal::{BufferSize, Sample, SampleFormat, StreamConfig, I24};
1515
use std::fmt;
1616
use std::io::{Read, Seek};
1717
use std::marker::Sync;
@@ -462,128 +462,58 @@ impl OutputStream {
462462
})
463463
}
464464

465-
fn init_stream<E>(
465+
fn init_stream<S, E>(
466466
device: &cpal::Device,
467467
config: &OutputStreamConfig,
468-
mut samples: MixerSource,
468+
mut samples: S,
469469
error_callback: E,
470470
) -> Result<cpal::Stream, StreamError>
471471
where
472+
S: Source + Send + 'static,
472473
E: FnMut(cpal::StreamError) + Send + 'static,
473474
{
474-
let sample_format = config.sample_format;
475-
let config = config.into();
476-
477-
match sample_format {
478-
cpal::SampleFormat::F32 => device.build_output_stream::<f32, _, _>(
479-
&config,
480-
move |data, _| {
481-
data.iter_mut()
482-
.for_each(|d| *d = samples.next().unwrap_or(0f32))
483-
},
484-
error_callback,
485-
None,
486-
),
487-
cpal::SampleFormat::F64 => device.build_output_stream::<f64, _, _>(
488-
&config,
489-
move |data, _| {
490-
data.iter_mut()
491-
.for_each(|d| *d = samples.next().map(Sample::from_sample).unwrap_or(0f64))
492-
},
493-
error_callback,
494-
None,
495-
),
496-
cpal::SampleFormat::I8 => device.build_output_stream::<i8, _, _>(
497-
&config,
498-
move |data, _| {
499-
data.iter_mut()
500-
.for_each(|d| *d = samples.next().map(Sample::from_sample).unwrap_or(0i8))
501-
},
502-
error_callback,
503-
None,
504-
),
505-
cpal::SampleFormat::I16 => device.build_output_stream::<i16, _, _>(
506-
&config,
507-
move |data, _| {
508-
data.iter_mut()
509-
.for_each(|d| *d = samples.next().map(Sample::from_sample).unwrap_or(0i16))
510-
},
511-
error_callback,
512-
None,
513-
),
514-
cpal::SampleFormat::I32 => device.build_output_stream::<i32, _, _>(
515-
&config,
516-
move |data, _| {
517-
data.iter_mut()
518-
.for_each(|d| *d = samples.next().map(Sample::from_sample).unwrap_or(0i32))
519-
},
520-
error_callback,
521-
None,
522-
),
523-
cpal::SampleFormat::I64 => device.build_output_stream::<i64, _, _>(
524-
&config,
525-
move |data, _| {
526-
data.iter_mut()
527-
.for_each(|d| *d = samples.next().map(Sample::from_sample).unwrap_or(0i64))
528-
},
529-
error_callback,
530-
None,
531-
),
532-
cpal::SampleFormat::U8 => device.build_output_stream::<u8, _, _>(
533-
&config,
534-
move |data, _| {
535-
data.iter_mut().for_each(|d| {
536-
*d = samples
537-
.next()
538-
.map(Sample::from_sample)
539-
.unwrap_or(u8::MAX / 2)
540-
})
541-
},
542-
error_callback,
543-
None,
544-
),
545-
cpal::SampleFormat::U16 => device.build_output_stream::<u16, _, _>(
546-
&config,
547-
move |data, _| {
548-
data.iter_mut().for_each(|d| {
549-
*d = samples
550-
.next()
551-
.map(Sample::from_sample)
552-
.unwrap_or(u16::MAX / 2)
553-
})
554-
},
555-
error_callback,
556-
None,
557-
),
558-
cpal::SampleFormat::U32 => device.build_output_stream::<u32, _, _>(
559-
&config,
560-
move |data, _| {
561-
data.iter_mut().for_each(|d| {
562-
*d = samples
563-
.next()
564-
.map(Sample::from_sample)
565-
.unwrap_or(u32::MAX / 2)
566-
})
567-
},
568-
error_callback,
569-
None,
570-
),
571-
cpal::SampleFormat::U64 => device.build_output_stream::<u64, _, _>(
572-
&config,
573-
move |data, _| {
574-
data.iter_mut().for_each(|d| {
575-
*d = samples
576-
.next()
577-
.map(Sample::from_sample)
578-
.unwrap_or(u64::MAX / 2)
579-
})
580-
},
581-
error_callback,
582-
None,
583-
),
584-
_ => return Err(StreamError::UnsupportedSampleFormat),
475+
let cpal_config = config.into();
476+
477+
macro_rules! build_output_streams {
478+
($($sample_format:tt, $generic:ty);+) => {
479+
match config.sample_format {
480+
$(
481+
cpal::SampleFormat::$sample_format => device.build_output_stream::<$generic, _, _>(
482+
&cpal_config,
483+
move |data, _| {
484+
data.iter_mut().for_each(|d| {
485+
*d = samples
486+
.next()
487+
.map(Sample::from_sample)
488+
.unwrap_or(<$generic>::EQUILIBRIUM)
489+
})
490+
},
491+
error_callback,
492+
None,
493+
),
494+
)+
495+
_ => return Err(StreamError::UnsupportedSampleFormat),
496+
}
497+
};
585498
}
586-
.map_err(StreamError::BuildStreamError)
499+
500+
let result = build_output_streams!(
501+
F32, f32;
502+
F64, f64;
503+
I8, i8;
504+
I16, i16;
505+
I24, I24;
506+
I32, i32;
507+
I64, i64;
508+
U8, u8;
509+
U16, u16;
510+
// TODO: uncomment when https://github.com/RustAudio/cpal/pull/1011 is merged
511+
// U24, U24;
512+
U32, u32;
513+
U64, u64
514+
);
515+
516+
result.map_err(StreamError::BuildStreamError)
587517
}
588518
}
589519

0 commit comments

Comments
 (0)