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

yumBegin work on evaluation frameworkf7d5741

master
4.9 KiB196 linesraw
1#define WIN32_LEAN_AND_MEAN
2
3#include <Unknwn.h>
4#include <windows.h>
5
6#include "Whisper/API/whisperWindows.h"
7
8#include <iostream>
9#include <locale>
10#include <set>
11#include <string>
12#include <string_view>
13
14using std::cout;
15using std::cerr;
16using std::endl;
17using namespace Whisper;
18
19struct Config {
20	std::wstring audio_path = L"input.wav";
21	std::wstring model_path = L"model.bin";
22	eSamplingStrategy decode_method = eSamplingStrategy::BeamSearch;
23};
24
25bool hasArg(int argc, int shift, char* arg) {
26	if (shift + 1 >= argc) {
27		cerr << "Error: " << arg << " is missing argument" << endl;
28		return false;
29	}
30	return true;
31}
32
33std::wstring cstrToWstr(char* c_str) {
34	int length = MultiByteToWideChar(CP_UTF8, 0, c_str, -1, NULL, 0);
35	std::wstring result(length, 0);
36	MultiByteToWideChar(CP_UTF8, 0, c_str, -1, result.data(), result.size());
37	return result;
38}
39
40
41bool parseArgs(int argc, char* argv[], Config& c) {
42	int shift = 1;
43	while (shift < argc) {
44		if (std::string_view(argv[shift]) == "--audio_path") {
45			if (!hasArg(argc, shift, argv[shift])) {
46				return false;
47			}
48			c.audio_path = cstrToWstr(argv[shift + 1]);
49			shift += 2;
50			continue;
51		}
52		if (std::string_view(argv[shift]) == "--model_path") {
53			if (!hasArg(argc, shift, argv[shift])) {
54				return false;
55			}
56			c.model_path = cstrToWstr(argv[shift + 1]);
57			shift += 2;
58			continue;
59		}
60		if (std::string_view(argv[shift]) == "--decode_method") {
61			if (!hasArg(argc, shift, argv[shift])) {
62				return false;
63			}
64			std::string_view decode_method(argv[shift + 1]);
65			if (decode_method == "greedy") {
66				cerr << "Using greedy decode " << endl;
67				c.decode_method = eSamplingStrategy::Greedy;
68			}
69			else if (decode_method == "beam") {
70				cerr << "Using beam decode " << endl;
71				c.decode_method = eSamplingStrategy::BeamSearch;
72			}
73			else {
74				cerr << "Unsupported decode method: " << decode_method << endl;
75				return false;
76			}
77			shift += 2;
78			continue;
79		}
80		cerr << "Unrecognized argument: \"" << argv[shift] << '"' << endl;
81		return false;
82	}
83	return true;
84}
85
86int main(int argc, char* argv[])
87{
88	Config c;
89	if (!parseArgs(argc, argv, c)) {
90		cerr << "Failed to parse args";
91		return 1;
92	}
93
94	iMediaFoundation* f = nullptr;
95	HRESULT err = initMediaFoundation(&f);
96	if (FAILED(err)) {
97		cerr << "Failed to init media foundation: " << err << endl;
98		return 1;
99	}
100
101	Whisper::iAudioBuffer* buffer = nullptr;
102	err = f->loadAudioFile(c.audio_path.c_str(), /*stereo=*/false, &buffer);
103	if (FAILED(err)) {
104		cerr << "Failed to load audio file 'input.wav': " << err << endl;
105		return 1;
106	}
107
108	Whisper::iModel* model = nullptr;
109	err = Whisper::loadModel(c.model_path.c_str(), eModelImplementation::GPU, /*flags=*/0, /*callbacks=*/nullptr, &model);
110	if (FAILED(err)) {
111		cerr << "Failed to open model 'model.bin': " << err << endl;
112		return 1;
113	}
114
115	Whisper::iContext* context = nullptr;
116	err = model->createContext(&context);
117	if (FAILED(err)) {
118		cerr << "Failed to create context: " << err << endl;
119		return 1;
120	}
121
122	Whisper::sFullParams wparams{};
123	context->fullDefaultParams(c.decode_method, &wparams);
124	if (c.decode_method == eSamplingStrategy::BeamSearch) {
125		wparams.beam_search.beam_width = 5;
126		wparams.beam_search.n_best = 5;
127	}
128	wparams.language = Whisper::makeLanguageKey("en");
129	wparams.n_max_text_ctx = 100;
130
131	err = context->runFull(wparams, buffer);
132	if (FAILED(err)) {
133		cerr << "Failed to transcribe: " << err << endl;
134		return 1;
135	}
136
137	Whisper::iTranscribeResult* result = nullptr;
138	err = context->getResults(eResultFlags::Tokens, &result);
139	if (FAILED(err)) {
140		cerr << "Failed to get transcription results: " << err << endl;
141		return 1;
142	}
143
144	std::set<int> special_tokens;
145	{
146		Whisper::SpecialTokens st;
147		err = model->getSpecialTokens(st);
148		if (FAILED(err)) {
149			cerr << "Failed to get special tokens: " << err << endl;
150		}
151		special_tokens.insert(st.Not);
152		special_tokens.insert(st.PreviousWord);
153		special_tokens.insert(st.SentenceStart);
154		special_tokens.insert(st.TaskTranscribe);
155		special_tokens.insert(st.TaskTranslate);
156		special_tokens.insert(st.TranscriptionBegin);
157		special_tokens.insert(st.TranscriptionEnd);
158		special_tokens.insert(st.TranscriptionStart);
159	}
160
161	sTranscribeLength length;
162	err = result->getSize(length);
163	if (FAILED(err)) {
164		cerr << "Failed to get transcription length: " << err << endl;
165	}
166	auto* segments = result->getSegments();
167	auto* tokens = result->getTokens();
168	bool is_metadata = false;
169	for (int i = 0; i < length.countSegments; i++) {
170		auto& segment = segments[i];
171		for (int j = 0; j < segment.countTokens; j++) {
172			const sToken& tok = tokens[segment.firstToken + j];
173			if (special_tokens.contains(tok.id)) {
174				continue;
175			}
176			std::string_view tok_str(tok.text);
177			if (tok_str.starts_with("[") ||
178				tok_str.starts_with(" [")) {
179				if (tok_str.ends_with("]")) {
180					continue;
181				}
182				is_metadata = true;
183				continue;
184			}
185			if (is_metadata &&
186				tok_str.ends_with("]")) {
187				is_metadata = false;
188				continue;
189			}
190			cout << tok.text;
191		}
192	}
193	cout << endl;
194
195	return 0;
196}