Skip to content

Possible fix for audio corruption on Dante route disconnect when using alsa/pipewire #41

Description

@MeestorX

Using pipewire and inferno sinks, whenever a dante route is made to that inferno channel, the audio starts cleanly. Whenever that route is disconnected, there would be some corruption of the audio and distortion for a second or so while the buffer empties.

With a little help from AI, I have a patch which fixes this for me. Sorry, but I'm not experienced enough to create a PR for this, but If this works for others experiencing this issue, then a PR should be made.

It silences the audio on a dante connection being removed rather than keeping the audio running while the buffer empties.

silence-on-disconnect.patch

--- a/inferno_aoip/src/ring_buffer.rs
+++ b/inferno_aoip/src/ring_buffer.rs
@@ -253,6 +253,19 @@ impl<T: Default + NoUninit, P: ProxyToBuffer<Atomic<T>>> RBInput<T, P> {
 
     self.rb.commit_readable_pos();
   }
+
+  /// Zero all samples in the underlying buffer and reset item_ready flags.
+  /// Used on Dante flow disconnect to prevent stale audio tail being read
+  /// by PipeWire before the SilenceWriter fills the buffer with zeros.
+  pub fn zero_all_samples(&mut self) {
+    self.rb.buffer.map(|buffer| {
+      for sample in buffer.iter() {
+        sample.store(T::default(), Ordering::Relaxed);
+      }
+    });
+    atomic::fence(Ordering::SeqCst);
+    self.item_ready = boolvec![false; self.rb.items_size];
+  }
 }
 
 /// Result of the `RBOutput::read_at` function.
--- a/inferno_aoip/src/device_server/flows_rx.rs
+++ b/inferno_aoip/src/device_server/flows_rx.rs
@@ -222,13 +222,14 @@ impl<P: ProxyToSamplesBuffer> FlowsReceiverInternal<P> {
             Command::DisconnectChannel { socket_index, channel_in_flow, rb_shared } => {
               if let Some(sd) = self.sockets[socket_index].as_mut() {
                 if let Some(ch) = sd.channels[channel_in_flow].as_mut() {
                   let sink_index_opt =
                     ch.sinks.iter().position(|sink| Arc::ptr_eq(sink.shared(), &rb_shared));
                   if let Some(sink_index) = sink_index_opt {
-                    let sink = ch.sinks.swap_remove(sink_index);
+                    let mut sink = ch.sinks.swap_remove(sink_index);
                     if let Some(now) = self.clock.wrapping_now_in_timebase(self.sample_rate.into()) {
+                      sink.zero_all_samples();
                       let rb_size = sink.ring_buffer_size();
                       let writer = SilenceWriter {
                         sink,
                         end_timestamp: now
                           .wrapping_add(rb_size + rb_size / 2 /*TODO: ???*/)
                           .wrapping_add_signed(ch.timestamp_shift),

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions