From c027aaff750ff73fa6affe909af911e8918b8316 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 31 Oct 2020 11:56:54 +0530 Subject: [PATCH] Start wrapping espeak --- .github/workflows/ci.yml | 2 +- bypy/linux.conf | 2 +- setup/extensions.json | 6 ++++ setup/unix-ci.py | 2 +- src/calibre/constants.py | 2 +- src/calibre/utils/tts/__init__.py | 0 src/calibre/utils/tts/espeak.cpp | 56 +++++++++++++++++++++++++++++++ 7 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 src/calibre/utils/tts/__init__.py create mode 100644 src/calibre/utils/tts/espeak.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 896fea44f4..94e46956e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: # set -xe # useradd -m ci # pacman -Syu --noconfirm - # pacman -S --noconfirm --needed base-devel sudo git sip chmlib icu jxrlib hunspell libmtp libusbx libwmf optipng podofo python-apsw python-beautifulsoup4 python-cssselect python-css-parser python-dateutil python-dbus python-dnspython python-dukpy python-feedparser python-html2text python-html5-parser python-lxml python-markdown python-mechanize python-msgpack python-netifaces python-unrardll python-pillow python-psutil python-pygments python-pyqt5 python-regex python-zeroconf python-pyqtwebengine qt5-x11extras qt5-svg qt5-imageformats udisks2 hyphen python-pychm + # pacman -S --noconfirm --needed base-devel sudo git sip chmlib icu jxrlib hunspell libmtp libusbx libwmf optipng podofo python-apsw python-beautifulsoup4 python-cssselect python-css-parser python-dateutil python-dbus python-dnspython python-dukpy python-feedparser python-html2text python-html5-parser python-lxml python-markdown python-mechanize python-msgpack python-netifaces python-unrardll python-pillow python-psutil python-pygments python-pyqt5 python-regex python-zeroconf python-pyqtwebengine qt5-x11extras qt5-svg qt5-imageformats udisks2 hyphen python-pychm espeak-ng # # - name: Checkout source code # uses: actions/checkout@master diff --git a/bypy/linux.conf b/bypy/linux.conf index 76b8b4c192..c863d16ad3 100644 --- a/bypy/linux.conf +++ b/bypy/linux.conf @@ -1,4 +1,4 @@ image 'https://partner-images.canonical.com/core/xenial/current/ubuntu-xenial-core-cloudimg-{}-root.tar.gz' # Build time deps for Qt. See http://doc.qt.io/qt-5/linux-requirements.html and https://wiki.qt.io/Building_Qt_5_from_Git -deps 'flex bison gperf ruby libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libx11-xcb-dev libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libxcb-xinerama0-dev xkb-data libglu1-mesa-dev libxkbcommon-dev libinput-dev libxkbcommon-x11-dev libgtk2.0-dev libvulkan-dev libwayland-dev libwayland-egl1-mesa libegl1-mesa-dev libxtst-dev libnss3-dev libfreetype6-dev libfontconfig-dev' +deps 'flex bison gperf ruby libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libx11-xcb-dev libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libxcb-xinerama0-dev xkb-data libglu1-mesa-dev libxkbcommon-dev libinput-dev libxkbcommon-x11-dev libgtk2.0-dev libvulkan-dev libwayland-dev libwayland-egl1-mesa libegl1-mesa-dev libxtst-dev libnss3-dev libfreetype6-dev libfontconfig-dev libespeak-ng-dev' diff --git a/setup/extensions.json b/setup/extensions.json index 2c9d5187de..38421c258f 100644 --- a/setup/extensions.json +++ b/setup/extensions.json @@ -179,6 +179,12 @@ "sources": "calibre/utils/cocoa.m calibre/utils/cocoa_wrapper.c", "ldflags": "-framework Cocoa" }, + { + "name": "espeak", + "only": "linux haiku", + "sources": "calibre/utils/tts/espeak.cpp", + "libraries": "espeak-ng" + }, { "name": "libusb", "only": "macos linux haiku", diff --git a/setup/unix-ci.py b/setup/unix-ci.py index feb9776730..4e493b44f9 100644 --- a/setup/unix-ci.py +++ b/setup/unix-ci.py @@ -111,7 +111,7 @@ def run_python(*args): def install_linux_deps(): run('sudo', 'apt-get', 'update', '-y') # run('sudo', 'apt-get', 'upgrade', '-y') - run('sudo', 'apt-get', 'install', '-y', 'gettext', 'libgl1-mesa-dev') + run('sudo', 'apt-get', 'install', '-y', 'gettext', 'libgl1-mesa-dev', 'libespeak-ng-dev') def main(): diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 386270cda5..2cdf7b9e3d 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -253,7 +253,7 @@ class ExtensionsImporter: elif ismacos: extra = ('usbobserver', 'cocoa', 'libusb', 'libmtp') elif isfreebsd or ishaiku or islinux: - extra = ('libusb', 'libmtp') + extra = ('libusb', 'libmtp', 'espeak') else: extra = () self.calibre_extensions = frozenset(extensions + extra) diff --git a/src/calibre/utils/tts/__init__.py b/src/calibre/utils/tts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/calibre/utils/tts/espeak.cpp b/src/calibre/utils/tts/espeak.cpp new file mode 100644 index 0000000000..4d3413824c --- /dev/null +++ b/src/calibre/utils/tts/espeak.cpp @@ -0,0 +1,56 @@ +/* + * espeak.cpp + * Copyright (C) 2020 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + + +#define PY_SSIZE_T_CLEAN +#define UNICODE +#define _UNICODE +#include +#include + +static bool initialize_called = false; + +// Boilerplate {{{ +#define M(name, args) { #name, (PyCFunction)name, args, ""} +static PyMethodDef methods[] = { + {NULL, NULL, 0, NULL} +}; +#undef M + +static int +exec_module(PyObject *m) { +#define AI(name) if (PyModule_AddIntMacro(m, name) != 0) { return -1; } +#undef AI + int sample_rate = espeak_Initialize(AUDIO_OUTPUT_SYNCH_PLAYBACK, 0, NULL, espeakINITIALIZE_DONT_EXIT); + if (sample_rate == -1) { + PyErr_SetString(PyExc_OSError, "Failed to initialize espeak library, are the data files missing?"); + return 1; + } + initialize_called = true; + return 0; +} + +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; + } +} + +CALIBRE_MODINIT_FUNC PyInit_espeak(void) { + module_def.m_name = "espeak"; + module_def.m_doc = "espeak-ng wrapper"; + module_def.m_slots = slots; + module_def.m_free = finalize; + module_def.m_methods = methods; + return PyModuleDef_Init(&module_def); +}