summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Wildemann <metalstrolch@users.noreply.github.com>2017-05-05 20:03:51 +0200
committerPierre GRANDIN <pgrandin@users.noreply.github.com>2017-05-05 11:03:51 -0700
commit8c48382a411a0c16f1d1cf7d015ea921f1adb2d0 (patch)
treec88595bba7cb50af51e63c95b4cc3e5dea84ca39
parentb6d8bc28e34d0f3623659a9c724fa24310cfd5d7 (diff)
downloadnavit-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-xCMakeLists.txt22
-rw-r--r--ci/build_linux.sh2
-rw-r--r--cmake/Findespeak.cmake29
-rwxr-xr-xcontrib/sailfish/navit-sailfish.spec9
-rwxr-xr-xcontrib/sailfish/navit.xml2
-rwxr-xr-xcontrib/sailfish/say_de_DE.sh2
-rw-r--r--navit/speech/qt5_espeak/CMakeLists.txt12
-rw-r--r--navit/speech/qt5_espeak/Qt5EspeakAudioOut.cpp117
-rw-r--r--navit/speech/qt5_espeak/Qt5EspeakAudioOut.h55
-rwxr-xr-xnavit/speech/qt5_espeak/qt5_espeak.cpp252
-rw-r--r--navit/support/espeak/CMakeLists.txt6
-rw-r--r--navit/support/espeak/speak_lib.h2
-rwxr-xr-xnavit/support/espeak/speech.h4
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