HY-2012's picture
LS-EEND streaming speaker diarization for AX650N: axmodel, Python SDK, prebuilt C++ binary
d2f59ca verified
Raw
History Blame Contribute Delete
2.14 kB
"""8 kHz log-mel frontend for LS-EEND (``logmel23_cummn``).
Mirrors ``FS-EEND/LS-EEND/datasets/feature.py`` exactly: 23 mel bins, cumulative
mean normalization, +-7 frame splicing, then 10x subsampling. One output frame
therefore covers 0.1 s of audio.
"""
from __future__ import annotations
import numpy as np
SAMPLE_RATE = 8000
N_MELS = 23
CONTEXT = 7
SUBSAMPLING = 10
FRAME_SHIFT = 80
WIN_LENGTH = 200
N_FFT = 1024
FEATURE_DIM = (2 * CONTEXT + 1) * N_MELS
FRAME_SEC = FRAME_SHIFT * SUBSAMPLING / SAMPLE_RATE # 0.1 s
def load_audio(wav_path):
"""Read a wav as mono float32 at 8 kHz. Returns (audio, duration_seconds)."""
import soundfile as sf
audio, sr = sf.read(str(wav_path), dtype='float32', always_2d=False)
if audio.ndim > 1:
audio = audio[:, 0]
duration = len(audio) / sr
if sr != SAMPLE_RATE:
import librosa
audio = librosa.resample(audio, orig_sr=sr, target_sr=SAMPLE_RATE)
return audio, duration
def extract_features(audio):
"""audio (float32, 8 kHz) -> features (T, 345) float32."""
import librosa
spec = librosa.stft(audio, n_fft=N_FFT, win_length=WIN_LENGTH, hop_length=FRAME_SHIFT).T
if len(audio) % FRAME_SHIFT == 0:
spec = spec[:-1]
mag = np.abs(spec)
mel_fb = librosa.filters.mel(sr=SAMPLE_RATE, n_fft=2 * (mag.shape[1] - 1), n_mels=N_MELS)
logmel = np.log10(np.maximum(np.dot(mag ** 2, mel_fb.T), 1e-10))
# Cumulative mean normalization: frame t is normalized by the mean of frames 0..t.
cum = np.cumsum(logmel, axis=0)
idx = np.arange(1, logmel.shape[0] + 1, dtype=np.float32)
logmel = logmel - cum / idx[:, None]
padded = np.pad(logmel, ((CONTEXT, CONTEXT), (0, 0)), mode='constant')
n = logmel.shape[0]
spliced = np.lib.stride_tricks.as_strided(
padded, (n, FEATURE_DIM), (padded.itemsize * N_MELS, padded.itemsize)
).copy()
return spliced[::SUBSAMPLING].astype(np.float32)
def wav_to_features(wav_path):
"""Convenience wrapper: wav path -> (features (T,345), duration_seconds)."""
audio, duration = load_audio(wav_path)
return extract_features(audio), duration