import shutil import struct import subprocess from io import BytesIO def _find_ffmpeg(configured: str = "ffmpeg") -> str: if shutil.which(configured): return configured try: from imageio_ffmpeg import get_ffmpeg_exe return get_ffmpeg_exe() except Exception: raise RuntimeError( "ffmpeg not found: install it on PATH, set runtime.ffmpeg_path " "in config.json, or `pip install imageio-ffmpeg`" ) from None _SUPPORTED_FORMATS = {"mp3", "wav", "pcm", "opus", "aac", "flac"} _CONTENT_TYPES = { "mp3": "audio/mpeg", "wav": "audio/wav", "pcm": "audio/pcm", "opus": "audio/opus", "aac": "audio/aac", "flac": "audio/flac", } _FFMPEG_CODECS = { "mp3": "libmp3lame", "opus": "libopus", "aac": "aac", "flac": "flac", } def pcm_to_wav(pcm: bytes, sample_rate: int = 24000) -> bytes: data_size = len(pcm) byte_rate = sample_rate * 2 header = struct.pack( "<4sI4s4sI4sI", b"RIFF", 36 + data_size, b"WAVE", b"fmt ", 16, b"data", data_size, ) fmt = struct.pack(" bytes: return pcm def pcm_to_ffmpeg( pcm: bytes, fmt: str, sample_rate: int = 24000, ffmpeg_path: str = "ffmpeg" ) -> bytes: codec = _FFMPEG_CODECS.get(fmt) if codec is None: raise ValueError(f"Unsupported ffmpeg format: {fmt}") cmd = [ ffmpeg_path, "-y", "-f", "s16le", "-ar", str(sample_rate), "-ac", "1", "-i", "pipe:0", "-f", _ffprobe_fmt(fmt), "-codec:a", codec, "pipe:1", ] proc = subprocess.run( cmd, input=pcm, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) if proc.returncode != 0: raise RuntimeError(f"ffmpeg failed: {proc.stderr.decode(errors='replace')}") return proc.stdout def _ffprobe_fmt(fmt: str) -> str: return {"mp3": "mp3", "opus": "opus", "aac": "adts", "flac": "flac"}.get(fmt, fmt) def encode(pcm: bytes, fmt: str, sample_rate: int = 24000, ffmpeg_path: str = "ffmpeg") -> tuple[bytes, str]: if fmt not in _SUPPORTED_FORMATS: raise ValueError(f"Unsupported response_format: {fmt}") if fmt == "wav": data = pcm_to_wav(pcm, sample_rate) elif fmt == "pcm": data = pcm else: data = pcm_to_ffmpeg(pcm, fmt, sample_rate, _find_ffmpeg(ffmpeg_path)) return data, _CONTENT_TYPES[fmt]