114 lines
4.4 KiB
C++
114 lines
4.4 KiB
C++
// Test standalone S1.3 : exercer kazeia_mel (config_whisper) + VAD RMS.
|
|
// Usage : kazeia_stt_test <mel_filters.bin> <audio16k_mono.wav>
|
|
//
|
|
// Vérifie que le mel produit a la bonne shape [80 x 3000] et que les valeurs sont
|
|
// dans la plage Whisper (~[-1, 0.5] après norm). Permet de valider le mel partagé
|
|
// AVANT de brancher ORT/QNN en S2.
|
|
#include "kazeia_mel.h"
|
|
#include "stt_engine.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include <fstream>
|
|
|
|
static std::vector<int16_t> read_wav_pcm16(const char* path, int & sr_out, int & channels_out) {
|
|
FILE* f = fopen(path, "rb");
|
|
if (!f) { fprintf(stderr, "open %s FAIL\n", path); return {}; }
|
|
char riff[12];
|
|
if (fread(riff, 1, 12, f) != 12 || memcmp(riff, "RIFF", 4) != 0 || memcmp(riff+8, "WAVE", 4) != 0) {
|
|
fprintf(stderr, "not RIFF/WAVE\n"); fclose(f); return {};
|
|
}
|
|
uint16_t channels = 0, bps = 0;
|
|
uint32_t sr = 0, data_sz = 0;
|
|
long data_off = -1;
|
|
while (!feof(f)) {
|
|
char cid[4]; uint32_t csz;
|
|
if (fread(cid, 1, 4, f) != 4) break;
|
|
if (fread(&csz, 4, 1, f) != 1) break;
|
|
if (memcmp(cid, "fmt ", 4) == 0) {
|
|
std::vector<uint8_t> buf(csz);
|
|
fread(buf.data(), 1, csz, f);
|
|
channels = *(uint16_t*)&buf[2];
|
|
sr = *(uint32_t*)&buf[4];
|
|
bps = *(uint16_t*)&buf[14];
|
|
} else if (memcmp(cid, "data", 4) == 0) {
|
|
data_sz = csz; data_off = ftell(f); break;
|
|
} else {
|
|
fseek(f, csz, SEEK_CUR);
|
|
}
|
|
}
|
|
if (data_off < 0 || channels != 1 || bps != 16) {
|
|
fprintf(stderr, "WAV mono 16-bit attendu (got ch=%d bps=%d)\n", channels, bps);
|
|
fclose(f); return {};
|
|
}
|
|
sr_out = (int)sr; channels_out = channels;
|
|
fseek(f, data_off, SEEK_SET);
|
|
std::vector<int16_t> pcm(data_sz / 2);
|
|
fread(pcm.data(), 2, pcm.size(), f);
|
|
fclose(f);
|
|
return pcm;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
setvbuf(stderr, nullptr, _IONBF, 0);
|
|
setvbuf(stdout, nullptr, _IONBF, 0);
|
|
if (argc < 3) {
|
|
fprintf(stderr, "usage: %s <mel_filters.bin> <audio16k_mono.wav>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
// 1) Mel basis 80x201 (Whisper)
|
|
auto cfg = kazeia_mel::config_whisper();
|
|
std::vector<float> mel_basis;
|
|
if (!kazeia_mel::load_mel_basis(argv[1], cfg, mel_basis)) return 2;
|
|
fprintf(stderr, "mel_basis Whisper [%d, %d] OK (%zu f32)\n",
|
|
cfg.n_mels, cfg.n_fft/2+1, mel_basis.size());
|
|
|
|
// 2) WAV
|
|
int sr = 0, ch = 0;
|
|
auto pcm = read_wav_pcm16(argv[2], sr, ch);
|
|
if (pcm.empty()) return 3;
|
|
fprintf(stderr, "wav : %zu samples @ %d Hz = %.2f s\n",
|
|
pcm.size(), sr, (double)pcm.size() / sr);
|
|
if (sr != 16000) fprintf(stderr, "WARNING: sr=%d != 16000 (resample externe requis)\n", sr);
|
|
|
|
// 3) Convert to f32
|
|
std::vector<float> wav(pcm.size());
|
|
for (size_t i = 0; i < pcm.size(); ++i) wav[i] = (float)pcm[i] / 32768.0f;
|
|
|
|
// 4) Mel via kazeia_mel
|
|
int T = 0;
|
|
auto mel = kazeia_mel::compute(wav, mel_basis, cfg, T);
|
|
fprintf(stderr, "mel computed : [%d mels, %d frames] = %zu f32 (cible 80 x 3000 Whisper)\n",
|
|
cfg.n_mels, T, mel.size());
|
|
|
|
float mn = 1e9f, mx = -1e9f; double sum = 0;
|
|
for (float v : mel) { if (v < mn) mn = v; if (v > mx) mx = v; sum += v; }
|
|
fprintf(stderr, "mel stats : min=%.4f max=%.4f mean=%.4f\n",
|
|
mn, mx, (float)(sum / mel.size()));
|
|
fprintf(stderr, "(plage Whisper attendue ~[-1.0, 0.5])\n");
|
|
|
|
// 5) VAD test : push tout le PCM par chunks de 1600 samples
|
|
fprintf(stderr, "\n=== VAD RMS test (frame=1600=100ms, seuil=150) ===\n");
|
|
SttVadState * vad = stt_vad_new(16000, 1600, 150, 3, 8);
|
|
int speech_seen = 0, eos_count = 0;
|
|
for (size_t off = 0; off + 1600 <= pcm.size(); off += 1600) {
|
|
stt_vad_push(vad, pcm.data() + off, 1600);
|
|
if (stt_vad_is_speech(vad)) speech_seen += 1;
|
|
if (stt_vad_is_end_of_speech(vad)) eos_count += 1;
|
|
}
|
|
fprintf(stderr, "VAD : %d frames de parole / %zu total, %d end-of-speech\n",
|
|
speech_seen, pcm.size() / 1600, eos_count);
|
|
stt_vad_free(vad);
|
|
|
|
// 6) Dump mel pour comparaison externe si KZSTT_DUMP_MEL=path
|
|
if (const char * p = std::getenv("KZSTT_DUMP_MEL")) {
|
|
std::ofstream f(p, std::ios::binary);
|
|
f.write((char*)mel.data(), mel.size() * sizeof(float));
|
|
fprintf(stderr, "mel dumped -> %s\n", p);
|
|
}
|
|
return 0;
|
|
}
|