#include #include #include #include "festival_private.h" #include "Python.h" #if PY_MAJOR_VERSION >= 3 #define PyUnicodeObject23 PyObject #else #define PyUnicodeObject23 PyUnicodeObject #endif /// The lock which guards access to the festival library, and related /// calls. This makes calls into this code serialised without relying on the GIL /// to do so. static std::mutex _lock; static char setStretchFactor_doc[] = "setStretchFactor(int) -> bool success"; static PyObject* setStretchFactor(PyObject* self, PyObject* args) { float stretch_factor; if (!PyArg_ParseTuple(args, "f:setStretchFactor", &stretch_factor)) return NULL; char buffer[40]; sprintf(buffer, "(Parameter.set 'Duration_Stretch %.2f)", stretch_factor); bool success; Py_BEGIN_ALLOW_THREADS { const std::lock_guard lock(_lock); success = festival_eval_command(buffer); } Py_END_ALLOW_THREADS if (success) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } static char execCommand_doc[] = "execCommand(command) -> bool success\n" "e.g. execCommand(\"(Parameter.set 'Duration_Stretch 1.2)\")"; static PyObject* execCommand(PyObject* self, PyObject* args) { const char* command; if (!PyArg_ParseTuple(args, "s:execCommand", &command)) return NULL; bool success; Py_BEGIN_ALLOW_THREADS { const std::lock_guard lock(_lock); success = festival_eval_command(command); } Py_END_ALLOW_THREADS if (success) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } static char _textToWav_doc[] = "_textToWav(text) -> unicode filename\n" "This is a private method."; static PyObject* _textToWav(PyObject* self, PyObject* args) { const char* text; if (!PyArg_ParseTuple(args, "s:_textToWav", &text)) return NULL; bool success; EST_Wave wave; Py_BEGIN_ALLOW_THREADS { const std::lock_guard lock(_lock); success = festival_text_to_wave(text, wave); } Py_END_ALLOW_THREADS if (!success) { PyErr_SetString(PyExc_SystemError, "Unable to convert text to wave"); return NULL; } EST_String tmpfile = make_tmp_filename(); FILE *fp; Py_BEGIN_ALLOW_THREADS fp = fopen(tmpfile, "wb"); success = (wave.save(fp, "riff") == write_ok); Py_END_ALLOW_THREADS if (!success) { fclose(fp); remove(tmpfile); PyErr_SetString(PyExc_SystemError, "Unable to create wav file"); return NULL; } fclose(fp); PyObject *filename = PyUnicode_FromStringAndSize((const char *)tmpfile, tmpfile.length()); return filename; } static char info_doc[] = "info() -> None\n" "Prints out information to stdout on " "the current festival version"; static PyObject* info(PyObject* self) { festival_banner(); Py_RETURN_NONE; } static char _sayText_doc[] = "_sayText(text) -> bool success"; static PyObject* _sayText(PyObject* self, PyObject* args) { const char *text; if (!PyArg_ParseTuple(args, "s:_sayText", &text)) return NULL; // Festival doesn't like empty strings, or NULL ones bool success = false; if (text != NULL && text[0] != '\0') { Py_BEGIN_ALLOW_THREADS { const std::lock_guard lock(_lock); success = festival_say_text(text); } Py_END_ALLOW_THREADS } if (success) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } static char sayFile_doc[] = "sayFile(filename) -> bool success"; static PyObject* sayFile(PyObject* self, PyObject* args) { const char *filename; if (!PyArg_ParseTuple(args, "s:sayFile", &filename)) return NULL; bool success; Py_BEGIN_ALLOW_THREADS { const std::lock_guard lock(_lock); success = festival_say_file(filename); } Py_END_ALLOW_THREADS // The C++ API docs for festival say you should use this to wait for the audio to finish playing // but it seems to cause the audio spooler to die // festival_wait_for_spooler(); if (success) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; } } /////////////////////////////////////////////////////////////////////// // module stuff static char module_doc[] = "Festival Python API"; static struct PyMethodDef festival_methods[] = { {"_sayText", (PyCFunction) _sayText, METH_VARARGS, _sayText_doc}, {"_textToWav", (PyCFunction) _textToWav, METH_VARARGS, _textToWav_doc}, {"execCommand", (PyCFunction) execCommand, METH_VARARGS, execCommand_doc}, {"setStretchFactor", (PyCFunction) setStretchFactor, METH_VARARGS, setStretchFactor_doc}, {"sayFile", (PyCFunction) sayFile, METH_VARARGS, sayFile_doc}, {"info", (PyCFunction) info, METH_NOARGS, info_doc}, {NULL, NULL} /* sentinel */ }; #ifndef Py_TYPE #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #endif static char module_name[] = "_festival"; static PyObject *festivalinit(void) { PyObject* module; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, module_name, /* m_name */ module_doc, /* m_doc */ -1, /* m_size */ festival_methods, /* m_methods */ NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ }; module = PyModule_Create(&moduledef); #else module = Py_InitModule3(module_name, festival_methods, module_doc); #endif if (module == NULL) { return NULL; } // init festival - make the heap size 7MB festival_initialize (1, 7*1024*1024); return module; } #if PY_MAJOR_VERSION >= 3 PyMODINIT_FUNC PyInit__festival(void) { return festivalinit(); } #else PyMODINIT_FUNC init_festival(void) { festivalinit(); } #endif