233 lines
6.2 KiB
C
233 lines
6.2 KiB
C
#include "transcribe.h"
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifndef WITH_WHISPER
|
|
#define WITH_WHISPER 0
|
|
#endif
|
|
|
|
static int g_filter_bracketed_tags = 1;
|
|
|
|
static char *filter_bracketed_tags_copy(const char *input) {
|
|
if (!input) return NULL;
|
|
|
|
const size_t len = strlen(input);
|
|
char *no_tags = (char *)malloc(len + 1);
|
|
if (!no_tags) return NULL;
|
|
|
|
size_t w = 0;
|
|
int bracket_depth = 0;
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
unsigned char ch = (unsigned char)input[i];
|
|
|
|
if (ch == '[') {
|
|
bracket_depth++;
|
|
continue;
|
|
}
|
|
if (ch == ']' && bracket_depth > 0) {
|
|
bracket_depth--;
|
|
continue;
|
|
}
|
|
if (bracket_depth > 0) {
|
|
continue;
|
|
}
|
|
|
|
no_tags[w++] = (char)ch;
|
|
}
|
|
no_tags[w] = '\0';
|
|
|
|
char *out = (char *)malloc(w + 1);
|
|
if (!out) {
|
|
free(no_tags);
|
|
return NULL;
|
|
}
|
|
|
|
size_t j = 0;
|
|
int in_space = 1;
|
|
for (size_t i = 0; i < w; ++i) {
|
|
unsigned char ch = (unsigned char)no_tags[i];
|
|
if (isspace(ch)) {
|
|
if (!in_space) {
|
|
out[j++] = ' ';
|
|
in_space = 1;
|
|
}
|
|
} else {
|
|
out[j++] = (char)ch;
|
|
in_space = 0;
|
|
}
|
|
}
|
|
|
|
if (j > 0 && out[j - 1] == ' ') {
|
|
j--;
|
|
}
|
|
out[j] = '\0';
|
|
|
|
free(no_tags);
|
|
return out;
|
|
}
|
|
|
|
void transcribe_set_filter_bracketed_tags(int enabled) {
|
|
g_filter_bracketed_tags = enabled ? 1 : 0;
|
|
}
|
|
|
|
#if WITH_WHISPER
|
|
#include <whisper.h>
|
|
|
|
static struct whisper_context *g_ctx = NULL;
|
|
static transcribe_params_t g_params;
|
|
|
|
static void apply_full_params_from_config(struct whisper_full_params *dst, const transcribe_params_t *src) {
|
|
if (!dst || !src) return;
|
|
|
|
dst->n_threads = src->n_threads > 0 ? src->n_threads : 4;
|
|
dst->n_max_text_ctx = src->n_max_text_ctx;
|
|
dst->offset_ms = src->offset_ms;
|
|
dst->duration_ms = src->duration_ms;
|
|
|
|
dst->translate = src->translate ? true : false;
|
|
dst->detect_language = src->detect_language ? true : false;
|
|
dst->no_context = src->no_context ? true : false;
|
|
dst->no_timestamps = src->no_timestamps ? true : false;
|
|
dst->single_segment = src->single_segment ? true : false;
|
|
|
|
dst->token_timestamps = src->token_timestamps ? true : false;
|
|
dst->thold_pt = src->thold_pt;
|
|
dst->thold_ptsum = src->thold_ptsum;
|
|
dst->max_len = src->max_len;
|
|
dst->split_on_word = src->split_on_word ? true : false;
|
|
dst->max_tokens = src->max_tokens;
|
|
|
|
dst->audio_ctx = src->audio_ctx;
|
|
dst->tdrz_enable = src->tdrz_enable ? true : false;
|
|
|
|
dst->suppress_blank = src->suppress_blank ? true : false;
|
|
dst->suppress_nst = src->suppress_nst ? true : false;
|
|
|
|
dst->temperature = src->temperature;
|
|
dst->max_initial_ts = src->max_initial_ts;
|
|
dst->length_penalty = src->length_penalty;
|
|
|
|
dst->temperature_inc = src->temperature_inc;
|
|
dst->entropy_thold = src->entropy_thold;
|
|
dst->logprob_thold = src->logprob_thold;
|
|
dst->no_speech_thold = src->no_speech_thold;
|
|
|
|
dst->greedy.best_of = src->greedy_best_of;
|
|
dst->beam_search.beam_size = src->beam_size;
|
|
dst->beam_search.patience = src->beam_patience;
|
|
|
|
dst->language = src->language[0] ? src->language : "en";
|
|
}
|
|
|
|
int transcribe_init(const transcribe_params_t *params) {
|
|
if (!params) return -1;
|
|
g_params = *params;
|
|
transcribe_set_filter_bracketed_tags(g_params.filter_bracketed_tags);
|
|
|
|
struct whisper_context_params cparams = whisper_context_default_params();
|
|
cparams.use_gpu = g_params.use_gpu ? true : false;
|
|
g_ctx = whisper_init_from_file_with_params(g_params.model_path, cparams);
|
|
if (!g_ctx) {
|
|
fprintf(stderr, "transcribe: failed to load model: %s\n", g_params.model_path);
|
|
return -2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void transcribe_update_params(const transcribe_params_t *params) {
|
|
if (!params) return;
|
|
g_params = *params;
|
|
transcribe_set_filter_bracketed_tags(g_params.filter_bracketed_tags);
|
|
}
|
|
|
|
char *transcribe_buffer(const float *samples, size_t count) {
|
|
if (!g_ctx || !samples || count == 0) return NULL;
|
|
|
|
enum whisper_sampling_strategy strategy = g_params.sampling_strategy ? WHISPER_SAMPLING_BEAM_SEARCH : WHISPER_SAMPLING_GREEDY;
|
|
struct whisper_full_params params = whisper_full_default_params(strategy);
|
|
params.print_progress = false;
|
|
params.print_special = false;
|
|
params.print_realtime = false;
|
|
params.print_timestamps = false;
|
|
apply_full_params_from_config(¶ms, &g_params);
|
|
|
|
if (whisper_full(g_ctx, params, samples, (int)count) != 0) {
|
|
fprintf(stderr, "transcribe: whisper_full failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
int nseg = whisper_full_n_segments(g_ctx);
|
|
size_t total = 1;
|
|
for (int i = 0; i < nseg; ++i) {
|
|
const char *seg = whisper_full_get_segment_text(g_ctx, i);
|
|
if (seg) total += strlen(seg) + 1;
|
|
}
|
|
|
|
char *out = (char *)malloc(total);
|
|
if (!out) return NULL;
|
|
out[0] = '\0';
|
|
|
|
for (int i = 0; i < nseg; ++i) {
|
|
const char *seg = whisper_full_get_segment_text(g_ctx, i);
|
|
if (!seg) continue;
|
|
strcat(out, seg);
|
|
if (i + 1 < nseg) strcat(out, " ");
|
|
}
|
|
|
|
if (!g_filter_bracketed_tags) {
|
|
return out;
|
|
}
|
|
|
|
char *filtered = filter_bracketed_tags_copy(out);
|
|
if (!filtered) {
|
|
return out;
|
|
}
|
|
|
|
free(out);
|
|
return filtered;
|
|
}
|
|
|
|
void transcribe_cleanup(void) {
|
|
if (g_ctx) {
|
|
whisper_free(g_ctx);
|
|
g_ctx = NULL;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
static transcribe_params_t g_params;
|
|
|
|
int transcribe_init(const transcribe_params_t *params) {
|
|
if (!params) return -1;
|
|
g_params = *params;
|
|
fprintf(stderr, "transcribe: built without whisper support. Rebuild with WITH_WHISPER=1 bash ./build.sh\n");
|
|
return 0;
|
|
}
|
|
|
|
void transcribe_update_params(const transcribe_params_t *params) {
|
|
if (!params) return;
|
|
g_params = *params;
|
|
}
|
|
|
|
char *transcribe_buffer(const float *samples, size_t count) {
|
|
(void)samples;
|
|
(void)count;
|
|
|
|
const char *msg = "[transcription unavailable: rebuild with WITH_WHISPER=1 and install whisper.cpp]";
|
|
char *out = (char *)malloc(strlen(msg) + 1);
|
|
if (!out) return NULL;
|
|
strcpy(out, msg);
|
|
return out;
|
|
}
|
|
|
|
void transcribe_cleanup(void) {
|
|
memset(&g_params, 0, sizeof(g_params));
|
|
}
|
|
|
|
#endif
|