|
1 | 1 | use std::{fmt::Debug, marker::PhantomData}; |
2 | 2 |
|
3 | 3 | use cpal::{ |
4 | | - traits::{DeviceTrait, HostTrait}, |
| 4 | + traits::{DeviceTrait, HostTrait, StreamTrait}, |
5 | 5 | SupportedStreamConfigRange, |
6 | 6 | }; |
7 | 7 |
|
8 | 8 | 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, |
12 | 11 | }; |
13 | 12 |
|
14 | 13 | /// Error configuring or opening speakers output |
@@ -546,10 +545,112 @@ where |
546 | 545 | /// # Ok::<(), Box<dyn std::error::Error>>(()) |
547 | 546 | /// ``` |
548 | 547 | 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) |
554 | 552 | } |
| 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), |
555 | 656 | } |
0 commit comments