- Geluidsbestand naar tekst
- whisper.cpp (CPU / NVIDIA)
- Opname via een microfoon
- whisper.cpp STT via commandline
- whisper.cpp STT compileren voor VULKAN support (AMD)
- whisper.cpp server
- Realtime met whisper.cpp server en ffmpeg microphone wav chunks
- Voice Activity Detection (VAD)
- Twee populaire VAD engines
- VAD - Python script 1
- VAD - Python script 2
- VAD - Python script 3 (UDP)
- Silero VAD gebruiken met whisper.cpp

Op deze pagina gaan we lokaal spraak naar tekst (Speech To Text) zetten om dit evt later naar een LLM door te sturen.
Het doel besturingssysteem is Windows (of WSL indien aangegeven)
Geluidsbestand naar tekst #
faster-whisper #

In dit voorbeeld genereren we tekst van een WAV of MP3 bestand, hiervoor heb je nodig: faster-whisper
Faster-Whisper Github: https://github.com/SYSTRAN/faster-whisper
Installeer faster-whisper met het commando:
pip install faster-whisper
Python script – STT met faster-whisper (CPU / NVIDIA) #
Gebruik onderstaande pythonscript om een WAV/MP3 bestand in tekst te zetten via de commandline: transcribe.py
from faster_whisper import WhisperModel
import sys
audio_file = sys.argv[1]
model_size = "base" # tiny, base, small, medium, large-v3
model = WhisperModel(model_size, compute_type="int8")
segments, info = model.transcribe(audio_file)
print("Language:", info.language)
print("Duration:", info.duration)
for segment in segments:
print(f"[{segment.start:.2f}s -> {segment.end:.2f}s] {segment.text}")
Nederlandse taal forceren (sneller)
Als je alleen NL verwacht:
segments, info = model.transcribe(audio_file, language="nl")
Dan hoeft Whisper geen language detection te doen.
GPU gebruiken ipv CPU en werkgeheugen (alleen nVIDIA / CUDA)
WhisperModel(
"small",
device="cuda",
compute_type="float16"
)
Testbestanden
Hier heb ik een git gevonden met een aantal test bestanden:
https://github.com/voxserv/audio_quality_testing_samples
Bijvoorbeeld:
https://github.com/voxserv/audio_quality_testing_samples/blob/master/testaudio/8000/test01_20s.wav
Start het STT python script:
python3 transcribe.py test01_20s.wav
Output:
Language: en
Duration: 24.0
[0.00s -> 8.52s] Dancing in the masquerade, idle truth and plain sight jaded, pop, roll, click, shot.
[8.52s -> 10.64s] Who will I be today or not?
[10.64s -> 15.56s] But such a tide as moving seems asleep, too full for sound and foam.
[15.56s -> 20.56s] When that witch drew from out the boundless deep turns again home, twilight and evening
[20.56s -> 22.56s] bell, and after that,
Het bestand uit die git “165187__blaukreuz__global-village-hochdeutsch.wav”, geeft:
Language: de
Duration: 24.92
[0.00s -> 2.80s] Vor dem Gesetz steht ein Türhüter.
[2.80s -> 8.16s] Zu diesem Türhüter kommt ein Mann vom Lande und bittet um Eintritt in das Gesetz.
[8.16s -> 14.08s] Aber der Türhüter sagt, dass er ihm jetzt den Eintritt nicht gewähren könne.
[14.08s -> 19.96s] Der Mann überlegt und fragt dann, ob er also später werde Eintreten dürfen.
[19.96s -> 24.80s] Es ist möglich, sagt der Türhüter, jetzt aber nicht.
faster-whisper modellen kiezen #
| model | RAM | snelheid |
|---|---|---|
| tiny | ~1GB | super snel |
| base | ~1GB | snel |
| small | ~2GB | beter |
| medium | ~5GB | goed |
| large-v3 | ~10GB | beste |
Waar worden deze modellen neergezet (download)?
Bij Faster-Whisper worden de modellen automatisch gedownload via de HuggingFace cache. In Windows komen ze standaard hier terecht:
C:\Users\<USERNAME>\.cache\huggingface\hub\
Bijvoorbeeld:
C:\Users\Sebastiaan\.cache\huggingface\hub\models--Systran--faster-whisper-base
Daar zie je vaak:
models--Systran--faster-whisper-base
models--Systran--faster-whisper-small
models--Systran--faster-whisper-large-v3
Binnen zo’n map:
snapshots\
refs\
blobs\
De snapshots map bevat het echte model dat CTranslate2 gebruikt.
Grootte van Faster-Whisper modellen
| model | approx size | RAM gebruik |
|---|---|---|
| tiny | ~75 MB | ~300-500 MB |
| base | ~150 MB | ~500-700 MB |
| small | ~480 MB | ~1-2 GB |
| medium | ~1.5 GB | ~3-4 GB |
| large-v3 | ~3.1 GB | ~6-8 GB |
De base van Faster-Whisper is verrassend klein.
Dat komt omdat Faster-Whisper geconverteerde CTranslate2 modellen gebruikt i.p.v. de originele PyTorch modellen van OpenAI Whisper.
Hierdoor:
- model is gequantized
- inference engine is C++
- veel minder overhead
Waarom zo klein?
De originele Whisper base: ~500 MB
Faster-Whisper base: ~150 MB
Door: FP16 / INT8 quantization + CTranslate2 format + weight packing
Dus zeg maar: wat GGUF is voor taalmodellen is CTranslate2 voor audio modellen.
Waarom Faster-Whisper zo snel is
Omdat CTranslate2:
- SIMD optimalisaties
- AVX / AVX2 / AVX512
- GPU kernels
- INT8 quantization
- streaming inference
heeft.
Daarom zie je vaak:
faster-whisper = 4x sneller dan whisper
whisper.cpp (CPU / NVIDIA) #

