"""Audio decoding helpers. We shell out to ffmpeg so we can ingest *anything* (wav, mp3, flac, ogg, m4a) and always get back mono 16-bit PCM at LiveKit's preferred 48 kHz — no per-format branching, no resampling surprises. """ import asyncio import numpy as np SAMPLE_RATE = 48000 NUM_CHANNELS = 1 async def decode_to_pcm(path: str, sample_rate: int = SAMPLE_RATE) -> np.ndarray: """Decode `path` to a 1-D int16 numpy array (mono, `sample_rate` Hz).""" proc = await asyncio.create_subprocess_exec( "ffmpeg", "-v", "error", "-i", str(path), "-f", "s16le", "-acodec", "pcm_s16le", "-ac", str(NUM_CHANNELS), "-ar", str(sample_rate), "-", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) out, err = await proc.communicate() if proc.returncode != 0: raise RuntimeError(f"ffmpeg failed decoding {path}: {err.decode(errors='ignore')}") return np.frombuffer(out, dtype=np.int16).copy()