From d932d07e7ec87dfa64716e18c2ac6555590076b3 Mon Sep 17 00:00:00 2001 From: pstruebi Date: Fri, 14 Nov 2025 15:53:55 +0100 Subject: [PATCH] tune consumption of samples --- src/auracast/multicast.py | 71 ++++++++++++++------------------------- src/misc/asound.conf | 8 ++--- 2 files changed, 30 insertions(+), 49 deletions(-) diff --git a/src/auracast/multicast.py b/src/auracast/multicast.py index 33508fd..935ff00 100644 --- a/src/auracast/multicast.py +++ b/src/auracast/multicast.py @@ -58,17 +58,8 @@ from auracast.utils.webrtc_audio_input import WebRTCAudioInput # Patch sounddevice.InputStream globally to use low-latency settings import sounddevice as sd from collections import deque -import statistics -def popleft_n(d: deque, n: int) -> bytes: - # pops up to n items (handles short deques) - # if the deque was too short, fill with 0s and print a warning - it = (d.popleft() for _ in range(min(n, len(d)))) - if len(d) < n: - logging.warning("SoundDeviceAudioInput: deque was too short, requested %d, got %d", n, len(d)) - return b"".join(it) - class ModSoundDeviceAudioInput(audio_io.SoundDeviceAudioInput): """Patched SoundDeviceAudioInput with low-latency capture and adaptive resampling.""" @@ -94,13 +85,10 @@ class ModSoundDeviceAudioInput(audio_io.SoundDeviceAudioInput): # Target ~2 ms blocksize (48 kHz -> 96 frames). For other rates, keep ~2 ms. _sr = int(self._pcm_format.sample_rate) - self.log_counter0=0 - self._runavg_samps = deque(maxlen=30) - self._runavg = 0 + self.counter=0 self.max_avail=0 self.logfile_name="available_samples.txt" - self.blocksize = 480 - self._frames_offset = 0 + self.blocksize = 120 if os.path.exists(self.logfile_name): os.remove(self.logfile_name) @@ -111,7 +99,7 @@ class ModSoundDeviceAudioInput(audio_io.SoundDeviceAudioInput): channels=self._pcm_format.channels, dtype='int16', blocksize=self.blocksize, - latency=0.005, + latency=0.004, ) self._stream.start() @@ -123,50 +111,43 @@ class ModSoundDeviceAudioInput(audio_io.SoundDeviceAudioInput): ) def _read(self, frame_size: int) -> bytes: - """Generate frames while minimally adjusting frames_offset to keep _runavg ~ blocksize/2.""" - # Persist frames_offset across calls; clamp within 1% of blocksize + """Read PCM samples from the stream.""" - max_offset = 30 - target = 0.5*self.blocksize - deadband = 2 + #if self.counter % 50 == 0: + frame_size = frame_size + 1 # consume samples a little faster to avoid latency akkumulation - pcm_buffer, overflowed = self._stream.read(frame_size + self._frames_offset) + pcm_buffer, overflowed = self._stream.read(frame_size) if overflowed: logging.warning("SoundDeviceAudioInput: overflowed") - available = self._stream.read_available - if available > 0: - self.max_avail = max(self.max_avail, available) + n_available = self._stream.read_available - self._runavg_samps.append(available) - self._runavg = max(self._runavg_samps) # statistics.median(self._runavg_samps) + # adapt = n_available > 20 + # if adapt: + # pcm_extra, overflowed = self._stream.read(3) + # logging.info('consuming extra samples, available was %d', n_available) + # if overflowed: + # logging.warning("SoundDeviceAudioInput: overflowed") + + # out = bytes(pcm_buffer) + bytes(pcm_extra) + # else: + out = bytes(pcm_buffer) - # Minimal feedback: nudge frames_offset by 1 to target blocksize/2 with small deadband - - increment = self._runavg > (target + deadband) or overflowed - decrement = self._runavg < (target - deadband) - clipped_upper = self._frames_offset >= max_offset - clipped_lower = self._frames_offset <= -1 * max_offset - - self._frames_offset = 0 - if increment and not clipped_upper: - self._frames_offset += 1 - elif decrement and not clipped_lower: - self._frames_offset -= 1 + self.max_avail = max(self.max_avail, n_available) #Diagnostics - with open(self.logfile_name, "a", encoding="utf-8") as f: - f.write(f"{available}, {round(self._runavg, 2)}, {self._frames_offset}, {overflowed}\n") + #with open(self.logfile_name, "a", encoding="utf-8") as f: + # f.write(f"{n_available}, {adapt}, {round(self._runavg, 2)}, {overflowed}\n") - if self.log_counter0 % 500 == 0: + if self.counter % 500 == 0: logging.info( - "read available=%d, max=%d, runavg=%.3f, frames_offset=%d (max %d)", - available, self.max_avail, round(self._runavg, 2), self._frames_offset, max_offset + "read available=%d, max=%d, latency:%d", + n_available, self.max_avail, self._stream.latency ) self.max_avail = 0 - self.log_counter0 += 1 - return bytes(pcm_buffer) + self.counter += 1 + return out audio_io.SoundDeviceAudioInput = ModSoundDeviceAudioInput diff --git a/src/misc/asound.conf b/src/misc/asound.conf index 165b9da..8a38112 100644 --- a/src/misc/asound.conf +++ b/src/misc/asound.conf @@ -6,8 +6,8 @@ pcm.ch1 { channels 2 rate 48000 format S16_LE - period_size 480 # 2.5ms - buffer_size 960 + period_size 120 + buffer_size 240 } bindings.0 0 } @@ -21,8 +21,8 @@ pcm.ch2 { channels 2 rate 48000 format S16_LE - period_size 480 - buffer_size 960 + period_size 120 + buffer_size 240 } bindings.0 1 } \ No newline at end of file