summaryrefslogtreecommitdiffstats
path: root/GUI
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2023-02-22 18:40:09 -0800
committeryum <yum.food.vr@gmail.com>2023-02-22 21:54:27 -0800
commit4840ab9beb831ec68887a5199fbb50ae6932f498 (patch)
treeef8bde6f4da7b051c0336dd83977829ea06d5fee /GUI
parentd701f80119ecb51366a45f429ec7006926527a40 (diff)
Checkpoint: begin work reimplementing processes
It appears that you cannot spawn a wxProcess from an independent thread of execution. I imagine they're supposed to be spawned from the main thread. Fuck that, I'm going to try to use the raw WIN32 API to spawn helper processes, and do it from arbitrary thread context. * Log() now delegates to a queue which the main thread periodically drains. * Log() now writes to a file. * WhisperCPP thread is now done with std::async. * Default chars per sync is now 8 * oatpp: Promising web framework.
Diffstat (limited to 'GUI')
-rw-r--r--GUI/GUI/GUI/Config.cpp2
-rw-r--r--GUI/GUI/GUI/Frame.cpp1
-rw-r--r--GUI/GUI/GUI/Logging.cpp56
-rw-r--r--GUI/GUI/GUI/Logging.h53
-rw-r--r--GUI/GUI/GUI/PythonWrapper.cpp117
-rw-r--r--GUI/GUI/GUI/PythonWrapper.h2
-rw-r--r--GUI/GUI/GUI/WhisperCPP.cpp159
-rw-r--r--GUI/GUI/GUI/WhisperCPP.h22
-rw-r--r--GUI/Libraries/.gitignore2
-rw-r--r--GUI/Libraries/fetch.ps131
-rw-r--r--GUI/README.md2
11 files changed, 251 insertions, 196 deletions
diff --git a/GUI/GUI/GUI/Config.cpp b/GUI/GUI/GUI/Config.cpp
index 50f0dca..a877d4a 100644
--- a/GUI/GUI/GUI/Config.cpp
+++ b/GUI/GUI/GUI/Config.cpp
@@ -78,7 +78,7 @@ AppConfig::AppConfig()
use_cpu(false),
use_builtin(false),
- chars_per_sync(20),
+ chars_per_sync(8),
bytes_per_char(1),
rows(4),
cols(48),
diff --git a/GUI/GUI/GUI/Frame.cpp b/GUI/GUI/GUI/Frame.cpp
index 5332490..0bac3aa 100644
--- a/GUI/GUI/GUI/Frame.cpp
+++ b/GUI/GUI/GUI/Frame.cpp
@@ -1991,6 +1991,7 @@ void Frame::OnWhisperStop(wxCommandEvent& event) {
void Frame::OnAppDrain(wxTimerEvent& event) {
DrainAsyncOutput(py_app_, transcribe_out_);
DrainAsyncOutput(env_proc_, transcribe_out_);
+ Logging::kThreadLogger.Drain();
}
void Frame::LoadAndSetIcons() {
diff --git a/GUI/GUI/GUI/Logging.cpp b/GUI/GUI/GUI/Logging.cpp
index 78594b4..f799e31 100644
--- a/GUI/GUI/GUI/Logging.cpp
+++ b/GUI/GUI/GUI/Logging.cpp
@@ -1,8 +1,48 @@
#include "Logging.h"
+#include <wx/wxprec.h>
+
+#ifndef WX_PRECOMP
+#include <wx/wx.h>
+#endif
+
+#include <wx/process.h>
+#include <wx/txtstrm.h>
+
+#include <fstream>
#include <regex>
#include <string>
+Logging::ThreadLogger Logging::kThreadLogger = Logging::ThreadLogger();
+
+Logging::ThreadLogger::ThreadLogger() {}
+
+void Logging::ThreadLogger::Append(wxTextCtrl* frame, const std::string&& message)
+{
+ std::scoped_lock l(mu_);
+ auto entry = messages_.find(frame);
+ if (entry == messages_.end()) {
+ messages_[frame] = { std::move(message) };
+ }
+ else {
+ messages_[frame].push_back(message);
+ }
+}
+
+void Logging::ThreadLogger::Drain()
+{
+ std::scoped_lock l(mu_);
+ std::ofstream log_ofs("Resources/log.txt", std::ios_base::app);
+ for (const auto& [frame, messages] : messages_) {
+ for (const auto& message : messages) {
+ frame->AppendText(message);
+ log_ofs << message;
+ }
+ }
+ log_ofs.close();
+ messages_.clear();
+}
+
std::string Logging::HidePII(const std::string&& str,
const std::string& replacement) {
try {
@@ -16,3 +56,19 @@ std::string Logging::HidePII(const std::string&& str,
wxLogFatalError("Unhandled regex error (HidePII)");
return ""; // Compiler thinks we can get here (we can't) and prints a warning.
}
+
+void Logging::DrainAsyncOutput(wxProcess* proc, wxTextCtrl* frame) {
+ if (!proc) {
+ return;
+ }
+
+ while (proc->IsInputAvailable()) {
+ wxTextInputStream iss(*(proc->GetInputStream()));
+ Log(frame, " {}\n", iss.ReadLine());
+ }
+
+ while (proc->IsErrorAvailable()) {
+ wxTextInputStream iss(*(proc->GetErrorStream()));
+ Log(frame, " {}\n", iss.ReadLine());
+ }
+}
diff --git a/GUI/GUI/GUI/Logging.h b/GUI/GUI/GUI/Logging.h
index 99462c0..c85a376 100644
--- a/GUI/GUI/GUI/Logging.h
+++ b/GUI/GUI/GUI/Logging.h
@@ -16,30 +16,23 @@
#include <string_view>
namespace Logging {
+ // Remove personally identifying information (PII) from str.
+ //
+ // For example, this translates "C:/Users/foo/Desktop" to "C:/Users/*****/Desktop".
+ std::string HidePII(const std::string&& str, const std::string& replacement = "*****");
-#if 0
- class Log {
+ class ThreadLogger {
public:
- static Log& Get() {
- static Log l;
- return l;
- }
-
- bool Write(const std::string& text);
+ ThreadLogger();
+ void Append(wxTextCtrl* frame, const std::string&& message);
+ void Drain();
private:
- Log() {}
-
- bool Open(const std::string& path);
-
- int fd_;
+ std::mutex mu_;
+ std::unordered_map<wxTextCtrl*, std::list<std::string>> messages_;
};
-#endif
- // Remove personally identifying information (PII) from str.
- //
- // For example, this translates "C:/Users/foo/Desktop" to "C:/Users/*****/Desktop".
- std::string HidePII(const std::string&& str, const std::string& replacement = "*****");
+ extern ThreadLogger kThreadLogger;
// Provides a simple Python format()-like interface to wxTextCtrl.
// Ex: Log(my_textctrl_, "{}\n", "Hello, world!");
@@ -47,28 +40,10 @@ namespace Logging {
void Log(wxTextCtrl* frame, std::string_view format, Args&&... args) {
const std::string raw = std::vformat(format, std::make_format_args(args...));
const std::string masked = HidePII(std::move(raw));
- frame->AppendText(masked);
- // Limit log to 10 MB to avoid runaway memory usage.
- const int max_frame_len_bytes = 10 * 1000 * 1000;
- if (frame->GetLastPosition() > max_frame_len_bytes) {
- frame->Remove(0, frame->GetLastPosition() - max_frame_len_bytes);
- }
- }
-
- inline void DrainAsyncOutput(wxProcess* proc, wxTextCtrl* frame) {
- if (!proc) {
- return;
- }
- while (proc->IsInputAvailable()) {
- wxTextInputStream iss(*(proc->GetInputStream()));
- Log(frame, " {}\n", iss.ReadLine());
- }
-
- while (proc->IsErrorAvailable()) {
- wxTextInputStream iss(*(proc->GetErrorStream()));
- Log(frame, " {}\n", iss.ReadLine());
- }
+ kThreadLogger.Append(frame, std::move(masked));
}
+
+ void DrainAsyncOutput(wxProcess* proc, wxTextCtrl* frame);
}
diff --git a/GUI/GUI/GUI/PythonWrapper.cpp b/GUI/GUI/GUI/PythonWrapper.cpp
index bcb7b1d..c90520d 100644
--- a/GUI/GUI/GUI/PythonWrapper.cpp
+++ b/GUI/GUI/GUI/PythonWrapper.cpp
@@ -1,10 +1,12 @@
#include "Logging.h"
#include "PythonWrapper.h"
+#include "ScopeGuard.h"
#include "Util.h"
#include "Config.h"
#include <stdio.h>
+#include <Windows.h>
#include <filesystem>
#include <fstream>
@@ -60,58 +62,95 @@ bool PythonWrapper::InvokeCommandWithArgs(
cmd_oss << " " << arg;
}
- wxString path;
- if (!wxGetEnv("PATH", &path)) {
- *py_stderr = "Failed to get PATH";
- return false;
- }
- if (!wxSetEnv("PATH", path + ";Resources/PortableGit/bin")) {
- *py_stderr = "Failed to append to PATH";
+ HANDLE stdout_read{};
+ HANDLE stdout_write{};
+ SECURITY_ATTRIBUTES sec_attr{};
+ sec_attr.nLength = sizeof(sec_attr);
+ sec_attr.bInheritHandle = TRUE;
+ if (!CreatePipe(&stdout_read, &stdout_write, &sec_attr, 0)) {
+ if (py_stderr) {
+ std::ostringstream err_oss;
+ err_oss << "Error while executing python command \"" << cmd_oss.str()
+ << "\": Failed to create stdout pipe" << std::endl;
+ *py_stderr = err_oss.str();
+ }
return false;
}
+ ScopeGuard stdout_cleanup([&]() {
+ CloseHandle(stdout_read);
+ CloseHandle(stdout_write);
+ });
- wxArrayString cmd_stdout;
- wxArrayString cmd_stderr;
- long result = wxExecute(cmd_oss.str(), cmd_stdout, cmd_stderr, /*flags=*/0);
- std::ostringstream cmd_stdout_oss;
- for (const auto& line : cmd_stdout) {
- if (!cmd_stdout_oss.str().empty()) {
- cmd_stdout_oss << std::endl;
- }
- cmd_stdout_oss << line;
- }
- std::ostringstream cmd_stderr_oss;
- for (const auto& line : cmd_stderr) {
- if (!cmd_stderr_oss.str().empty()) {
- cmd_stderr_oss << std::endl;
- }
- cmd_stderr_oss << line;
- }
- if (result == -1) {
- std::ostringstream err_oss;
- err_oss << "Error while executing python command \"" << cmd_oss.str() << "\": Failed to launch process" << std::endl;
- err_oss << cmd_stdout_oss.str() << std::endl;
- err_oss << cmd_stderr_oss.str() << std::endl;
+ HANDLE stderr_read{};
+ HANDLE stderr_write{};
+ if (!CreatePipe(&stderr_read, &stderr_write, &sec_attr, 0)) {
if (py_stderr) {
+ std::ostringstream err_oss;
+ err_oss << "Error while executing python command \"" << cmd_oss.str()
+ << "\": Failed to create stderr pipe" << std::endl;
*py_stderr = err_oss.str();
}
return false;
- } else if (result) {
+ }
+ ScopeGuard stderr_cleanup([&]() {
+ CloseHandle(stderr_read);
+ CloseHandle(stderr_write);
+ });
+
+ STARTUPINFOA si{};
+ si.cb = sizeof(si);
+ si.hStdOutput = stdout_write;
+ si.hStdError = stderr_write;
+ PROCESS_INFORMATION pi{};
+ std::string path = "Resources/PortableGit/bin";
+ std::string env = "PATH=" + path;
+ //std::string env;
+
+ //std::string tmp_cmd = "Get-ChildItem";
+
+ std::string cmd_str = cmd_oss.str();
+ if (!CreateProcessA(NULL, // application name
+ cmd_str.data(),
+ //tmp_cmd.data(),
+ NULL, // process attributes
+ NULL, // thread attributes
+ FALSE, // whether to inherit parent's handles
+ 0, // creation flags
+ env.data(),
+ NULL, // current directory (use parent's)
+ &si,
+ &pi)) {
if (py_stderr) {
std::ostringstream err_oss;
- err_oss << "Error while executing python command \"" << cmd_oss.str() <<
- "\"" << std::endl <<
- "Process returned " << result << ": " << std::endl <<
- cmd_stdout_oss.str() << std::endl <<
- cmd_stderr_oss.str() << std::endl;
+ err_oss << "Error while executing python command \"" << cmd_oss.str()
+ << "\": Failed to launch process" << std::endl;
*py_stderr = err_oss.str();
}
return false;
}
+ ScopeGuard pi_cleanup([&] {
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ });
+
+ std::ostringstream stdout_oss, stderr_oss;
+ std::vector<char> buf(4096, 0);
+ DWORD bytes_read = 0;
+ while (ReadFile(si.hStdOutput, buf.data(), buf.size(), &bytes_read, NULL) && bytes_read > 0) {
+ stdout_oss << std::string(buf.data(), bytes_read);
+ }
+ bytes_read = 0;
+ while (ReadFile(si.hStdError, buf.data(), buf.size(), &bytes_read, NULL) && bytes_read > 0) {
+ stderr_oss << std::string(buf.data(), bytes_read);
+ }
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ // TODO(yum) retrieve exit code
- *py_stdout = cmd_stdout_oss.str();
+ *py_stdout = stdout_oss.str();
if (py_stderr) {
- *py_stderr = cmd_stderr_oss.str();
+ *py_stderr = stderr_oss.str();
}
return true;
}
@@ -165,7 +204,7 @@ std::string PythonWrapper::DumpMics() {
return py_stdout;
}
-bool PythonWrapper::InstallPip(std::string* out) {
+bool PythonWrapper::InstallPip(std::string* out, std::string* err) {
std::string result;
std::filesystem::path pip_flag = "Resources/Python/.pip_installed";
@@ -174,7 +213,7 @@ bool PythonWrapper::InstallPip(std::string* out) {
}
std::string pip_path = "Resources/Python/get-pip.py";
- if (!InvokeWithArgs({ pip_path }, out)) {
+ if (!InvokeWithArgs({ pip_path }, out, err)) {
return false;
}
diff --git a/GUI/GUI/GUI/PythonWrapper.h b/GUI/GUI/GUI/PythonWrapper.h
index 05de538..3433b0f 100644
--- a/GUI/GUI/GUI/PythonWrapper.h
+++ b/GUI/GUI/GUI/PythonWrapper.h
@@ -47,7 +47,7 @@ namespace PythonWrapper
std::string DumpMics();
// Execute get-pip.py.
- bool InstallPip(std::string* out);
+ bool InstallPip(std::string* out, std::string* err = nullptr);
// TODO(yum) both StartApp and GenerateAnimator should be
// parameterized with config files instead of these ever-growing lists of
diff --git a/GUI/GUI/GUI/WhisperCPP.cpp b/GUI/GUI/GUI/WhisperCPP.cpp
index 809415e..362bad0 100644
--- a/GUI/GUI/GUI/WhisperCPP.cpp
+++ b/GUI/GUI/GUI/WhisperCPP.cpp
@@ -14,6 +14,7 @@
#include <codecvt>
#include <cwchar>
#include <fstream>
+#include <future>
#include <locale>
#include <string>
#include <vector>
@@ -60,9 +61,11 @@ namespace {
};
WhisperCPP::WhisperCPP(wxTextCtrl* out)
- : out_(out), f_(nullptr), did_init_(false), proc_(nullptr), run_(false)
+ : out_(out), f_(nullptr), did_init_(false), proc_(), run_(false)
{
- Log(out_, "Setting concurrency to 2: {}\n", wxThread::SetConcurrency(2));
+ auto p = std::promise<void>();
+ proc_ = p.get_future();
+ p.set_value();
}
WhisperCPP::~WhisperCPP() {
@@ -135,32 +138,26 @@ bool WhisperCPP::OpenMic(const int idx, Whisper::iAudioCapture*& stream) {
return true;
}
-bool WhisperCPP::InstallDependencies(wxProcess*& proc) {
+bool WhisperCPP::InstallDependencies() {
std::filesystem::path flag_file = "Resources/.whisper_deps_installed";
flag_file = flag_file.lexically_normal();
if (std::filesystem::exists(flag_file)) {
- proc = nullptr;
return true;
}
- auto cb = [&](wxProcess* proc, int ret) -> void {
- Log(out_, "Dependency installation exited with code {}\n", ret);
- if (ret == 0) {
- Log(out_, "Dependency installation finished\n");
- }
- DrainAsyncOutput(proc, out_);
- return;
- };
-
- proc = PythonWrapper::InvokeAsyncWithArgs({
+ std::string py_stdout, py_stderr;
+ bool ret = PythonWrapper::InvokeWithArgs({
"-u", // Unbuffered output
"-m pip",
"install",
"-r Resources/Scripts/whisper_requirements.txt",
- }, std::move(cb));
- if (!proc) {
- Log(out_, "Failed to launch installation thread!\n");
+ }, &py_stdout, &py_stderr);
+
+ Log(out_, py_stdout);
+ Log(out_, py_stderr);
+ if (!ret) {
+ Log(out_, "Failed to install dependencies!\n");
return false;
}
@@ -172,30 +169,25 @@ bool WhisperCPP::InstallDependencies(wxProcess*& proc) {
}
bool WhisperCPP::DownloadModel(const std::string& model_name,
- const std::filesystem::path& fs_path, wxProcess*& proc) {
- auto cb = [&](wxProcess* proc, int ret) -> void {
- Log(out_, "Model download completed with code {}\n", ret);
- if (ret == 0) {
- Log(out_, "Model download finished\n");
- }
- DrainAsyncOutput(proc, out_);
- return;
- };
-
+ const std::filesystem::path& fs_path) {
std::ostringstream url_oss;
url_oss << "https://huggingface.co/datasets/ggerganov/whisper.cpp/resolve/main/";
url_oss << model_name;
Log(out_, "Model will be saved to {}\n", fs_path.lexically_normal().string());
- proc = PythonWrapper::InvokeAsyncWithArgs({
+ std::string py_stdout, py_stderr;
+ bool ret = PythonWrapper::InvokeWithArgs({
"-u", // Unbuffered output
"-m wget",
url_oss.str(),
"-o", fs_path.string(),
- }, std::move(cb));
- if (!proc) {
- Log(out_, "Failed to launch download thread!\n");
+ }, &py_stdout, &py_stderr);
+ Log(out_, py_stdout);
+ Log(out_, py_stderr);
+ if (!ret) {
+ Log(out_, "Failed to download model!\n");
return false;
}
+
return true;
}
@@ -229,29 +221,13 @@ bool WhisperCPP::CreateContext(Whisper::iModel* model, Whisper::iContext*& conte
return true;
}
-WhisperCPP::AppThread::AppThread(
- const std::function<void(AppThread* thd)>&& cb,
- WhisperCPP* app)
- : wxThread(wxTHREAD_DETACHED), cb_(cb), app_(app)
-{}
-WhisperCPP::AppThread::~AppThread()
-{
- Log(app_->out_, "Destroy transcription thread\n");
- app_->proc_ = nullptr;
-}
-
-void* WhisperCPP::AppThread::Entry() {
- cb_(this);
- return nullptr;
-}
-
void WhisperCPP::Start(const AppConfig& c) {
- if (proc_) {
+ if (!proc_.valid()) {
Log(out_, "Transcription engine already running\n");
return;
}
- proc_ = new AppThread([&](AppThread* thd) {
+ proc_ = std::async(std::launch::async, [&]() -> void {
Log(out_, "Transcription thread top\n");
run_ = true;
@@ -261,28 +237,17 @@ void WhisperCPP::Start(const AppConfig& c) {
}
ScopeGuard mic_stream_cleanup([mic_stream]() { mic_stream->Release(); });
- {
- std::string output;
- Log(out_, "Installing pip\n");
- if (!PythonWrapper::InstallPip(&output)) {
- Log(out_, "Failed to install pip: {}\n", output);
- }
+ std::string pip_out, pip_err;
+ Log(out_, "Installing pip\n");
+ if (!PythonWrapper::InstallPip(&pip_out, &pip_err)) {
+ Log(out_, "Failed to install pip: {}\n", pip_err);
+ return;
}
-
- {
- Log(out_, "Installing Python dependencies\n");
- wxProcess* proc = nullptr;
- if (!InstallDependencies(proc)) {
- return;
- }
- while (proc && proc->Exists(proc->GetPid())) {
- if (!run_ || thd->TestDestroy()) {
- proc->Kill(proc->GetPid(), wxSIGKILL);
- return;
- }
- wxThread::Sleep(100);
- }
+ Log(out_, "Installing Python dependencies\n");
+ if (!InstallDependencies()) {
+ return;
}
+#if 1
std::filesystem::path model_path = "Resources/Models";
model_path /= c.whisper_model;
@@ -291,19 +256,9 @@ void WhisperCPP::Start(const AppConfig& c) {
}
else {
Log(out_, "Downloading model {}\n", c.whisper_model);
- wxProcess* proc = nullptr;
- model_path = model_path.lexically_normal();
- if (!DownloadModel(c.whisper_model, model_path, proc)) {
+ if (!DownloadModel(c.whisper_model, model_path)) {
return;
}
- while (proc->Exists(proc->GetPid())) {
- if (!run_ || thd->TestDestroy()) {
- proc->Kill(proc->GetPid(), wxSIGKILL);
- std::filesystem::remove(model_path);
- return;
- }
- wxThread::Sleep(100);
- }
}
Whisper::iModel* model;
@@ -349,22 +304,25 @@ void WhisperCPP::Start(const AppConfig& c) {
bool is_metadata = false;
for (int j = 0; j < seg.countTokens; j++) {
const sToken& tok = tokens[seg.firstToken + j];
- if (tok.text[0] == '[') {
- continue;
- }
- if (tok.text[0] == ' ' && (
- tok.text[1] == '[' ||
- tok.text[1] == '(')) {
- if (tok.text[strlen(tok.text) - 1] == ']') {
+ std::string_view tok_str(tok.text);
+ if (tok_str.starts_with("[") ||
+ tok_str.starts_with(" [") ||
+ tok_str.starts_with(" (")) {
+ if (tok_str.ends_with("]") ||
+ tok_str.ends_with(")")) {
continue;
}
is_metadata = true;
continue;
}
- if (is_metadata && (
- tok.text[strlen(tok.text) - 1] == ']' ||
- tok.text[strlen(tok.text) - 1] == ')')) {
- is_metadata = false;
+ if (is_metadata) {
+ if (tok_str.ends_with("]") ||
+ tok_str.ends_with(")")) {
+ is_metadata = false;
+ }
+ continue;
+ }
+ if (tok_str.ends_with("BLANK_AUDIO")) {
continue;
}
Log(out, "{}", tok.text);
@@ -379,9 +337,10 @@ void WhisperCPP::Start(const AppConfig& c) {
wparams.new_segment_callback_user_data = out_;
sCaptureCallbacks callbacks{};
+
callbacks.shouldCancel = [](void* pv) noexcept -> HRESULT __stdcall {
WhisperCPP* app = static_cast<WhisperCPP*>(pv);
- if (app->proc_->TestDestroy() || !app->run_) {
+ if (!app->run_) {
Log(app->out_, "Exit transcription loop\n");
return S_FALSE;
}
@@ -391,7 +350,6 @@ void WhisperCPP::Start(const AppConfig& c) {
if (++i % 20 == 0) {
Log(app->out_, "Spin {}\n", i);
}
- wxThread::Sleep(10);
return S_OK;
};
callbacks.pv = this;
@@ -402,11 +360,18 @@ void WhisperCPP::Start(const AppConfig& c) {
Log(out_, "Capture failed: {}\n", hresultToString(err));
return;
}
+#else
+ while (run_) {
+ static int i = 0;
+ if (++i % 100 == 0) {
+ Log(out_, "Spin {}\n", i);
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+#endif
Log(out_, "Exit transcription engine\n");
- }, this);
-
- proc_->Run();
+ });
Log(out_, "Success!\n");
return;
@@ -415,6 +380,8 @@ void WhisperCPP::Start(const AppConfig& c) {
void WhisperCPP::Stop() {
Log(out_, "Stopping transcription engine...\n");
run_ = false;
+ proc_.wait();
+ Log(out_, "Done!\n");
}
bool WhisperCPP::GetMicsImpl(std::vector<sCaptureDevice>& mics) {
diff --git a/GUI/GUI/GUI/WhisperCPP.h b/GUI/GUI/GUI/WhisperCPP.h
index 20b0106..ed934be 100644
--- a/GUI/GUI/GUI/WhisperCPP.h
+++ b/GUI/GUI/GUI/WhisperCPP.h
@@ -2,8 +2,6 @@
#include <wx/filepicker.h>
#include <wx/wxprec.h>
-#include <wx/process.h>
-#include <wx/thread.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
@@ -19,6 +17,7 @@
#include <filesystem>
#include <functional>
+#include <future>
#include <string>
#include <vector>
@@ -30,9 +29,9 @@ public:
bool Init();
bool GetMics(std::vector<std::string>& mics);
bool OpenMic(const int idx, Whisper::iAudioCapture*& stream);
- bool InstallDependencies(wxProcess*& proc);
+ bool InstallDependencies();
bool DownloadModel(const std::string& model_name,
- const std::filesystem::path& fs_path, wxProcess*& proc);
+ const std::filesystem::path& fs_path);
bool LoadModel(const std::string& path, Whisper::iModel*& model);
bool CreateContext(Whisper::iModel* model, Whisper::iContext*& context);
@@ -42,22 +41,9 @@ public:
private:
bool GetMicsImpl(std::vector<Whisper::sCaptureDevice>& mics);
- class AppThread : public wxThread {
- public:
- AppThread(const std::function<void(AppThread* thd)>&& cb, WhisperCPP* app);
-
- virtual ~AppThread();
-
- virtual void* Entry() wxOVERRIDE;
-
- private:
- const std::function<void(AppThread* thd)> cb_;
- WhisperCPP* app_;
- };
-
wxTextCtrl* out_;
Whisper::iMediaFoundation* f_;
bool did_init_;
- AppThread* volatile proc_;
+ std::future<void> proc_;
volatile bool run_;
};
diff --git a/GUI/Libraries/.gitignore b/GUI/Libraries/.gitignore
index 0e14a69..26be5d9 100644
--- a/GUI/Libraries/.gitignore
+++ b/GUI/Libraries/.gitignore
@@ -2,4 +2,4 @@
wx
rapidyaml
whisper
-
+oatpp
diff --git a/GUI/Libraries/fetch.ps1 b/GUI/Libraries/fetch.ps1
index f13bad5..218d48b 100644
--- a/GUI/Libraries/fetch.ps1
+++ b/GUI/Libraries/fetch.ps1
@@ -12,6 +12,15 @@ $WHISPER_1_7_0_URL = "https://github.com/Const-me/Whisper/releases/download/1.7.
$WHISPER_URL = $WHISPER_1_7_0_URL
$WHISPER_FILE = $(Split-Path -Path $WHISPER_URL -Leaf)
+$OATPP_1_3_0_URL = "https://github.com/oatpp/oatpp/archive/refs/tags/1.3.0.zip"
+$OATPP_URL = $OATPP_1_3_0_URL
+$OATPP_FILE = $(Split-Path -Path $OATPP_URL -Leaf)
+$OATPP_VER = $OATPP_FILE -replace '\.[a-z\.]*$'
+$OATPP_DIR = "oatpp-$OATPP_VER"
+
+$NPROC = $(Get-CimInstance Win32_Processor).NumberOfCores
+echo "nproc: $NPROC"
+
pushd $PSScriptRoot
# WX
@@ -61,6 +70,28 @@ if (-Not (Test-Path whisper)) {
popd > $null
}
+if ((Test-Path oatpp) -And ($overwrite)) {
+ rm -Recurse oatpp
+}
+
+if (-Not (Test-Path oatpp)) {
+ mkdir oatpp
+ pushd oatpp > $null
+ Invoke-WebRequest $OATPP_URL -OutFile $OATPP_FILE
+ Expand-Archive $OATPP_FILE -DestinationPath .
+ if (Test-Path ../../GUI/GUI/oatpp/) {
+ rm -Recurse ../../GUI/GUI/oatpp/
+ }
+ mkdir ../../GUI/GUI/oatpp/
+ pushd $OATPP_DIR > $null
+ mkdir build
+ pushd build > $null
+ cmake.exe .. -DCMAKE_BUILD_TYPE=Release -DOATPP_BUILD_TESTS=OFF
+ cmake.exe --build . -j $NPROC --config Release
+ popd > $null
+ popd > $null
+}
+
popd > $null # rapidyaml
popd > $null # $PSScriptRoot
diff --git a/GUI/README.md b/GUI/README.md
index d2fa999..ea28124 100644
--- a/GUI/README.md
+++ b/GUI/README.md
@@ -1,6 +1,6 @@
## Build instructions
-0. Install build dependencies: git, python3, Visual Studio 2022
+0. Install build dependencies: cmake, git, python3, Visual Studio 2022
1. Open Powershell.
2. Make sure you've downloaded submodules:
```