From 06160c37acb26cfac9bab568bd3759c2386fb175 Mon Sep 17 00:00:00 2001 From: yum Date: Sun, 22 Jan 2023 15:28:12 -0800 Subject: GUI: Save Unity input fields across app restarts I found that I tend to regenerate the animator on the same avatar a lot, requiring me to re-enter the same paths and parameters over and over again. Persist them across restarts. * Refactor Config classes * Use safe `get_if` instead of the exception-throwing `operator>>` when deserializing from YAML * Begin sketching out Log singleton * Put Quote() and Unquote() into their own little lib; they shouldn't hide inside PythonApp --- GUI/GUI/GUI/Config.cpp | 161 ++++++++++++++++++++++++++++++---------- GUI/GUI/GUI/Config.h | 53 ++++++++++++- GUI/GUI/GUI/Frame.cpp | 115 +++++++++++++++------------- GUI/GUI/GUI/GUI.vcxproj | 1 + GUI/GUI/GUI/GUI.vcxproj.filters | 3 + GUI/GUI/GUI/Logging.h | 20 +++++ GUI/GUI/GUI/PythonWrapper.cpp | 55 ++++++-------- GUI/GUI/GUI/PythonWrapper.h | 9 +-- GUI/GUI/GUI/Util.h | 20 +++++ 9 files changed, 302 insertions(+), 135 deletions(-) create mode 100644 GUI/GUI/GUI/Util.h (limited to 'GUI') diff --git a/GUI/GUI/GUI/Config.cpp b/GUI/GUI/GUI/Config.cpp index 0436f3b..9ce498a 100644 --- a/GUI/GUI/GUI/Config.cpp +++ b/GUI/GUI/GUI/Config.cpp @@ -4,42 +4,17 @@ #include #endif -#include "Config.h" - #define RYML_SINGLE_HDR_DEFINE_NOW #include "ryml.h" +#include "Config.h" + #include #include #include -TranscriptionAppConfig::TranscriptionAppConfig() - : microphone("index"), - language("english"), - model("base.en"), - chars_per_sync("20"), - bytes_per_char("1"), - rows("4"), - cols("48"), - window_duration("15"), - enable_local_beep(true), - use_cpu(false) -{} - -bool TranscriptionAppConfig::Serialize(const std::filesystem::path& path) { - ryml::Tree t; - ryml::NodeRef root = t.rootref(); - root |= ryml::MAP; - root["microphone"] << ryml::to_substr(microphone); - root["language"] << ryml::to_substr(language); - root["model"] << ryml::to_substr(model); - root["chars_per_sync"] << ryml::to_substr(chars_per_sync); - root["bytes_per_char"] << ryml::to_substr(bytes_per_char); - root["rows"] << ryml::to_substr(rows); - root["cols"] << ryml::to_substr(cols); - root["window_duration"] << ryml::to_substr(window_duration); - root["enable_local_beep"] << enable_local_beep; - root["use_cpu"] << use_cpu; +bool Config::Serialize(const std::filesystem::path& path, + const ryml::Tree* const t) { // Write the config to a tmp file. If we crash in the middle of this, it // doesn't matter, since the next process will just overwrite it. @@ -75,7 +50,8 @@ bool TranscriptionAppConfig::Serialize(const std::filesystem::path& path) { return true; } -bool TranscriptionAppConfig::Deserialize(const std::filesystem::path& path) { +bool Config::Deserialize(const std::filesystem::path& path, + ryml::Tree* t) { std::ifstream file(path, std::ios::binary | std::ios::ate); if (!file.is_open()) { return false; @@ -87,19 +63,124 @@ bool TranscriptionAppConfig::Deserialize(const std::filesystem::path& path) { return false; } - ryml::Tree t = ryml::parse_in_place(ryml::to_substr(yaml_buf.data())); + *t = ryml::parse_in_place(ryml::to_substr(yaml_buf.data())); + return true; +} + +TranscriptionAppConfig::TranscriptionAppConfig() + : microphone("index"), + language("english"), + model("base.en"), + chars_per_sync("20"), + bytes_per_char("1"), + rows("4"), + cols("48"), + window_duration("15"), + enable_local_beep(true), + use_cpu(false) +{} + +bool TranscriptionAppConfig::Serialize(const std::filesystem::path& path) { + ryml::Tree t; + ryml::NodeRef root = t.rootref(); + root |= ryml::MAP; + root["microphone"] << ryml::to_substr(microphone); + root["language"] << ryml::to_substr(language); + root["model"] << ryml::to_substr(model); + root["chars_per_sync"] << ryml::to_substr(chars_per_sync); + root["bytes_per_char"] << ryml::to_substr(bytes_per_char); + root["rows"] << ryml::to_substr(rows); + root["cols"] << ryml::to_substr(cols); + root["window_duration"] << ryml::to_substr(window_duration); + root["enable_local_beep"] << enable_local_beep; + root["use_cpu"] << use_cpu; + + return Config::Serialize(path, &t); +} + +bool TranscriptionAppConfig::Deserialize(const std::filesystem::path& path) { + std::error_code err; + if (!std::filesystem::exists(path, err)) { + *this = TranscriptionAppConfig(); + return true; + } + + ryml::Tree t{}; + if (!Config::Deserialize(path, &t)) { + wxLogError("Deserialization failed at %s", path.string()); + return false; + } + ryml::ConstNodeRef root = t.rootref(); TranscriptionAppConfig c; - root["microphone"] >> c.microphone; - root["language"] >> c.language; - root["model"] >> c.model; - root["chars_per_sync"] >> c.chars_per_sync; - root["bytes_per_char"] >> c.bytes_per_char; - root["rows"] >> c.rows; - root["cols"] >> c.cols; - root["window_duration"] >> c.window_duration; - root["enable_local_beep"] >> c.enable_local_beep; + root.get_if("microphone", &c.microphone); + root.get_if("language", &c.language); + root.get_if("model", &c.model); + root.get_if("chars_per_sync", &c.chars_per_sync); + root.get_if("bytes_per_char", &c.bytes_per_char); + root.get_if("rows", &c.rows); + root.get_if("cols", &c.cols); + root.get_if("window_duration", &c.window_duration); + root.get_if("enable_local_beep", &c.enable_local_beep); + root.get_if("use_cpu", &c.use_cpu); *this = std::move(c); return true; } + +UnityAppConfig::UnityAppConfig() + : assets_path(), + fx_path(), + params_path(), + menu_path(), + chars_per_sync(20), + bytes_per_char(1), + rows(4), + cols(48) +{} + +bool UnityAppConfig::Serialize(const std::filesystem::path& path) { + ryml::Tree t; + ryml::NodeRef root = t.rootref(); + root |= ryml::MAP; + root["assets_path"] << ryml::to_substr(assets_path); + root["fx_path"] << ryml::to_substr(fx_path); + root["params_path"] << ryml::to_substr(params_path); + root["menu_path"] << ryml::to_substr(menu_path); + root["chars_per_sync"] << chars_per_sync; + root["bytes_per_char"] << bytes_per_char; + root["rows"] << rows; + root["cols"] << cols; + + return Config::Serialize(path, &t); +} + +bool UnityAppConfig::Deserialize(const std::filesystem::path& path) { + std::error_code err; + if (!std::filesystem::exists(path, err)) { + *this = UnityAppConfig(); + return true; + } + + ryml::Tree t; + if (!Config::Deserialize(path, &t)) { + return false; + } + + ryml::ConstNodeRef root = t.rootref(); + UnityAppConfig c; + + root.get_if("chars_per_sync", &c.chars_per_sync); + root.get_if("bytes_per_char", &c.bytes_per_char); + root.get_if("rows", &c.rows); + root.get_if("cols", &c.cols); + + root.get_if("assets_path", &c.assets_path); + root.get_if("fx_path", &c.fx_path); + root.get_if("params_path", &c.params_path); + root.get_if("menu_path", &c.menu_path); + + *this = std::move(c); + return true; +} + diff --git a/GUI/GUI/GUI/Config.h b/GUI/GUI/GUI/Config.h index e142773..985380b 100644 --- a/GUI/GUI/GUI/Config.h +++ b/GUI/GUI/GUI/Config.h @@ -1,14 +1,37 @@ #pragma once +#include "ryml.h" + #include -class TranscriptionAppConfig { +// Represents a disk-backed configuration. Knows how to save to disk +// (Serialize) and restore from disk (Deserialize). +class Config { public: + virtual ~Config() {} + + virtual bool Serialize(const std::filesystem::path& path) = 0; + + virtual bool Deserialize(const std::filesystem::path& path) = 0; + +protected: + virtual bool Serialize(const std::filesystem::path& path, + const ryml::Tree* t); + + virtual bool Deserialize(const std::filesystem::path& path, + ryml::Tree* t); +}; + +// Represents the configurable fields for the transcription app. +class TranscriptionAppConfig : public Config { +public: + virtual ~TranscriptionAppConfig() {} + TranscriptionAppConfig(); - bool Serialize(const std::filesystem::path& path); + bool Serialize(const std::filesystem::path& path) override; - bool Deserialize(const std::filesystem::path& path); + bool Deserialize(const std::filesystem::path& path) override; // The default path at which configs are serialized. static constexpr char kConfigPath[] = "Resources/transcription_app_config.yml"; @@ -24,3 +47,27 @@ public: bool enable_local_beep; bool use_cpu; }; + +// Represents the configurable fields for the Unity app. +class UnityAppConfig : public Config { +public: + virtual ~UnityAppConfig() {} + + UnityAppConfig(); + + bool Serialize(const std::filesystem::path& path) override; + + bool Deserialize(const std::filesystem::path& path) override; + + // The default path at which configs are serialized. + static constexpr char kConfigPath[] = "Resources/unity_app_config.yml"; + + std::string assets_path; + std::string fx_path; + std::string params_path; + std::string menu_path; + int chars_per_sync; + int bytes_per_char; + int rows; + int cols; +}; diff --git a/GUI/GUI/GUI/Frame.cpp b/GUI/GUI/GUI/Frame.cpp index 6c1f356..f26cbec 100644 --- a/GUI/GUI/GUI/Frame.cpp +++ b/GUI/GUI/GUI/Frame.cpp @@ -251,8 +251,11 @@ Frame::Frame() py_app_(nullptr), py_app_drain_(this, ID_PY_APP_DRAIN) { - TranscriptionAppConfig c; - c.Deserialize(TranscriptionAppConfig::kConfigPath); + TranscriptionAppConfig py_c; + py_c.Deserialize(TranscriptionAppConfig::kConfigPath); + + UnityAppConfig unity_c; + unity_c.Deserialize(UnityAppConfig::kConfigPath); auto* main_panel = new wxPanel(this, ID_MAIN_PANEL); main_panel_ = main_panel; @@ -301,7 +304,7 @@ Frame::Frame() { auto* py_app_mic = new wxChoice(py_app_config_panel_pairs, ID_PY_APP_MIC, wxDefaultPosition, wxDefaultSize, kNumMicChoices, kMicChoices); - int mic_idx = GetDropdownChoiceIndex(kMicChoices, kNumMicChoices, c.microphone, kMicDefault); + int mic_idx = GetDropdownChoiceIndex(kMicChoices, kNumMicChoices, py_c.microphone, kMicDefault); py_app_mic->SetSelection(mic_idx); py_app_mic->SetToolTip( "Select which microphone to listen to when " @@ -311,7 +314,7 @@ Frame::Frame() auto* py_app_lang = new wxChoice(py_app_config_panel_pairs, ID_PY_APP_LANG, wxDefaultPosition, wxDefaultSize, kNumLangChoices, kLangChoices); - int lang_idx = GetDropdownChoiceIndex(kLangChoices, kNumLangChoices, c.language, kLangDefault); + int lang_idx = GetDropdownChoiceIndex(kLangChoices, kNumLangChoices, py_c.language, kLangDefault); py_app_lang->SetSelection(lang_idx); py_app_lang->SetToolTip("Select which language you will " "speak in. It will be transcribed into that language. " @@ -323,7 +326,7 @@ Frame::Frame() auto* py_app_model = new wxChoice(py_app_config_panel_pairs, ID_PY_APP_MODEL, wxDefaultPosition, wxDefaultSize, kNumModelChoices, kModelChoices); - int model_idx = GetDropdownChoiceIndex(kModelChoices, kNumModelChoices, c.model, kModelDefault); + int model_idx = GetDropdownChoiceIndex(kModelChoices, kNumModelChoices, py_c.model, kModelDefault); py_app_model->SetSelection(model_idx); py_app_model->SetToolTip("Select which version of " "the transcription model to use. 'base' is a good " @@ -336,7 +339,7 @@ Frame::Frame() auto* py_app_chars_per_sync = new wxChoice(py_app_config_panel_pairs, ID_PY_APP_CHARS_PER_SYNC, wxDefaultPosition, wxDefaultSize, kNumCharsPerSync, kCharsPerSync); - int chars_idx = GetDropdownChoiceIndex(kCharsPerSync, kNumCharsPerSync, c.chars_per_sync, kCharsDefault); + int chars_idx = GetDropdownChoiceIndex(kCharsPerSync, kNumCharsPerSync, py_c.chars_per_sync, kCharsDefault); py_app_chars_per_sync->SetSelection(chars_idx); py_app_chars_per_sync->SetToolTip( "VRChat syncs avatar parameters roughly 5 times per " @@ -348,7 +351,7 @@ Frame::Frame() auto* py_app_bytes_per_char = new wxChoice(py_app_config_panel_pairs, ID_PY_APP_BYTES_PER_CHAR, wxDefaultPosition, wxDefaultSize, kNumBytesPerChar, kBytesPerChar); - int bytes_idx = GetDropdownChoiceIndex(kBytesPerChar, kNumBytesPerChar, c.bytes_per_char, kBytesDefault); + int bytes_idx = GetDropdownChoiceIndex(kBytesPerChar, kNumBytesPerChar, py_c.bytes_per_char, kBytesDefault); py_app_bytes_per_char->SetSelection(bytes_idx); py_app_bytes_per_char->SetToolTip( "If you speak a language that uses non-ASCII " @@ -356,21 +359,21 @@ Frame::Frame() py_app_bytes_per_char_ = py_app_bytes_per_char; auto* py_app_rows = new wxTextCtrl(py_app_config_panel_pairs, - ID_PY_APP_ROWS, c.rows, + ID_PY_APP_ROWS, py_c.rows, wxDefaultPosition, wxDefaultSize, /*style=*/0); py_app_rows->SetToolTip( "The number of rows on the text box."); py_app_rows_ = py_app_rows; auto* py_app_cols = new wxTextCtrl(py_app_config_panel_pairs, - ID_PY_APP_COLS, c.cols, + ID_PY_APP_COLS, py_c.cols, wxDefaultPosition, wxDefaultSize, /*style=*/0); py_app_cols->SetToolTip( "The number of columns on the text box."); py_app_cols_ = py_app_cols; auto* py_app_window_duration = new wxTextCtrl(py_app_config_panel_pairs, - ID_PY_APP_WINDOW_DURATION, c.window_duration, + ID_PY_APP_WINDOW_DURATION, py_c.window_duration, wxDefaultPosition, wxDefaultSize, /*style=*/0); py_app_window_duration->SetToolTip( "This controls how long the slice of audio that " @@ -411,7 +414,7 @@ Frame::Frame() auto* py_app_enable_local_beep = new wxCheckBox(py_config_panel, ID_PY_APP_ENABLE_LOCAL_BEEP, "Enable local beep"); - py_app_enable_local_beep->SetValue(c.enable_local_beep); + py_app_enable_local_beep->SetValue(py_c.enable_local_beep); py_app_enable_local_beep->SetToolTip( "By default, TaSTT will play a sound (audible only to " "you) when it begins transcription and when it stops. " @@ -421,7 +424,7 @@ Frame::Frame() auto* py_app_use_cpu = new wxCheckBox(py_config_panel, ID_PY_APP_USE_CPU, "Use CPU"); - py_app_use_cpu->SetValue(c.use_cpu); + py_app_use_cpu->SetValue(py_c.use_cpu); py_app_use_cpu->SetToolTip( "If checked, the transcription engine will run on your " "CPU instead of your GPU. This is typically much slower " @@ -468,19 +471,19 @@ Frame::Frame() auto* unity_assets_file_picker = new wxDirPickerCtrl( unity_config_panel_pairs, ID_UNITY_ASSETS_FILE_PICKER, - /*path=*/wxEmptyString, + /*path=*/unity_c.assets_path, /*message=*/"Unity Assets folder" ); unity_assets_file_picker->SetToolTip( "The path to the Assets folder for your avatar's " "Unity project. Example:\n" - "C:\\Users\\yum\\unity\\kumadan\\Assets"); + "py_c:\\Users\\yum\\unity\\kumadan\\Assets"); unity_assets_file_picker_ = unity_assets_file_picker; auto* unity_animator_file_picker = new wxFilePickerCtrl( unity_config_panel_pairs, ID_UNITY_ANIMATOR_FILE_PICKER, - /*path=*/wxEmptyString, + /*path=*/unity_c.fx_path, /*message=*/"FX controller path", /*wildcard=*/wxFileSelectorDefaultWildcardStr, /*pos=*/wxDefaultPosition, @@ -489,13 +492,13 @@ Frame::Frame() unity_animator_file_picker->SetToolTip( "The path to your avatar's FX layer. You can find " "this in your avatar descriptor. Example:\n" - "C:\\Users\\yum\\unity\\kumadan\\Assets\\kumadan_fx.controller"); + "py_c:\\Users\\yum\\unity\\kumadan\\Assets\\kumadan_fx.controller"); unity_animator_file_picker_ = unity_animator_file_picker; auto* unity_parameters_file_picker = new wxFilePickerCtrl( unity_config_panel_pairs, ID_UNITY_PARAMETERS_FILE_PICKER, - /*path=*/wxEmptyString, + /*path=*/unity_c.params_path, /*message=*/"Avatar parameters path", /*wildcard=*/wxFileSelectorDefaultWildcardStr, /*pos=*/wxDefaultPosition, @@ -504,13 +507,13 @@ Frame::Frame() unity_parameters_file_picker->SetToolTip( "The path to your avatar's parameters. You can find " "this in your avatar descriptor. Example:\n" - "C:\\Users\\yum\\unity\\kumadan\\Assets\\kumadan_parameters.asset"); + "py_c:\\Users\\yum\\unity\\kumadan\\Assets\\kumadan_parameters.asset"); unity_parameters_file_picker_ = unity_parameters_file_picker; auto* unity_menu_file_picker = new wxFilePickerCtrl( unity_config_panel_pairs, ID_UNITY_MENU_FILE_PICKER, - /*path=*/wxEmptyString, + /*path=*/unity_c.menu_path, /*message=*/"Avatar menu path", /*wildcard=*/wxFileSelectorDefaultWildcardStr, /*pos=*/wxDefaultPosition, @@ -519,7 +522,7 @@ Frame::Frame() unity_menu_file_picker->SetToolTip( "The path to your avatar's menu. You can find " "this in your avatar descriptor. Example:\n" - "C:\\Users\\yum\\unity\\kumadan\\Assets\\kumadan_menu.asset"); + "py_c:\\Users\\yum\\unity\\kumadan\\Assets\\kumadan_menu.asset"); unity_menu_file_picker_ = unity_menu_file_picker; auto* unity_animator_generated_dir = new wxTextCtrl(unity_config_panel_pairs, @@ -574,7 +577,9 @@ Frame::Frame() auto* unity_chars_per_sync = new wxChoice(unity_config_panel_pairs, ID_UNITY_CHARS_PER_SYNC, wxDefaultPosition, wxDefaultSize, kNumCharsPerSync, kCharsPerSync); - unity_chars_per_sync->SetSelection(kCharsDefault); + int chars_idx = GetDropdownChoiceIndex(kCharsPerSync, kNumCharsPerSync, + std::to_string(unity_c.chars_per_sync), kCharsDefault); + unity_chars_per_sync->SetSelection(chars_idx); unity_chars_per_sync->SetToolTip( "VRChat syncs avatar parameters roughly 5 times per " "second. We use this to send text to the box. By " @@ -585,21 +590,23 @@ Frame::Frame() auto* unity_bytes_per_char = new wxChoice(unity_config_panel_pairs, ID_UNITY_BYTES_PER_CHAR, wxDefaultPosition, wxDefaultSize, kNumBytesPerChar, kBytesPerChar); - unity_bytes_per_char->SetSelection(kBytesDefault); + int bytes_idx = GetDropdownChoiceIndex(kBytesPerChar, + kNumBytesPerChar, std::to_string(unity_c.bytes_per_char), kBytesDefault); + unity_bytes_per_char->SetSelection(bytes_idx); unity_bytes_per_char->SetToolTip( "If you speak a language that uses non-ASCII " "characters (i.e. not English), set this to 2."); unity_bytes_per_char_ = unity_bytes_per_char; auto* unity_rows = new wxTextCtrl(unity_config_panel_pairs, - ID_UNITY_ROWS, /*value=*/"4", + ID_UNITY_ROWS, std::to_string(unity_c.rows), wxDefaultPosition, wxDefaultSize, /*style=*/0); unity_rows->SetToolTip( "The number of rows on the text box."); unity_rows_ = unity_rows; auto* unity_cols = new wxTextCtrl(unity_config_panel_pairs, - ID_UNITY_COLS, /*value=*/"48", + ID_UNITY_COLS, std::to_string(unity_c.cols), wxDefaultPosition, wxDefaultSize, /*style=*/0); unity_cols->SetToolTip( "The number of columns on the text box."); @@ -797,22 +804,26 @@ void Frame::OnGenerateFX(wxCommandEvent& event) if (chars_per_sync_idx == wxNOT_FOUND) { chars_per_sync_idx = kCharsDefault; } - std::string chars_per_sync = kCharsPerSync[chars_per_sync_idx].ToStdString(); + std::string chars_per_sync_str = kCharsPerSync[chars_per_sync_idx].ToStdString(); int bytes_per_char_idx = unity_bytes_per_char_->GetSelection(); if (bytes_per_char_idx == wxNOT_FOUND) { bytes_per_char_idx = kBytesDefault; } - std::string bytes_per_char = kBytesPerChar[bytes_per_char_idx].ToStdString(); + std::string bytes_per_char_str = kBytesPerChar[bytes_per_char_idx].ToStdString(); std::string rows_str = unity_rows_->GetValue().ToStdString(); std::string cols_str = unity_cols_->GetValue().ToStdString(); - int rows, cols; + int rows, cols, bytes_per_char, chars_per_sync; try { rows = std::stoi(rows_str); cols = std::stoi(cols_str); + bytes_per_char = std::stoi(bytes_per_char_str); + chars_per_sync = std::stoi(chars_per_sync_str); } catch (const std::invalid_argument&) { - Log(unity_out_, "Could not parse rows \"{}\" or cols \"{}\" as an integer\n", rows_str, cols_str); + Log(unity_out_, "Could not parse rows \"{}\", cols \"{}\", bytes per " + "char \"{}\", or chars per sync \"{}\" as an integer\n", + rows_str, cols_str, bytes_per_char_str, chars_per_sync_str); return; } catch (const std::out_of_range&) { @@ -820,20 +831,24 @@ void Frame::OnGenerateFX(wxCommandEvent& event) return; } + UnityAppConfig unity_c; + unity_c.assets_path = unity_assets_path.string(); + unity_c.fx_path = unity_animator_path.string(); + unity_c.params_path = unity_parameters_path.string(); + unity_c.menu_path = unity_menu_path.string(); + unity_c.bytes_per_char = chars_per_sync; + unity_c.chars_per_sync = chars_per_sync; + unity_c.rows = rows; + unity_c.cols = cols; + unity_c.Serialize(UnityAppConfig::kConfigPath); + std::string out; if (!PythonWrapper::GenerateAnimator( - unity_assets_path, - unity_animator_path, - unity_parameters_path, - unity_menu_path, + unity_c, unity_animator_generated_dir, unity_animator_generated_name, unity_parameters_generated_name, unity_menu_generated_name, - chars_per_sync, - bytes_per_char, - rows, - cols, unity_out_)) { wxLogError("Failed to generate animator:\n%s\n", out.c_str()); } @@ -952,20 +967,20 @@ void Frame::OnAppStart(wxCommandEvent& event) { return; } - TranscriptionAppConfig c; - c.microphone = kMicChoices[which_mic].ToStdString(); - c.language = kLangChoices[which_lang].ToStdString(); - c.model = kModelChoices[which_model].ToStdString(); - c.chars_per_sync = kCharsPerSync[chars_per_sync_idx].ToStdString(); - c.bytes_per_char = kBytesPerChar[bytes_per_char_idx].ToStdString(); - c.rows = std::to_string(rows); - c.cols = std::to_string(cols); - c.window_duration = std::to_string(window_duration); - c.enable_local_beep = enable_local_beep; - c.use_cpu = use_cpu; - c.Serialize(TranscriptionAppConfig::kConfigPath); - - wxProcess* p = PythonWrapper::StartApp(std::move(cb), c); + TranscriptionAppConfig py_c; + py_c.microphone = kMicChoices[which_mic].ToStdString(); + py_c.language = kLangChoices[which_lang].ToStdString(); + py_c.model = kModelChoices[which_model].ToStdString(); + py_c.chars_per_sync = kCharsPerSync[chars_per_sync_idx].ToStdString(); + py_c.bytes_per_char = kBytesPerChar[bytes_per_char_idx].ToStdString(); + py_c.rows = std::to_string(rows); + py_c.cols = std::to_string(cols); + py_c.window_duration = std::to_string(window_duration); + py_c.enable_local_beep = enable_local_beep; + py_c.use_cpu = use_cpu; + py_c.Serialize(TranscriptionAppConfig::kConfigPath); + + wxProcess* p = PythonWrapper::StartApp(std::move(cb), py_c); if (!p) { Log(transcribe_out_, "Failed to launch transcription engine\n"); return; diff --git a/GUI/GUI/GUI/GUI.vcxproj b/GUI/GUI/GUI/GUI.vcxproj index cbe3a92..23f3d88 100644 --- a/GUI/GUI/GUI/GUI.vcxproj +++ b/GUI/GUI/GUI/GUI.vcxproj @@ -155,6 +155,7 @@ + diff --git a/GUI/GUI/GUI/GUI.vcxproj.filters b/GUI/GUI/GUI/GUI.vcxproj.filters index 3fa31c7..2798d1e 100644 --- a/GUI/GUI/GUI/GUI.vcxproj.filters +++ b/GUI/GUI/GUI/GUI.vcxproj.filters @@ -59,6 +59,9 @@ Header Files + + Header Files + diff --git a/GUI/GUI/GUI/Logging.h b/GUI/GUI/GUI/Logging.h index 6c1bf16..9fb88fd 100644 --- a/GUI/GUI/GUI/Logging.h +++ b/GUI/GUI/GUI/Logging.h @@ -13,6 +13,26 @@ #include namespace Logging { + +#if 0 + class Log { + public: + static Log& Get() { + static Log l; + return l; + } + + bool Write(const std::string& text); + + private: + Log() {} + + bool Open(const std::string& path); + + int fd_; + }; +#endif + // Remove personally identifying information (PII) from str. // // For example, this translates "C:/Users/foo/Desktop" to "C:/Users/*****/Desktop". diff --git a/GUI/GUI/GUI/PythonWrapper.cpp b/GUI/GUI/GUI/PythonWrapper.cpp index 0c43fa4..5581739 100644 --- a/GUI/GUI/GUI/PythonWrapper.cpp +++ b/GUI/GUI/GUI/PythonWrapper.cpp @@ -1,5 +1,6 @@ #include "Logging.h" #include "PythonWrapper.h" +#include "Util.h" #include "Config.h" @@ -162,26 +163,12 @@ wxProcess* PythonWrapper::StartApp( std::move(exit_callback)); } -// Wrap the filesystem path in quotes, escaping intermediate quotes with \\. -std::string Quote(const std::filesystem::path& p) { - std::ostringstream oss; - oss << std::quoted(p.string()); - return oss.str(); -} - bool PythonWrapper::GenerateAnimator( - const std::filesystem::path& unity_assets_path, - const std::filesystem::path& unity_animator_path, - const std::filesystem::path& unity_parameters_path, - const std::filesystem::path& unity_menu_path, + const UnityAppConfig& config, const std::string& unity_animator_generated_dir, const std::string& unity_animator_generated_name, const std::string& unity_parameters_generated_name, const std::string& unity_menu_generated_name, - const std::string& chars_per_sync, - const std::string& bytes_per_char, - const int rows, - const int cols, wxTextCtrl* out) { // Python script locations std::string libunity_path = "Resources/Scripts/libunity.py"; @@ -194,7 +181,7 @@ bool PythonWrapper::GenerateAnimator( // Generated directory locations std::filesystem::path tastt_generated_dir_path = - std::filesystem::path(unity_assets_path) / unity_animator_generated_dir; + std::filesystem::path(config.assets_path) / unity_animator_generated_dir; std::filesystem::path guid_map_path = tastt_generated_dir_path / "guid.map"; std::filesystem::path tastt_animations_path = @@ -222,13 +209,13 @@ bool PythonWrapper::GenerateAnimator( tastt_generated_dir_path / unity_animator_generated_name; { - Log(out, "Generating shader for {}x{} board...", rows, cols); + Log(out, "Generating shader for {}x{} board...", config.rows, config.cols); std::string py_stdout, py_stderr; if (InvokeWithArgs({ generate_shader_path, - "--bytes_per_char", bytes_per_char, - "--rows", std::to_string(rows), - "--cols", std::to_string(cols), + "--bytes_per_char", std::to_string(config.bytes_per_char), + "--rows", std::to_string(config.rows), + "--cols", std::to_string(config.cols), "--shader_template", shader_template_path, "--shader_path", shader_path }, &py_stdout, &py_stderr)) { @@ -312,7 +299,7 @@ bool PythonWrapper::GenerateAnimator( Log(out, "Generating guid.map... "); std::string py_stdout, py_stderr; if (PythonWrapper::InvokeWithArgs({ libunity_path, "guid_map", - "--project_root", Quote(unity_assets_path), + "--project_root", Quote(config.assets_path), "--save_to", Quote(guid_map_path), }, &py_stdout, &py_stderr)) { Log(out, "success!\n"); @@ -337,10 +324,10 @@ bool PythonWrapper::GenerateAnimator( if (InvokeWithArgs({ libtastt_path, "gen_anims", "--gen_anim_dir", Quote(tastt_animations_path), "--guid_map", Quote(guid_map_path), - "--chars_per_sync", chars_per_sync, - "--bytes_per_char", bytes_per_char, - "--rows", std::to_string(rows), - "--cols", std::to_string(cols)}, + "--chars_per_sync", std::to_string(config.chars_per_sync), + "--bytes_per_char", std::to_string(config.bytes_per_char), + "--rows", std::to_string(config.rows), + "--cols", std::to_string(config.cols)}, &py_stdout, &py_stderr)) { Log(out, "success!\n"); Log(out, py_stdout.c_str()); @@ -365,10 +352,10 @@ bool PythonWrapper::GenerateAnimator( "--fx_dest", Quote(tastt_fx0_path), "--gen_anim_dir", Quote(tastt_animations_path), "--guid_map", Quote(guid_map_path), - "--chars_per_sync", chars_per_sync, - "--bytes_per_char", bytes_per_char, - "--rows", std::to_string(rows), - "--cols", std::to_string(cols)}, + "--chars_per_sync", std::to_string(config.chars_per_sync), + "--bytes_per_char", std::to_string(config.bytes_per_char), + "--rows", std::to_string(config.rows), + "--cols", std::to_string(config.cols)}, &py_stdout, &py_stderr)) { Log(out, "success!\n"); Log(out, py_stdout.c_str()); @@ -415,7 +402,7 @@ bool PythonWrapper::GenerateAnimator( Log(out, "Merging with user animator... "); std::string py_stdout, py_stderr; if (InvokeWithArgs({ libunity_path, "merge", - "--fx0", Quote(unity_animator_path), + "--fx0", Quote(config.fx_path), "--fx1", Quote(tastt_fx1_path), "--fx_dest", Quote(tastt_fx2_path), }, &py_stdout, &py_stderr)) { @@ -464,10 +451,10 @@ bool PythonWrapper::GenerateAnimator( Log(out, "Generating avatar parameters... "); std::string py_stdout, py_stderr; if (InvokeWithArgs({ generate_params_path, - "--old_params", Quote(unity_parameters_path), + "--old_params", Quote(config.params_path), "--new_params", Quote(tastt_params_path), - "--chars_per_sync", chars_per_sync, - "--bytes_per_char", bytes_per_char }, + "--chars_per_sync", std::to_string(config.chars_per_sync), + "--bytes_per_char", std::to_string(config.bytes_per_char) }, &py_stdout, &py_stderr)) { Log(out, "success!\n"); Log(out, py_stdout.c_str()); @@ -490,7 +477,7 @@ bool PythonWrapper::GenerateAnimator( std::string py_stdout, py_stderr; // No idea why, but inlining this into `InvokeWithArgs` confuses the compiler. std::vector args = { generate_menu_path, - "--old_menu", Quote(unity_menu_path), + "--old_menu", Quote(config.menu_path), "--new_menu", Quote(tastt_menu_path), }; if (InvokeWithArgs( std::move(args), &py_stdout, &py_stderr)) { diff --git a/GUI/GUI/GUI/PythonWrapper.h b/GUI/GUI/GUI/PythonWrapper.h index 38b35d4..c28a1f1 100644 --- a/GUI/GUI/GUI/PythonWrapper.h +++ b/GUI/GUI/GUI/PythonWrapper.h @@ -55,18 +55,11 @@ namespace PythonWrapper const TranscriptionAppConfig& config); bool GenerateAnimator( - const std::filesystem::path& unity_assets_path, - const std::filesystem::path& unity_animator_path, - const std::filesystem::path& unity_parameters_path, - const std::filesystem::path& unity_menu_path, + const UnityAppConfig& config, const std::string& unity_animator_generated_dir, const std::string& unity_animator_generated_name, const std::string& unity_parameters_generated_name, const std::string& unity_menu_generated_name, - const std::string& chars_per_sync, - const std::string& bytes_per_char, - int rows, - int cols, wxTextCtrl* out); }; diff --git a/GUI/GUI/GUI/Util.h b/GUI/GUI/GUI/Util.h new file mode 100644 index 0000000..594972e --- /dev/null +++ b/GUI/GUI/GUI/Util.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +// Wrap the filesystem path in quotes, escaping intermediate quotes with \\. +inline std::string Quote(const std::filesystem::path& p) { + std::ostringstream oss; + oss << std::quoted(p.string()); + return oss.str(); +} + +inline std::string Unquote(const std::string& s) { + std::istringstream iss(s); + + std::string result; + iss >> quoted(result); + + return result; +} -- cgit v1.2.3