Spaces:
Running on Zero
Running on Zero
| from __future__ import annotations | |
| import base64 | |
| import wave | |
| from array import array | |
| from pathlib import Path | |
| def wav_to_float32_base64(path: Path, target_rate: int = 16000) -> tuple[str, float]: | |
| """Return mono float32 PCM expected by the MiniCPM-o Comni APIs. | |
| ``audioop`` was removed from the standard library in Python 3.13, so it is | |
| imported lazily here: only the optional MiniCPM-o voice path needs it. The | |
| default text backend never calls this function, keeping the app importable | |
| on modern Python without the ``audioop-lts`` backport installed. | |
| """ | |
| try: | |
| import audioop # stdlib <=3.12; provided by the audioop-lts backport on 3.13+ | |
| except ModuleNotFoundError as exc: # pragma: no cover - exercised only on 3.13+ without backport | |
| raise RuntimeError( | |
| "Audio/voice features require the 'audioop' module. On Python 3.13+ " | |
| "install the backport with: pip install audioop-lts" | |
| ) from exc | |
| with wave.open(str(path), "rb") as source: | |
| channels = source.getnchannels() | |
| sample_width = source.getsampwidth() | |
| source_rate = source.getframerate() | |
| pcm = source.readframes(source.getnframes()) | |
| if channels > 1: | |
| pcm = audioop.tomono(pcm, sample_width, 0.5, 0.5) | |
| if sample_width != 2: | |
| pcm = audioop.lin2lin(pcm, sample_width, 2) | |
| if source_rate != target_rate: | |
| pcm, _ = audioop.ratecv(pcm, 2, 1, source_rate, target_rate, None) | |
| int_samples = array("h") | |
| int_samples.frombytes(pcm) | |
| float_samples = array("f", (sample / 32768.0 for sample in int_samples)) | |
| duration = len(float_samples) / target_rate | |
| return base64.b64encode(float_samples.tobytes()).decode("ascii"), duration | |