"""PipeWire/Pulse microphone capture through the local ffmpeg binary.""" from __future__ import annotations import subprocess from collections.abc import Iterator import numpy as np from mic_clipper.vad import FRAME_SAMPLES class AudioInputUnavailable(RuntimeError): """The dynamic Pulse default source could not supply another frame.""" class PulseAudioInput: """Yield fixed 16 kHz mono float PCM frames from Pulse's `default` source.""" @staticmethod def command() -> list[str]: return [ "ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-f", "pulse", "-sample_rate", "16000", "-channels", "1", "-i", "default", "-ac", "1", "-ar", "16000", "-f", "f32le", "pipe:1", ] def frames(self) -> Iterator[np.ndarray]: process = subprocess.Popen( self.command(), stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) assert process.stdout is not None assert process.stderr is not None try: while True: payload = process.stdout.read(FRAME_SAMPLES * np.dtype("