104 lines
4.5 KiB
C++
104 lines
4.5 KiB
C++
// Engine STT Whisper-Small Qualcomm AI Hub (HfWhisper KV-cache) in-process.
|
|
// Load une fois (encoder QAIRT context + decoder QAIRT context + mel_filters +
|
|
// vocab), transcribe N fois (PCM 16kHz mono -> texte FR/EN).
|
|
//
|
|
// Backend : ONNX Runtime + QNN ExecutionProvider (HTP V79 sur SM8750).
|
|
// Empreinte RAM : ~545 MB (encoder ctx 201 MB + decoder ctx 345 MB).
|
|
//
|
|
// Bench de référence prod (Whisper-Small, FR, audio 1.6s) :
|
|
// mel 189 ms (CPU C++) + encoder 125 ms (NPU) + decoder 510 ms (NPU, 22 tokens)
|
|
// total 825 ms, RTF 0.51, RAM peak 545 MB.
|
|
//
|
|
// Construit pour remplacer WhisperHybridEngine.kt + libmel_extractor.so + VadStage.kt
|
|
// derrière une API unifiée Kazeia-Engine. La sélection de backend (HTP vs CPU) est
|
|
// automatique via QNN EP : si le contexte QAIRT est trouvé, HTP ; sinon CPU fallback.
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
struct SttEngine; // opaque
|
|
|
|
struct SttEngineLoadCfg {
|
|
// Dir contenant : HfWhisperEncoder.onnx + HfWhisperEncoder_qairt_context.bin,
|
|
// HfWhisperDecoder.onnx + HfWhisperDecoder_qairt_context.bin,
|
|
// mel_filters.json (ou .bin), vocab.json (BPE byte-level).
|
|
const char * model_dir = nullptr;
|
|
|
|
// 1 = QNN HTP (NPU, défaut prod). 0 = CPU fallback (ONNX Runtime CPU EP).
|
|
// QNN EP requiert que libQnnHtp.so + libQnnHtpV79Skel.so + libcdsprpc.so soient
|
|
// dans LD_LIBRARY_PATH (côté tablette : push depuis dist/lib/).
|
|
bool use_htp = true;
|
|
|
|
int n_threads = 6;
|
|
int max_decode_steps = 200; // borne hard du loop decoder autorégressif
|
|
};
|
|
|
|
struct SttTranscribeCfg {
|
|
// PCM mono 16-bit signed, n'importe quelle longueur (le mel pad fait à 30s).
|
|
// Si vous avez du PCM 24kHz, resamplez externe avant (ou utilisez SttTranscribeWaveform).
|
|
const int16_t * pcm16 = nullptr;
|
|
int n_samples = 0;
|
|
int sample_rate = 16000;
|
|
|
|
// "fr", "en", "auto", etc. Whisper-Small supporte 99 langues. "auto" = détection
|
|
// via la langue token de Whisper.
|
|
const char * language = "fr";
|
|
|
|
// 1 = force le mode "transcribe" (FR -> texte FR). 0 = laisse Whisper choisir
|
|
// entre transcribe et translate (translate force la sortie en EN, pas voulu pour
|
|
// Kazeia FR). Défaut : 1 (cf override prod KazeiaService).
|
|
bool force_transcribe = true;
|
|
|
|
// Inclure les timestamps (segments + offsets) dans la sortie.
|
|
bool with_timestamps = false;
|
|
};
|
|
|
|
struct SttSegment {
|
|
int t0_ms; // début segment (ms relatif au début audio)
|
|
int t1_ms; // fin segment
|
|
std::string text;
|
|
};
|
|
|
|
struct SttTranscribeResult {
|
|
int err = 0; // 0 = ok, <0 = err
|
|
std::string text; // texte concaténé (sans timestamps)
|
|
std::vector<SttSegment> segments; // vide si !with_timestamps
|
|
std::string detected_language; // "fr", "en", ...
|
|
|
|
// Métriques perf (ms)
|
|
int mel_ms = 0;
|
|
int encoder_ms = 0;
|
|
int decoder_ms = 0;
|
|
int total_ms = 0;
|
|
float rtf = 0.0f; // total_ms / (audio_duration_ms)
|
|
int n_tokens = 0;
|
|
};
|
|
|
|
// Charge encoder + decoder + mel basis + vocab. Retourne nullptr si échec.
|
|
// Idempotent vis-à-vis de llama_backend_init() (cohabite avec tts_engine).
|
|
SttEngine * stt_engine_load(const SttEngineLoadCfg & cfg);
|
|
|
|
// Transcrit une fenêtre audio. Stateful pour le warm path NPU (sessions ORT
|
|
// gardées en RAM entre appels).
|
|
SttTranscribeResult stt_engine_transcribe(SttEngine * eng, const SttTranscribeCfg & cfg);
|
|
|
|
// Libère tout (encoder ctx, decoder ctx, sessions ORT, vocab).
|
|
void stt_engine_free(SttEngine * eng);
|
|
|
|
// Helper VAD RMS énergie : renvoie true si le buffer PCM contient de la parole
|
|
// (énergie > seuil). Compatible drop-in avec VadStage.kt prod (frame=1600 @ 16kHz,
|
|
// seuil 150 PCM, ≥3 frames consécutives parole, ≥8 frames silence).
|
|
// Tu peux passer le PCM en streaming par chunks de 100ms et lire is_speech / is_end_of_speech.
|
|
struct SttVadState;
|
|
SttVadState * stt_vad_new(int sample_rate = 16000,
|
|
int frame_size = 1600,
|
|
int rms_threshold = 150,
|
|
int min_speech_frames = 3,
|
|
int silence_end_frames = 8);
|
|
bool stt_vad_push(SttVadState * s, const int16_t * pcm, int n_samples);
|
|
bool stt_vad_is_speech(const SttVadState * s);
|
|
bool stt_vad_is_end_of_speech(const SttVadState * s);
|
|
void stt_vad_reset(SttVadState * s);
|
|
void stt_vad_free(SttVadState * s);
|