Github: https://github.com/ggml-org/whisper.cpp
Een ander programma “kant-en-klaar” voor windows is whisper.cpp, download een Windows binairy zoals whisper-blas-bin-x64.zip en pak deze uit.
Modellen voor whisper.cpp
Je kan de modellen voor whisper.cpp vinden op huggingface: https://huggingface.co/ggerganov/whisper.cpp/tree/main
Download bijvoorbeeld “ggml-base.bin” en plaats deze in dezelfde folder als waar de bestanden van whisper.cpp staan.
Note: bestanden zonder toevoegingen zijn F16
Performance verschil op CPU:
| model | snelheid |
|---|---|
| FP16 | baseline |
| q8 | ~1.3× sneller |
| q5 | ~1.6× sneller |
Kwaliteit: Voor speech recognition blijft het verrassend goed.
| quant | kwaliteit |
|---|---|
| FP16 | perfect |
| q8 | vrijwel identiek |
| q5 | klein beetje verlies |
De officiële Whisper modellen
Dit zijn de originele modellen van OpenAI:
| Model | Parameters | Gebruik |
|---|---|---|
| tiny | 39M | extreem snel |
| base | 74M | licht |
| small | 244M | realtime |
| medium | 769M | goede accuracy |
| large | 1.55B | beste accuracy |
Daar bovenop kwamen later:
| Model | Opmerking |
|---|---|
| large-v2 | verbeterde training |
| large-v3 | huidige beste |
| large-v3-turbo | sneller |
Voor nu een commando uit om een audiobestand naar tekst te zetten:
whisper-cli -m ggml-base.bin -f test01_20s.wav
Output:
whisper_init_from_file_with_params_no_state: loading model from 'ggml-base-q8_0.bin'
whisper_init_with_params_no_state: use gpu = 1
whisper_init_with_params_no_state: flash attn = 1
whisper_init_with_params_no_state: gpu_device = 0
whisper_init_with_params_no_state: dtw = 0
whisper_init_with_params_no_state: devices = 2
whisper_init_with_params_no_state: backends = 2
whisper_model_load: loading model
whisper_model_load: n_vocab = 51865
whisper_model_load: n_audio_ctx = 1500
whisper_model_load: n_audio_state = 512
whisper_model_load: n_audio_head = 8
whisper_model_load: n_audio_layer = 6
whisper_model_load: n_text_ctx = 448
whisper_model_load: n_text_state = 512
whisper_model_load: n_text_head = 8
whisper_model_load: n_text_layer = 6
whisper_model_load: n_mels = 80
whisper_model_load: ftype = 7
whisper_model_load: qntvr = 2
whisper_model_load: type = 2 (base)
whisper_model_load: adding 1608 extra tokens
whisper_model_load: n_langs = 99
whisper_model_load: CPU total size = 81.18 MB
whisper_model_load: model size = 81.18 MB
whisper_backend_init_gpu: device 0: BLAS (type: 3)
whisper_backend_init_gpu: device 1: CPU (type: 0)
whisper_backend_init_gpu: no GPU found
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 6.29 MB
whisper_init_state: kv cross size = 18.87 MB
whisper_init_state: kv pad size = 3.15 MB
whisper_init_state: compute buffer (conv) = 16.28 MB
whisper_init_state: compute buffer (encode) = 23.09 MB
whisper_init_state: compute buffer (cross) = 4.66 MB
whisper_init_state: compute buffer (decode) = 96.37 MB
system_info: n_threads = 4 / 32 | WHISPER : COREML = 0 | OPENVINO = 0 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | OPENMP = 1 | REPACK = 1 |
main: processing 'test01_20s.wav' (384000 samples, 24.0 sec), 4 threads, 1 processors, 5 beams + best of 5, lang = en, task = transcribe, timestamps = 1 ...
[00:00:00.000 --> 00:00:08.460] Dancing in the masquerade, idle truth and plain sight jaded, pop, roll, click, shot.
[00:00:08.460 --> 00:00:10.660] Who will I be today or not?
[00:00:10.660 --> 00:00:16.240] But such a tide as moving seems asleep, too full for sound and foam, when that witch
[00:00:16.240 --> 00:00:21.680] drew from out the boundless deep turns again home, twilight and evening bell and after
[00:00:21.680 --> 00:00:21.980] that.
whisper_print_timings: load time = 66.37 ms
whisper_print_timings: fallbacks = 0 p / 0 h
whisper_print_timings: mel time = 9.52 ms
whisper_print_timings: sample time = 168.87 ms / 408 runs ( 0.41 ms per run)
whisper_print_timings: encode time = 704.81 ms / 1 runs ( 704.81 ms per run)
whisper_print_timings: decode time = 0.00 ms / 1 runs ( 0.00 ms per run)
whisper_print_timings: batchd time = 533.88 ms / 406 runs ( 1.31 ms per run)
whisper_print_timings: prompt time = 0.00 ms / 1 runs ( 0.00 ms per run)
whisper_print_timings: total time = 1505.57 ms
Opname via een microfoon #

