"""音频加载与重采样工具:统一到 16k/mono,供 gummy(pcm16) 与本地引擎(float) 使用。""" from __future__ import annotations import numpy as np import soundfile as sf TARGET_SR = 16000 def _resample(data: np.ndarray, sr: int, target_sr: int) -> np.ndarray: if sr == target_sr: return data import soxr return soxr.resample(data, sr, target_sr) def load_pcm16(path: str, target_sr: int = TARGET_SR) -> tuple[np.ndarray, int]: """读为 int16 单声道 PCM(gummy 推流用)。""" data, sr = sf.read(path, dtype="float32", always_2d=False) if data.ndim > 1: data = data.mean(axis=1) data = _resample(data, sr, target_sr) # float32(-1,1) -> int16 pcm = np.clip(data, -1.0, 1.0) pcm = (pcm * 32767.0).astype(np.int16) return pcm, target_sr def load_float(path: str, target_sr: int = TARGET_SR) -> tuple[np.ndarray, int]: """读为 float32 单声道(本地引擎用)。""" data, sr = sf.read(path, dtype="float32", always_2d=False) if data.ndim > 1: data = data.mean(axis=1) data = _resample(data, sr, target_sr) return data.astype(np.float32), target_sr def duration_sec(path: str) -> float: info = sf.info(path) return info.frames / info.samplerate