titanet-large-coreml / example.py
nikioth's picture
Add TitaNet-L Core ML conversion, conversion script and examples
245922c verified
Raw History Blame Contribute Delete
1.79 kB
"""Compare two voices with TitaNet.mlpackage (macOS, Python 3.9+).
pip install coremltools kaldi-native-fbank soundfile numpy
python example.py a.wav b.wav
The WAV files must be mono 16 kHz. Prints the cosine similarity of the two
speaker embeddings.
"""
import sys
import coremltools as ct
import kaldi_native_fbank as knf
import numpy as np
import soundfile as sf
def features(samples: np.ndarray) -> np.ndarray:
"""The features sherpa-onnx feeds TitaNet-L, shaped (1, 80, T)."""
o = knf.FbankOptions()
o.frame_opts.samp_freq = 16000
o.frame_opts.frame_length_ms = 25
o.frame_opts.frame_shift_ms = 10
o.frame_opts.snip_edges = True
o.frame_opts.dither = 0
o.frame_opts.remove_dc_offset = False
o.frame_opts.preemph_coeff = 0.97
o.frame_opts.window_type = "hann"
o.mel_opts.num_bins = 80
o.mel_opts.low_freq = 0
o.mel_opts.high_freq = -400 # 7600 Hz
o.mel_opts.is_librosa = True
fbank = knf.OnlineFbank(o)
fbank.accept_waveform(16000, samples.tolist())
fbank.input_finished()
f = np.array([fbank.get_frame(i) for i in range(fbank.num_frames_ready)], dtype=np.float32)
f = (f - f.mean(0)) / (f.std(0) + 1e-5) # per-bin, population std
return f.T[None].copy()
def embed(model: ct.models.MLModel, path: str) -> np.ndarray:
samples, rate = sf.read(path, dtype="float32")
assert rate == 16000 and samples.ndim == 1, "expected mono 16 kHz audio"
return model.predict({"features": features(samples)})["embedding"][0]
if __name__ == "__main__":
model = ct.models.MLModel("TitaNet.mlpackage", compute_units=ct.ComputeUnit.CPU_AND_GPU)
a, b = embed(model, sys.argv[1]), embed(model, sys.argv[2])
print(f"cosine similarity: {a @ b / np.linalg.norm(a) / np.linalg.norm(b):.3f}")