Er zijn veel programma’s die kunnen opnemen van een microfoon, wat we willen is streaming audio, het liefst via een server IP/poort, de kortste klap is om ffmpeg te gebruiken
FFmpeg #

Installatie van ffmpeg, als je winget hebt (Windows 10/11):
winget install ffmpeg
Handmatig installeren
- Download build:
https://www.gyan.dev/ffmpeg/builds/ - Neem:
ffmpeg-release-essentials.zip
- Uitpakken naar bijvoorbeeld:
C:\ffmpeg
- Voeg toe aan PATH:
C:\ffmpeg\bin
Dan werkt overal:
ffmpeg
ffplay
ffprobe
Controleer beschikbare apparaten met:
ffmpeg -list_devices true -f dshow -i dummy
Voorbeeld output:
[dshow @ 0000028030ff5540] "Logitech Webcam C930e" (video)
[dshow @ 0000028030ff5540] "Microfoon (Logitech Webcam C930e)" (audio)
[dshow @ 0000028030ff5540] "What U Hear (3- Sound BlasterX AE-5)" (audio)
[dshow @ 0000028030ff5540] "Microphone (Yeti Stereo Microphone)" (audio)
Opname stream starten #
Om bijvoorbeeld een opname te starten via TCP (alle devices):
ffmpeg -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f wav tcp://0.0.0.0:9999
RAW PCM / Localhost:
ffmpeg -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f s16le tcp://127.0.0.1:9999?listen=1
Zonder foutmeldingen (als er bijvoorbeeld geen clients is verbonden en de buffer wordt te vol):
ffmpeg -loglevel error -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f s16le tcp://127.0.0.1:9999?listen=1
Via UDP:
ffmpeg -loglevel error -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f s16le udp://127.0.0.1:9999
whisper.cpp STT via commandline #
Nu kan je met één commando een stream starten met ffmpeg en deze d.m.v. een pipe doorzetten naar whisper.cpp:
ffmpeg -loglevel error -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f s16le - | whisper-stream.exe -m ggml-base.bin
Maar het model heeft wel moeite met de nederlandse taal:
- gebruik een beter model: ggml-small-q8_0.bin
- voeg de taal toe: -l nl
- voeg een treshold toe om ruis uit te filteren: -vth 0.6
-vth (voice activity threshold)
In whisper.cpp betekent:
| waarde | gedrag |
|---|---|
| 0.3 | zeer gevoelig |
| 0.6 | default |
| 0.8 | alleen duidelijke spraak |
| 0.9 | bijna geen ruis meer |
ffmpeg -loglevel error -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f s16le - | whisper-stream.exe -m ggml-small-q8_0.bin -l nl -vth 0.6
Er is nog steeds kans op buffer overflow vanuit ffmpeg, zet de buffer uit, en stream “live”.
ffmpeg -loglevel error -f dshow -fflags nobuffer -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f s16le - | whisper-stream.exe -m ggml-small-q8_0.bin -l nl -t 16 -vth 0.6
- Voeg eventueel meer CPU threads to met: -t 16
- Laat een kleine buffer toe 64M: -rtbufsize 64M -f dshow
ffmpeg -loglevel error -fflags nobuffer -rtbufsize 64M -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f s16le - | whisper-stream.exe -m ggml-small-q8_0.bin -l nl -t 16 -vth 0.6
Om nog meer ruis te onderdrukken
- voeg een filter toe bij ffmpeg: -af highpass=f=120
- verhoog de voice activity threshold: -vth 0.8
ffmpeg -loglevel error -fflags nobuffer -rtbufsize 64M -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -af highpass=f=120 -ac 1 -ar 16000 -f s16le - | whisper-stream.exe -m ggml-small-q8_0.bin -l nl -t 16 -vth 0.8
whisper.cpp STT compileren voor VULKAN support (AMD) #

