+fn build_input_cb<T: NumCast + WrappingNeg + Clone>(
+ samples: SyncSender<SoundmodemEvent>,
+ channels: u16,
+ rx_inverted: bool,
+) -> impl Fn(&[T], &InputCallbackInfo) {
+ move |data: &[T], _info: &cpal::InputCallbackInfo| {
+ let mut out = vec![];
+ for d in data.chunks(channels as usize) {
+ // if we were given multi-channel input we'll pick the first channel
+ let mut sample = d[0].clone();
+ if rx_inverted {
+ sample = sample.wrapping_neg();
+ }
+ out.push(NumCast::from(sample).unwrap());
+ }
+ let _ = samples.try_send(SoundmodemEvent::BasebandInput(out.into()));
+ }
+}
+
+fn build_input_stream(
+ device: &Device,
+ input_config: SupportedStreamConfig,
+ errors: SoundmodemErrorSender,
+ samples: SyncSender<SoundmodemEvent>,
+ channels: u16,
+ rx_inverted: bool,
+) -> Result<Stream, BuildStreamError> {
+ if input_config.sample_format() == SampleFormat::I16 {
+ device.build_input_stream(
+ &input_config.into(),
+ build_input_cb::<i16>(samples, channels, rx_inverted),
+ move |e| {
+ errors.send_error(SoundcardError::Stream(e));
+ },
+ None,
+ )
+ } else {
+ device.build_input_stream(
+ &input_config.into(),
+ build_input_cb::<i32>(samples, channels, rx_inverted),
+ move |e| {
+ errors.send_error(SoundcardError::Stream(e));
+ },
+ None,
+ )
+ }
+}
+
+fn build_output_cb<T: NumCast + WrappingNeg + Clone>(
+ event_tx: SyncSender<SoundmodemEvent>,
+ channels: u16,
+ tx_inverted: bool,
+ buffer: Arc<RwLock<OutputBuffer>>,
+) -> impl Fn(&mut [T], &OutputCallbackInfo) {
+ move |data: &mut [T], info: &cpal::OutputCallbackInfo| {
+ let mut taken = 0;
+ let ts = info.timestamp();
+ let latency = ts
+ .playback
+ .duration_since(&ts.callback)
+ .unwrap_or(Duration::ZERO);
+ let mut buffer = buffer.write().unwrap();
+ buffer.latency = latency;
+ for out in data.chunks_mut(channels as usize) {
+ if let Some(s) = buffer.samples.pop_front() {
+ out.fill(NumCast::from(if tx_inverted { s.saturating_neg() } else { s }).unwrap());
+ taken += 1;
+ } else if buffer.idling {
+ out.fill(NumCast::from(0).unwrap());
+ } else {
+ let _ = event_tx.send(SoundmodemEvent::OutputUnderrun);
+ break;
+ }
+ }
+ let _ = event_tx.send(SoundmodemEvent::DidReadFromOutputBuffer {
+ len: taken,
+ timestamp: Instant::now(),
+ });
+ }
+}
+
+fn build_output_stream(
+ device: &Device,
+ output_config: SupportedStreamConfig,
+ errors: SoundmodemErrorSender,
+ event_tx: SyncSender<SoundmodemEvent>,
+ channels: u16,
+ tx_inverted: bool,
+ buffer: Arc<RwLock<OutputBuffer>>,
+) -> Result<Stream, BuildStreamError> {
+ if output_config.sample_format() == SampleFormat::I16 {
+ device.build_output_stream(
+ &output_config.into(),
+ build_output_cb::<i16>(event_tx, channels, tx_inverted, buffer),
+ move |e| {
+ errors.send_error(SoundcardError::Stream(e));
+ },
+ None,
+ )
+ } else {
+ device.build_output_stream(
+ &output_config.into(),
+ build_output_cb::<i32>(event_tx, channels, tx_inverted, buffer),
+ move |e| {
+ errors.send_error(SoundcardError::Stream(e));
+ },
+ None,
+ )
+ }
+}
+