Skip to content

Commit 1427642

Browse files
authored
Merge pull request #828 from RustAudio/fixed-source-attempt-3
Introduce FixedSource as experimental
2 parents dd70cd4 + 5139efe commit 1427642

4 files changed

Lines changed: 136 additions & 27 deletions

File tree

src/fixed_source.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Sources of sound and various filters which never change sample rate or
2+
//! channel count.
3+
use std::time::Duration;
4+
5+
use crate::{ChannelCount, Sample, SampleRate};
6+
7+
/// Similar to `Source`, something that can produce interleaved samples for a
8+
/// fixed amount of channels at a fixed sample rate. Those parameters never
9+
/// change.
10+
pub trait FixedSource: Iterator<Item = Sample> {
11+
/// May NEVER return something else once its returned a value
12+
fn channels(&self) -> ChannelCount;
13+
/// May NEVER return something else once its returned a value
14+
fn sample_rate(&self) -> SampleRate;
15+
/// Returns the total duration of this source, if known.
16+
///
17+
/// `None` indicates at the same time "infinite" or "unknown".
18+
fn total_duration(&self) -> Option<Duration>;
19+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ mod wav_output;
190190
pub mod buffer;
191191
pub mod conversions;
192192
pub mod decoder;
193+
#[cfg(feature = "experimental")]
194+
pub mod fixed_source;
193195
pub mod math;
194196
#[cfg(feature = "recording")]
195197
/// Microphone input support for audio recording.
@@ -201,6 +203,8 @@ pub mod static_buffer;
201203

202204
pub use crate::common::{BitDepth, ChannelCount, Float, Sample, SampleRate};
203205
pub use crate::decoder::Decoder;
206+
#[cfg(feature = "experimental")]
207+
pub use crate::fixed_source::FixedSource;
204208
pub use crate::player::Player;
205209
pub use crate::source::Source;
206210
pub use crate::spatial_player::SpatialPlayer;

src/speakers.rs

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -101,21 +101,16 @@
101101
102102
use core::fmt;
103103

104-
use cpal::{
105-
traits::{DeviceTrait, HostTrait},
106-
Device,
107-
};
104+
use cpal::traits::{DeviceTrait, HostTrait};
108105

109-
use crate::{common::assert_error_traits, DeviceSinkError};
106+
use crate::common::assert_error_traits;
110107

111108
mod builder;
112109
mod config;
113110

114111
pub use builder::SpeakersBuilder;
115112
pub use config::OutputConfig;
116113

117-
struct Speakers;
118-
119114
/// Error that can occur when we can not list the output devices
120115
#[derive(Debug, thiserror::Error, Clone)]
121116
#[error("Could not list output devices")]
@@ -130,7 +125,7 @@ pub struct Output {
130125
}
131126

132127
impl Output {
133-
/// TODO doc comment also mirror to microphone api
128+
/// Whether this output is the default sound output for the OS
134129
pub fn is_default(&self) -> bool {
135130
self.default
136131
}
@@ -176,13 +171,3 @@ pub fn available_outputs() -> Result<Vec<Output>, ListError> {
176171
});
177172
Ok(devices.collect())
178173
}
179-
180-
impl Speakers {
181-
fn open(
182-
device: Device,
183-
config: OutputConfig,
184-
error_callback: impl FnMut(cpal::StreamError) + Send + 'static,
185-
) -> Result<crate::stream::MixerDeviceSink, DeviceSinkError> {
186-
crate::stream::MixerDeviceSink::open(&device, &config.into_cpal_config(), error_callback)
187-
}
188-
}

src/speakers/builder.rs

Lines changed: 110 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
use std::{fmt::Debug, marker::PhantomData};
22

33
use cpal::{
4-
traits::{DeviceTrait, HostTrait},
4+
traits::{DeviceTrait, HostTrait, StreamTrait},
55
SupportedStreamConfigRange,
66
};
77

88
use crate::{
9-
common::assert_error_traits,
10-
speakers::{self, config::OutputConfig},
11-
ChannelCount, MixerDeviceSink, SampleRate,
9+
common::assert_error_traits, speakers::config::OutputConfig, ChannelCount, DeviceSinkError,
10+
FixedSource, MixerDeviceSink, SampleRate,
1211
};
1312

