From 3dd31eae0b9945364646641c3cf12a566baf7b6c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 14 Nov 2020 11:41:54 +0530 Subject: [PATCH] Start work on python wrapper for espeak-ng --- src/calibre/utils/tts/espeak.cpp | 22 ++++++++++-------- src/calibre/utils/tts/espeak.py | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 src/calibre/utils/tts/espeak.py diff --git a/src/calibre/utils/tts/espeak.cpp b/src/calibre/utils/tts/espeak.cpp index 54ead3c796..94a4cba8f4 100644 --- a/src/calibre/utils/tts/espeak.cpp +++ b/src/calibre/utils/tts/espeak.cpp @@ -53,6 +53,15 @@ class pyobject_raii { static bool initialize_called = false; +static PyObject* +terminate(PyObject *self, PyObject *args) { + if (initialize_called) { + espeak_Terminate(); + initialize_called = false; + } + Py_RETURN_NONE; +} + static PyObject* info(PyObject *self, PyObject *args) { const char *path_data; @@ -208,7 +217,7 @@ int_as_four_bytes(int32_t value, unsigned char *output) { static PyObject* create_recording_wav(PyObject *self, PyObject *args) { - int buflength = 1000; + int buflength = 0; unsigned int flags = 0; const char *text; Py_ssize_t text_len; @@ -225,7 +234,7 @@ create_recording_wav(PyObject *self, PyObject *args) { }; int_as_four_bytes(rate, wave_hdr + 24); int_as_four_bytes(rate * 2, wave_hdr + 28); - PyObject *ret = PyObject_CallFunction(cbdata.data_callback, "s#", wave_hdr, sizeof(wave_hdr)); + PyObject *ret = PyObject_CallFunction(cbdata.data_callback, "y#", wave_hdr, sizeof(wave_hdr)); if (!ret) return NULL; Py_DECREF(ret); @@ -241,11 +250,11 @@ create_recording_wav(PyObject *self, PyObject *args) { Py_RETURN_NONE; } - // Boilerplate {{{ #define M(name, args, doc) { #name, (PyCFunction)name, args, ""} static PyMethodDef methods[] = { M(info, METH_NOARGS, "version and path"), + M(terminate, METH_NOARGS, "terminate the library"), M(cancel, METH_NOARGS, "cancel all ongoing speech activity"), M(synchronize, METH_NOARGS, "synchronize all ongoing speech activity"), M(is_playing, METH_NOARGS, "True iff speech is happening"), @@ -282,12 +291,7 @@ static PyModuleDef_Slot slots[] = { {Py_mod_exec, (void*)exec_module}, {0, NULL} static struct PyModuleDef module_def = {PyModuleDef_HEAD_INIT}; static void -finalize(void*) { - if (initialize_called) { - espeak_Terminate(); - initialize_called = false; - } -} +finalize(void*) { terminate(NULL, NULL); } CALIBRE_MODINIT_FUNC PyInit_espeak(void) { module_def.m_name = "espeak"; diff --git a/src/calibre/utils/tts/espeak.py b/src/calibre/utils/tts/espeak.py new file mode 100644 index 0000000000..15104becb4 --- /dev/null +++ b/src/calibre/utils/tts/espeak.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2020, Kovid Goyal + +def info(): + from calibre_extensions.espeak import info + return info() + + +def create_recording_wav(text, seekable_file_object_or_path, buflength=0, ssml=False, phonemes=False, endpause=False): + import struct + + from calibre_extensions.espeak import ( + ENDPAUSE, PHONEMES, SSML, create_recording_wav as doit + ) + flags = 0 + if ssml: + flags |= SSML + if phonemes: + flags |= PHONEMES + if endpause: + flags |= ENDPAUSE + if isinstance(seekable_file_object_or_path, str): + seekable_file_object = open(seekable_file_object_or_path, 'w+b') + else: + seekable_file_object = seekable_file_object_or_path + + w = seekable_file_object.write + + def write(data): + w(data) + return False + + doit(text, write, buflength, flags) + sz = seekable_file_object.tell() + seekable_file_object.seek(4) + seekable_file_object.write(struct.pack('