diff --git a/src/calibre/utils/windows/common.h b/src/calibre/utils/windows/common.h index 76366f0921..dc3627b090 100644 --- a/src/calibre/utils/windows/common.h +++ b/src/calibre/utils/windows/common.h @@ -62,6 +62,8 @@ static inline void co_task_mem_free(void* m) { CoTaskMemFree(m); } typedef generic_raii(NULL)> com_wchar_raii; static inline void handle_destructor(HANDLE p) { CloseHandle(p); } typedef generic_raii handle_raii; +static inline void mapping_destructor(void *p) { UnmapViewOfFile(p); } +typedef generic_raii(NULL)> mapping_raii; struct prop_variant : PROPVARIANT { prop_variant(VARTYPE vt=VT_EMPTY) noexcept : PROPVARIANT{} { PropVariantInit(this); this->vt = vt; } diff --git a/src/calibre/utils/windows/winspeech.cpp b/src/calibre/utils/windows/winspeech.cpp index 9fc38f178f..7c41293716 100644 --- a/src/calibre/utils/windows/winspeech.cpp +++ b/src/calibre/utils/windows/winspeech.cpp @@ -865,10 +865,25 @@ handle_speak(id_type cmd_id, std::vector &parts) { throw std::string("Not a well formed speak command"); } parts.erase(parts.begin(), parts.begin() + 2); - auto address = join(parts); - if (address.size() == 0) throw std::string("Address missing"); + std::wstring address; + id_type shm_size = 0; if (is_shm) { - throw std::string("TODO: Implement support for SHM"); + shm_size = parse_id(parts.at(0)); + address = parts.at(1); + handle_raii handle(OpenFileMappingW(FILE_MAP_READ, false, address.data())); + if (handle.ptr() == INVALID_HANDLE_VALUE) { + output_error(cmd_id, "Could not open shared memory at", winrt::to_string(address), __LINE__); + return; + } + mapping_raii mapping(MapViewOfFile(handle.ptr(), FILE_MAP_READ, 0, 0, (SIZE_T)shm_size)); + if (mapping.ptr() == NULL) { + output_error(cmd_id, "Could not map shared memory with error", std::to_string(GetLastError()), __LINE__); + return; + } + address = winrt::to_hstring((const char*)mapping.ptr()); + } else { + address = join(parts); + if (address.size() == 0) throw std::string("Address missing"); } sx.speak(cmd_id, address, is_ssml); } diff --git a/src/calibre/utils/windows/winspeech.py b/src/calibre/utils/windows/winspeech.py index cb433b500d..a6ee9745ee 100644 --- a/src/calibre/utils/windows/winspeech.py +++ b/src/calibre/utils/windows/winspeech.py @@ -3,11 +3,13 @@ import json +import struct import sys from contextlib import closing from queue import Queue from threading import Thread +from calibre.utils.shm import SharedMemory from calibre.utils.ipc.simple_worker import start_pipe_worker SSML_SAMPLE = ''' @@ -30,7 +32,36 @@ def start_worker(): return start_pipe_worker('from calibre_extensions.winspeech import run_main_loop; raise SystemExit(run_main_loop())') -def develop_speech(text=SSML_SAMPLE): +def max_buffer_size(text) -> int: + if isinstance(text, str): + text = [text] + ans = 0 + for x in text: + if isinstance(x, int): + ans += 5 + else: + ans += 4 * len(x) + return ans + + +def encode_to_file_object(text, output) -> int: + if isinstance(text, str): + text = [text] + p = struct.pack + sz = 0 + for x in text: + if isinstance(x, int): + output.write(b'\0') + output.write(p('=I', x)) + sz += 5 + else: + b = x.encode('utf-8') + output.write(b) + sz += len(b) + return sz + + +def develop_speech(text='Lucca Brazzi sleeps with the fishes.'): p = start_worker() print('\x1b[32mSpeaking', text, '\x1b[39m]]'[:-2], flush=True) q = Queue() @@ -48,18 +79,20 @@ def develop_speech(text=SSML_SAMPLE): Thread(name='Echo', target=echo_output, args=(p,), daemon=True).start() exit_code = 0 - with closing(p.stdin), closing(p.stdout): - text = text.replace('\n', ' ') + with closing(p.stdin), closing(p.stdout), SharedMemory(size=max_buffer_size(text)) as shm: st = 'ssml' if '