import numpy as np import struct EPS_F32 = struct.unpack(' 0 and start == -1: start = j if upper <= 0 and stop == -1: stop = j - 1 if temp > 0: coeff.append(temp) bank_pos.append((start, stop)) return np.asarray(coeff, dtype=np.float32), bank_pos _HANN = np.hanning(513)[:-1].astype(np.float32) # periodic 512 hann == reference window array _COEFF, _BANK_POS = _mel_filterbank() _OFFS = [] _off = 0 for _s, _e in _BANK_POS: _OFFS.append((_s, _e, _off, _off + _e - _s + 1)) _off += _e - _s + 1 def frame_fbank(samples: np.ndarray) -> np.ndarray: """单帧 (512,) int16 -> (26,) log-fbank (Q6.10 值,float)。""" x = samples.astype(np.float32) * (1.0 / 32768.0) * _HANN spec = np.abs(np.fft.rfft(x, n=512)) out = np.zeros(26, dtype=np.float32) for f, (s, e, c0, c1) in enumerate(_OFFS): out[f] = np.dot(spec[s : e + 1], _COEFF[c0:c1]) return np.log(out + EPS_F32) class FbankStream: """流式 fbank:每帧 512 个 int16 样本 -> 维护 64 帧窗口,输出 (1,26,64)。""" def __init__(self, window: int = 64): self.window = window self.buf = np.zeros((window, 26), dtype=np.float32) def push(self, samples: np.ndarray) -> np.ndarray: """喂入一帧 int16 (512,),返回当前窗口 (1,26,64) 模型输入。""" f = frame_fbank(samples) q = np.trunc(np.clip(f, -32, 32) * 1024.0 + 0.5) / 1024.0 self.buf = np.roll(self.buf, -1, axis=0) self.buf[-1] = q return self.buf.T[np.newaxis].astype(np.float32) def reset(self): self.buf[:] = 0.0 def preprocess(*arrays): """兼容通用入口:输入 int16 PCM (N,) 或 (1,N),返回全部滑窗 (M,1,26,64)。""" if len(arrays) != 1: raise ValueError('wakeup 需要单个 int16 PCM 数组') pcm = np.asarray(arrays[0]).reshape(-1).astype(np.int16) n_frames = (pcm.shape[0] - 512) // 512 + 1 if n_frames < 1: raise ValueError('PCM 太短') fs = FbankStream() wins = [] for i in range(n_frames): wins.append(fs.push(pcm[i * 512 : i * 512 + 512])) return [np.stack(wins)]