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 <io.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Media.SpeechSynthesis.h>
@ -66,12 +67,12 @@ rtrim(std::string &s) {
}).base(), s.end());
}
static std::vector<std::string_view>
split(std::string const &src_, std::string const &delim = " ") {
static std::vector<std::wstring_view>
split(std::wstring_view const &src, std::wstring const &delim = L" ") {
size_t pos;
std::vector<std::string_view> ans; ans.reserve(16);
std::string_view sv(src_);
while ((pos = sv.find(delim)) != std::string_view::npos) {
std::vector<std::wstring_view> ans; ans.reserve(16);
std::wstring_view sv(src);
while ((pos = sv.find(delim)) != std::wstring_view::npos) {
if (pos > 0) ans.emplace_back(sv.substr(0, pos));
sv = sv.substr(pos + 1);
}
@ -79,9 +80,9 @@ split(std::string const &src_, std::string const &delim = " ") {
return ans;
}
static std::string
join(std::vector<std::string_view> parts, std::string const &delim = " ") {
std::string ans; ans.reserve(1024);
static std::wstring
join(std::vector<std::wstring_view> parts, std::wstring const &delim = L" ") {
std::wstring ans; ans.reserve(1024);
for (auto const &x : parts) {
ans.append(x);
ans.append(delim);
@ -91,12 +92,12 @@ join(std::vector<std::string_view> parts, std::string const &delim = " ") {
}
static id_type
parse_id(std::string_view const& s) {
parse_id(std::wstring_view const& s) {
id_type ans = 0;
for (auto ch : s) {
auto delta = ch - '0';
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;
}
@ -139,6 +140,7 @@ public:
json_val(std::string &&text) : type(DT_STRING), s(text) {}
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(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(long long num) : type(DT_INT), i(num) {}
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));
}
#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()); \
} catch (std::exception const &ex) { \
output_error(cmd_id, msg, ex.what(), __LINE__); \
} catch (std::string const &ex) { \
output_error(cmd_id, msg, ex, __LINE__); \
} catch (std::wstring const &ex) { \
output_error(cmd_id, msg, winrt::to_string(ex), __LINE__); \
} catch (...) { \
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 {
MediaPlaybackSession::PlaybackStateChanged_revoker playback_state_changed;
@ -748,11 +729,11 @@ decode_utf8(std::string_view const& src) {
}
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;
try {
is_ssml = parts.at(0) == "ssml";
is_shm = parts.at(1) == "shm";
is_ssml = parts.at(0) == L"ssml";
is_shm = parts.at(1) == L"shm";
} catch (std::exception const&) {
throw std::string("Not a well formed speak command");
}
@ -762,66 +743,58 @@ handle_speak(id_type cmd_id, std::vector<std::string_view> &parts) {
if (is_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
handle_stdin_messages(void) {
co_await winrt::resume_background();
std::scoped_lock sl(stdin_messages_lock);
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;
}
try {
parts = split(msg);
command = parts.at(1); cmd_id = parse_id(parts.at(0));
if (cmd_id == 0) {
throw std::exception("Command id of zero is not allowed");
}
parts.erase(parts.begin(), parts.begin() + 2);
ok = true;
} CATCH_ALL_EXCEPTIONS((std::string("Invalid input message: ") + msg), 0);
if (!ok) continue;
try {
if (command == "exit") {
try {
post_message(EXIT_REQUESTED, parse_id(parts.at(2)));
} catch(...) {
post_message(EXIT_REQUESTED);
}
break;
}
else if (command == "echo") {
output(cmd_id, command, {{"msg", json_val(std::move(join(parts)))}});
}
else if (command == "default_voice") {
output(cmd_id, "default_voice", SpeechSynthesizer::DefaultVoice());
}
else if (command == "all_voices") {
output(cmd_id, "all_voices", SpeechSynthesizer::AllVoices());
}
else if (command == "speak") {
handle_speak(cmd_id, parts);
}
else throw std::string("Unknown command: ") + std::string(command);
} CATCH_ALL_EXCEPTIONS("Error handling input message", cmd_id);
static long long
handle_stdin_message(winrt::hstring const &&msg) {
if (msg == L"exit") {
return 0;
}
stdin_messages.clear();
id_type cmd_id;
std::wstring_view command;
bool ok = false;
std::vector<std::wstring_view> parts;
try {
parts = split(msg);
command = parts.at(1); cmd_id = parse_id(parts.at(0));
if (cmd_id == 0) {
throw std::exception("Command id of zero is not allowed");
}
parts.erase(parts.begin(), parts.begin() + 2);
ok = true;
} CATCH_ALL_EXCEPTIONS((std::string("Invalid input message: ") + winrt::to_string(msg)), 0);
if (!ok) return -1;
try {
if (command == L"exit") {
try {
return parse_id(parts.at(0));
} catch(...) { }
return 0;
}
else if (command == L"echo") {
output(cmd_id, "echo", {{"msg", json_val(std::move(join(parts)))}});
}
else if (command == L"default_voice") {
output(cmd_id, "default_voice", SpeechSynthesizer::DefaultVoice());
}
else if (command == L"all_voices") {
output(cmd_id, "all_voices", SpeechSynthesizer::AllVoices());
}
else if (command == L"speak") {
handle_speak(cmd_id, parts);
}
else throw std::string("Unknown command: ") + winrt::to_string(command);
} CATCH_ALL_EXCEPTIONS("Error handling input message", cmd_id);
return -1;
}
static PyObject*
run_main_loop(PyObject*, PyObject*) {
winrt::init_apartment();
winrt::init_apartment(); // MTA (multi-threaded apartment)
main_thread_id = GetCurrentThreadId();
MSG msg;
unsigned long long exit_code = 0;
long long exit_code = 0;
bool ok = false;
try {
new (&sx) Synthesizer();
@ -837,29 +810,34 @@ run_main_loop(PyObject*, PyObject*) {
if (_isatty(_fileno(stdin))) {
std::cout << "Welcome to winspeech. Type exit to quit." << std::endl;
}
run_input_loop();
std::string input_buffer;
while (true) {
BOOL ret = GetMessage(&msg, NULL, 0, 0);
if (ret <= 0) { // WM_QUIT or error
exit_code = msg.message == WM_QUIT ? msg.wParam : 1;
try {
if (!std::getline(std::cin, input_buffer)) {
if (!std::cin.eof()) exit_code = 1;
break;
}
rtrim(input_buffer);
if (input_buffer.size() > 0) {
if ((exit_code = handle_stdin_message(std::move(winrt::to_hstring(input_buffer)))) >= 0) break;
}
} catch(...) {
exit_code = 1;
output_error(0, "Unknown exception type reading and handling line of input", "", __LINE__);
break;
}
if (msg.message == WM_USER) {
if (msg.lParam == STDIN_FAILED || msg.lParam == EXIT_REQUESTED) { exit_code = msg.wParam; break; }
else if (msg.lParam == STDIN_MSG) handle_stdin_messages();
} else {
DispatchMessage(&msg);
}
}
main_loop_is_running.store(false);
Py_END_ALLOW_THREADS;
try {
sx.stop_current_activity();
(&sx)->~Synthesizer();
} 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, ""}