yum-archive/TaSTT-Whisper
High-performance GPGPU inference of OpenAI's Whisper automatic speech recognition (ASR) model
git clone https://git.yummers.dev/yum-archive/TaSTT-Whisper
8c4603c
master
1#include "whisper.h" 2 3// third-party utilities 4// use your favorite implementations 5#define DR_WAV_IMPLEMENTATION 6#include "dr_wav.h" 7 8#include <cmath> 9#include <fstream> 10#include <cstdio> 11#include <string> 12#include <thread> 13#include <vector> 14 15// Terminal color map. 10 colors grouped in ranges [0.0, 0.1, ..., 0.9] 16// Lowest is red, middle is yellow, highest is green. 17const std::vector < std::string > k_colors = { 18"\033[38;5;196m" ,"\033[38;5;202m" ,"\033[38;5;208m" ,"\033[38;5;214m" ,"\033[38;5;220m" , 19"\033[38;5;226m" ,"\033[38;5;190m" ,"\033[38;5;154m" ,"\033[38;5;118m" ,"\033[38;5;82m" , 20}; 21 22// 500 -> 00:05.000 23// 6000 -> 01:00.000 24std::string to_timestamp (int64_t t ,bool comma = false) { 25int64_t msec = t * 10 ; 26int64_t hr = msec / (1000 * 60 * 60 ); 27msec = msec - hr * (1000 * 60 * 60 ); 28int64_t min = msec / (1000 * 60 ); 29msec = msec - min * (1000 * 60 ); 30int64_t sec = msec /1000 ; 31msec = msec - sec * 1000 ; 32 33char buf [32 ]; 34snprintf (buf ,sizeof (buf ),"%02d:%02d:%02d%s%03d" , (int )hr , (int )min , (int )sec ,comma ?"," :"." , (int )msec ); 35 36return std::string (buf ); 37} 38 39int timestamp_to_sample (int64_t t ,int n_samples ) { 40return std::max (0 , std::min ((int )n_samples - 1 , (int ) ((t * WHISPER_SAMPLE_RATE )/100 ))); 41} 42 43// helper function to replace substrings 44void replace_all (std::string & s ,const std::string & search ,const std::string & replace ) { 45for (size_t pos = 0 ; ;pos += replace .length ()) { 46pos = s .find (search ,pos ); 47if (pos == std::string::npos )break ; 48s .erase (pos ,search .length ()); 49s .insert (pos ,replace ); 50 } 51} 52 53// command-line parameters 54struct whisper_params { 55int32_t n_threads = std::min (4 , (int32_t ) std::thread::hardware_concurrency ()); 56int32_t n_processors = 1 ; 57int32_t offset_t_ms = 0 ; 58int32_t offset_n = 0 ; 59int32_t duration_ms = 0 ; 60int32_t max_context = -1 ; 61int32_t max_len = 0 ; 62 63float word_thold = 0.01f ; 64 65bool speed_up = false; 66bool translate = false; 67bool diarize = false; 68bool output_txt = false; 69bool output_vtt = false; 70bool output_srt = false; 71bool output_wts = false; 72bool print_special = false; 73bool print_colors = false; 74bool print_progress = false; 75bool no_timestamps = false; 76 77 std::string language = "en" ; 78 std::string prompt ; 79 std::string model = "models/ggml-base.en.bin" ; 80 81 std::vector < std::string > fname_inp = {}; 82}; 83 84void whisper_print_usage (int argc ,char ** argv ,const whisper_params & params ); 85 86bool whisper_params_parse (int argc ,char ** argv ,whisper_params & params ) { 87for (int i = 1 ;i < argc ;i ++ ) { 88 std::string arg = argv [i ]; 89 90if (arg [0 ]!= '-' ) { 91params .fname_inp .push_back (arg ); 92continue ; 93 } 94 95if (arg == "-h" || arg == "--help" ) { 96whisper_print_usage (argc ,argv ,params ); 97exit (0 ); 98 } 99else if (arg == "-t" || arg == "--threads" ) {params .n_threads = std::stoi (argv [++ i ]); } 100else if (arg == "-p" || arg == "--processors" ) {params .n_processors = std::stoi (argv [++ i ]); } 101else if (arg == "-ot" || arg == "--offset-t" ) {params .offset_t_ms = std::stoi (argv [++ i ]); } 102else if (arg == "-on" || arg == "--offset-n" ) {params .offset_n = std::stoi (argv [++ i ]); } 103else if (arg == "-d" || arg == "--duration" ) {params .duration_ms = std::stoi (argv [++ i ]); } 104else if (arg == "-mc" || arg == "--max-context" ) {params .max_context = std::stoi (argv [++ i ]); } 105else if (arg == "-ml" || arg == "--max-len" ) {params .max_len = std::stoi (argv [++ i ]); } 106else if (arg == "-wt" || arg == "--word-thold" ) {params .word_thold = std::stof (argv [++ i ]); } 107else if (arg == "-su" || arg == "--speed-up" ) {params .speed_up = true; } 108else if (arg == "-tr" || arg == "--translate" ) {params .translate = true; } 109else if (arg == "-di" || arg == "--diarize" ) {params .diarize = true; } 110else if (arg == "-otxt" || arg == "--output-txt" ) {params .output_txt = true; } 111else if (arg == "-ovtt" || arg == "--output-vtt" ) {params .output_vtt = true; } 112else if (arg == "-osrt" || arg == "--output-srt" ) {params .output_srt = true; } 113else if (arg == "-owts" || arg == "--output-words" ) {params .output_wts = true; } 114else if (arg == "-ps" || arg == "--print-special" ) {params .print_special = true; } 115else if (arg == "-pc" || arg == "--print-colors" ) {params .print_colors = true; } 116else if (arg == "-pp" || arg == "--print-progress" ) {params .print_progress = true; } 117else if (arg == "-nt" || arg == "--no-timestamps" ) {params .no_timestamps = true; } 118else if (arg == "-l" || arg == "--language" ) {params .language = argv [++ i ]; } 119else if (arg == "--prompt" ) {params .prompt = argv [++ i ]; } 120else if (arg == "-m" || arg == "--model" ) {params .model = argv [++ i ]; } 121else if (arg == "-f" || arg == "--file" ) {params .fname_inp .emplace_back (argv [++ i ]); } 122else { 123fprintf (stderr ,"error: unknown argument: %s\n" ,arg .c_str ()); 124whisper_print_usage (argc ,argv ,params ); 125exit (0 ); 126 } 127 } 128 129return true; 130} 131 132void whisper_print_usage (int /*argc*/ ,char ** argv ,const whisper_params & params ) { 133fprintf (stderr ,"\n" ); 134fprintf (stderr ,"usage: %s [options] file0.wav file1.wav ...\n" ,argv [0 ]); 135fprintf (stderr ,"\n" ); 136fprintf (stderr ,"options:\n" ); 137fprintf (stderr ," -h, --help [default] show this help message and exit\n" ); 138fprintf (stderr ," -t N, --threads N [%-7d] number of threads to use during computation\n" ,params .n_threads ); 139fprintf (stderr ," -p N, --processors N [%-7d] number of processors to use during computation\n" ,params .n_processors ); 140fprintf (stderr ," -ot N, --offset-t N [%-7d] time offset in milliseconds\n" ,params .offset_t_ms ); 141fprintf (stderr ," -on N, --offset-n N [%-7d] segment index offset\n" ,params .offset_n ); 142fprintf (stderr ," -d N, --duration N [%-7d] duration of audio to process in milliseconds\n" ,params .duration_ms ); 143fprintf (stderr ," -mc N, --max-context N [%-7d] maximum number of text context tokens to store\n" ,params .max_context ); 144fprintf (stderr ," -ml N, --max-len N [%-7d] maximum segment length in characters\n" ,params .max_len ); 145fprintf (stderr ," -wt N, --word-thold N [%-7.2f] word timestamp probability threshold\n" ,params .word_thold ); 146fprintf (stderr ," -su, --speed-up [%-7s] speed up audio by x2 (reduced accuracy)\n" ,params .speed_up ?"true" :"false" ); 147fprintf (stderr ," -tr, --translate [%-7s] translate from source language to english\n" ,params .translate ?"true" :"false" ); 148fprintf (stderr ," -di, --diarize [%-7s] stereo audio diarization\n" ,params .diarize ?"true" :"false" ); 149fprintf (stderr ," -otxt, --output-txt [%-7s] output result in a text file\n" ,params .output_txt ?"true" :"false" ); 150fprintf (stderr ," -ovtt, --output-vtt [%-7s] output result in a vtt file\n" ,params .output_vtt ?"true" :"false" ); 151fprintf (stderr ," -osrt, --output-srt [%-7s] output result in a srt file\n" ,params .output_srt ?"true" :"false" ); 152fprintf (stderr ," -owts, --output-words [%-7s] output script for generating karaoke video\n" ,params .output_wts ?"true" :"false" ); 153fprintf (stderr ," -ps, --print-special [%-7s] print special tokens\n" ,params .print_special ?"true" :"false" ); 154fprintf (stderr ," -pc, --print-colors [%-7s] print colors\n" ,params .print_colors ?"true" :"false" ); 155fprintf (stderr ," -pp, --print-progress [%-7s] print progress\n" ,params .print_progress ?"true" :"false" ); 156fprintf (stderr ," -nt, --no-timestamps [%-7s] do not print timestamps\n" ,params .no_timestamps ?"false" :"true" ); 157fprintf (stderr ," -l LANG, --language LANG [%-7s] spoken language ('auto' for auto-detect)\n" ,params .language .c_str ()); 158fprintf (stderr ," --prompt PROMPT [%-7s] initial prompt\n" ,params .prompt .c_str ()); 159fprintf (stderr ," -m FNAME, --model FNAME [%-7s] model path\n" ,params .model .c_str ()); 160fprintf (stderr ," -f FNAME, --file FNAME [%-7s] input WAV file path\n" ,"" ); 161fprintf (stderr ,"\n" ); 162} 163 164struct whisper_print_user_data { 165const whisper_params * params ; 166 167const std::vector < std::vector < float >> * pcmf32s ; 168}; 169 170void whisper_print_segment_callback (struct whisper_context * ctx ,int n_new ,void * user_data ) { 171const auto & params = * ((whisper_print_user_data * )user_data )-> params ; 172const auto & pcmf32s = * ((whisper_print_user_data * )user_data )-> pcmf32s ; 173 174const int n_segments = whisper_full_n_segments (ctx ); 175 176// print the last n_new segments 177const int s0 = n_segments - n_new ; 178if (s0 == 0 ) { 179printf ("\n" ); 180 } 181 182for (int i = s0 ;i < n_segments ;i ++ ) { 183if (params .no_timestamps ) { 184if (params .print_colors ) { 185for (int j = 0 ;j < whisper_full_n_tokens (ctx ,i );++ j ) { 186if (params .print_special == false) { 187const whisper_token id = whisper_full_get_token_id (ctx ,i ,j ); 188if (id >=whisper_token_eot (ctx )) { 189continue ; 190 } 191 } 192 193const char * text = whisper_full_get_token_text (ctx ,i ,j ); 194const float p = whisper_full_get_token_p (ctx ,i ,j ); 195 196const int col = std::max (0 , std::min ((int )k_colors .size (), (int ) (std::pow (p ,3 )* float (k_colors .size ())))); 197 198printf ("%s%s%s" ,k_colors [col ].c_str (),text ,"\033[0m" ); 199 } 200 }else { 201const char * text = whisper_full_get_segment_text (ctx ,i ); 202printf ("%s" ,text ); 203 } 204fflush (stdout ); 205 }else { 206const int64_t t0 = whisper_full_get_segment_t0 (ctx ,i ); 207const int64_t t1 = whisper_full_get_segment_t1 (ctx ,i ); 208 209 std::string speaker ; 210 211if (params .diarize && pcmf32s .size ()== 2 ) { 212const int64_t n_samples = pcmf32s [0 ].size (); 213 214const int64_t is0 = timestamp_to_sample (t0 ,n_samples ); 215const int64_t is1 = timestamp_to_sample (t1 ,n_samples ); 216 217double energy0 = 0.0f ; 218double energy1 = 0.0f ; 219 220for (int64_t j = is0 ;j < is1 ;j ++ ) { 221energy0 += fabs (pcmf32s [0 ][j ]); 222energy1 += fabs (pcmf32s [1 ][j ]); 223 } 224 225if (energy0 > 1.1 * energy1 ) { 226speaker = "(speaker 0)" ; 227 }else if (energy1 > 1.1 * energy0 ) { 228speaker = "(speaker 1)" ; 229 }else { 230speaker = "(speaker ?)" ; 231 } 232 233//printf("is0 = %lld, is1 = %lld, energy0 = %f, energy1 = %f, %s\n", is0, is1, energy0, energy1, speaker.c_str()); 234 } 235 236if (params .print_colors ) { 237printf ("[%s --> %s] " ,to_timestamp (t0 ).c_str (),to_timestamp (t1 ).c_str ()); 238for (int j = 0 ;j < whisper_full_n_tokens (ctx ,i );++ j ) { 239if (params .print_special == false) { 240const whisper_token id = whisper_full_get_token_id (ctx ,i ,j ); 241if (id >=whisper_token_eot (ctx )) { 242continue ; 243 } 244 } 245 246const char * text = whisper_full_get_token_text (ctx ,i ,j ); 247const float p = whisper_full_get_token_p (ctx ,i ,j ); 248 249const int col = std::max (0 , std::min ((int )k_colors .size (), (int ) (std::pow (p ,3 )* float (k_colors .size ())))); 250 251printf ("%s%s%s%s" ,speaker .c_str (),k_colors [col ].c_str (),text ,"\033[0m" ); 252 } 253printf ("\n" ); 254 }else { 255const char * text = whisper_full_get_segment_text (ctx ,i ); 256 257printf ("[%s --> %s] %s%s\n" ,to_timestamp (t0 ).c_str (),to_timestamp (t1 ).c_str (),speaker .c_str (),text ); 258 } 259 } 260 } 261} 262 263bool output_txt (struct whisper_context * ctx ,const char * fname ) { 264 std::ofstream fout (fname ); 265if (!fout .is_open ()) { 266fprintf (stderr ,"%s: failed to open '%s' for writing\n" ,__func__ ,fname ); 267return false; 268 } 269 270fprintf (stderr ,"%s: saving output to '%s'\n" ,__func__ ,fname ); 271 272const int n_segments = whisper_full_n_segments (ctx ); 273for (int i = 0 ;i < n_segments ;++ i ) { 274const char * text = whisper_full_get_segment_text (ctx ,i ); 275fout <<text <<"\n" ; 276 } 277 278return true; 279} 280 281bool output_vtt (struct whisper_context * ctx ,const char * fname ) { 282 std::ofstream fout (fname ); 283if (!fout .is_open ()) { 284fprintf (stderr ,"%s: failed to open '%s' for writing\n" ,__func__ ,fname ); 285return false; 286 } 287 288fprintf (stderr ,"%s: saving output to '%s'\n" ,__func__ ,fname ); 289 290fout <<"WEBVTT\n\n" ; 291 292const int n_segments = whisper_full_n_segments (ctx ); 293for (int i = 0 ;i < n_segments ;++ i ) { 294const char * text = whisper_full_get_segment_text (ctx ,i ); 295const int64_t t0 = whisper_full_get_segment_t0 (ctx ,i ); 296const int64_t t1 = whisper_full_get_segment_t1 (ctx ,i ); 297 298fout <<to_timestamp (t0 ) <<" --> " <<to_timestamp (t1 ) <<"\n" ; 299fout <<text <<"\n\n" ; 300 } 301 302return true; 303} 304 305bool output_srt (struct whisper_context * ctx ,const char * fname ,const whisper_params & params ) { 306 std::ofstream fout (fname ); 307if (!fout .is_open ()) { 308fprintf (stderr ,"%s: failed to open '%s' for writing\n" ,__func__ ,fname ); 309return false; 310 } 311 312fprintf (stderr ,"%s: saving output to '%s'\n" ,__func__ ,fname ); 313 314const int n_segments = whisper_full_n_segments (ctx ); 315for (int i = 0 ;i < n_segments ;++ i ) { 316const char * text = whisper_full_get_segment_text (ctx ,i ); 317const int64_t t0 = whisper_full_get_segment_t0 (ctx ,i ); 318const int64_t t1 = whisper_full_get_segment_t1 (ctx ,i ); 319 320fout <<i + 1 + params .offset_n <<"\n" ; 321fout <<to_timestamp (t0 , true) <<" --> " <<to_timestamp (t1 , true) <<"\n" ; 322fout <<text <<"\n\n" ; 323 } 324 325return true; 326} 327 328// karaoke video generation 329// outputs a bash script that uses ffmpeg to generate a video with the subtitles 330// TODO: font parameter adjustments 331bool output_wts (struct whisper_context * ctx ,const char * fname ,const char * fname_inp ,const whisper_params & /*params*/ ,float t_sec ) { 332 std::ofstream fout (fname ); 333 334fprintf (stderr ,"%s: saving output to '%s'\n" ,__func__ ,fname ); 335 336// TODO: become parameter 337static const char * font = "/System/Library/Fonts/Supplemental/Courier New Bold.ttf" ; 338 339fout <<"#!/bin/bash" <<"\n" ; 340fout <<"\n" ; 341 342fout <<"ffmpeg -i " <<fname_inp <<" -f lavfi -i color=size=1200x120:duration=" <<t_sec <<":rate=25:color=black -vf \"" ; 343 344for (int i = 0 ;i < whisper_full_n_segments (ctx );i ++ ) { 345const int64_t t0 = whisper_full_get_segment_t0 (ctx ,i ); 346const int64_t t1 = whisper_full_get_segment_t1 (ctx ,i ); 347 348const int n = whisper_full_n_tokens (ctx ,i ); 349 350 std::vector < whisper_token_data > tokens (n ); 351for (int j = 0 ;j < n ;++ j ) { 352tokens [j ]= whisper_full_get_token_data (ctx ,i ,j ); 353 } 354 355if (i > 0 ) { 356fout <<"," ; 357 } 358 359// background text 360fout <<"drawtext=fontfile='" <<font <<"':fontsize=24:fontcolor=gray:x=(w-text_w)/2:y=h/2:text='':enable='between(t," <<t0 /100.0 <<"," <<t0 /100.0 <<")'" ; 361 362bool is_first = true; 363 364for (int j = 0 ;j < n ;++ j ) { 365const auto & token = tokens [j ]; 366 367if (tokens [j ].id >=whisper_token_eot (ctx )) { 368continue ; 369 } 370 371 std::string txt_bg ; 372 std::string txt_fg ;// highlight token 373 std::string txt_ul ;// underline 374 375txt_bg = "> " ; 376txt_fg = "> " ; 377txt_ul = "\\ \\ " ; 378 379 { 380for (int k = 0 ;k < n ;++ k ) { 381const auto & token2 = tokens [k ]; 382 383if (tokens [k ].id >=whisper_token_eot (ctx )) { 384continue ; 385 } 386 387const std::string txt = whisper_token_to_str (ctx ,token2 .id ); 388 389txt_bg += txt ; 390 391if (k == j ) { 392for (int l = 0 ;l < (int )txt .size ();++ l ) { 393txt_fg += txt [l ]; 394txt_ul += "_" ; 395 } 396txt_fg += "|" ; 397 }else { 398for (int l = 0 ;l < (int )txt .size ();++ l ) { 399txt_fg += "\\ " ; 400txt_ul += "\\ " ; 401 } 402 } 403 } 404 405 ::replace_all (txt_bg ,"'" ,"\u2019" ); 406 ::replace_all (txt_bg ,"\"" ,"\\\"" ); 407 ::replace_all (txt_fg ,"'" ,"\u2019" ); 408 ::replace_all (txt_fg ,"\"" ,"\\\"" ); 409 } 410 411if (is_first ) { 412// background text 413fout <<",drawtext=fontfile='" <<font <<"':fontsize=24:fontcolor=gray:x=(w-text_w)/2:y=h/2:text='" <<txt_bg <<"':enable='between(t," <<t0 /100.0 <<"," <<t1 /100.0 <<")'" ; 414is_first = false; 415 } 416 417// foreground text 418fout <<",drawtext=fontfile='" <<font <<"':fontsize=24:fontcolor=lightgreen:x=(w-text_w)/2+8:y=h/2:text='" <<txt_fg <<"':enable='between(t," <<token .t0 /100.0 <<"," <<token .t1 /100.0 <<")'" ; 419 420// underline 421fout <<",drawtext=fontfile='" <<font <<"':fontsize=24:fontcolor=lightgreen:x=(w-text_w)/2+8:y=h/2+16:text='" <<txt_ul <<"':enable='between(t," <<token .t0 /100.0 <<"," <<token .t1 /100.0 <<")'" ; 422 } 423 } 424 425fout <<"\" -c:v libx264 -pix_fmt yuv420p -y " <<fname_inp <<".mp4" <<"\n" ; 426 427fout <<"\n\n" ; 428fout <<"echo \"Your video has been saved to " <<fname_inp <<".mp4\"" <<"\n" ; 429fout <<"\n" ; 430fout <<"echo \" ffplay " <<fname_inp <<".mp4\"\n" ; 431fout <<"\n" ; 432 433fout .close (); 434 435fprintf (stderr ,"%s: run 'source %s' to generate karaoke video\n" ,__func__ ,fname ); 436 437return true; 438} 439 440int main (int argc ,char ** argv ) { 441whisper_params params ; 442 443if (whisper_params_parse (argc ,argv ,params )== false) { 444return 1 ; 445 } 446 447if (params .fname_inp .empty ()) { 448fprintf (stderr ,"error: no input files specified\n" ); 449whisper_print_usage (argc ,argv ,params ); 450return 2 ; 451 } 452 453if (params .language != "auto" && whisper_lang_id (params .language .c_str ())== -1 ) { 454fprintf (stderr ,"error: unknown language '%s'\n" ,params .language .c_str ()); 455whisper_print_usage (argc ,argv ,params ); 456exit (0 ); 457 } 458 459// whisper init 460 461struct whisper_context * ctx = whisper_init (params .model .c_str ()); 462 463if (ctx == nullptr ) { 464fprintf (stderr ,"error: failed to initialize whisper context\n" ); 465return 3 ; 466 } 467 468// initial prompt 469 std::vector < whisper_token > prompt_tokens ; 470 471if (!params .prompt .empty ()) { 472prompt_tokens .resize (1024 ); 473prompt_tokens .resize (whisper_tokenize (ctx ,params .prompt .c_str (),prompt_tokens .data (),prompt_tokens .size ())); 474 475fprintf (stderr ,"\n" ); 476fprintf (stderr ,"initial prompt: '%s'\n" ,params .prompt .c_str ()); 477fprintf (stderr ,"initial tokens: [ " ); 478for (int i = 0 ;i < (int )prompt_tokens .size ();++ i ) { 479fprintf (stderr ,"%d " ,prompt_tokens [i ]); 480 } 481fprintf (stderr ,"]\n" ); 482 } 483 484for (int f = 0 ;f < (int )params .fname_inp .size ();++ f ) { 485const auto fname_inp = params .fname_inp [f ]; 486 487 std::vector < float > pcmf32 ;// mono-channel F32 PCM 488 std::vector < std::vector < float >> pcmf32s ;// stereo-channel F32 PCM 489 490// WAV input 491 { 492drwav wav ; 493 std::vector < uint8_t > wav_data ;// used for pipe input from stdin 494 495if (fname_inp == "-" ) { 496 { 497uint8_t buf [1024 ]; 498while (true) 499 { 500const size_t n = fread (buf ,1 ,sizeof (buf ),stdin ); 501if (n == 0 ) { 502break ; 503 } 504wav_data .insert (wav_data .end (),buf ,buf + n ); 505 } 506 } 507 508if (drwav_init_memory (& wav ,wav_data .data (),wav_data .size (),nullptr )== false) { 509fprintf (stderr ,"error: failed to open WAV file from stdin\n" ); 510return 4 ; 511 } 512 513fprintf (stderr ,"%s: read %zu bytes from stdin\n" ,__func__ ,wav_data .size ()); 514 } 515else if (drwav_init_file (& wav ,fname_inp .c_str (),nullptr )== false) { 516fprintf (stderr ,"error: failed to open '%s' as WAV file\n" ,fname_inp .c_str ()); 517return 5 ; 518 } 519 520if (wav .channels != 1 && wav .channels != 2 ) { 521fprintf (stderr ,"%s: WAV file '%s' must be mono or stereo\n" ,argv [0 ],fname_inp .c_str ()); 522return 6 ; 523 } 524 525if (params .diarize && wav .channels != 2 && params .no_timestamps == false) { 526fprintf (stderr ,"%s: WAV file '%s' must be stereo for diarization and timestamps have to be enabled\n" ,argv [0 ],fname_inp .c_str ()); 527return 6 ; 528 } 529 530if (wav .sampleRate != WHISPER_SAMPLE_RATE ) { 531fprintf (stderr ,"%s: WAV file '%s' must be 16 kHz\n" ,argv [0 ],fname_inp .c_str ()); 532return 8 ; 533 } 534 535if (wav .bitsPerSample != 16 ) { 536fprintf (stderr ,"%s: WAV file '%s' must be 16-bit\n" ,argv [0 ],fname_inp .c_str ()); 537return 9 ; 538 } 539 540const uint64_t n = wav_data .empty () ?wav .totalPCMFrameCount :wav_data .size ()/(wav .channels * wav .bitsPerSample /8 ); 541 542 std::vector < int16_t > pcm16 ; 543pcm16 .resize (n * wav .channels ); 544drwav_read_pcm_frames_s16 (& wav ,n ,pcm16 .data ()); 545drwav_uninit (& wav ); 546 547// convert to mono, float 548pcmf32 .resize (n ); 549if (wav .channels == 1 ) { 550for (uint64_t i = 0 ;i < n ;i ++ ) { 551pcmf32 [i ]= float (pcm16 [i ])/32768.0f ; 552 } 553 }else { 554for (uint64_t i = 0 ;i < n ;i ++ ) { 555pcmf32 [i ]= float (pcm16 [2 * i ]+ pcm16 [2 * i + 1 ])/65536.0f ; 556 } 557 } 558 559if (params .diarize ) { 560// convert to stereo, float 561pcmf32s .resize (2 ); 562 563pcmf32s [0 ].resize (n ); 564pcmf32s [1 ].resize (n ); 565for (uint64_t i = 0 ;i < n ;i ++ ) { 566pcmf32s [0 ][i ]= float (pcm16 [2 * i ])/32768.0f ; 567pcmf32s [1 ][i ]= float (pcm16 [2 * i + 1 ])/32768.0f ; 568 } 569 } 570 } 571 572// print system information 573 { 574fprintf (stderr ,"\n" ); 575fprintf (stderr ,"system_info: n_threads = %d / %d | %s\n" , 576params .n_threads * params .n_processors , std::thread::hardware_concurrency (),whisper_print_system_info ()); 577 } 578 579// print some info about the processing 580 { 581fprintf (stderr ,"\n" ); 582if (!whisper_is_multilingual (ctx )) { 583if (params .language != "en" || params .translate ) { 584params .language = "en" ; 585params .translate = false; 586fprintf (stderr ,"%s: WARNING: model is not multilingual, ignoring language and translation options\n" ,__func__ ); 587 } 588 } 589fprintf (stderr ,"%s: processing '%s' (%d samples, %.1f sec), %d threads, %d processors, lang = %s, task = %s, timestamps = %d ...\n" , 590__func__ ,fname_inp .c_str (),int (pcmf32 .size ()),float (pcmf32 .size ())/WHISPER_SAMPLE_RATE , 591params .n_threads ,params .n_processors , 592params .language .c_str (), 593params .translate ?"translate" :"transcribe" , 594params .no_timestamps ?0 :1 ); 595 596fprintf (stderr ,"\n" ); 597 } 598 599// run the inference 600 { 601whisper_full_params wparams = whisper_full_default_params (WHISPER_SAMPLING_GREEDY ); 602 603wparams .print_realtime = false; 604wparams .print_progress = params .print_progress ; 605wparams .print_timestamps = !params .no_timestamps ; 606wparams .print_special = params .print_special ; 607wparams .translate = params .translate ; 608wparams .language = params .language .c_str (); 609wparams .n_threads = params .n_threads ; 610wparams .n_max_text_ctx = params .max_context >=0 ?params .max_context :wparams .n_max_text_ctx ; 611wparams .offset_ms = params .offset_t_ms ; 612wparams .duration_ms = params .duration_ms ; 613 614wparams .token_timestamps = params .output_wts || params .max_len > 0 ; 615wparams .thold_pt = params .word_thold ; 616wparams .max_len = params .output_wts && params .max_len == 0 ?60 :params .max_len ; 617 618wparams .speed_up = params .speed_up ; 619 620wparams .prompt_tokens = prompt_tokens .empty () ?nullptr :prompt_tokens .data (); 621wparams .prompt_n_tokens = prompt_tokens .empty () ?0 :prompt_tokens .size (); 622 623whisper_print_user_data user_data = {& params ,& pcmf32s }; 624 625// this callback is called on each new segment 626if (!wparams .print_realtime ) { 627wparams .new_segment_callback = whisper_print_segment_callback ; 628wparams .new_segment_callback_user_data = & user_data ; 629 } 630 631// example for abort mechanism 632// in this example, we do not abort the processing, but we could if the flag is set to true 633// the callback is called before every encoder run - if it returns false, the processing is aborted 634 { 635static bool is_aborted = false;// NOTE: this should be atomic to avoid data race 636 637wparams .encoder_begin_callback = [](struct whisper_context * /*ctx*/ ,void * user_data ) { 638bool is_aborted = * (bool * )user_data ; 639return !is_aborted ; 640 }; 641wparams .encoder_begin_callback_user_data = & is_aborted ; 642 } 643 644if (whisper_full_parallel (ctx ,wparams ,pcmf32 .data (),pcmf32 .size (),params .n_processors )!= 0 ) { 645fprintf (stderr ,"%s: failed to process audio\n" ,argv [0 ]); 646return 10 ; 647 } 648 } 649 650// output stuff 651 { 652printf ("\n" ); 653 654// output to text file 655if (params .output_txt ) { 656const auto fname_txt = fname_inp + ".txt" ; 657output_txt (ctx ,fname_txt .c_str ()); 658 } 659 660// output to VTT file 661if (params .output_vtt ) { 662const auto fname_vtt = fname_inp + ".vtt" ; 663output_vtt (ctx ,fname_vtt .c_str ()); 664 } 665 666// output to SRT file 667if (params .output_srt ) { 668const auto fname_srt = fname_inp + ".srt" ; 669output_srt (ctx ,fname_srt .c_str (),params ); 670 } 671 672// output to WTS file 673if (params .output_wts ) { 674const auto fname_wts = fname_inp + ".wts" ; 675output_wts (ctx ,fname_wts .c_str (),fname_inp .c_str (),params ,float (pcmf32 .size ()+ 1000 )/WHISPER_SAMPLE_RATE ); 676 } 677 } 678 } 679 680whisper_print_timings (ctx ); 681whisper_free (ctx ); 682 683return 0 ; 684}