From 49f47e76a85b3477835a0adec05649507d94b2e8 Mon Sep 17 00:00:00 2001
From: xegineering <me@xegineering.eu>
Date: Sun, 8 Dec 2024 17:36:28 +0100
Subject: pipewire: Fix huge latency by dropping silence

The current architecture uses the following processing:

- capture raw audio from PipeWire as unsigned 16 bit integers
- convert with a `ffmpeg` process to OGG / FLAC
- stream the `ffmpeg` output to multiple soundboxes via TCP

Only the first part is different for URL sources. Since using PipeWire
significant latency (up to 15 seconds) were measured.

It turned out that this happens exactly when zero bytes (silence) are
fed into the `ffmpeg` process. This commit avoids this by dropping those
empty samples.

It has to be made sure that only samples are dropped where both channels
are zero. Otherwise audible noise is the result.
---
 soundbox/pipewire.go | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/soundbox/pipewire.go b/soundbox/pipewire.go
index fbae73b..fcb5968 100644
--- a/soundbox/pipewire.go
+++ b/soundbox/pipewire.go
@@ -18,6 +18,20 @@ import (
 
 var pipewireAudio = make(chan []byte, 5)
 
+func s16leDropSilence(input []byte) []byte {
+	output := make([]byte, 0)
+
+	for i := 0; i < (len(input)-1); i+=2 {
+		if input[i] == byte(0) && input[i+1] == byte(0) {
+			continue
+		}
+		output = append(output, input[i])
+		output = append(output, input[i+1])
+	}
+
+	return output
+}
+
 func StreamPipewireContext(ctx context.Context, targets []net.HardwareAddr) error {
 	cmd := exec.CommandContext(
 		ctx,
@@ -51,7 +65,7 @@ func StreamPipewireContext(ctx context.Context, targets []net.HardwareAddr) erro
 
 	go func() {
 		for buffer := range pipewireAudio {
-			tempReader := bytes.NewReader(buffer)
+			tempReader := bytes.NewReader(s16leDropSilence(buffer))
 			_, err := io.Copy(stdin, tempReader)
 			if err != nil {
 				log.Println("Failed to copy from PipeWire to ffmpeg.")
-- 
cgit v1.2.3-70-g09d2