De standaard build van whisper.cpp ondersteunt geen VULKAN voor AMD kaarten, maar dat kunnen we zelf compileren vanuit de source!
github: https://github.com/DomoticX/whisper.cpp-windows-vulkan
Vulkan SDK installeren #
Download: https://vulkan.lunarg.com/sdk/home
Neem: Windows x64 Installer (bijvoorbeeld vulkansdk-windows-X64-1.x.xxx.x.exe)
Tijdens installatie:
- Install everything
- Environment variables laten zetten


Start een Windows command en typ:
echo %VULKAN_SDK%
Je zou iets moeten zien zoals:
C:\VulkanSDK\1.3.xxx.x
Installeer CMake (compiler) #
In Windows command:
winget install Kitware.CMake
Output (voorbeeld)
Found CMake [Kitware.CMake] Version 4.2.3
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.
Downloading https://github.com/Kitware/CMake/releases/download/v4.2.3/cmake-4.2.3-windows-x86_64.msi
██████████████████████████████ 34.7 MB / 34.7 MB
Successfully verified installer hash
Starting package install...
Successfully installed
Installeer whisper.cpp source (GIT) #
In Windows command:
git clone https://github.com/ggml-org/whisper.cpp.git
Output voorbeeld:
Cloning into 'whisper.cpp'...
remote: Enumerating objects: 31665, done.
remote: Counting objects: 100% (149/149), done.
remote: Compressing objects: 100% (75/75), done.
remote: Total 31665 (delta 79), reused 74 (delta 74), pack-reused 31516 (from 4)
Receiving objects: 100% (31665/31665), 33.63 MiB | 23.60 MiB/s, done.
Resolving deltas: 100% (23050/23050), done.
Bouw whisper.cpp binaries (VULKAN) #
Ga naar de source folder:
cd whisper.cpp
Optioneel als je wilt experimenteren of telkens nieuwe builds wil maken, verwijder oude build:
rmdir /s /q build
Je kan nu de make configure bouwen met de flag: -DGGML_VULKAN=1
cmake -B build -DGGML_VULKAN=1
CPUs zonder AVX512 kunnen crashen!
Bijvoorbeeld:
| CPU | AVX512 |
|---|---|
| Intel 12/13 gen | ❌ |
| AMD Ryzen | ❌ |
| Xeon / Threadripper | soms |
Dus voor maximale compatibiliteit kun je beter bouwen met: -DGGML_AVX512=OFF
cmake -B build -DGGML_VULKAN=1 -DGGML_AVX512=OFF
Wat nog steeds automatisch actief blijft
Zelfs zonder AVX512 gebruikt ggml nog:
- AVX2
- FMA
- OpenMP
Dat zie je ook in je build log, dus performance blijft goed.
Output voorbeeld:
-- Building for: Visual Studio 18 2026
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
Compatibility with CMake < 3.10 will be removed from a future version of
CMake.
Update the VERSION argument <min> value. Or, use the <min>...<max> syntax
to tell CMake that the project requires at least <min> but has been updated
to work with policies introduced by <max> or earlier.
-- Selecting Windows SDK version 10.0.26100.0 to target Windows 10.0.26200.
-- The C compiler identification is MSVC 19.50.35726.0
-- The CXX compiler identification is MSVC 19.50.35726.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/18/BuildTools/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/18/BuildTools/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Git: C:/Program Files/Git/cmd/git.exe (found version "2.53.0.windows.1")
-- The ASM compiler identification is MSVC
CMake Warning (dev) at C:/Program Files/CMake/share/cmake-4.2/Modules/CMakeDetermineASMCompiler.cmake:234 (message):
Policy CMP194 is not set: MSVC is not an assembler for language ASM. Run
"cmake --help-policy CMP194" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.
Call Stack (most recent call first):
ggml/CMakeLists.txt:2 (project)
This warning is for project developers. Use -Wno-dev to suppress it.
-- Found assembler: C:/Program Files (x86)/Microsoft Visual Studio/18/BuildTools/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/cl.exe
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - not found
-- Found Threads: TRUE
-- Warning: ccache not found - consider installing it for faster compilation or disable this warning with GGML_CCACHE=OFF
-- CMAKE_SYSTEM_PROCESSOR: AMD64
-- CMAKE_GENERATOR_PLATFORM:
-- GGML_SYSTEM_ARCH: x86
-- Including CPU backend
-- Found OpenMP_C: -openmp (found version "2.0")
-- Found OpenMP_CXX: -openmp (found version "2.0")
-- Found OpenMP: TRUE (found version "2.0")
-- x86 detected
-- Performing Test HAS_AVX_1
-- Performing Test HAS_AVX_1 - Success
-- Performing Test HAS_AVX2_1
-- Performing Test HAS_AVX2_1 - Success
-- Performing Test HAS_FMA_1
-- Performing Test HAS_FMA_1 - Success
-- Performing Test HAS_AVX512_1
-- Performing Test HAS_AVX512_1 - Success
-- Adding CPU backend variant ggml-cpu: /arch:AVX512 GGML_AVX512
-- Found Vulkan: C:/VulkanSDK/1.4.341.1/Lib/vulkan-1.lib (found version "1.4.341") found components: glslc glslangValidator
-- Vulkan found
-- GL_KHR_cooperative_matrix supported by glslc
-- GL_NV_cooperative_matrix2 supported by glslc
-- GL_EXT_integer_dot_product supported by glslc
-- GL_EXT_bfloat16 supported by glslc
-- Including Vulkan backend
-- ggml version: 0.9.7
-- ggml commit: 30c5194c
-- Configuring done (21.4s)
-- Generating done (0.3s)
-- Build files have been written to: E:/whisper.cpp/build
Nadat make configure is voltooid begin nu met bouwen van de binaries:
cmake --build build --config Release
Nadat dit hele proces voltooid is vind je de binaries in: \build\bin\Release\
Gegenereerde bestanden:
whisper-vad-speech-segments.exe
whisper-server.exe
whisper-quantize.exe
whisper-cli.exe
whisper-bench.exe
test-vad-full.exe
test-vad.exe
main.exe
whisper.dll
ggml.dll
ggml-vulkan.dll
ggml-cpu.dll
ggml-base.dll
bench.exe
Waar is whisper-stream.exe gebleven?
In oudere builds zat:
examples/stream/
Die gebruikte PortAudio / SDL voor microfoon capture.
Die wordt tegenwoordig vaak niet meer standaard gebouwd, omdat:
- OS audio capture verschilt per platform
- veel mensen toch ffmpeg pipelines gebruiken
whisper.cpp server #
Je hebt nu ook de beschikking over de whisper server, je eigen STT endpoint!
Start de whisper server bijvoorbeeld met poort 9090:
whisper-server.exe -m ggml-small-q8_0.bin --port 9090
En stuur bijvoorbeeld vanuit een andere console window, een bestand met CURL POST naar de eindpoint /inference (test01_20s.wav)
curl -X POST http://127.0.0.1:9090/inference -F "file=@test01_20s.wav"
Je krijgt dan een JSON output terug (voorbeeld):
{"text":" Dancing in the masquerade, idle truth in plain sight jaded, pop, roll, click, shot,\n who will I be today or not?\n But such a tide as moving seems asleep, too full for sound and foam, when that which drew\n from out the boundless deep turns again home, twilight and evening bell and after that,\n [BLANK_AUDIO]\n"}
Realtime met whisper.cpp server en ffmpeg microphone wav chunks #
Nu er geen streaming exe bestaat moeten we het doen met de toosl die we hebben, als je zeg maar een audiostream start vanaf ffmpeg udp, dan is de stream onafgebroken en de whisper-server verwacht een “einde audio” alvorens hij een json uitput geeft. Ik heb samen met chatGPT dit enigszins werkend gekregen met behulp van een BAT/CMD bestand (zie ondeR), dit laat ffmpeg opnemen in chunk.wav bestanden en stuurt die stuk voor stuk naar de whisper-server op poort 9090 om de audio naar tekst om te zetten:
stt.cmd
@echo off
echo Starting microphone capture...
start "" ffmpeg -loglevel error -f dshow -i audio="Microphone (Yeti Stereo Microphone)" -ac 1 -ar 16000 -f segment -segment_time 2 -segment_format wav -reset_timestamps 1 chunk_%%03d.wav
echo Waiting for audio chunks...
:loop
for %%f in (chunk_*.wav) do (
timeout /t 1 >nul
echo Processing %%f
curl http://127.0.0.1:9090/inference -F "file=@%%f"
echo.
del %%f
)
timeout /t 1 >nul
goto loop
in de console met de server output zie je bijvoorbeeld:
Running whisper.cpp inference on chunk_034.wav
Received request: chunk_035.wav
Successfully loaded chunk_035.wav
system_info: n_threads = 4 / 32 | WHISPER : COREML = 0 | OPENVINO = 0 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | AVX512 = 1 | OPENMP = 1 | REPACK = 1 |
operator (): processing 'chunk_035.wav' (80000 samples, 5.0 sec), 4 threads, 1 processors, lang = nl, task = transcribe, timestamps = 1 ...
Running whisper.cpp inference on chunk_035.wav
Received request: chunk_036.wav
Successfully loaded chunk_036.wav
system_info: n_threads = 4 / 32 | WHISPER : COREML = 0 | OPENVINO = 0 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | AVX512 = 1 | OPENMP = 1 | REPACK = 1 |
operator (): processing 'chunk_036.wav' (80000 samples, 5.0 sec), 4 threads, 1 processors, lang = nl, task = transcribe, timestamps = 1 ...
Running whisper.cpp inference on chunk_036.wav
Received request: chunk_037.wav
Successfully loaded chunk_037.wav
system_info: n_threads = 4 / 32 | WHISPER : COREML = 0 | OPENVINO = 0 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | AVX512 = 1 | OPENMP = 1 | REPACK = 1 |
operator (): processing 'chunk_037.wav' (80000 samples, 5.0 sec), 4 threads, 1 processors, lang = nl, task = transcribe, timestamps = 1 ...
Running whisper.cpp inference on chunk_037.wav
En in de andere console zie je de tekst:
Processing chunk_000.wav
{"text":""}
Processing chunk_001.wav
{"text":" Hallo, hallo, hoe gaat het hier?\n"}
Processing chunk_002.wav
{"text":""}
Processing chunk_003.wav
{"text":" Het gaat prima hier!\n"}
Processing chunk_004.wav
...
Realtime met whisper.cpp server en ffmpeg microphone wav chunks #
Een mooiere oplossing is om met python direct de audiochunk in het geheugen te laden en door te sturen naar de server, ChatGPT, kwam met deze python code.
Benodigheden:
pip install sounddevice numpy requests
stt.py
import io
import json
import time
import wave
from queue import Queue
import numpy as np
import requests
import sounddevice as sd
# ===== Instellingen =====
SERVER_URL = "http://127.0.0.1:9090/inference"
SAMPLERATE = 16000
CHANNELS = 1
BLOCK_SECONDS = 1.0 # 1.0 = lage latency, 0.5 kan ook
DEVICE = None # None = standaard microfoon
NOISE_GATE = 250 # simpele stiltefilter op RMS-niveau
PRINT_EMPTY = False # True om ook lege responses te tonen
REQUEST_TIMEOUT = 15
# ===== Interne queue =====
audio_queue: Queue[np.ndarray] = Queue()
def pcm16_to_wav_bytes(audio_int16: np.ndarray, samplerate: int) -> bytes:
"""Zet int16 numpy audio om naar WAV-bytes in geheugen."""
buf = io.BytesIO()
with wave.open(buf, "wb") as wf:
wf.setnchannels(1)
wf.setsampwidth(2) # int16 = 2 bytes
wf.setframerate(samplerate)
wf.writeframes(audio_int16.tobytes())
return buf.getvalue()
def rms_level(audio_int16: np.ndarray) -> float:
"""Eenvoudige geluidssterkte-inschatting."""
audio_f32 = audio_int16.astype(np.float32)
return float(np.sqrt(np.mean(audio_f32 * audio_f32)))
def audio_callback(indata, frames, time_info, status):
if status:
print(f" {status}")
# Kopie maken zodat sounddevice buffer veilig vrijgegeven kan worden
audio_queue.put(indata.copy().reshape(-1))
def post_chunk(audio_int16: np.ndarray) -> str:
wav_bytes = pcm16_to_wav_bytes(audio_int16, SAMPLERATE)
files = {
"file": ("chunk.wav", wav_bytes, "audio/wav")
}
response = requests.post(
SERVER_URL,
files=files,
timeout=REQUEST_TIMEOUT,
)
response.raise_for_status()
data = response.json()
return data.get("text", "").strip()
def main():
print("Live STT gestart.")
print(f"Server: {SERVER_URL}")
print(f"Sample rate: {SAMPLERATE} Hz")
print(f"Block size: {BLOCK_SECONDS:.2f} s")
print("Spreek maar... Ctrl+C om te stoppen.\n")
block_samples = int(SAMPLERATE * BLOCK_SECONDS)
collected = np.empty(0, dtype=np.int16)
with sd.InputStream(
samplerate=SAMPLERATE,
channels=CHANNELS,
dtype="int16",
callback=audio_callback,
device=DEVICE,
blocksize=0,
):
while True:
chunk = audio_queue.get()
collected = np.concatenate((collected, chunk.astype(np.int16)))
while len(collected) >= block_samples:
block = collected[:block_samples]
collected = collected[block_samples:]
level = rms_level(block)
if level < NOISE_GATE:
if PRINT_EMPTY:
print("[stilte]")
continue
started = time.perf_counter()
try:
text = post_chunk(block)
elapsed_ms = int((time.perf_counter() - started) * 1000)
if text:
print(f"[{elapsed_ms} ms] {text}")
elif PRINT_EMPTY:
print(f"[{elapsed_ms} ms] [geen tekst]")
except requests.RequestException as e:
print(f"[HTTP fout] {e}")
time.sleep(0.5)
except json.JSONDecodeError:
print("[fout] Server gaf geen geldige JSON terug.")
time.sleep(0.5)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nGestopt.")
Start de server en start in een andere console:
python3 stt.py
Nu heb je echte realtime transcriptie.
Voice Activity Detection (VAD) #

