tune consumption of samples

This commit is contained in:
2025-11-14 15:53:55 +01:00
parent 204e287075
commit d932d07e7e
2 changed files with 30 additions and 49 deletions

View File

@@ -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

View File

@@ -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
}