diff options
author | Stefan Wildemann <metalstrolch@users.noreply.github.com> | 2017-05-05 20:03:51 +0200 |
---|---|---|
committer | Pierre GRANDIN <pgrandin@users.noreply.github.com> | 2017-05-05 11:03:51 -0700 |
commit | 8c48382a411a0c16f1d1cf7d015ea921f1adb2d0 (patch) | |
tree | c88595bba7cb50af51e63c95b4cc3e5dea84ca39 | |
parent | b6d8bc28e34d0f3623659a9c724fa24310cfd5d7 (diff) | |
download | navit-8c48382a411a0c16f1d1cf7d015ea921f1adb2d0.tar.gz |
Add: Add speech module using espeak on QMultimedia. (#233)R7471
* Add: Add speech module using (external) espeak on QMultimedia.
* Add: Find system espeak or use internal one
This patch adds a cmake module to find installed libespeak.
If not found, it builds against included libespeak, despite it being
heavily patched.
* Fix: allow building internal espeak for posix systems
Allow the usage of the internal (ported to C) version of espeak as
replacement for system installed libespeak.
* Fix: use synchronous espeak mode if internal espeak is used
* Fix: Clear buffer if samples are played
* Fix: use syncronous mode always, as QBuffer is not thread safe
* Sailfish: Use qt5_espeak
* Remove espeak shell script now obsolete
* Fix: remove the espeak script from the repo
* Fix: code cleanup. Fix audio buffer size
* Fix: apply coding style
Apply automatic coding style with 'clang-format -style=WebKit -i'
* Fix; restore correct include file order
Seems, our internal include files depend on correct order. This usually
is a bad thing and we should consider fixing them. But this is another
story...
* Add qtmultimedia-dev to windows build for qt5_espeak
* Rename CMake INTERNAL_ESPEAK -> INTERNAL_ESPEAK_COMPLETE
-rwxr-xr-x | CMakeLists.txt | 22 | ||||
-rw-r--r-- | ci/build_linux.sh | 2 | ||||
-rw-r--r-- | cmake/Findespeak.cmake | 29 | ||||
-rwxr-xr-x | contrib/sailfish/navit-sailfish.spec | 9 | ||||
-rwxr-xr-x | contrib/sailfish/navit.xml | 2 | ||||
-rwxr-xr-x | contrib/sailfish/say_de_DE.sh | 2 | ||||
-rw-r--r-- | navit/speech/qt5_espeak/CMakeLists.txt | 12 | ||||
-rw-r--r-- | navit/speech/qt5_espeak/Qt5EspeakAudioOut.cpp | 117 | ||||
-rw-r--r-- | navit/speech/qt5_espeak/Qt5EspeakAudioOut.h | 55 | ||||
-rwxr-xr-x | navit/speech/qt5_espeak/qt5_espeak.cpp | 252 | ||||
-rw-r--r-- | navit/support/espeak/CMakeLists.txt | 6 | ||||
-rw-r--r-- | navit/support/espeak/speak_lib.h | 2 | ||||
-rwxr-xr-x | navit/support/espeak/speech.h | 4 |
13 files changed, 502 insertions, 12 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8804c0184..c6354e1ee 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,8 @@ endif(EXTRA_LIBDIR) ### Detect environment +add_plugin(support/espeak "Default" FALSE) +add_feature(INTERNAL_ESPEAK_COMPLETE "build complete internal libespeak" FALSE) add_plugin(support/ezxml "native Glib found" FALSE) add_plugin(support/glib "native Glib found" FALSE) add_plugin(support/zlib "native zlib found" FALSE) @@ -106,6 +108,7 @@ add_module(binding/dbus "dbus-glib-1 not found" FALSE) add_module(binding/python "python libraries not found" FALSE) add_module(speech/dbus "dbus-glib-1 not found" FALSE) add_module(speech/cmdline "neither system() nor CreateProcess() found" FALSE) +add_module(speech/qt5_espeak "Qt5 multimedia not found" FALSE) add_module(vehicle/gpsd_dbus "dbus-glib-1 not found" FALSE) add_module(vehicle/qt5 "Qt5 libraries not found" FALSE) add_module(speech/speech_dispatcher "speech_dispatcher lib not found" FALSE) @@ -174,6 +177,8 @@ if (NOT DISABLE_QT) if (Qt5Widgets_FOUND OR Qt5Quick_FOUND) find_package(Qt5Svg REQUIRED) find_package(Qt5DBus REQUIRED) + find_package(Qt5Multimedia) + find_package(espeak) endif (Qt5Widgets_FOUND OR Qt5Quick_FOUND) if (Qt5Positioning_FOUND) find_package(Qt5Sensors REQUIRED) @@ -348,6 +353,19 @@ if (Qt5Widgets_FOUND OR Qt5Quick_FOUND) ${Qt5_ADDITIONAL_LIBRARIES} ${Qt5Svg_LIBRARIES} ${Qt5DBus_LIBRARIES}) + if (Qt5Multimedia_FOUND) + if (espeak_FOUND) + set_with_reason(support/espeak "native espeak found" FALSE) + set_with_reason(speech/qt5_espeak "Qt5Multimedia and libespeak found" TRUE + ${Qt5Multimedia_LIBRARIES} + ${espeak_LIBRARIES}) + else() + set_with_reason(support/espeak "native espeak missing" TRUE) + set_with_reason(INTERNAL_ESPEAK_COMPLETE "speech_qt5_espeak requires full libespeak" TRUE) + set_with_reason(speech/qt5_espeak "Qt5Multimedia found" TRUE + ${Qt5Multimedia_LIBRARIES}) + endif() + endif() endif () if (Qt5Positioning_FOUND) set_with_reason(vehicle/qt5 "Qt5 Positioning found" TRUE @@ -487,7 +505,6 @@ add_module(plugin/j1850 "Default" FALSE) add_module(speech/android "Default" FALSE) add_module(speech/espeak "Default" FALSE) add_module(speech/iphone "Default" FALSE) -add_plugin(support/espeak "Default" FALSE) add_module(vehicle/android "Default" FALSE) add_module(vehicle/iphone "Default" FALSE) add_module(vehicle/wince "Default" FALSE) @@ -567,6 +584,9 @@ if(WIN32 OR WINCE) SET(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff -I ${CMAKE_CURRENT_SOURCE_DIR}/navit/gui/win32/resources <DEFINES> -i <SOURCE> -o <OBJECT>") set_with_reason(support/ezxml "Windows detected" TRUE) set_with_reason(speech/espeak "Windows detected" TRUE) + #speech/espeak does use windows threads, and therefore needs only parts of + #espeak lib. INTERNAL_ESPEAK_COMPLETE controls this. + set_with_reason(INTERNAL_ESPEAK_COMPLETE "Windows detected" FALSE) set_with_reason(support/espeak "Windows detected" TRUE) set_with_reason(binding/win32 "Windows detected" TRUE) diff --git a/ci/build_linux.sh b/ci/build_linux.sh index 096651453..afaac34e2 100644 --- a/ci/build_linux.sh +++ b/ci/build_linux.sh @@ -1,4 +1,4 @@ -sudo apt-get install cmake libpng12-dev librsvg2-bin libfreetype6-dev libdbus-glib-1-dev g++ libgtk2.0-dev libqt5svg5-dev qtdeclarative5-qtquick2-plugin qtdeclarative5-window-plugin +sudo apt-get install cmake libpng12-dev librsvg2-bin libfreetype6-dev libdbus-glib-1-dev g++ libgtk2.0-dev libqt5svg5-dev qtdeclarative5-qtquick2-plugin qtdeclarative5-window-plugin qtmultimedia5-dev cmake_opts="-Dgraphics/qt_qpainter:BOOL=FALSE -Dgui/qml:BOOL=FALSE -DSVG2PNG:BOOL=FALSE -DSAMPLE_MAP=n -Dgraphics/gtk_drawing_area:BOOL=TRUE" diff --git a/cmake/Findespeak.cmake b/cmake/Findespeak.cmake new file mode 100644 index 000000000..6d390a9a8 --- /dev/null +++ b/cmake/Findespeak.cmake @@ -0,0 +1,29 @@ +# - Try to find espeak (with libespeak) +# Once done, this will define +# +# espeak_FOUND - system has Glib +# espeak_INCLUDE_DIRS - the Glib include directories +# espeak_LIBRARIES - link these to use Glib + +include(LibFindMacros) + +# espeak-related libraries +find_path(espeak_INCLUDE_DIR + NAMES speak_lib.h + PATHS /usr /sw/include + PATH_SUFFIXES espeak +) + +# Finally the library itself +find_library(espeak_LIBRARY + NAMES libespeak.so libespeak.a + PATHS /sw/lib +) + +# Set the include dir variables and the libraries and let libfind_process do the rest. +# NOTE: Singular variables for this library, plural for libraries this this lib depends on. +set(espeak_PROCESS_INCLUDES espeak_INCLUDE_DIR) +set(espeak_PROCESS_LIBS espeak_LIBRARY) +libfind_process(espeak) + + diff --git a/contrib/sailfish/navit-sailfish.spec b/contrib/sailfish/navit-sailfish.spec index c25af5fe5..9c7a79b9b 100755 --- a/contrib/sailfish/navit-sailfish.spec +++ b/contrib/sailfish/navit-sailfish.spec @@ -10,7 +10,7 @@ Name: harbour-navit Summary: Open Source car navigation system #Version: %{navit_version}_%{git_version} Version: 0.5.1 -Release: 1 +Release: 2 License: GPL Group: Applications/Productivity URL: http://navit-projet.org/ @@ -90,14 +90,13 @@ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ %make_install #copy in sailfish config cp %{navit_real_source}/contrib/sailfish/navit.xml %{buildroot}/usr/share/harbour-navit/navit.xml -#copy in espeak script -cp %{navit_real_source}/contrib/sailfish/say_de_DE.sh %{buildroot}/usr/bin/say_de_DE.sh %files %defattr(644, root, root, 755) %{_datadir}/harbour-navit/navit.xml %{_datadir}/harbour-navit/xpm/ %{_datadir}/harbour-navit/maps/osm_bbox_11.3,47.9,11.7,48.2.bin +%{_datadir}/harbour-navit/espeak-data/ %{_datadir}/applications/harbour-navit.desktop %{_datadir}/icons/hicolor/256x256/apps/harbour-navit.png %{_datadir}/icons/hicolor/128x128/apps/harbour-navit.png @@ -106,12 +105,12 @@ cp %{navit_real_source}/contrib/sailfish/say_de_DE.sh %{buildroot}/usr/bin/say_d %{_datadir}/icons/hicolor/22x22/apps/harbour-navit.png %{_datadir}/harbour-navit/locale/ %attr(755, root, root) %{_bindir}/harbour-navit -%attr(755, root, root) %{_bindir}/say_de_DE.sh %doc %{_mandir}/man1/harbour-navit.1.gz %doc %{_mandir}/man1/maptool.1.gz %changelog -*Mon Dec 14 2015 Initial sailfish release +*Wed May 03 2017 Use qt5_espeak *Mon Apr 10 2017 Almost harbour valid +*Mon Dec 14 2015 Initial sailfish release - Initial package. diff --git a/contrib/sailfish/navit.xml b/contrib/sailfish/navit.xml index faa5b23a1..4f2c96941 100755 --- a/contrib/sailfish/navit.xml +++ b/contrib/sailfish/navit.xml @@ -384,7 +384,7 @@ Waypoint</text></img> If you have a speech synthesizer like festival lite installed, you can get turn by turn directions out of navit. Please set the "cps"-value to how many characters your tts engine approximately speaks per second. The default is text output to the shell --> <!-- speech type="cmdline" data="echo 'Fix the speech tag in navit.xml to let navit say:' '%s'" cps="15"/ --> - <speech type="cmdline" data="say_de_DE.sh '%s'" cps="15"/> + <speech type="qt5_espeak" cps="15"/> <!-- Instead of using a speech synthesizer, navit can also play pre-recorded samples for each word. See http://wiki.navit-project.org/index.php/Configuration for details. diff --git a/contrib/sailfish/say_de_DE.sh b/contrib/sailfish/say_de_DE.sh deleted file mode 100755 index bba4aa4fc..000000000 --- a/contrib/sailfish/say_de_DE.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -espeak -s150 -a 200 -vde "$1" --stdout | paplay diff --git a/navit/speech/qt5_espeak/CMakeLists.txt b/navit/speech/qt5_espeak/CMakeLists.txt new file mode 100644 index 000000000..dbeb1efb2 --- /dev/null +++ b/navit/speech/qt5_espeak/CMakeLists.txt @@ -0,0 +1,12 @@ +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) + +module_add_library(speech_qt5_espeak qt5_espeak.cpp Qt5EspeakAudioOut.cpp) +if(INTERNAL_ESPEAK_COMPLETE) + add_definitions ("-DINTERNAL_ESPEAK=1") + target_link_libraries(speech_qt5_espeak support_espeak) +else() + add_definitions ("-DINTERNAL_ESPEAK=0") +endif() diff --git a/navit/speech/qt5_espeak/Qt5EspeakAudioOut.cpp b/navit/speech/qt5_espeak/Qt5EspeakAudioOut.cpp new file mode 100644 index 000000000..da2c6b3e1 --- /dev/null +++ b/navit/speech/qt5_espeak/Qt5EspeakAudioOut.cpp @@ -0,0 +1,117 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2017-2017 Navit Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +// style with: clang-format -style=WebKit -i * + +#include "Qt5EspeakAudioOut.h" +extern "C" { +#include "debug.h" +} + +Qt5EspeakAudioOut::Qt5EspeakAudioOut(int samplerate, const char* category) +{ + data = new QByteArray(); + buffer = new QBuffer(data); + buffer->open(QIODevice::ReadWrite); + + QAudioFormat format; + // Set up the format, eg. + format.setSampleRate(samplerate); + format.setChannelCount(1); + format.setSampleSize(16); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + + QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); + if (!info.isFormatSupported(format)) { + dbg(lvl_error, + "Raw audio format not supported by backend, cannot play audio.\n"); + return; + } + + audio = new QAudioOutput(format, this); + /* try to set a rather huge buffer size in order to avoid chopping due to + * event loop + * not getting idle. Drawing may take just too long time. This hopefully + * removes the + * need to do multi threading with all its problems. May be a problem on + * systems with + * really low memory.*/ + audio->setBufferSize((samplerate * 1 /*ch*/ * 2 /*samplezize*/) * 5 /*seconds*/); + dbg(lvl_debug, "Buffer size is: %d\n", audio->bufferSize()); + if (category != NULL) + audio->setCategory(QString(category)); + + connect(audio, SIGNAL(stateChanged(QAudio::State)), this, + SLOT(handleStateChanged(QAudio::State))); + /* to cope with resume coming from other thread (of libespeak)*/ + connect(this, SIGNAL(call_resume(int)), this, SLOT(resume(int))); +} + +Qt5EspeakAudioOut::~Qt5EspeakAudioOut() +{ + delete (audio); + audio = NULL; + delete (buffer); + buffer = NULL; + delete (data); + data = NULL; +} + +void Qt5EspeakAudioOut::handleStateChanged(QAudio::State newState) +{ + dbg(lvl_debug, "Enter %d\n", newState); + switch (newState) { + case QAudio::ActiveState: + break; + case QAudio::SuspendedState: + break; + case QAudio::StoppedState: + break; + case QAudio::IdleState: + /*remove all data that was already read*/ + data->remove(0, buffer->pos()); + buffer->seek(0); + dbg(lvl_debug, "Size %d\n", data->size()); + break; + } +} + +void Qt5EspeakAudioOut::resume(int state) +{ + dbg(lvl_debug, "Enter %d\n", state); + if (audio->state() != QAudio::ActiveState) + audio->start(buffer); +} + +void Qt5EspeakAudioOut::addSamples(short* wav, int numsamples) +{ + dbg(lvl_debug, "Enter (%d samples)\n", numsamples); + + /*remove all data that was already read (if any)*/ + data->remove(0, buffer->pos()); + buffer->seek(0); + + if (numsamples > 0) { + data->append((const char*)wav, numsamples * sizeof(short)); + dbg(lvl_debug, "%ld samples in buffer\n", + (long int)(buffer->size() / sizeof(short))); + emit call_resume(numsamples); + } +} diff --git a/navit/speech/qt5_espeak/Qt5EspeakAudioOut.h b/navit/speech/qt5_espeak/Qt5EspeakAudioOut.h new file mode 100644 index 000000000..d7eb632fd --- /dev/null +++ b/navit/speech/qt5_espeak/Qt5EspeakAudioOut.h @@ -0,0 +1,55 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2017-2017 Navit Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +// style with: clang-format -style=WebKit -i * + +#ifndef Qt5EspeakAudioOut_h +#define Qt5EspeakAudioOut_h +#include <QAudioOutput> +#include <QBuffer> +#include <QByteArray> +#include <QObject> +class Qt5EspeakAudioOut : public QObject { + Q_OBJECT +public: + /* Instantiate this. Parameters are the sample rate to use, + * and the category to sort this audio output to. Not all platforms + * will honour category */ + Qt5EspeakAudioOut(int samplerate, const char* category); + ~Qt5EspeakAudioOut(); + /* Add new samples to this class. The samples will be played*/ + void addSamples(short* wav, int numsamples); +public slots: + /* Deal with QAudioOutput status changes */ + void handleStateChanged(QAudio::State newState); + /* Cause QAusioOutput to resume playing (after samples were added)*/ + void resume(int state); +signals: + /* Cause QAusioOutput to resume playing. Emit this from different thread + * as this is not threadsafe*/ + void call_resume(int state); + +protected: +private: + /* internal buffer */ + QByteArray* data; + QBuffer* buffer; + /* audio output class */ + QAudioOutput* audio; +}; +#endif diff --git a/navit/speech/qt5_espeak/qt5_espeak.cpp b/navit/speech/qt5_espeak/qt5_espeak.cpp new file mode 100755 index 000000000..8d108eae6 --- /dev/null +++ b/navit/speech/qt5_espeak/qt5_espeak.cpp @@ -0,0 +1,252 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2017-2017 Navit Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +// style with: clang-format -style=WebKit -i * +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include <glib.h> +extern "C" { +#include "item.h" +#include "attr.h" +#include "debug.h" +#include "file.h" +#include "plugin.h" +#include "speech.h" +#include "util.h" +} +#include <espeak/speak_lib.h> + +#include "Qt5EspeakAudioOut.h" + +#ifndef INTERNAL_ESPEAK +#define INTERNAL_ESPEAK 0 +#endif +#define BUFFERLENGTH 1000 +#define SAMPLE_SIZE 16 + +// -------------------------------------------------------------------- + +/* private context handle type */ +struct speech_priv { + gchar* path_home; + int sample_rate; + + bool espeak_ok; + bool audio_ok; + Qt5EspeakAudioOut* audio; +}; + +/* callback from espeak to transfer generated samples */ +int qt5_espeak_SynthCallback(short* wav, int numsamples, espeak_EVENT* events) +{ + struct speech_priv* pr = NULL; + dbg(lvl_debug, "Callback %d samples\n", numsamples); + if (events != NULL) + pr = (struct speech_priv*)events->user_data; + if ((pr != NULL) && (pr->audio != NULL)) { + pr->audio->addSamples(wav, numsamples); + } + + return 0; + /* 0 - continue synthesis */ + /* 1 - abort synthesis */ +} + +/* set up espeak */ +static bool +qt5_espeak_init_espeak(struct speech_priv* sr, struct attr** attrs) +{ + struct attr* path; + + /* prepare espeak library path home */ + path = attr_search(attrs, NULL, attr_path); + if (path) { + sr->path_home = g_strdup(path->u.str); + } else { +#if INTERNAL_ESPEAK + sr->path_home = g_strdup_printf("%s", getenv("NAVIT_SHAREDIR")); +#else + /* since no path was given by config, we don't know the path to external + * espeak data. + * so give NULL to use default path */ + sr->path_home = NULL; +#endif + } + dbg(lvl_debug, "path_home set to %s\n", sr->path_home); +#if INTERNAL_ESPEAK + /*internal espeak is configured to support only synchronous modes */ + sr->sample_rate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, BUFFERLENGTH, sr->path_home, 0); +#else + // sr->sample_rate = espeak_Initialize(AUDIO_OUTPUT_RETRIEVAL, BUFFERLENGTH, + // sr->path_home, 0); + sr->sample_rate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, BUFFERLENGTH, sr->path_home, 0); +#endif + if (sr->sample_rate == EE_INTERNAL_ERROR) { + dbg(lvl_error, "Init failed %d\n", sr->sample_rate); + return 1; + } + dbg(lvl_error, "Sample rate is %d\n", sr->sample_rate); + + espeak_SetSynthCallback(qt5_espeak_SynthCallback); + return TRUE; +} + +/* set language to espeak */ +static bool +qt5_espeak_init_language(struct speech_priv* pr, struct attr** attrs) +{ + struct attr* language; + gchar* lang_str = NULL; + espeak_ERROR error; + espeak_VOICE voice_spec; + + language = attr_search(attrs, NULL, attr_language); + if (language) { + lang_str = g_strdup(language->u.str); + } else { + char* lang_env = getenv("LANG"); + if (lang_env != NULL) { + char* country; + + lang_str = g_strdup(lang_env); + /* convert to all lower */ + strtolower(lang_str, lang_env); + + /* extract language code from LANG environment */ + dbg(lvl_debug, "%s\n", lang_str); + country = strchr(lang_str, '_'); + dbg(lvl_debug, "%s\n", country); + if (country) { + lang_str[country - lang_str] = '\0'; + } + dbg(lvl_debug, "espeak lang: %s\n", lang_str); + } + } + /*TODO (golden water tap): expose all those values as attrs */ + voice_spec.name = NULL; /* here we could select a specific voice */ + voice_spec.languages = lang_str; + voice_spec.gender = 0; /* 0 auto, 1 male, 2 female */ + voice_spec.age = 20; + voice_spec.variant = 0; + + error = espeak_SetVoiceByProperties(&voice_spec); + if (lang_str != NULL) + g_free(lang_str); + if (error != EE_OK) { + dbg(lvl_error, "Unable to set Language\n"); + return false; + } + return true; +} + +/* init audio system */ +static bool +qt5_espeak_init_audio(struct speech_priv* sr, const char* category) +{ + try { + sr->audio = new Qt5EspeakAudioOut(sr->sample_rate, category); + } catch (void* exception) { + dbg(lvl_error, "Exception on Qt5EspeakAudioOut creation"); + return false; + } + return true; +} + +/* will be called to say new text. + * sr - private handle + * text - new (utf8) text + */ +static int +qt5_espeak_say(struct speech_priv* sr, const char* text) +{ + espeak_ERROR error; + dbg(lvl_debug, "Say \"%s\"\n", text); + error = espeak_Synth(text, strlen(text), 0, POS_CHARACTER, 0, + espeakCHARS_UTF8, NULL, sr); + if (error != EE_OK) + dbg(lvl_error, "Unable to speak! error == %d\n", error); + + return 0; +} + +/* destructor */ +static void +qt5_espeak_destroy(struct speech_priv* sr) +{ + dbg(lvl_debug, "Enter\n"); + + if (sr->path_home != NULL) + g_free(sr->path_home); + /* destroy handle */ + g_free(sr); +} + +/* the plugin interface for speech */ +static struct speech_methods qt5_espeak_meth = { + qt5_espeak_destroy, qt5_espeak_say, +}; + +/* create new instance of this. + * meth - return plugin interface + * attr - get decoded attributes from config + * parent - get parent attributes from config + */ +static struct speech_priv* +qt5_espeak_new(struct speech_methods* meth, struct attr** attrs, + struct attr* parent) +{ + struct speech_priv* sr = NULL; + dbg(lvl_debug, "Enter\n"); + + /* allocate handle */ + sr = g_new0(struct speech_priv, 1); + sr->path_home = NULL; + sr->espeak_ok = false; + sr->audio_ok = false; + sr->sample_rate = 0; + sr->audio = NULL; + /* register our methods */ + *meth = qt5_espeak_meth; + + /* init espeak library */ + if (!(sr->espeak_ok = qt5_espeak_init_espeak(sr, attrs))) { + dbg(lvl_error, "Unable to initialize espeak library\n"); + } + + /* init espeak voice and language */ + if (!(sr->espeak_ok = qt5_espeak_init_language(sr, attrs))) { + dbg(lvl_error, "Unable to initialize espeak language\n"); + } + + /* init qt5 audio using default category*/ + if (!(sr->audio_ok = qt5_espeak_init_audio(sr, NULL))) { + dbg(lvl_error, "Unable to initialize audio\n"); + } + return sr; +} + +/* initialize this as plugin */ +void plugin_init(void) +{ + dbg(lvl_debug, "Enter\n"); + plugin_register_category_speech("qt5_espeak", qt5_espeak_new); +} diff --git a/navit/support/espeak/CMakeLists.txt b/navit/support/espeak/CMakeLists.txt index c1c520133..6a20511e7 100644 --- a/navit/support/espeak/CMakeLists.txt +++ b/navit/support/espeak/CMakeLists.txt @@ -1,5 +1,9 @@ + +if(INTERNAL_ESPEAK_COMPLETE) + set(ESPEAK_LIBRARY_ADDITIONAL speak_lib.c) +endif() supportlib_add_library(support_espeak compiledict.c dictionary.c intonation.c readclause.c setlengths.c numbers.c synth_mbrola.c synthdata.c synthesize.c translate.c tr_languages.c voices.c wavegen.c - phonemelist.c klatt.c speak_init.c) + phonemelist.c klatt.c speak_init.c ${ESPEAK_LIBRARY_ADDITIONAL}) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/espeak-data DESTINATION ${SHARE_DIR} PATTERN ".svn" EXCLUDE) diff --git a/navit/support/espeak/speak_lib.h b/navit/support/espeak/speak_lib.h index 04482609c..25c24c173 100644 --- a/navit/support/espeak/speak_lib.h +++ b/navit/support/espeak/speak_lib.h @@ -26,7 +26,7 @@ /*************************************************************/ #include <stdio.h> - +#include <wchar.h> #define ESPEAK_API_REVISION 5 /* Revision 2 diff --git a/navit/support/espeak/speech.h b/navit/support/espeak/speech.h index 790b177d8..9673b0c8b 100755 --- a/navit/support/espeak/speech.h +++ b/navit/support/espeak/speech.h @@ -70,7 +70,11 @@ int LookupMnem(MNEM_TAB *table, char *string); #define ESPEAK_API #else #define N_PATH_HOME 150 +#ifdef __cplusplus #define ESPEAK_API extern "C" +#else +#define ESPEAK_API +#endif #endif extern char path_home[N_PATH_HOME]; // this is the espeak-data directory |