De volgende stap is het toepassen van VAD (Voice Activity Detection)
Het betekent simpelweg: detecteren of iemand spreekt of niet
Dus het algoritme beslist continu:
audio → spraak
audio → stilte / ruis
Resultaat:
0 = geen spraak
1 = spraak
Dat gebruik je om:
- audio te segmenteren
- alleen spraak naar STT te sturen
- latency te verlagen
- CPU/GPU te besparen
Twee populaire VAD engines #
webrtcvad (WebRTC) #
Dit is de klassieke VAD van Google WebRTC.
Voordelen:
- extreem licht
- realtime
- werkt op CPU
- gebruikt in Zoom / Meet / Discord
Eigenschappen:
10 / 20 / 30 ms audio frames
Dus het analyseert audio 100× per seconde.
Python voorbeeld:
import webrtcvadvad = webrtcvad.Vad(2)if vad.is_speech(frame, 16000):
print("speech")
Modes:
| mode | gevoeligheid |
|---|---|
| 0 | laag |
| 1 | medium |
| 2 | hoog |
| 3 | zeer hoog |
silero-vad #
Dit is een neuraal netwerk VAD.
Dus:
audio → AI model → spraak detectie
Voordelen:
- veel nauwkeuriger
- beter bij achtergrondgeluid
- werkt goed met Whisper
Nadelen:
- iets zwaarder
Het draait vaak met:
ONNX + PyTorch
Verschil #
| VAD | type | CPU | nauwkeurigheid |
|---|---|---|---|
| webrtcvad | klassiek DSP | zeer laag | goed |
| silero-vad | AI model | laag | zeer goed |
VAD – Python script 1 #
Met dit script luister je naar een TCP stream, bijvoorbeeld gegenereerd met ffmpeg.
Je hebt nodig: webrtcvad, instelleer deze met: pip install webrtcvad
Collecting webrtcvad
Using cached webrtcvad-2.0.10.tar.gz (66 kB)
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing metadata (pyproject.toml) ... done
Building wheels for collected packages: webrtcvad
Building wheel for webrtcvad (pyproject.toml) ... done
Created wheel for webrtcvad: filename=webrtcvad-2.0.10-cp311-cp311-win_amd64.whl size=18430 sha256=3e1eb82b284aff4eedf9eacbb541de6614d8ced6dd317eafeb960c92c5389735
Stored in directory: c:\users\orka2\appdata\local\pip\cache\wheels\94\65\3f\292d0b656be33d1c801831201c74b5f68f41a2ae465ff2ee2f
Successfully built webrtcvad
Installing collected packages: webrtcvad
Successfully installed webrtcvad-2.0.10
Python script voor VAD:
import socket
import webrtcvad
HOST = "127.0.0.1"
PORT = 9999
RATE = 16000
FRAME_MS = 20
FRAME_BYTES = int(RATE * FRAME_MS / 1000) * 2
vad = webrtcvad.Vad(2)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
buffer = b''
print("connected to stream")
while True:
data = sock.recv(4096)
buffer += data
while len(buffer) >= FRAME_BYTES:
frame = buffer[:FRAME_BYTES]
buffer = buffer[FRAME_BYTES:]
if vad.is_speech(frame, RATE):
print("speech detected")
VAD – Python script 2 #
Zoals je merkt en test gaat het prima, alles is het wat onoverzichtelijk als je telkens dezelfde regel ziet staan op de commandline, daarvoor deze update met frame en speech counter:
import socket
import webrtcvad
HOST = "127.0.0.1"
PORT = 9999
RATE = 16000
FRAME_MS = 20
FRAME_BYTES = int(RATE * FRAME_MS / 1000) * 2
vad = webrtcvad.Vad(2)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
buffer = b''
frame_counter = 0
speech_counter = 0
print("Connected to audio stream")
while True:
data = sock.recv(4096)
buffer += data
while len(buffer) >= FRAME_BYTES:
frame = buffer[:FRAME_BYTES]
buffer = buffer[FRAME_BYTES:]
frame_counter += 1
if vad.is_speech(frame, RATE):
speech_counter += 1
print(f"frame {frame_counter} | speech #{speech_counter}")
Maar telkens als je dit python script afsluit zie je bij ffmpeg de volgende foutmelding (voorbeeld:
[aost#0:0/pcm_s16le @ 0000015f51980d40] Error submitting a packet to the muxer: Error number -10054 occurred
Last message repeated 1 times
[out#0/s16le @ 0000015f51969000] Error muxing a packet
[out#0/s16le @ 0000015f51969000] Task finished with error code: -10054 (Error number -10054 occurred)
[out#0/s16le @ 0000015f51969000] Terminating thread with return code -10054 (Error number -10054 occurred)
[out#0/s16le @ 0000015f51969000] Error writing trailer: Error number -10054 occurred
[out#0/s16le @ 0000015f51969000] Error closing file: Error number -10054 occurred
dat gedrag is normaal. Wat er gebeurt:
ffmpeg TCP server
↓
python client connected
↓
python stopt
↓
TCP socket sluit
↓
ffmpeg kan niet meer schrijven
↓
ffmpeg stopt met error -10054
Error -10054 betekent in Windows:
WSAECONNRESET
connection reset by peer
Dus simpel gezegd:
client disconnect → server stopt
Oplossing: gebruik UDP i.p.v. TCP.
Dan maakt het niet uit of de client stopt.
VAD – Python script 3 (UDP) #
Start ffmpeg stream via UDP (voorbeeld):
ffmpeg -loglevel error -f dshow -i audio="Microfoon (Logitech Webcam C930e)" -ac 1 -ar 16000 -f s16le udp://127.0.0.1:9999
Python script om UDP audiostream te verwerken met VAD:
import socket
import webrtcvad
HOST = "127.0.0.1"
PORT = 9999
RATE = 16000
FRAME_MS = 20
FRAME_BYTES = int(RATE * FRAME_MS / 1000) * 2
vad = webrtcvad.Vad(3)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("127.0.0.1",9999))
buffer = b''
frame_counter = 0
speech_counter = 0
print("Connected to audio stream")
while True:
data = sock.recv(4096)
buffer += data
while len(buffer) >= FRAME_BYTES:
frame = buffer[:FRAME_BYTES]
buffer = buffer[FRAME_BYTES:]
frame_counter += 1
if vad.is_speech(frame, RATE):
speech_counter += 1
print(f"frame {frame_counter} | speech #{speech_counter}")
Voorbeeld output:
frame 968 | speech #155
frame 976 | speech #156
frame 977 | speech #157
frame 978 | speech #158
frame 979 | speech #159
frame 1006 | speech #160
frame 1007 | speech #161
frame 1008 | speech #162
frame 1009 | speech #163
frame 1010 | speech #164
frame 38436 | speech #165
frame 38437 | speech #166
frame 38438 | speech #167
frame 38439 | speech #168
frame 38443 | speech #169
frame 38444 | speech #170
frame 38445 | speech #171
frame 38446 | speech #172
De vier niveaus (webrtcvad.Vad([niveau])
| mode | gevoeligheid | gedrag |
|---|---|---|
| 0 | laag | laat veel door |
| 1 | normaal | redelijk tolerant |
| 2 | agressief | filtert meer ruis |
| 3 | zeer agressief | alleen duidelijke spraak |
Belangrijk detail
WebRTC VAD accepteert alleen frames van:
10 ms
20 ms
30 ms
Dus:
| frame | bytes |
|---|---|
| 10 ms | 320 |
| 20 ms | 640 |
| 30 ms | 960 |
Daarom gebruiken we:
FRAME_BYTES = 640
Silero VAD gebruiken met whisper.cpp #

Model Description #
Silero VAD: pre-trained enterprise-grade Voice Activity Detector (VAD). Enterprise-grade Speech Products made refreshingly simple (see our STT models). Each model is published separately.
Currently, there are hardly any high quality / modern / free / public voice activity detectors except for WebRTC Voice Activity Detector (link). WebRTC though starts to show its age and it suffers from many false positives.
(!!!) Important Notice (!!!) – the models are intended to run on CPU only and were optimized for performance on 1 CPU thread. Note that the model is quantized.
Download: https://huggingface.co/ggml-org/whisper-vad/tree/main
Dus met VAD hoeft Whisper veel minder audio te verwerken.
Waarom dit zo nuttig is
Vooral bij audio met veel stilte:
| situatie | winst |
|---|---|
| interviews | groot |
| podcasts | groot |
| meetings | groot |
| CCTV audio | enorm |
| livestream capture | enorm |
Wil je echt een profi STT commando, gebruik deze:
whisper-cli.exe -m "model/ggml-large-v2-q5_0.bin" --vad -vm "model/ggml-silero-v5.1.2.bin" -f "test.wav" -sns -osrt
Voor server:
whisper-server.exe -m "model/ggml-large-v2-q5_0.bin" --vad --vad-model "model/ggml-silero-v5.1.2.bin" --port 9090
Verschil met WebRTC #
WebRTC kijkt naar audio features zoals:
- energie
- frequentiebanden
- periodiciteit
- ruisniveau
Dus ongeveer:
audio frame
↓
feature extractie
↓
regels / thresholds
↓
speech / no speech
Voordelen:
✔ extreem snel
✔ bijna geen CPU
✔ stabiel
Nadelen:
❌ gevoelig voor ruis
❌ moeite met zachte stemmen
❌ kan muziek verwarren met spraak
❌ mist soms woorden aan begin/einde
Waarom dat 8 MB model zo klein is #
Omdat het alleen dit leert:
speech vs non speech
Dus geen taal, geen woorden, daarom kan het extreem klein blijven.
Performance vergelijking #
| VAD | CPU | accuracy | realtime |
|---|---|---|---|
| WebRTC | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| Silero | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |


