voice_audio_multi/tests/open_wake_word/reference.py
Fabio c36510c21e
Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

80 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""Capture official streaming inputs and stage timing for ARM parity probes."""
import argparse
import json
from pathlib import Path
import statistics
import sys
import time
import numpy as np
from conversion import write_model
from scipy.io import wavfile
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--source-repo", type=Path, required=True)
parser.add_argument("--models", type=Path, required=True)
parser.add_argument("--audio", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--threads", type=int, default=1)
args = parser.parse_args()
sys.path.insert(0, str(args.source_repo.resolve()))
from openwakeword.model import Model
sample_rate, audio = wavfile.read(args.audio)
if sample_rate != 16000 or audio.dtype != np.int16 or audio.ndim != 1:
raise ValueError("reference requires mono PCM16 at 16 kHz")
args.output.mkdir(parents=True, exist_ok=True)
records = {name: [] for name in ("mel", "embedding", "classifier")}
timings = {name: [] for name in records}
np.random.seed(0)
model = Model(
wakeword_models=[str(args.models / "alexa_v0.1.onnx")],
melspec_model_path=str(args.models / "melspectrogram.onnx"),
embedding_model_path=str(args.models / "embedding_model.onnx"),
inference_framework="onnx", ncpu=args.threads,
)
initial_features = np.array(model.preprocessor.feature_buffer, copy=True)
def capture(name, predict):
def run(value):
start = time.perf_counter_ns()
output = predict(value)
timings[name].append((time.perf_counter_ns() - start) / 1e6)
array = np.asarray(output[0] if isinstance(output, list) else output, dtype=np.float32)
records[name].append((np.array(value, copy=True), np.array(array, copy=True)))
return output
return run
model.preprocessor.melspec_model_predict = capture("mel", model.preprocessor.melspec_model_predict)
model.preprocessor.embedding_model_predict = capture("embedding", model.preprocessor.embedding_model_predict)
for key, predict in model.model_prediction_function.items():
model.model_prediction_function[key] = capture("classifier", predict)
scores = []
for offset in range(0, len(audio), 1280):
frame = audio[offset:offset + 1280]
if len(frame) < 1280:
frame = np.pad(frame, (0, 1280 - len(frame)))
scores.append(model.predict(frame))
report = {"feature_threads": args.threads, "classifier_threads": 1, "audio_seconds": len(audio) / sample_rate,
"frames": len(scores), "maximum_score": float(max(max(s.values()) for s in scores))}
for name, values in timings.items():
warm = values[1:]
report[name] = {"calls": len(values), "warm_median_ms": statistics.median(warm),
"warm_total_ms": sum(warm)}
print(json.dumps(report, indent=2))
(args.output / f"reference-t{args.threads}.json").write_text(json.dumps(report, indent=2) + "\n")
records["probabilities"] = [(audio.astype(np.float32) / 32768.0,
np.array([next(iter(frame.values())) for frame in scores], dtype=np.float32))]
write_model(args.models, args.output / "open-wake-word-reference-f32.gguf", initial_features, records)
if __name__ == "__main__":
main()