From 2d9dd81f4674e6ab1cb1416d4c80f84fcebbe3c5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 13 Nov 2020 11:29:42 +0530 Subject: [PATCH] Code to list espeak voices --- src/calibre/utils/tts/espeak.cpp | 65 +++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/calibre/utils/tts/espeak.cpp b/src/calibre/utils/tts/espeak.cpp index 4d3413824c..786fbce34a 100644 --- a/src/calibre/utils/tts/espeak.cpp +++ b/src/calibre/utils/tts/espeak.cpp @@ -12,11 +12,74 @@ #include #include +class pyobject_raii { + private: + PyObject *handle; + pyobject_raii( const pyobject_raii & ) ; + pyobject_raii & operator=( const pyobject_raii & ) ; + + public: + pyobject_raii() : handle(NULL) {} + pyobject_raii(PyObject* h) : handle(h) {} + + ~pyobject_raii() { Py_CLEAR(handle); } + + PyObject *ptr() { return handle; } + void set_ptr(PyObject *val) { handle = val; } + PyObject **address() { return &handle; } + explicit operator bool() const { return handle != NULL; } + PyObject *detach() { PyObject *ans = handle; handle = NULL; return ans; } +}; + static bool initialize_called = false; +static PyObject* +info(PyObject *self, PyObject *args) { + const char *path_data; + const char *version = espeak_Info(&path_data); + return Py_BuildValue("ss", version, path_data); +} + +static PyObject* +list_voices(PyObject *self, PyObject *args, PyObject *kw) { + espeak_VOICE q = {0}; + static const char* kwds[] = {"name", "language", "identifier", "gender", "age", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|$sssBB", (char**)kwds, &q.name, &q.languages, &q.identifier, &q.gender, &q.age)) return NULL; + const espeak_VOICE **voices; + Py_BEGIN_ALLOW_THREADS; + voices = espeak_ListVoices(&q); + Py_END_ALLOW_THREADS; + pyobject_raii ans(PyList_New(0)); + if (!ans) return NULL; + while (*voices) { + const espeak_VOICE *x = *voices; + pyobject_raii languages(PyList_New(0)); + if (!languages) return NULL; + const char *pos = x->languages; + while (pos && *pos) { + const char priority = *pos; + size_t sz = strlen(++pos); + if (!sz) break; + pyobject_raii lang(Py_BuildValue("bs", priority, pos)); + if (!lang) return NULL; + if (PyList_Append(languages.ptr(), lang.ptr()) != 0) return NULL; + pos += sz + 1; + } + pyobject_raii entry(Py_BuildValue("{ss ss sO sB sB}", + "name", x->name, "identifier", x->identifier, "languages", languages.ptr(), + "gender", x->gender, "age", x->age)); + if (!entry) return NULL; + if (PyList_Append(ans.ptr(), entry.ptr()) != 0) return NULL; + voices++; + } + return ans.detach(); +} + // Boilerplate {{{ -#define M(name, args) { #name, (PyCFunction)name, args, ""} +#define M(name, args, doc) { #name, (PyCFunction)name, args, ""} static PyMethodDef methods[] = { + M(info, METH_VARARGS, "version and path"), + M(list_voices, METH_VARARGS | METH_KEYWORDS, "list available voices"), {NULL, NULL, 0, NULL} }; #undef M