From 7116edfad718820228ca4d84bcd697f9bb03414c Mon Sep 17 00:00:00 2001 From: pstruebi Date: Tue, 12 Aug 2025 16:39:34 +0200 Subject: [PATCH] make latency setting more consistent --- README.md | 3 +-- src/auracast/multicast.py | 23 +++++++++++++++++++---- src/auracast/multicast_script.py | 22 ++++++++-------------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index b206e78..038c28e 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,7 @@ sudo apt update sudo apt remove -y libportaudio2 portaudio19-dev libportaudiocpp0 echo "y" | rpi-update stable -# TODO: needed ? -sudo apt install pipewire wireplumber pipewire-audio-client-libraries rtkit +sudo apt install -y pipewire wireplumber pipewire-audio-client-libraries rtkit mkdir -p ~/.config/pipewire/pipewire.conf.d cp src/service/pipewire/99-lowlatency.conf ~/.config/pipewire/pipewire.conf.d/ diff --git a/src/auracast/multicast.py b/src/auracast/multicast.py index 1a404c2..3dd5310 100644 --- a/src/auracast/multicast.py +++ b/src/auracast/multicast.py @@ -131,9 +131,9 @@ class ModSoundDeviceAudioInput(audio_io.ThreadedAudioInput): # Read tunables try: - blocksize = int(os.environ.get('AURACAST_SD_BLOCKSIZE', '64')) + blocksize = int(os.environ.get('AURACAST_SD_BLOCKSIZE', '128')) except Exception: - blocksize = 64 + blocksize = 128 raw_latency = os.environ.get('AURACAST_SD_LATENCY', 'low') if raw_latency in ('low', 'high', 'default'): latency: typing.Union[str, float] = raw_latency @@ -143,6 +143,21 @@ class ModSoundDeviceAudioInput(audio_io.ThreadedAudioInput): except Exception: latency = 'low' + # Log the chosen parameters and device info + try: + dev_info = sounddevice.query_devices(self._device, 'input') if self._device is not None else sounddevice.query_devices(kind='input') + except Exception: + dev_info = {} + logging.info( + "SoundDevice RawInputStream: rate=%s ch=%s blocksize=%s latency=%s device=%s hostapi=%s", + self._pcm_format.sample_rate, + self._pcm_format.channels, + blocksize, + latency, + dev_info.get('name', self._device), + dev_info.get('hostapi', 'unknown'), + ) + # Create the raw input stream with tighter buffering self._stream = sounddevice.RawInputStream( samplerate=self._pcm_format.sample_rate, @@ -167,7 +182,7 @@ class ModSoundDeviceAudioInput(audio_io.ThreadedAudioInput): return b'' pcm_buffer, overflowed = self._stream.read(frame_size) if overflowed: - logging.warning("input overflow") + logging.warning("SoundDevice input overflow: frame_size=%s", frame_size) # Duplicate mono to stereo for downstream expectations if self._pcm_format.channels == 1: @@ -252,7 +267,7 @@ async def init_broadcast( # Broadcast Audio Immediate Rendering flag (type 0x09), zero-length value le_audio.Metadata.Entry(tag = le_audio.Metadata.Tag.BROADCAST_AUDIO_IMMEDIATE_RENDERING_FLAG, data=b"") ] - if global_config.immediate_rendering + if global_config.immediate_rendering #TODO: verify this else [] ) ) diff --git a/src/auracast/multicast_script.py b/src/auracast/multicast_script.py index 70cea13..16782e0 100644 --- a/src/auracast/multicast_script.py +++ b/src/auracast/multicast_script.py @@ -13,15 +13,12 @@ if __name__ == "__main__": ) os.chdir(os.path.dirname(__file__)) - # Hint PipeWire to use a very small processing quantum for this client - # (we will refine after SRATE is known). Defaults target ~0.33–0.67 ms. - os.environ.setdefault("PIPEWIRE_LATENCY", "16/48000") - # Lower PulseAudio-side buffering (PortAudio backend typically goes through PA) + # Minimal setting to request PipeWire quantum ~128 at 48 kHz via PortAudio + os.environ.setdefault("AURACAST_SD_BLOCKSIZE", "128") + # Also hint PortAudio/Pulse and PipeWire about small buffers + os.environ.setdefault("AURACAST_SD_LATENCY", "0.0027") # ~128/48000 s os.environ.setdefault("PULSE_LATENCY_MSEC", "1") - # Request smaller PortAudio blocks via sounddevice shim - os.environ.setdefault("AURACAST_SD_BLOCKSIZE", "32") - # Accepts 'low'/'high'/'default' or seconds (float). Our shim parses number strings to float. - os.environ.setdefault("AURACAST_SD_LATENCY", "0.0015") + os.environ.setdefault("PIPEWIRE_LATENCY", "128/48000") logging.info("USB pw inputs:") usb_inputs = list_usb_pw_inputs() logging.info("AEs67 pw inputs:") @@ -40,15 +37,11 @@ if __name__ == "__main__": LC3_SRATE = 24000 OCTETS_PER_FRAME=60 - # After capture rate is known, constrain client buffers precisely relative to - # the active capture rate (so the client quantum matches the device rate). + # Ensure PipeWire latency hint reflects the actual capture rate try: - os.environ["PIPEWIRE_LATENCY"] = f"16/{CAPTURE_SRATE}" + os.environ["PIPEWIRE_LATENCY"] = f"128/{CAPTURE_SRATE}" except Exception: pass - os.environ["PULSE_LATENCY_MSEC"] = os.environ.get("PULSE_LATENCY_MSEC", "1") - os.environ["AURACAST_SD_BLOCKSIZE"] = os.environ.get("AURACAST_SD_BLOCKSIZE", "32") - os.environ["AURACAST_SD_LATENCY"] = os.environ.get("AURACAST_SD_LATENCY", "0.0015") config = auracast_config.AuracastConfigGroup( bigs = [ @@ -62,6 +55,7 @@ if __name__ == "__main__": #auracast_config.AuracastBigConfigEng(), ], immediate_rendering=True, + presentation_delay_us=40000, qos_config=auracast_config.AuracastQosHigh(), auracast_sampling_rate_hz = LC3_SRATE, octets_per_frame = OCTETS_PER_FRAME, # 32kbps@16kHz