"""Playback and recording with Qt Multimedia (bundled with PySide6; WASAPI on Windows, PulseAudio/PipeWire on Linux). Audio is exchanged as float32 numpy arrays (frames, channels). Set ACKIT_NO_AUDIO=1 to disable devices (headless tests); `Recorder.start_simulated` feeds a recording from an array in real time, so the recording logic is testable without a microphone. """ import os import numpy as np from PySide6.QtCore import QBuffer, QByteArray, QIODevice, QObject, QTimer, Signal from PySide6.QtMultimedia import QAudio, QAudioFormat, QAudioSink, QAudioSource, QMediaDevices from ackit import audio def audio_enabled(): return os.environ.get("ACKIT_NO_AUDIO") != "1" def output_devices(): return list(QMediaDevices.audioOutputs()) if audio_enabled() else [] def input_devices(): return list(QMediaDevices.audioInputs()) if audio_enabled() else [] def find_device(devices, description): return next((d for d in devices if d.description() == description), None) def _to_int16(x): return (np.clip(x, -1.0, 1.0) * 32767.0).astype(" (float32 array (n, ch), rate) of everything recorded.""" if self.source is not None: self._read() self.source.stop() self.source.deleteLater() self.source = None self.io = None if self._sim is not None: self._sim[0].stop() self._sim = None x = np.concatenate(self.chunks) if self.chunks else np.zeros((0, self.channels), np.float32) return x, self.rate