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

KonstantinSource codes8c4603c

master
6.7 KiB289 linesraw
1// https://github.com/Const-me/vis_avs_dx/blob/master/avs_dx/DxVisuals/Interop/ConsoleLogger.cpp
2#include "stdafx.h"
3#include "DebugConsole.h"
4#include "miscUtils.h"
5#include "../AppState.h"
6#include "logger.h"
7
8namespace
9{
10	using Whisper::eLogLevel;
11
12	constexpr uint16_t defaultAttributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
13
14	inline uint16_t textAttributes( eLogLevel lvl )
15	{
16		switch( lvl )
17		{
18		case eLogLevel::Error:
19			return FOREGROUND_RED | FOREGROUND_INTENSITY;
20		case eLogLevel::Warning:
21			return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
22		case eLogLevel::Info:
23			return FOREGROUND_GREEN | FOREGROUND_INTENSITY;
24		case eLogLevel::Debug:
25			return FOREGROUND_BLUE | FOREGROUND_INTENSITY;
26		}
27		return defaultAttributes;
28	}
29
30	// Background stuff: accumulate messages in a small buffer, in case user will want to see them in the console.
31	// Ideally, we should accumulate them in a more efficient data structure, maybe a circular buffer.
32	// However, we don't have that many messages per second, this simple solution that uses std::deque is probably good enough for the job.
33	static constexpr uint16_t bufferSize = 64;
34
35	using Lock = CComCritSecLock<CComAutoCriticalSection>;
36#define LOCK() Lock __lock{ critSec }
37
38	thread_local CStringA threadError;
39}
40
41HRESULT DebugConsole::Entry::print( HANDLE hConsole, CString& tempString ) const
42{
43	if( !SetConsoleTextAttribute( hConsole, textAttributes( level ) ) )
44		return getLastHr();
45
46	makeUtf16( tempString, message );
47	tempString += L"\r\n";
48	if( !WriteConsoleW( hConsole, tempString, (DWORD)tempString.GetLength(), nullptr, nullptr ) )
49		return getLastHr();
50	return S_OK;
51}
52
53void clearLastError()
54{
55	threadError = "";
56}
57
58bool getLastError( CString& rdi )
59{
60	if( threadError.GetLength() <= 0 )
61	{
62		rdi = L"";
63		return false;
64	}
65	else
66	{
67		makeUtf16( rdi, threadError );
68		threadError = "";
69		return true;
70	}
71}
72
73inline void DebugConsole::logSink( eLogLevel lvl, const char* message )
74{
75	LOCK();
76
77	// Add to the buffer
78	while( buffer.size() >= bufferSize )
79		buffer.pop_front();
80	buffer.emplace_back( Entry{ lvl, message } );
81
82	// If the console window is shown, print there, too.
83	if( output )
84		buffer.rbegin()->print( output, tempString );
85}
86
87void __stdcall DebugConsole::logSinkStatic( void* context, eLogLevel lvl, const char* message )
88{
89	if( lvl == eLogLevel::Error )
90		threadError = message;
91
92	DebugConsole* con = (DebugConsole*)context;
93	con->logSink( lvl, message );
94}
95
96HRESULT DebugConsole::initialize( Whisper::eLogLevel level )
97{
98	if( nullptr != pGlobalInstance )
99		return HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED );
100	pGlobalInstance = this;
101
102	Whisper::sLoggerSetup setup;
103	setup.sink = &logSinkStatic;
104	setup.context = this;
105	setup.level = level;
106	setup.flags = Whisper::eLoggerFlags::SkipFormatMessage;
107	return Whisper::setupLogger( setup );
108}
109
110DebugConsole::~DebugConsole()
111{
112	hide();
113
114	Whisper::sLoggerSetup setup;
115	memset( &setup, 0, sizeof( setup ) );
116	Whisper::setupLogger( setup );
117
118	pGlobalInstance = nullptr;
119}
120
121DebugConsole* DebugConsole::pGlobalInstance = nullptr;
122
123void DebugConsole::windowClosed()
124{
125	LOCK();
126	if( FreeConsole() )
127	{
128		// Apparently, FreeConsole already closes that handle: https://stackoverflow.com/q/12676312/126995
129		output.Detach();
130	}
131	output.Close();
132
133	for( CButton* b : checkboxes )
134	{
135		if( !*b )
136			continue;
137		if( !b->IsWindow() )
138			continue;
139		PostMessage( *b, BM_SETCHECK, BST_UNCHECKED, 0 );
140	}
141}
142
143BOOL __stdcall DebugConsole::consoleHandlerRoutine( DWORD dwCtrlType )
144{
145	switch( dwCtrlType )
146	{
147	case CTRL_CLOSE_EVENT:
148	case CTRL_C_EVENT:
149	case CTRL_BREAK_EVENT:
150		pGlobalInstance->windowClosed();
151		return TRUE;
152	}
153	return TRUE;
154}
155
156HRESULT DebugConsole::show()
157{
158	HWND hWnd = GetConsoleWindow();
159	if( nullptr != hWnd )
160	{
161		ShowWindow( hWnd, SW_RESTORE );
162		SetForegroundWindow( hWnd );
163		return S_FALSE;
164	}
165
166	if( !AllocConsole() )
167		return getLastHr();
168
169	output.Close();
170	output.Attach( GetStdHandle( STD_OUTPUT_HANDLE ) );
171	if( !output )
172		return getLastHr();
173
174	constexpr UINT cp = CP_UTF8;
175	if( IsValidCodePage( cp ) )
176		SetConsoleOutputCP( cp );
177
178	// Enable ANSI color coding
179	DWORD mode = 0;
180	if( !GetConsoleMode( output, &mode ) )
181		return getLastHr();
182	if( 0 == ( mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING ) )
183	{
184		mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
185		if( !SetConsoleMode( output, mode ) )
186			return getLastHr();
187	}
188
189	SetConsoleTitle( L"Whisper Desktop Debug Console" );
190
191	SetConsoleCtrlHandler( &consoleHandlerRoutine, TRUE );
192
193	// Disable close command in the sys.menu of the new console, otherwise the whole process will quit: https://stackoverflow.com/a/12015131/126995
194	HWND hwnd = ::GetConsoleWindow();
195	if( hwnd != nullptr )
196	{
197		HMENU hMenu = ::GetSystemMenu( hwnd, FALSE );
198		if( hMenu != NULL )
199			DeleteMenu( hMenu, SC_CLOSE, MF_BYCOMMAND );
200	}
201
202	// Print old log entries
203	for( const auto& e : buffer )
204		CHECK( e.print( output, tempString ) );
205
206	const CStringA msg = "Press Control+C or Control+Break to close this window\r\n";
207	if( !SetConsoleTextAttribute( output, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY ) )
208		return getLastHr();
209	if( !WriteConsoleA( output, cstr( msg ), msg.GetLength(), nullptr, nullptr ) )
210		return getLastHr();
211
212	return S_OK;
213}
214
215HRESULT DebugConsole::hide()
216{
217	LOCK();
218	if( !output )
219		return S_FALSE;
220	windowClosed();
221	return S_OK;
222}
223
224void DebugConsole::addCheckbox( CButton& cb )
225{
226	checkboxes.emplace( &cb );
227}
228void DebugConsole::removeCheckbox( CButton& cb )
229{
230	checkboxes.erase( &cb );
231}
232
233HRESULT ConsoleCheckbox::initialize( HWND dialog, int idc, AppState& state )
234{
235	control = GetDlgItem( dialog, idc );
236	assert( control );
237
238	console = &state.console;
239	if( state.console.isVisible() )
240		control.SetCheck( BST_CHECKED );
241
242	state.console.addCheckbox( control );
243	return S_OK;
244}
245
246void ConsoleCheckbox::click()
247{
248	const int state = control.GetCheck();
249	if( state == BST_CHECKED )
250		console->show();
251	else
252		console->hide();
253}
254
255void ConsoleCheckbox::ensureChecked()
256{
257	const int state = control.GetCheck();
258	if( state == BST_CHECKED )
259		return;
260	control.SetCheck( BST_CHECKED );
261	console->show();
262}
263
264void DebugConsole::log( eLogLevel lvl, const char* pszFormat, va_list args )
265{
266	LOCK();
267	// Add to the buffer
268	while( buffer.size() >= bufferSize )
269		buffer.pop_front();
270
271	tempStringA.FormatV( pszFormat, args );
272	buffer.emplace_back( Entry{ lvl, tempStringA } );
273
274	// If the console window is shown, print there, too.
275	if( output )
276		buffer.rbegin()->print( output, tempString );
277}
278
279void DebugConsole::logMessage( eLogLevel lvl, const char* pszFormat, va_list args )
280{
281	if( nullptr == pGlobalInstance )
282		return;
283	pGlobalInstance->log( lvl, pszFormat, args );
284}
285
286void logMessage( Whisper::eLogLevel lvl, const char8_t* pczFormat, va_list args )
287{
288	DebugConsole::logMessage( lvl, (const char*)pczFormat, args );
289}