This commit is contained in:
lakshya_ai 2026-08-15 16:41:41 -04:00 committed by GitHub
commit 2b6812db9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 90 additions and 1 deletions

View File

@ -25,6 +25,20 @@ It's best to tune it to the specific use case, but a value around `0.6` should b
When silence is detected, it will transcribe the last `--length` milliseconds of audio and output
a transcription block that is suitable for parsing.
## Interactive start/stop
By default `whisper-stream` transcribes continuously from launch. Pass `-i` (`--interactive`) to
control capture from the keyboard instead: the tool starts paused, **SPACE** starts/stops recording,
and **Q** quits.
```bash
./build/bin/whisper-stream -m ./models/ggml-base.en.bin -i
```
This is handy for dictation / push-to-talk style usage where you only want to transcribe the audio
you explicitly record. It works in both the default and sliding-window (`--step 0`) modes, and only
takes effect when stdin is a terminal (piped/redirected input is unaffected).
## Building
The `whisper-stream` tool depends on SDL2 library to capture audio from the microphone. You can build it like this:

View File

@ -9,11 +9,49 @@
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>
#include <thread>
#include <vector>
// --- minimal cross-platform non-blocking keyboard, for interactive start/stop ---
#ifdef _WIN32
#include <conio.h>
static void kbd_enable() {}
static void kbd_restore() {}
static int kbd_get() { return _kbhit() ? _getch() : -1; }
#else
#include <termios.h>
#include <unistd.h>
#include <sys/select.h>
static struct termios g_kbd_old;
static bool g_kbd_saved = false;
static void kbd_restore() {
if (g_kbd_saved) { tcsetattr(STDIN_FILENO, TCSANOW, &g_kbd_old); g_kbd_saved = false; }
}
static void kbd_enable() {
if (!isatty(STDIN_FILENO) || g_kbd_saved) return;
tcgetattr(STDIN_FILENO, &g_kbd_old);
g_kbd_saved = true;
atexit(kbd_restore);
struct termios tio = g_kbd_old;
tio.c_lflag &= ~(ICANON | ECHO);
tio.c_cc[VMIN] = 0; tio.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &tio);
}
static int kbd_get() {
if (!isatty(STDIN_FILENO)) return -1;
fd_set fds; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds);
struct timeval tv{0, 0};
if (select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &tv) > 0) {
unsigned char c;
if (read(STDIN_FILENO, &c, 1) == 1) return c;
}
return -1;
}
#endif
// command-line parameters
struct whisper_params {
int32_t n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency());
@ -37,6 +75,7 @@ struct whisper_params {
bool save_audio = false; // save audio to wav file
bool use_gpu = true;
bool flash_attn = true;
bool interactive = false; // press SPACE to start/stop capture
std::string language = "en";
std::string model = "models/ggml-base.en.bin";
@ -75,6 +114,7 @@ static bool whisper_params_parse(int argc, char ** argv, whisper_params & params
else if (arg == "-ng" || arg == "--no-gpu") { params.use_gpu = false; }
else if (arg == "-fa" || arg == "--flash-attn") { params.flash_attn = true; }
else if (arg == "-nfa" || arg == "--no-flash-attn") { params.flash_attn = false; }
else if (arg == "-i" || arg == "--interactive") { params.interactive = true; }
else {
fprintf(stderr, "error: unknown argument: %s\n", arg.c_str());
@ -114,6 +154,7 @@ void whisper_print_usage(int /*argc*/, char ** argv, const whisper_params & para
fprintf(stderr, " -ng, --no-gpu [%-7s] disable GPU inference\n", params.use_gpu ? "false" : "true");
fprintf(stderr, " -fa, --flash-attn [%-7s] enable flash attention during inference\n", params.flash_attn ? "true" : "false");
fprintf(stderr, " -nfa, --no-flash-attn [%-7s] disable flash attention during inference\n", params.flash_attn ? "false" : "true");
fprintf(stderr, " -i, --interactive [%-7s] press SPACE to start/stop capture, Q to quit\n", params.interactive ? "true" : "false");
fprintf(stderr, "\n");
}
@ -230,7 +271,16 @@ int main(int argc, char ** argv) {
wavWriter.open(filename, WHISPER_SAMPLE_RATE, 16, 1);
}
printf("[Start speaking]\n");
bool recording = true;
if (params.interactive) {
kbd_enable();
audio.pause(); // start paused; wait for the user to press SPACE
audio.clear();
recording = false;
printf("[Interactive mode: press SPACE to start/stop recording, Q to quit]\n");
} else {
printf("[Start speaking]\n");
}
fflush(stdout);
auto t_last = std::chrono::high_resolution_clock::now();
@ -238,6 +288,30 @@ int main(int argc, char ** argv) {
// main audio loop
while (is_running) {
// interactive start/stop via keyboard (SPACE toggles capture, Q quits)
if (params.interactive) {
const int key = kbd_get();
if (key == 'q' || key == 'Q') {
break;
}
if (key == ' ') {
recording = !recording;
if (recording) {
audio.clear();
audio.resume();
printf("\n[recording... press SPACE to stop]\n");
} else {
audio.pause();
printf("\n[stopped. press SPACE to record]\n");
}
fflush(stdout);
}
if (!recording) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
continue;
}
}
if (params.save_audio) {
wavWriter.write(pcmf32_new.data(), pcmf32_new.size());
}
@ -429,6 +503,7 @@ int main(int argc, char ** argv) {
}
audio.pause();
kbd_restore();
whisper_print_timings(ctx);
whisper_free(ctx);