mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Allow passing speech text with shared memory
This commit is contained in:
parent
83891ed63e
commit
623f999a54
@ -62,6 +62,8 @@ static inline void co_task_mem_free(void* m) { CoTaskMemFree(m); }
|
||||
typedef generic_raii<wchar_t*, co_task_mem_free, static_cast<wchar_t*>(NULL)> com_wchar_raii;
|
||||
static inline void handle_destructor(HANDLE p) { CloseHandle(p); }
|
||||
typedef generic_raii<HANDLE, handle_destructor, INVALID_HANDLE_VALUE> handle_raii;
|
||||
static inline void mapping_destructor(void *p) { UnmapViewOfFile(p); }
|
||||
typedef generic_raii<void*, mapping_destructor, static_cast<void*>(NULL)> mapping_raii;
|
||||
|
||||
struct prop_variant : PROPVARIANT {
|
||||
prop_variant(VARTYPE vt=VT_EMPTY) noexcept : PROPVARIANT{} { PropVariantInit(this); this->vt = vt; }
|
||||
|
@ -865,10 +865,25 @@ handle_speak(id_type cmd_id, std::vector<std::wstring_view> &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);
|
||||
}
|
||||
|
@ -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 '<speak' in text else 'text'
|
||||
sz = encode_to_file_object(text, shm)
|
||||
try:
|
||||
send('1 echo Synthesizer started')
|
||||
send('1 volume 0.1')
|
||||
send(f'2 speak {st} inline', text)
|
||||
send(f'2 speak {st} shm {sz} {shm.name}')
|
||||
while True:
|
||||
m = q.get()
|
||||
if m['related_to'] != 2:
|
||||
continue
|
||||
if m['payload_type'] == 'media_state_changed' and m['state'] == 'ended':
|
||||
break
|
||||
if m['payload_type'] == 'error' and m['related_to'] == 2:
|
||||
if m['payload_type'] == 'error':
|
||||
exit_code = 1
|
||||
break
|
||||
send(f'3 echo Synthesizer exiting with exit code: {exit_code}')
|
||||
|
Loading…
x
Reference in New Issue
Block a user