1413
/// Error configuring or opening speakers output
@@ -546,10 +545,112 @@ where
546545
/// # Ok::<(), Box<dyn std::error::Error>>(())
547546
/// ```
548547
pub fn open_mixer(&self) -> Result<MixerDeviceSink, crate::DeviceSinkError> {
549-
speakers::Speakers::open(
550-
self.device.as_ref().expect("DeviceIsSet").0.clone(),
551-
*self.config.as_ref().expect("ConfigIsSet"),
552-
self.error_callback.clone(),
553-
)
548+
let device = self.device.as_ref().expect("DeviceIsSet").0.clone();
549+
let config = *self.config.as_ref().expect("ConfigIsSet");
550+
let error_callback = self.error_callback.clone();
551+
crate::stream::MixerDeviceSink::open(&device, &config.into_cpal_config(), error_callback)
554552
}
553+
554+
// TODO
555+
// pub fn open_queue() -> Result<QueueSink, DeviceSinkError> {
556+
// todo!()
557+
// }
558+
559+
/// Open the device with the current configuration and play a single
560+
/// `FixedSource` on it.
561+
pub fn play(
562+
self,
563+
mut source: impl FixedSource + Send + 'static,
564+
) -> Result<SinkHandle, PlayError> {
565+
use cpal::Sample as _;
566+
567+
let config = self.config.expect("ConfigIsSet");
568+
let device = self.device.expect("DeviceIsSet").0;
569+
570+
if config.channel_count != source.channels() {
571+
return Err(PlayError::WrongChannelCount {
572+
sink: config.channel_count,
573+
fixed_source: source.channels(),
574+
});
575+
}
576+
if config.sample_rate != source.sample_rate() {
577+
return Err(PlayError::WrongSampleRate {
578+
sink: config.sample_rate,
579+
fixed_source: source.sample_rate(),
580+
});
581+
}
582+
583+
let cpal_config1 = config.into_cpal_config();
584+
let cpal_config2 = (&cpal_config1).into();
585+
586+
macro_rules! build_output_streams {
587+
($($sample_format:tt, $generic:ty);+) => {
588+
match config.sample_format {
589+
$(
590+
cpal::SampleFormat::$sample_format => device.build_output_stream::<$generic, _, _>(
591+
&cpal_config2,
592+
move |data, _| {
593+
data.iter_mut().for_each(|d| {
594+
*d = source
595+
.next()
596+
.map(cpal::Sample::from_sample)
597+
.unwrap_or(<$generic>::EQUILIBRIUM)
598+
})
599+
},
600+
self.error_callback,
601+
None,
602+
),
603+
)+
604+
_ => return Err(DeviceSinkError::UnsupportedSampleFormat.into()),
605+
}
606+
};
607+
}
608+
609+
let result = build_output_streams!(
610+
F32, f32;
611+
F64, f64;
612+
I8, i8;
613+
I16, i16;
614+
I24, cpal::I24;
615+
I32, i32;
616+
I64, i64;
617+
U8, u8;
618+
U16, u16;
619+
U24, cpal::U24;
620+
U32, u32;
621+
U64, u64
622+
);
623+
624+
let stream = result.map_err(DeviceSinkError::BuildError)?;
625+
stream.play().map_err(DeviceSinkError::PlayError)?;
626+
627+
Ok(SinkHandle { _stream: stream })
628+
}
629+
}
630+
631+
// TODO cant introduce till we have introduced the other fixed source parts
632+
// pub struct QueueSink;
633+
634+
/// A sink handle. When this is dropped anything playing through this Sink will
635+
/// stop playing.
636+
pub struct SinkHandle {
637+
_stream: cpal::Stream,
638+
}
639+
640+
#[derive(Debug, thiserror::Error)]
641+
pub enum PlayError {
642+
#[error("DeviceSink channel count ({sink}) does not match the source channel count ({fixed_source})")]
643+
WrongChannelCount {
644+
sink: ChannelCount,
645+
fixed_source: ChannelCount,
646+
},
647+
#[error(
648+
"DeviceSink sample rate ({sink}) does not match the source sample rate ({fixed_source})"
649+
)]
650+
WrongSampleRate {
651+
sink: SampleRate,
652+
fixed_source: SampleRate,
653+
},
654+
#[error(transparent)]
655+
DeviceSink(#[from] crate::DeviceSinkError),
555656
}

0 commit comments

Comments
 (0)