Second commit is to be removed.
Signed-off-by: Bernhard Kölbl besentv@gmail.com
-- v9: windows.media.speech: Implement Vosk create and release functions in the unixlib.
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- configure.ac | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac index 9ff7c5e8914..e6b5947a0eb 100644 --- a/configure.ac +++ b/configure.ac @@ -59,6 +59,7 @@ AC_ARG_WITH(udev, AS_HELP_STRING([--without-udev],[do not use udev (plug an AC_ARG_WITH(unwind, AS_HELP_STRING([--without-unwind],[do not use the libunwind library (exception handling)])) AC_ARG_WITH(usb, AS_HELP_STRING([--without-usb],[do not use the libusb library])) AC_ARG_WITH(v4l2, AS_HELP_STRING([--without-v4l2],[do not use v4l2 (video capture)])) +AC_ARG_WITH(vosk, AS_HELP_STRING([--without-vosk],[do not use Vosk])) AC_ARG_WITH(vulkan, AS_HELP_STRING([--without-vulkan],[do not use Vulkan])) AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]), [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xcomposite_h=no; fi]) @@ -483,7 +484,8 @@ AC_CHECK_HEADERS(\ syscall.h \ utime.h \ valgrind/memcheck.h \ - valgrind/valgrind.h + valgrind/valgrind.h \ + vosk_api.h ) WINE_HEADER_MAJOR() AC_HEADER_STAT() @@ -1787,6 +1789,15 @@ then WINE_WARNING([No sound system was found. Windows applications will be silent.]) fi
+dnl **** Check for Vosk **** +if test x$with_vosk != xno +then + WINE_CHECK_SONAME(vosk,vosk_recognizer_new,[AC_SUBST(VOSK_LIBS,"-lvosk") ac_cv_lib_vosk=yes],,) +fi +WINE_NOTICE_WITH(vosk,[test x$with_vosk != xno -a x$ac_cv_lib_vosk != xyes], + [libvosk ${notice_platform}development files not found (or too old), Vosk aka speech recognition won't be supported.], + [enable_vosk]) + dnl *** Check for Vulkan *** if test "x$with_vulkan" != "xno" then
From: Bernhard Kölbl besentv@gmail.com
windows.media.speech: Add unixlib stub.
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/Makefile.in | 2 ++ dlls/windows.media.speech/main.c | 19 +++++++++++++++ dlls/windows.media.speech/private.h | 4 ++++ dlls/windows.media.speech/unixlib.h | 33 +++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 dlls/windows.media.speech/unixlib.h
diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 10903cb1d7b..c06a142780b 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,5 +1,7 @@ MODULE = windows.media.speech.dll +UNIXLIB = windows.media.speech.so IMPORTS = combase uuid +UNIX_LIBS = $(VOSK_LIBS)
C_SRCS = \ async.c \ diff --git a/dlls/windows.media.speech/main.c b/dlls/windows.media.speech/main.c index e772a791588..a3eb980438a 100644 --- a/dlls/windows.media.speech/main.c +++ b/dlls/windows.media.speech/main.c @@ -20,9 +20,28 @@ #include "initguid.h" #include "private.h"
+#include "unixlib.h" + #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(speech); +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) +{ + if (reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(instance); + if (__wine_init_unix_call()) + { + ERR_(winediag)("Wine is unable to load the Unix side dependencies for speech recognition. " + "Make sure Vosk is installed on your system and try again.\n"); + return FALSE; + } + } + + return TRUE; +}
HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) { diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index e80d73ec1fb..2f804fbf1a7 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -22,6 +22,10 @@
#include <stdarg.h>
+#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" #define COBJMACROS #include "corerror.h" #include "windef.h" diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h new file mode 100644 index 00000000000..5516b51d235 --- /dev/null +++ b/dlls/windows.media.speech/unixlib.h @@ -0,0 +1,33 @@ +/* + * Unix library interface for Windows.Media.Speech + * + * Copyright 2023 Bernhard Kölbl for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINDOWS_MEDIA_SPEECH_UNIXLIB_H +#define __WINE_WINDOWS_MEDIA_SPEECH_UNIXLIB_H + +#include <stdbool.h> +#include <stdint.h> + +#include "windef.h" +#include "winternl.h" +#include "wtypes.h" + +#include "wine/unixlib.h" + +#endif
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/tests/speech.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 6bc5a8b1751..c7c7b6eb040 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -42,7 +42,6 @@ #define AsyncStatus_Closed 4
#define SPERR_WINRT_INTERNAL_ERROR 0x800455a0 -#define SPERR_WINRT_INCORRECT_FORMAT 0x80131537
#define IHandler_RecognitionResult ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs #define IHandler_RecognitionResultVtbl ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgsVtbl @@ -1005,7 +1004,7 @@ static void test_SpeechSynthesizer(void) operation_ss_stream = (void *)0xdeadbeef; hr = ISpeechSynthesizer_SynthesizeSsmlToStreamAsync(synthesizer, str, &operation_ss_stream); /* Broken on Win 8 + 8.1 */ - ok(hr == S_OK || broken(hr == SPERR_WINRT_INCORRECT_FORMAT), "ISpeechSynthesizer_SynthesizeSsmlToStreamAsync failed, hr %#lx\n", hr); + ok(hr == S_OK || broken(hr == COR_E_FORMAT), "ISpeechSynthesizer_SynthesizeSsmlToStreamAsync failed, hr %#lx\n", hr);
if (hr == S_OK) {
From: Bernhard Kölbl besentv@gmail.com
To allow for error handling of missing Unix-side dependencies.
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/tests/speech.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index c7c7b6eb040..57e216b78d5 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1192,7 +1192,7 @@ static void test_SpeechRecognizer(void) ok(ref == 1, "Got unexpected ref %lu.\n", ref);
hr = RoActivateInstance(hstr, &inspectable); - ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR), "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK || hr == SPERR_WINRT_INTERNAL_ERROR, "Got unexpected hr %#lx.\n", hr);
if (hr == S_OK) { @@ -1411,7 +1411,7 @@ skip_operation: } else if (hr == SPERR_WINRT_INTERNAL_ERROR) /* Not sure when this triggers. Probably if a language pack is not installed. */ { - win_skip("Could not init SpeechRecognizer with default language!\n"); + skip("Could not init SpeechRecognizer with default language!\n"); }
done: @@ -1651,12 +1651,12 @@ static void test_Recognition(void) ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr);
hr = RoActivateInstance(hstr, &inspectable); - ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR || hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK || hr == SPERR_WINRT_INTERNAL_ERROR || broken(hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); WindowsDeleteString(hstr);
- if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. */ + if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. Wine with missing Unix side dependencies. */ { - win_skip("SpeechRecognizer cannot be activated!\n"); + skip("SpeechRecognizer cannot be activated!\n"); goto done; }
From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/Makefile.in | 5 +- dlls/windows.media.speech/private.h | 3 + dlls/windows.media.speech/recognizer.c | 42 +++++ dlls/windows.media.speech/unixlib.h | 20 +++ dlls/windows.media.speech/vosk.c | 222 +++++++++++++++++++++++++ 5 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 dlls/windows.media.speech/vosk.c
diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index c06a142780b..7a7f9711799 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,6 +1,6 @@ MODULE = windows.media.speech.dll UNIXLIB = windows.media.speech.so -IMPORTS = combase uuid +IMPORTS = combase uuid user32 UNIX_LIBS = $(VOSK_LIBS)
C_SRCS = \ @@ -10,6 +10,7 @@ C_SRCS = \ main.c \ recognizer.c \ synthesizer.c \ - vector.c + vector.c \ + vosk.c
IDL_SRCS = classes.idl diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 2f804fbf1a7..62952478bdf 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -31,6 +31,7 @@ #include "windef.h" #include "winbase.h" #include "winstring.h" +#include "winuser.h" #include "objbase.h"
#include "activation.h" @@ -47,6 +48,8 @@
#include "wine/list.h"
+#define SPERR_WINRT_INTERNAL_ERROR 0x800455a0 + /* * * Windows.Media.SpeechRecognition diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index c2f386206b8..ff23acc2720 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -25,6 +25,9 @@
#include "wine/debug.h"
+#include "unixlib.h" +#include "wine/unixlib.h" + WINE_DEFAULT_DEBUG_CHANNEL(speech);
/* @@ -171,6 +174,8 @@ struct session IAudioCaptureClient *capture_client; WAVEFORMATEX capture_wfx;
+ vosk_handle vosk_handle; + HANDLE worker_thread, worker_control_event, audio_buf_event; BOOLEAN worker_running, worker_paused; CRITICAL_SECTION cs; @@ -318,7 +323,9 @@ static ULONG WINAPI session_AddRef( ISpeechContinuousRecognitionSession *iface ) static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface ) { struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + struct vosk_release_params vosk_release_params; ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref);
if (!ref) @@ -344,6 +351,9 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface impl->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&impl->cs);
+ vosk_release_params.vosk_handle = impl->vosk_handle; + WINE_UNIX_CALL(unix_vosk_release, &vosk_release_params); + IVector_ISpeechRecognitionConstraint_Release(impl->constraints); free(impl); } @@ -1079,6 +1089,35 @@ cleanup: return hr; }
+static HRESULT recognizer_factory_create_vosk_instance(struct session *session) +{ + struct vosk_create_params vosk_create_params = { 0 }; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + NTSTATUS status; + INT len; + + if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + return E_FAIL; + + if (CharLowerBuffW(locale, len) != len) + return E_FAIL; + + if (!WideCharToMultiByte(CP_ACP, 0, locale, -1, (LPSTR)vosk_create_params.locale, len, NULL, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + + vosk_create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + + if ((status = WINE_UNIX_CALL(unix_vosk_create, &vosk_create_params))) + { + ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(vosk_create_params.locale), status); + return SPERR_WINRT_INTERNAL_ERROR; + } + + session->vosk_handle = vosk_create_params.vosk_handle; + + return S_OK; +} + static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer ) { struct recognizer *impl; @@ -1125,6 +1164,9 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (FAILED(hr = recognizer_factory_create_audio_capture(session))) goto error;
+ if (FAILED(hr = recognizer_factory_create_vosk_instance(session))) + goto error; + InitializeCriticalSection(&session->cs); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs");
diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index 5516b51d235..5f45dcc0dc9 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -30,4 +30,24 @@
#include "wine/unixlib.h"
+typedef UINT64 vosk_handle; + +struct vosk_create_params +{ + vosk_handle vosk_handle; + CHAR locale[LOCALE_NAME_MAX_LENGTH]; + FLOAT sample_rate; +}; + +struct vosk_release_params +{ + vosk_handle vosk_handle; +}; + +enum unix_funcs +{ + unix_vosk_create, + unix_vosk_release, +}; + #endif diff --git a/dlls/windows.media.speech/vosk.c b/dlls/windows.media.speech/vosk.c new file mode 100644 index 00000000000..8517a7562f5 --- /dev/null +++ b/dlls/windows.media.speech/vosk.c @@ -0,0 +1,222 @@ +/* + * Vosk interface for Windows.Media.Speech + * + * Copyright 2023 Bernhard Kölbl for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include <stdarg.h> +#include <dirent.h> +#include <dlfcn.h> +#include <errno.h> + +#ifdef HAVE_VOSK_API_H +#include <vosk_api.h> +#endif /* HAVE_VOSK_API_H */ + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" + +#include "wine/debug.h" + +#include "unixlib.h" + +WINE_DEFAULT_DEBUG_CHANNEL(speech); + +#ifdef SONAME_LIBVOSK + +static inline vosk_handle vosk_recognizer_to_handle( VoskRecognizer *recognizer ) +{ + return (vosk_handle)(UINT_PTR)recognizer; +} + +static inline VoskRecognizer *vosk_recognizer_from_handle( vosk_handle handle ) +{ + return (VoskRecognizer *)(UINT_PTR)handle; +} + +static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) +{ + static const char *vosk_model_identifier_small = "vosk-model-small-"; + static const char *vosk_model_identifier = "vosk-model-"; + size_t ident_small_len = strlen(vosk_model_identifier_small); + size_t ident_len = strlen(vosk_model_identifier); + NTSTATUS status = STATUS_UNSUCCESSFUL; + char *dir_name, *model_path; + struct dirent *dirent; + size_t path_len; + DIR *dir; + + TRACE("path %s, locale %s, model %p.\n", path, debugstr_a(locale), model); + + if (!path || !locale || strlen(locale) < 4) + return STATUS_UNSUCCESSFUL; + + if (!(dir = opendir(path))) + return STATUS_UNSUCCESSFUL; + + path_len = strlen(path); + *model = NULL; + + while ((dirent = readdir(dir))) + { + if (dirent->d_type != DT_DIR) + continue; + + dir_name = dirent->d_name; + + if (!strncmp(dir_name, vosk_model_identifier_small, ident_small_len)) + dir_name += ident_small_len; + else if (!strncmp(dir_name, vosk_model_identifier, ident_len)) + dir_name += ident_len; + else + continue; + + /* First match for lang and region (en-us), then only lang (en). */ + if (strncmp(dir_name, locale, 5) && strncmp(dir_name, locale, 2)) + continue; + + if(!(model_path = malloc(path_len + 1 /* '/' */ + strlen(dirent->d_name) + 1))) + { + status = STATUS_NO_MEMORY; + break; + } + + sprintf(model_path, "%s/%s", path, dirent->d_name); + + TRACE("Trying to load Vosk model %s.\n", debugstr_a(model_path)); + + *model = vosk_model_new(model_path); + free(model_path); + + if (*model) + { + status = STATUS_SUCCESS; + break; + } + } + + closedir(dir); + + return status; +} + +static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) +{ + const char *suffix = NULL; + char *env, *path = NULL; + NTSTATUS status; + + TRACE("locale %s, model %p.\n", debugstr_a(locale), model); + + if (!model) + return STATUS_UNSUCCESSFUL; + + if (!find_model_by_locale_and_path(getenv("VOSK_MODEL_PATH"), locale, model)) + return STATUS_SUCCESS; + if (!find_model_by_locale_and_path("/usr/share/vosk", locale, model)) + return STATUS_SUCCESS; + + if ((env = getenv("XDG_CACHE_HOME"))) + suffix = "/vosk"; + else if ((env = getenv("HOME"))) + suffix = "/.cache/vosk"; + else + return STATUS_UNSUCCESSFUL; + + if (!(path = malloc(strlen(env) + strlen(suffix) + 1))) + return STATUS_NO_MEMORY; + + sprintf(path, "%s%s", env, suffix); + status = find_model_by_locale_and_path(path, locale, model); + free(path); + + return status; +} + +static NTSTATUS vosk_create( void *args ) +{ + struct vosk_create_params *params = args; + VoskRecognizer *recognizer = NULL; + VoskModel *model = NULL; + NTSTATUS status = STATUS_SUCCESS; + + TRACE("args %p.\n", args); + + if ((status = find_model_by_locale(params->locale, &model))) + return status; + + if (!(recognizer = vosk_recognizer_new(model, params->sample_rate))) + status = STATUS_UNSUCCESSFUL; + + /* The model is kept alive inside the recognizer, so we can safely free our ref here. */ + vosk_model_free(model); + + params->vosk_handle = vosk_recognizer_to_handle(recognizer); + return status; +} + +static NTSTATUS vosk_release( void *args ) +{ + struct vosk_release_params *params = args; + + TRACE("args %p.\n", args); + + vosk_recognizer_free(vosk_recognizer_from_handle(params->vosk_handle)); + + return STATUS_SUCCESS; +} + +#else /* SONAME_LIBVOSK */ + +#define MAKE_UNSUPPORTED_FUNC( f ) \ + static NTSTATUS f( void *args ) \ + { \ + WARN("wine was compiled without Vosk support. Speech recognition won't work.\n"); \ + return STATUS_NOT_SUPPORTED; \ + } + +MAKE_UNSUPPORTED_FUNC(vosk_create) +MAKE_UNSUPPORTED_FUNC(vosk_release) +#undef MAKE_UNSUPPORTED_FUNC + +#endif /* SONAME_LIBVOSK */ + +unixlib_entry_t __wine_unix_call_funcs[] = +{ + vosk_create, + vosk_release, +}; + +unixlib_entry_t __wine_unix_call_wow64_funcs[] = +{ + vosk_create, + vosk_release, +};
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=129402
Your paranoid android.
=== debian11 (32 bit report) ===
d3d11: d3d11.c:25778: Test failed: Got 0, expected 33 (index 1). d3d11.c:25778: Test failed: Got 0, expected 66 (index 2). d3d11.c:25778: Test failed: Got 0, expected 99 (index 3). d3d11.c:25778: Test failed: Got 0, expected 132 (index 4). d3d11.c:25778: Test failed: Got 0, expected 165 (index 5). d3d11.c:25778: Test failed: Got 0, expected 198 (index 6). d3d11.c:25778: Test failed: Got 0, expected 231 (index 7). d3d11.c:25778: Test failed: Got 0, expected 264 (index 8). d3d11.c:25778: Test failed: Got 0, expected 297 (index 9). d3d11.c:25778: Test failed: Got 0, expected 330 (index 10). d3d11.c:25778: Test failed: Got 0, expected 363 (index 11). d3d11.c:25778: Test failed: Got 0, expected 396 (index 12). d3d11.c:25778: Test failed: Got 0, expected 429 (index 13). d3d11.c:25778: Test failed: Got 0, expected 462 (index 14). d3d11.c:25778: Test failed: Got 0, expected 495 (index 15). d3d11.c:25778: Test failed: Got 0, expected 528 (index 16). d3d11.c:25778: Test failed: Got 0, expected 561 (index 17). d3d11.c:25778: Test failed: Got 0, expected 594 (index 18). d3d11.c:25778: Test failed: Got 0, expected 627 (index 19). d3d11.c:25778: Test failed: Got 0, expected 660 (index 20). d3d11.c:25778: Test failed: Got 0, expected 693 (index 21). d3d11.c:25778: Test failed: Got 0, expected 726 (index 22). d3d11.c:25778: Test failed: Got 0, expected 759 (index 23). d3d11.c:25778: Test failed: Got 0, expected 792 (index 24). d3d11.c:25778: Test failed: Got 0, expected 825 (index 25). d3d11.c:25778: Test failed: Got 0, expected 858 (index 26). d3d11.c:25778: Test failed: Got 0, expected 891 (index 27). d3d11.c:25778: Test failed: Got 0, expected 924 (index 28). d3d11.c:25778: Test failed: Got 0, expected 957 (index 29). d3d11.c:25778: Test failed: Got 0, expected 990 (index 30). d3d11.c:25778: Test failed: Got 0, expected 1023 (index 31). d3d11.c:25778: Test failed: Got 0, expected 1056 (index 32). d3d11.c:25778: Test failed: Got 0, expected 1089 (index 33). d3d11.c:25778: Test failed: Got 0, expected 1122 (index 34). d3d11.c:25778: Test failed: Got 0, expected 1155 (index 35). d3d11.c:25778: Test failed: Got 0, expected 1188 (index 36). d3d11.c:25778: Test failed: Got 0, expected 1221 (index 37). d3d11.c:25778: Test failed: Got 0, expected 1254 (index 38). d3d11.c:25778: Test failed: Got 0, expected 1287 (index 39). d3d11.c:25778: Test failed: Got 0, expected 1320 (index 40). d3d11.c:25778: Test failed: Got 0, expected 1353 (index 41). d3d11.c:25778: Test failed: Got 0, expected 1386 (index 42). d3d11.c:25778: Test failed: Got 0, expected 1419 (index 43). d3d11.c:25778: Test failed: Got 0, expected 1452 (index 44). d3d11.c:25778: Test failed: Got 0, expected 1485 (index 45). d3d11.c:25778: Test failed: Got 0, expected 1518 (index 46). d3d11.c:25778: Test failed: Got 0, expected 1551 (index 47). d3d11.c:25778: Test failed: Got 0, expected 1584 (index 48). d3d11.c:25778: Test failed: Got 0, expected 1617 (index 49). d3d11.c:25778: Test failed: Got 0, expected 1650 (index 50). d3d11.c:25778: Test failed: Got 0, expected 1683 (index 51). d3d11.c:25778: Test failed: Got 0, expected 1716 (index 52). d3d11.c:25778: Test failed: Got 0, expected 1749 (index 53). d3d11.c:25778: Test failed: Got 0, expected 1782 (index 54). d3d11.c:25778: Test failed: Got 0, expected 1815 (index 55). d3d11.c:25778: Test failed: Got 0, expected 1848 (index 56). d3d11.c:25778: Test failed: Got 0, expected 1881 (index 57). d3d11.c:25778: Test failed: Got 0, expected 1914 (index 58). d3d11.c:25778: Test failed: Got 0, expected 1947 (index 59). d3d11.c:25778: Test failed: Got 0, expected 1980 (index 60). d3d11.c:25778: Test failed: Got 0, expected 2013 (index 61). d3d11.c:25778: Test failed: Got 0, expected 2046 (index 62). d3d11.c:25778: Test failed: Got 0, expected 2079 (index 63). d3d11.c:25810: Test failed: Got 0, expected 32 (index 0). d3d11.c:25810: Test failed: Got 0, expected 96 (index 1). d3d11.c:25810: Test failed: Got 0, expected 160 (index 2). d3d11.c:25810: Test failed: Got 0, expected 224 (index 3). d3d11.c:25810: Test failed: Got 0, expected 288 (index 4). d3d11.c:25810: Test failed: Got 0, expected 352 (index 5). d3d11.c:25810: Test failed: Got 0, expected 416 (index 6). d3d11.c:25810: Test failed: Got 0, expected 480 (index 7). d3d11.c:25810: Test failed: Got 0, expected 544 (index 8). d3d11.c:25810: Test failed: Got 0, expected 608 (index 9). d3d11.c:25810: Test failed: Got 0, expected 672 (index 10). d3d11.c:25810: Test failed: Got 0, expected 736 (index 11). d3d11.c:25810: Test failed: Got 0, expected 800 (index 12). d3d11.c:25810: Test failed: Got 0, expected 864 (index 13). d3d11.c:25810: Test failed: Got 0, expected 928 (index 14). d3d11.c:25810: Test failed: Got 0, expected 992 (index 15). d3d11.c:25810: Test failed: Got 0, expected 1056 (index 16). d3d11.c:25810: Test failed: Got 0, expected 1120 (index 17). d3d11.c:25810: Test failed: Got 0, expected 1184 (index 18). d3d11.c:25810: Test failed: Got 0, expected 1248 (index 19). d3d11.c:25810: Test failed: Got 0, expected 1312 (index 20). d3d11.c:25810: Test failed: Got 0, expected 1376 (index 21). d3d11.c:25810: Test failed: Got 0, expected 1440 (index 22). d3d11.c:25810: Test failed: Got 0, expected 1504 (index 23). d3d11.c:25810: Test failed: Got 0, expected 1568 (index 24). d3d11.c:25810: Test failed: Got 0, expected 1632 (index 25). d3d11.c:25810: Test failed: Got 0, expected 1696 (index 26). d3d11.c:25810: Test failed: Got 0, expected 1760 (index 27). d3d11.c:25810: Test failed: Got 0, expected 1824 (index 28). d3d11.c:25810: Test failed: Got 0, expected 1888 (index 29). d3d11.c:25810: Test failed: Got 0, expected 1952 (index 30). d3d11.c:25810: Test failed: Got 0, expected 2016 (index 31). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 1 (index 32). d3d11.c:25854: Test failed: Got 0, expected 1 (index 32). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 2 (index 33). d3d11.c:25854: Test failed: Got 0, expected 2 (index 33). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 3 (index 34). d3d11.c:25854: Test failed: Got 0, expected 3 (index 34). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 4 (index 35). d3d11.c:25854: Test failed: Got 0, expected 4 (index 35). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 5 (index 36). d3d11.c:25854: Test failed: Got 0, expected 5 (index 36). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 6 (index 37). d3d11.c:25854: Test failed: Got 0, expected 6 (index 37). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 7 (index 38). d3d11.c:25854: Test failed: Got 0, expected 7 (index 38). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 8 (index 39). d3d11.c:25854: Test failed: Got 0, expected 8 (index 39). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 9 (index 40). d3d11.c:25854: Test failed: Got 0, expected 9 (index 40). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 10 (index 41). d3d11.c:25854: Test failed: Got 0, expected 10 (index 41). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 11 (index 42). d3d11.c:25854: Test failed: Got 0, expected 11 (index 42). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 12 (index 43). d3d11.c:25854: Test failed: Got 0, expected 12 (index 43). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 13 (index 44). d3d11.c:25854: Test failed: Got 0, expected 13 (index 44). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 14 (index 45). d3d11.c:25854: Test failed: Got 0, expected 14 (index 45). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 15 (index 46). d3d11.c:25854: Test failed: Got 0, expected 15 (index 46). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 16 (index 47). d3d11.c:25854: Test failed: Got 0, expected 16 (index 47). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 17 (index 48). d3d11.c:25854: Test failed: Got 0, expected 17 (index 48). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 18 (index 49). d3d11.c:25854: Test failed: Got 0, expected 18 (index 49). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 19 (index 50). d3d11.c:25854: Test failed: Got 0, expected 19 (index 50). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 20 (index 51). d3d11.c:25854: Test failed: Got 0, expected 20 (index 51). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 21 (index 52). d3d11.c:25854: Test failed: Got 0, expected 21 (index 52). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 22 (index 53). d3d11.c:25854: Test failed: Got 0, expected 22 (index 53). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 23 (index 54). d3d11.c:25854: Test failed: Got 0, expected 23 (index 54). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 24 (index 55). d3d11.c:25854: Test failed: Got 0, expected 24 (index 55). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 25 (index 56). d3d11.c:25854: Test failed: Got 0, expected 25 (index 56). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 26 (index 57). d3d11.c:25854: Test failed: Got 0, expected 26 (index 57). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 27 (index 58). d3d11.c:25854: Test failed: Got 0, expected 27 (index 58). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 28 (index 59). d3d11.c:25854: Test failed: Got 0, expected 28 (index 59). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 29 (index 60). d3d11.c:25854: Test failed: Got 0, expected 29 (index 60). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 30 (index 61). d3d11.c:25854: Test failed: Got 0, expected 30 (index 61). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 31 (index 62). d3d11.c:25854: Test failed: Got 0, expected 31 (index 62). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 32 (index 63). d3d11.c:25854: Test failed: Got 0, expected 32 (index 63). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 2 (index 64). d3d11.c:25854: Test failed: Got 0, expected 2 (index 64). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 4 (index 65). d3d11.c:25854: Test failed: Got 0, expected 4 (index 65). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 6 (index 66). d3d11.c:25854: Test failed: Got 0, expected 6 (index 66). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 8 (index 67). d3d11.c:25854: Test failed: Got 0, expected 8 (index 67). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 10 (index 68). d3d11.c:25854: Test failed: Got 0, expected 10 (index 68). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 12 (index 69). d3d11.c:25854: Test failed: Got 0, expected 12 (index 69). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 14 (index 70). d3d11.c:25854: Test failed: Got 0, expected 14 (index 70). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 16 (index 71). d3d11.c:25854: Test failed: Got 0, expected 16 (index 71). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 18 (index 72). d3d11.c:25854: Test failed: Got 0, expected 18 (index 72). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 20 (index 73). d3d11.c:25854: Test failed: Got 0, expected 20 (index 73). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 22 (index 74). d3d11.c:25854: Test failed: Got 0, expected 22 (index 74). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 24 (index 75). d3d11.c:25854: Test failed: Got 0, expected 24 (index 75). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 26 (index 76). d3d11.c:25854: Test failed: Got 0, expected 26 (index 76). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 28 (index 77). d3d11.c:25854: Test failed: Got 0, expected 28 (index 77). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 30 (index 78). d3d11.c:25854: Test failed: Got 0, expected 30 (index 78). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 32 (index 79). d3d11.c:25854: Test failed: Got 0, expected 32 (index 79). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 34 (index 80). d3d11.c:25854: Test failed: Got 0, expected 34 (index 80). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 36 (index 81). d3d11.c:25854: Test failed: Got 0, expected 36 (index 81). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 38 (index 82). d3d11.c:25854: Test failed: Got 0, expected 38 (index 82). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 40 (index 83). d3d11.c:25854: Test failed: Got 0, expected 40 (index 83). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 42 (index 84). d3d11.c:25854: Test failed: Got 0, expected 42 (index 84). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 44 (index 85). d3d11.c:25854: Test failed: Got 0, expected 44 (index 85). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 46 (index 86). d3d11.c:25854: Test failed: Got 0, expected 46 (index 86). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 48 (index 87). d3d11.c:25854: Test failed: Got 0, expected 48 (index 87). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 50 (index 88). d3d11.c:25854: Test failed: Got 0, expected 50 (index 88). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 52 (index 89). d3d11.c:25854: Test failed: Got 0, expected 52 (index 89). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 54 (index 90). d3d11.c:25854: Test failed: Got 0, expected 54 (index 90). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 56 (index 91). d3d11.c:25854: Test failed: Got 0, expected 56 (index 91). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 58 (index 92). d3d11.c:25854: Test failed: Got 0, expected 58 (index 92). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 60 (index 93). d3d11.c:25854: Test failed: Got 0, expected 60 (index 93). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 62 (index 94). d3d11.c:25854: Test failed: Got 0, expected 62 (index 94). d3d11.c:25852: Test failed: Got 0.00000000e+000, expected 64 (index 95). d3d11.c:25854: Test failed: Got 0, expected 64 (index 95).
On Tue Feb 14 22:15:44 2023 +0000, Zebediah Figura wrote:
Sorry, what I was trying to assert is that less workarounds is still better than more. And if using JSON is painful, the same thing applies: surely we can add more friendly C APIs to the library?
Well, I don't think it's reasonable to change their API just for our needs and the tradeoff of implementing this is just too little.
On Tue Feb 14 23:24:33 2023 +0000, Bernhard Kölbl wrote:
Well, I don't think it's reasonable to change their API just for our needs and the tradeoff of implementing this is just too little.
It's not just for our needs, though. Such APIs would be useful to any C consumer of the library.
I don't understand the tradeoff, either. Either we make changes to Wine or we make changes to vosk, and I don't see how it'd be any harder to make changes to vosk. (In the case of JSON I have to imagine it would even be easier, since presumably they parse it down to a more structured format internally.)
On Wed Feb 15 00:26:33 2023 +0000, Zebediah Figura wrote:
It's not just for our needs, though. Such APIs would be useful to any C consumer of the library. I don't understand the tradeoff, either. Either we make changes to Wine or we make changes to vosk, and I don't see how it'd be any harder to make changes to vosk. (In the case of JSON I have to imagine it would even be easier, since presumably they parse it down to a more structured format internally.)
It's also worth mentioning that I'm not going to use all of their functions. Generally I'd be more convinced if their library had a higher market share than it does now, but making their whole API more C compatible just for us and 3 used functions isn't really worth it to me.
Rémi Bernon (@rbernon) commented about configure.ac:
WINE_WARNING([No sound system was found. Windows applications will be silent.])
fi
+dnl **** Check for Vosk **** +if test x$with_vosk != xno +then
- WINE_CHECK_SONAME(vosk,vosk_recognizer_new,[AC_SUBST(VOSK_LIBS,"-lvosk") ac_cv_lib_vosk=yes],,)
+fi +WINE_NOTICE_WITH(vosk,[test x$with_vosk != xno -a x$ac_cv_lib_vosk != xyes],
[libvosk ${notice_platform}development files not found (or too old), Vosk aka speech recognition won't be supported.],
[enable_vosk])
```suggestion:-3+0 WINE_NOTICE_WITH(vosk,[test x$with_vosk != xno -a x$ac_cv_lib_vosk != xyes], [libvosk ${notice_platform}development files not found (or too old), speech recognition won't be supported.], [enable_windows_media_speech])
```
Or maybe if you don't want to disable the module entirely:
```suggestion:-3+0 WINE_NOTICE_WITH(vosk,[test x$with_vosk != xno -a x$ac_cv_lib_vosk != xyes], [libvosk ${notice_platform}development files not found (or too old), speech recognition won't be supported.])
```
(I'm also guessing here, I am not very familiar with `configure.ac`)
Rémi Bernon (@rbernon) commented about dlls/windows.media.speech/unixlib.h:
+{
- vosk_handle vosk_handle;
- CHAR locale[LOCALE_NAME_MAX_LENGTH];
- FLOAT sample_rate;
+};
+struct vosk_release_params +{
- vosk_handle vosk_handle;
+};
+enum unix_funcs +{
- unix_vosk_create,
- unix_vosk_release,
+};
A suggestion which should address the `vosk` namespace comment made in some of the discussion thread, would be to use `speech` here instead or some other generic prefix.
Although the unixlib interface might be designed a little bit to match the backend library usage, I don't think we need to tightly couple it with a specific backend, and having a generic prefix and generic unixlib interface will make it easier to change to (or add) a different backend in the future.
Rémi Bernon (@rbernon) commented about dlls/windows.media.speech/recognizer.c:
return hr;
}
+static HRESULT recognizer_factory_create_vosk_instance(struct session *session)
```suggestion:-0+0 static HRESULT recognizer_factory_create_vosk_instance( struct session *session ) ```
I believe you used this style elsewhere for function signatures.
Rémi Bernon (@rbernon) commented about dlls/windows.media.speech/recognizer.c:
+static HRESULT recognizer_factory_create_vosk_instance(struct session *session) +{
- struct vosk_create_params vosk_create_params = { 0 };
- WCHAR locale[LOCALE_NAME_MAX_LENGTH];
- NTSTATUS status;
- INT len;
- if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH)))
return E_FAIL;
- if (CharLowerBuffW(locale, len) != len)
return E_FAIL;
- if (!WideCharToMultiByte(CP_ACP, 0, locale, -1, (LPSTR)vosk_create_params.locale, len, NULL, NULL))
return HRESULT_FROM_WIN32(GetLastError());
```suggestion:-2+0 if (!WideCharToMultiByte(CP_ACP, 0, locale, len, vosk_create_params.locale, ARRAY_SIZE(vosk_create_params.locale), NULL, NULL)) return HRESULT_FROM_WIN32(GetLastError());
```
Rémi Bernon (@rbernon) commented about dlls/windows.media.speech/vosk.c:
- {
if (dirent->d_type != DT_DIR)
continue;
dir_name = dirent->d_name;
if (!strncmp(dir_name, vosk_model_identifier_small, ident_small_len))
dir_name += ident_small_len;
else if (!strncmp(dir_name, vosk_model_identifier, ident_len))
dir_name += ident_len;
else
continue;
/* First match for lang and region (en-us), then only lang (en). */
if (strncmp(dir_name, locale, 5) && strncmp(dir_name, locale, 2))
continue;
Can you really assume fixed length here? If not sure, what about `strchr( locale, '-' )` somewhere to find the separator?
Rémi Bernon (@rbernon) commented about dlls/windows.media.speech/vosk.c:
continue;
dir_name = dirent->d_name;
if (!strncmp(dir_name, vosk_model_identifier_small, ident_small_len))
dir_name += ident_small_len;
else if (!strncmp(dir_name, vosk_model_identifier, ident_len))
dir_name += ident_len;
else
continue;
/* First match for lang and region (en-us), then only lang (en). */
if (strncmp(dir_name, locale, 5) && strncmp(dir_name, locale, 2))
continue;
if(!(model_path = malloc(path_len + 1 /* '/' */ + strlen(dirent->d_name) + 1)))
```suggestion:-0+0 if (!(model_path = malloc(path_len + 1 /* '/' */ + strlen(dirent->d_name) + 1))) ```
On Wed Feb 15 10:08:48 2023 +0000, Rémi Bernon wrote:
Can you really assume fixed length here? If not sure, what about `strchr( locale, '-' )` somewhere to find the separator?
Yeah good question. Vosk uses a "2-2" language code, but in any case your solution sounds more future proof. I wonder if Chinese will even work?
On Wed Feb 15 10:08:46 2023 +0000, Rémi Bernon wrote:
A suggestion which should address the `vosk` namespace comment made in some of the discussion thread, would be to use `speech` here instead or some other generic prefix. Although the unixlib interface might be designed a little bit to match the backend library usage, I don't think we need to tightly couple it with a specific backend, and having a generic prefix and generic unixlib interface will make it easier to change to (or add) a different backend in the future.
The plan for the future should be to have something like winegstreamer for speech. So Sapi can work with this unixlib as well. In any case, I'll change it.
On Wed Feb 15 10:08:46 2023 +0000, Rémi Bernon wrote:
WINE_NOTICE_WITH(vosk,[test x$with_vosk != xno -a x$ac_cv_lib_vosk != xyes], [libvosk ${notice_platform}development files not found (or too old), speech recognition won't be supported.], [enable_windows_media_speech])
Or maybe if you don't want to disable the module entirely:
WINE_NOTICE_WITH(vosk,[test x$with_vosk != xno -a x$ac_cv_lib_vosk != xyes], [libvosk ${notice_platform}development files not found (or too old), speech recognition won't be supported.])
(I'm also guessing here, I am not very familiar with `configure.ac`)
This part also has some different issues, but in general the disable var can be removed.
On Wed Feb 15 10:47:24 2023 +0000, Bernhard Kölbl wrote:
Yeah good question. Vosk uses a "2-2" language code, but in any case your solution sounds more future proof. I wonder if Chinese will even work?
how about `strncmp(dir_name, locale, strlen(locale))` ?
On Wed Feb 15 13:10:35 2023 +0000, Bernhard Kölbl wrote:
how about `strncmp(dir_name, locale, strlen(locale))` ?
Sure, as long as you compute the locale length and the first locale component length upfront.