- let device = host
- .output_devices()
- .unwrap()
- .find(|d| d.name().unwrap() == output_card)
- .unwrap();
- let mut configs = device.supported_output_configs().unwrap();
- // TODO: channels == 1 doesn't work on a Raspberry Pi
- // make this configurable and support interleaving LRLR stereo samples if using 2 channels
- let config = configs
- .find(|c| c.channels() == 1 && c.sample_format() == SampleFormat::I16)
- .unwrap()
- .with_sample_rate(SampleRate(8000));
- let stream = device
- .build_output_stream(
- &config.into(),
- move |data: &mut [i16], _info: &cpal::OutputCallbackInfo| {
- output_cb(data, &state);
- },
- |e| {
- // trigger end_tx here? always more edge cases
- debug!("error occurred in codec2 playback: {e:?}");
- },
- None,
- )
- .unwrap();
- stream.play().unwrap();
+ let device = if let Some(output_card) = output_card {
+ // TODO: more error handling for unwraps
+ match host
+ .output_devices()
+ .unwrap()
+ .find(|d| d.name().unwrap() == output_card)
+ {
+ Some(d) => d,
+ None => {
+ let _ = setup_tx.send(Err(M17Codec2Error::CardUnavailable(output_card).into()));
+ return;
+ }
+ }
+ } else {
+ match host.default_output_device() {
+ Some(d) => d,
+ None => {
+ let _ = setup_tx.send(Err(M17Codec2Error::DefaultCardUnavailable.into()));
+ return;
+ }
+ }
+ };
+ let card_name = device.name().unwrap();
+ let mut configs = match device.supported_output_configs() {
+ Ok(c) => c,
+ Err(e) => {
+ let _ = setup_tx.send(Err(
+ M17Codec2Error::OutputConfigsUnavailable(card_name, e).into()
+ ));
+ return;
+ }
+ };
+ let config = match configs.find(|c| {
+ (c.channels() == 1 || c.channels() == 2) && c.sample_format() == SampleFormat::I16
+ }) {
+ Some(c) => c,
+ None => {
+ let _ = setup_tx.send(Err(
+ M17Codec2Error::SupportedOutputUnavailable(card_name).into()
+ ));
+ return;
+ }
+ };
+
+ let target_sample_rate =
+ if config.min_sample_rate().0 <= 8000 && config.max_sample_rate().0 >= 8000 {
+ 8000
+ } else {
+ config.max_sample_rate().0
+ };
+ let channels = config.channels();
+
+ let config = config.with_sample_rate(SampleRate(target_sample_rate));
+ let stream = match device.build_output_stream(
+ &config.into(),
+ move |data: &mut [i16], _info: &cpal::OutputCallbackInfo| {
+ output_cb(data, &state, channels);
+ },
+ |e| {
+ // trigger end_tx here? always more edge cases
+ debug!("error occurred in codec2 playback: {e:?}");
+ },
+ None,
+ ) {
+ Ok(s) => s,
+ Err(e) => {
+ let _ = setup_tx.send(Err(
+ M17Codec2Error::OutputStreamBuildError(card_name, e).into()
+ ));
+ return;
+ }
+ };
+ match stream.play() {
+ Ok(()) => (),
+ Err(e) => {
+ let _ = setup_tx.send(Err(
+ M17Codec2Error::OutputStreamPlayError(card_name, e).into()
+ ));
+ return;
+ }
+ }
+ let _ = setup_tx.send(Ok(target_sample_rate));