No need for event loop just use co-routines

This commit is contained in:
Kovid Goyal 2023-01-24 17:11:55 +05:30
parent 7b47a70493
commit 61a16f78a8
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -20,6 +20,7 @@
#include <unordered_map> #include <unordered_map>
#include <io.h> #include <io.h>
#include <winrt/base.h> #include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.Streams.h> #include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Media.SpeechSynthesis.h> #include <winrt/Windows.Media.SpeechSynthesis.h>
@ -66,12 +67,12 @@ rtrim(std::string &s) {
}).base(), s.end()); }).base(), s.end());
} }
static std::vector<std::string_view> static std::vector<std::wstring_view>
split(std::string const &src_, std::string const &delim = " ") { split(std::wstring_view const &src, std::wstring const &delim = L" ") {
size_t pos; size_t pos;
std::vector<std::string_view> ans; ans.reserve(16); std::vector<std::wstring_view> ans; ans.reserve(16);
std::string_view sv(src_); std::wstring_view sv(src);
while ((pos = sv.find(delim)) != std::string_view::npos) { while ((pos = sv.find(delim)) != std::wstring_view::npos) {
if (pos > 0) ans.emplace_back(sv.substr(0, pos)); if (pos > 0) ans.emplace_back(sv.substr(0, pos));
sv = sv.substr(pos + 1); sv = sv.substr(pos + 1);
} }
@ -79,9 +80,9 @@ split(std::string const &src_, std::string const &delim = " ") {
return ans; return ans;
} }
static std::string static std::wstring
join(std::vector<std::string_view> parts, std::string const &delim = " ") { join(std::vector<std::wstring_view> parts, std::wstring const &delim = L" ") {
std::string ans; ans.reserve(1024); std::wstring ans; ans.reserve(1024);
for (auto const &x : parts) { for (auto const &x : parts) {
ans.append(x); ans.append(x);
ans.append(delim); ans.append(delim);
@ -91,12 +92,12 @@ join(std::vector<std::string_view> parts, std::string const &delim = " ") {
} }
static id_type static id_type
parse_id(std::string_view const& s) { parse_id(std::wstring_view const& s) {
id_type ans = 0; id_type ans = 0;
for (auto ch : s) { for (auto ch : s) {
auto delta = ch - '0'; auto delta = ch - '0';
if (delta < 0 || delta > 9) { if (delta < 0 || delta > 9) {
throw std::invalid_argument(std::string("Not a valid id: ") + std::string(s)); throw std::wstring(L"Not a valid id: ") + std::wstring(s);
} }
ans = (ans * 10) + delta; ans = (ans * 10) + delta;
} }
@ -139,6 +140,7 @@ public:
json_val(std::string &&text) : type(DT_STRING), s(text) {} json_val(std::string &&text) : type(DT_STRING), s(text) {}
json_val(const char *ns) : type(DT_STRING), s(ns) {} json_val(const char *ns) : type(DT_STRING), s(ns) {}
json_val(winrt::hstring const& text) : type(DT_STRING), s(winrt::to_string(text)) {} json_val(winrt::hstring const& text) : type(DT_STRING), s(winrt::to_string(text)) {}
json_val(std::wstring const& text) : type(DT_STRING), s(winrt::to_string(text)) {}
json_val(std::string_view text) : type(DT_STRING), s(text) {} json_val(std::string_view text) : type(DT_STRING), s(text) {}
json_val(long long num) : type(DT_INT), i(num) {} json_val(long long num) : type(DT_INT), i(num) {}
json_val(std::vector<json_val> &&items) : type(DT_LIST), list(items) {} json_val(std::vector<json_val> &&items) : type(DT_LIST), list(items) {}
@ -232,12 +234,15 @@ output_error(id_type cmd_id, std::string_view const &msg, std::string_view const
output(cmd_id, "error", std::move(m)); output(cmd_id, "error", std::move(m));
} }
#define CATCH_ALL_EXCEPTIONS(msg, cmd_id) catch(winrt::hresult_error const& ex) { \ #define CATCH_ALL_EXCEPTIONS(msg, cmd_id) \
catch(winrt::hresult_error const& ex) { \
output_error(cmd_id, msg, winrt::to_string(ex.message()), __LINE__, ex.to_abi()); \ output_error(cmd_id, msg, winrt::to_string(ex.message()), __LINE__, ex.to_abi()); \
} catch (std::exception const &ex) { \ } catch (std::exception const &ex) { \
output_error(cmd_id, msg, ex.what(), __LINE__); \ output_error(cmd_id, msg, ex.what(), __LINE__); \
} catch (std::string const &ex) { \ } catch (std::string const &ex) { \
output_error(cmd_id, msg, ex, __LINE__); \ output_error(cmd_id, msg, ex, __LINE__); \
} catch (std::wstring const &ex) { \
output_error(cmd_id, msg, winrt::to_string(ex), __LINE__); \
} catch (...) { \ } catch (...) { \
output_error(cmd_id, msg, "Unknown exception type was raised", __LINE__); \ output_error(cmd_id, msg, "Unknown exception type was raised", __LINE__); \
} }
@ -604,30 +609,6 @@ pump_waiting_messages(PyObject*, PyObject*) {
}}} */ }}} */
static std::vector<std::string> stdin_messages;
static std::mutex stdin_messages_lock;
static void
post_message(LPARAM type, WPARAM data = 0) {
PostThreadMessageA(main_thread_id, WM_USER, data, type);
}
static winrt::fire_and_forget
run_input_loop(void) {
co_await winrt::resume_background();
std::string line;
while(!std::cin.eof() && std::getline(std::cin, line)) {
if (line.size() > 0) {
{
std::scoped_lock sl(stdin_messages_lock);
stdin_messages.push_back(line);
}
post_message(STDIN_MSG);
}
}
post_message(STDIN_FAILED, std::cin.fail() ? 1 : 0);
}
struct Revokers { struct Revokers {
MediaPlaybackSession::PlaybackStateChanged_revoker playback_state_changed; MediaPlaybackSession::PlaybackStateChanged_revoker playback_state_changed;
@ -748,11 +729,11 @@ decode_utf8(std::string_view const& src) {
} }
static void static void
handle_speak(id_type cmd_id, std::vector<std::string_view> &parts) { handle_speak(id_type cmd_id, std::vector<std::wstring_view> &parts) {
bool is_ssml = false, is_shm = false; bool is_ssml = false, is_shm = false;
try { try {
is_ssml = parts.at(0) == "ssml"; is_ssml = parts.at(0) == L"ssml";
is_shm = parts.at(1) == "shm"; is_shm = parts.at(1) == L"shm";
} catch (std::exception const&) { } catch (std::exception const&) {
throw std::string("Not a well formed speak command"); throw std::string("Not a well formed speak command");
} }
@ -762,23 +743,18 @@ handle_speak(id_type cmd_id, std::vector<std::string_view> &parts) {
if (is_shm) { if (is_shm) {
throw std::string("TODO: Implement support for SHM"); throw std::string("TODO: Implement support for SHM");
} }
sx.speak(cmd_id, decode_utf8(address), is_ssml); sx.speak(cmd_id, address, is_ssml);
} }
static winrt::fire_and_forget static long long
handle_stdin_messages(void) { handle_stdin_message(winrt::hstring const &&msg) {
co_await winrt::resume_background(); if (msg == L"exit") {
std::scoped_lock sl(stdin_messages_lock); return 0;
std::vector<std::string_view> parts;
std::string_view command;
id_type cmd_id;
for (auto & msg : stdin_messages) {
rtrim(msg);
bool ok = false;
if (msg == "exit") {
post_message(EXIT_REQUESTED);
break;
} }
id_type cmd_id;
std::wstring_view command;
bool ok = false;
std::vector<std::wstring_view> parts;
try { try {
parts = split(msg); parts = split(msg);
command = parts.at(1); cmd_id = parse_id(parts.at(0)); command = parts.at(1); cmd_id = parse_id(parts.at(0));
@ -787,41 +763,38 @@ handle_stdin_messages(void) {
} }
parts.erase(parts.begin(), parts.begin() + 2); parts.erase(parts.begin(), parts.begin() + 2);
ok = true; ok = true;
} CATCH_ALL_EXCEPTIONS((std::string("Invalid input message: ") + msg), 0); } CATCH_ALL_EXCEPTIONS((std::string("Invalid input message: ") + winrt::to_string(msg)), 0);
if (!ok) continue; if (!ok) return -1;
try { try {
if (command == "exit") { if (command == L"exit") {
try { try {
post_message(EXIT_REQUESTED, parse_id(parts.at(2))); return parse_id(parts.at(0));
} catch(...) { } catch(...) { }
post_message(EXIT_REQUESTED); return 0;
} }
break; else if (command == L"echo") {
output(cmd_id, "echo", {{"msg", json_val(std::move(join(parts)))}});
} }
else if (command == "echo") { else if (command == L"default_voice") {
output(cmd_id, command, {{"msg", json_val(std::move(join(parts)))}});
}
else if (command == "default_voice") {
output(cmd_id, "default_voice", SpeechSynthesizer::DefaultVoice()); output(cmd_id, "default_voice", SpeechSynthesizer::DefaultVoice());
} }
else if (command == "all_voices") { else if (command == L"all_voices") {
output(cmd_id, "all_voices", SpeechSynthesizer::AllVoices()); output(cmd_id, "all_voices", SpeechSynthesizer::AllVoices());
} }
else if (command == "speak") { else if (command == L"speak") {
handle_speak(cmd_id, parts); handle_speak(cmd_id, parts);
} }
else throw std::string("Unknown command: ") + std::string(command); else throw std::string("Unknown command: ") + winrt::to_string(command);
} CATCH_ALL_EXCEPTIONS("Error handling input message", cmd_id); } CATCH_ALL_EXCEPTIONS("Error handling input message", cmd_id);
} return -1;
stdin_messages.clear();
} }
static PyObject* static PyObject*
run_main_loop(PyObject*, PyObject*) { run_main_loop(PyObject*, PyObject*) {
winrt::init_apartment(); winrt::init_apartment(); // MTA (multi-threaded apartment)
main_thread_id = GetCurrentThreadId(); main_thread_id = GetCurrentThreadId();
MSG msg; MSG msg;
unsigned long long exit_code = 0; long long exit_code = 0;
bool ok = false; bool ok = false;
try { try {
new (&sx) Synthesizer(); new (&sx) Synthesizer();
@ -837,29 +810,34 @@ run_main_loop(PyObject*, PyObject*) {
if (_isatty(_fileno(stdin))) { if (_isatty(_fileno(stdin))) {
std::cout << "Welcome to winspeech. Type exit to quit." << std::endl; std::cout << "Welcome to winspeech. Type exit to quit." << std::endl;
} }
run_input_loop();
std::string input_buffer;
while (true) { while (true) {
BOOL ret = GetMessage(&msg, NULL, 0, 0); try {
if (ret <= 0) { // WM_QUIT or error if (!std::getline(std::cin, input_buffer)) {
exit_code = msg.message == WM_QUIT ? msg.wParam : 1; if (!std::cin.eof()) exit_code = 1;
break; break;
} }
if (msg.message == WM_USER) { rtrim(input_buffer);
if (msg.lParam == STDIN_FAILED || msg.lParam == EXIT_REQUESTED) { exit_code = msg.wParam; break; } if (input_buffer.size() > 0) {
else if (msg.lParam == STDIN_MSG) handle_stdin_messages(); if ((exit_code = handle_stdin_message(std::move(winrt::to_hstring(input_buffer)))) >= 0) break;
} else { }
DispatchMessage(&msg); } catch(...) {
exit_code = 1;
output_error(0, "Unknown exception type reading and handling line of input", "", __LINE__);
break;
} }
} }
main_loop_is_running.store(false); main_loop_is_running.store(false);
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
try { try {
sx.stop_current_activity(); sx.stop_current_activity();
(&sx)->~Synthesizer(); (&sx)->~Synthesizer();
} CATCH_ALL_EXCEPTIONS("Error stopping all activity", 0); } CATCH_ALL_EXCEPTIONS("Error stopping all activity", 0);
return PyLong_FromUnsignedLongLong(exit_code); return PyLong_FromLongLong(exit_code);
} }
#define M(name, args) { #name, name, args, ""} #define M(name, args) { #name, name, args, ""}