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;
|
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); }
|
static inline void handle_destructor(HANDLE p) { CloseHandle(p); }
|
||||||
typedef generic_raii<HANDLE, handle_destructor, INVALID_HANDLE_VALUE> handle_raii;
|
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 {
|
struct prop_variant : PROPVARIANT {
|
||||||
prop_variant(VARTYPE vt=VT_EMPTY) noexcept : PROPVARIANT{} { PropVariantInit(this); this->vt = vt; }
|
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");
|
throw std::string("Not a well formed speak command");
|
||||||
}
|
}
|
||||||
parts.erase(parts.begin(), parts.begin() + 2);
|
parts.erase(parts.begin(), parts.begin() + 2);
|
||||||
auto address = join(parts);
|
std::wstring address;
|
||||||
if (address.size() == 0) throw std::string("Address missing");
|
id_type shm_size = 0;
|
||||||
if (is_shm) {
|
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);
|
sx.speak(cmd_id, address, is_ssml);
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import struct
|
||||||
import sys
|
import sys
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
from calibre.utils.shm import SharedMemory
|
||||||
from calibre.utils.ipc.simple_worker import start_pipe_worker
|
from calibre.utils.ipc.simple_worker import start_pipe_worker
|
||||||
|
|
||||||
SSML_SAMPLE = '''
|
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())')
|
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()
|
p = start_worker()
|
||||||
print('\x1b[32mSpeaking', text, '\x1b[39m]]'[:-2], flush=True)
|
print('\x1b[32mSpeaking', text, '\x1b[39m]]'[:-2], flush=True)
|
||||||
q = Queue()
|
q = Queue()
|
||||||
@ -48,18 +79,20 @@ def develop_speech(text=SSML_SAMPLE):
|
|||||||
|
|
||||||
Thread(name='Echo', target=echo_output, args=(p,), daemon=True).start()
|
Thread(name='Echo', target=echo_output, args=(p,), daemon=True).start()
|
||||||
exit_code = 0
|
exit_code = 0
|
||||||
with closing(p.stdin), closing(p.stdout):
|
with closing(p.stdin), closing(p.stdout), SharedMemory(size=max_buffer_size(text)) as shm:
|
||||||
text = text.replace('\n', ' ')
|
|
||||||
st = 'ssml' if '<speak' in text else 'text'
|
st = 'ssml' if '<speak' in text else 'text'
|
||||||
|
sz = encode_to_file_object(text, shm)
|
||||||
try:
|
try:
|
||||||
send('1 echo Synthesizer started')
|
send('1 echo Synthesizer started')
|
||||||
send('1 volume 0.1')
|
send('1 volume 0.1')
|
||||||
send(f'2 speak {st} inline', text)
|
send(f'2 speak {st} shm {sz} {shm.name}')
|
||||||
while True:
|
while True:
|
||||||
m = q.get()
|
m = q.get()
|
||||||
|
if m['related_to'] != 2:
|
||||||
|
continue
|
||||||
if m['payload_type'] == 'media_state_changed' and m['state'] == 'ended':
|
if m['payload_type'] == 'media_state_changed' and m['state'] == 'ended':
|
||||||
break
|
break
|
||||||
if m['payload_type'] == 'error' and m['related_to'] == 2:
|
if m['payload_type'] == 'error':
|
||||||
exit_code = 1
|
exit_code = 1
|
||||||
break
|
break
|
||||||
send(f'3 echo Synthesizer exiting with exit code: {exit_code}')
|
send(f'3 echo Synthesizer exiting with exit code: {exit_code}')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user