summaryrefslogtreecommitdiff
path: root/navit/speech
diff options
context:
space:
mode:
authorrikky <rikky@ffa7fe5e-494d-0410-b361-a75ebd5db220>2009-10-24 19:12:02 +0000
committerrikky <rikky@ffa7fe5e-494d-0410-b361-a75ebd5db220>2009-10-24 19:12:02 +0000
commit282da42893f9496ad9dc3cd7706a3d7856af810d (patch)
tree7406cbaa653517fc4a0a671d5b127a300833002e /navit/speech
parentf8b84fea724ca1d71e64d7a38d230cee41a5166d (diff)
downloadnavit-svn-282da42893f9496ad9dc3cd7706a3d7856af810d.tar.gz
Add:speech/espeak:Added new speech plugin for tts output by espeak. This first version only supports win32.
git-svn-id: http://svn.code.sf.net/p/navit/code/trunk/navit@2684 ffa7fe5e-494d-0410-b361-a75ebd5db220
Diffstat (limited to 'navit/speech')
-rw-r--r--navit/speech/Makefile.am3
-rw-r--r--navit/speech/espeak/Makefile.am6
-rw-r--r--navit/speech/espeak/speak.c475
3 files changed, 484 insertions, 0 deletions
diff --git a/navit/speech/Makefile.am b/navit/speech/Makefile.am
index 1d40c8e1..ddab19c7 100644
--- a/navit/speech/Makefile.am
+++ b/navit/speech/Makefile.am
@@ -5,6 +5,9 @@ endif
if SPEECH_CMDLINE
SUBDIRS += cmdline
endif
+if SPEECH_ESPEAK
+ SUBDIRS += espeak
+endif
if SPEECH_SPEECH_DISPATCHER
SUBDIRS += speech_dispatcher
endif
diff --git a/navit/speech/espeak/Makefile.am b/navit/speech/espeak/Makefile.am
new file mode 100644
index 00000000..aef0ec81
--- /dev/null
+++ b/navit/speech/espeak/Makefile.am
@@ -0,0 +1,6 @@
+include $(top_srcdir)/Makefile.inc
+AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=speech_espeak
+modulespeech_LTLIBRARIES = libspeech_espeak.la
+libspeech_espeak_la_SOURCES = speak.c
+libspeech_espeak_la_LDFLAGS = -module -avoid-version @MODULE_LDFLAGS@
+
diff --git a/navit/speech/espeak/speak.c b/navit/speech/espeak/speak.c
new file mode 100644
index 00000000..6a8d10cb
--- /dev/null
+++ b/navit/speech/espeak/speak.c
@@ -0,0 +1,475 @@
+/**
+ * Navit, a modular navigation system.
+ * Copyright (C) 2005-2008 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.
+ */
+
+#define _WIN32_WINNT 0x0500
+
+#include "config.h"
+
+#ifdef HAVE_API_WIN32_BASE
+#include <windows.h>
+#include <mmsystem.h>
+#include <winreg.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <sys/stat.h>
+#include <glib.h>
+#include "item.h"
+#include "plugin.h"
+#include "speech.h"
+#include "debug.h"
+
+#include "support/espeak/speech.h"
+#include "support/espeak/speak_lib.h"
+#include "support/espeak/phoneme.h"
+#include "support/espeak/synthesize.h"
+#include "support/espeak/voice.h"
+#include "support/espeak/translate.h"
+
+
+#define SAMPLES_PER_BUFFER 1024
+#define BUFFERS 8
+
+
+// ----- some stuff needed by espeak ----------------------------------
+char path_home[N_PATH_HOME]; // this is the espeak-data directory
+int (* uri_callback)(int, const char *, const char *) = NULL;
+int (* phoneme_callback)(const char *) = NULL;
+FILE *f_wave = NULL;
+
+int GetFileLength(const char *filename)
+{
+ struct stat statbuf;
+
+ if(stat(filename,&statbuf) != 0)
+ return(0);
+
+ if((statbuf.st_mode & S_IFMT) == S_IFDIR)
+ return(-2); // a directory
+
+ return(statbuf.st_size);
+}
+
+void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
+{
+}
+
+char *Alloc(int size)
+{
+ return g_malloc(size);
+}
+
+void Free(void *ptr)
+{
+ g_free(ptr);
+}
+
+// --------------------------------------------------------------------
+
+
+enum speech_messages
+{
+ msg_say = WM_USER,
+ msg_exit
+};
+
+enum speech_state
+{
+ state_available,
+ state_speaking_phase_1,
+ state_speaking_phase_2,
+ state_speaking_phase_3
+
+};
+
+struct speech_priv {
+ GList *free_buffers;
+ HWAVEOUT h_wave_out;
+ enum speech_state state;
+ GList *phrases;
+ HWND h_queue;
+ HANDLE h_message_thread;
+};
+
+
+static void waveout_close(struct speech_priv* sp_priv)
+{
+ waveOutClose(sp_priv->h_wave_out);
+}
+
+static BOOL waveout_open(struct speech_priv* sp_priv)
+{
+ MMRESULT result = 0;
+
+ HWAVEOUT hwo;
+ static WAVEFORMATEX wmTemp;
+ wmTemp.wFormatTag = WAVE_FORMAT_PCM;
+ wmTemp.nChannels = 1;
+ wmTemp.nSamplesPerSec = 22050;
+ wmTemp.wBitsPerSample = 16;
+ wmTemp.nBlockAlign = wmTemp.nChannels * wmTemp.wBitsPerSample / 8;
+ wmTemp.nAvgBytesPerSec = wmTemp.nSamplesPerSec * wmTemp.nBlockAlign;
+ wmTemp.cbSize = 0;
+ result = waveOutOpen(&hwo, (UINT) WAVE_MAPPER, &wmTemp, (DWORD)sp_priv->h_queue, (DWORD)sp_priv, CALLBACK_WINDOW);
+ sp_priv->h_wave_out = hwo;
+
+ return (result==MMSYSERR_NOERROR);
+}
+
+static int wave_out(struct speech_priv* sp_priv)
+{
+ unsigned char wav_outbuf[SAMPLES_PER_BUFFER * 2];
+ int isDone;
+
+ WAVEHDR *WaveHeader = g_list_first(sp_priv->free_buffers)->data;
+ sp_priv->free_buffers = g_list_remove(sp_priv->free_buffers, WaveHeader);
+
+ out_ptr = out_start = wav_outbuf;
+ out_end = wav_outbuf + sizeof(wav_outbuf);
+
+ isDone = WavegenFill(0);
+
+ if ( out_ptr < out_end )
+ {
+ memset ( out_ptr, 0, out_end - out_ptr );
+ }
+ memcpy(WaveHeader->lpData, wav_outbuf, WaveHeader->dwBufferLength);
+ waveOutWrite(sp_priv->h_wave_out, WaveHeader, sizeof(WAVEHDR));
+
+ return isDone;
+}
+
+static BOOL initialise(void)
+{
+ int param;
+ int result;
+
+ WavegenInit(22050,0); // 22050
+ if((result = LoadPhData()) != 1)
+ {
+ if(result == -1)
+ {
+ dbg(0, "Failed to load espeak-data\n");
+ return FALSE;
+ }
+ else
+ dbg(0, "Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n",result,version_phdata,path_home);
+ }
+ LoadConfig();
+ SetVoiceStack(NULL);
+ SynthesizeInit();
+
+ for(param=0; param<N_SPEECH_PARAM; param++)
+ param_stack[0].parameter[param] = param_defaults[param];
+
+ return TRUE;
+}
+
+static void fill_buffer(struct speech_priv *this)
+{
+ while ( this->free_buffers && this->state != state_speaking_phase_3 )
+ {
+ if(Generate(phoneme_list,&n_phoneme_list,1)==0)
+ {
+ if (!SpeakNextClause(NULL,NULL,1))
+ {
+ this->state = state_speaking_phase_2;
+ }
+ }
+
+ if ( wave_out(this)!= 0 && this->state == state_speaking_phase_2)
+ {
+ this->state = state_speaking_phase_3;
+ }
+ }
+}
+
+static void start_speaking(struct speech_priv* sp_priv)
+{
+ char *phrase = g_list_first(sp_priv->phrases)->data;
+
+ sp_priv->state = state_speaking_phase_1;
+
+ SpeakNextClause(NULL, phrase,0);
+ wave_out(sp_priv);
+ fill_buffer(sp_priv);
+}
+
+static LRESULT CALLBACK speech_message_handler( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ dbg(1, "message_handler called\n");
+
+ switch (uMsg)
+ {
+ case msg_say:
+ {
+ struct speech_priv* sp_priv = (struct speech_priv*)wParam;
+ sp_priv->phrases = g_list_append(sp_priv->phrases, (char*)lParam);
+
+ if ( sp_priv->state == state_available )
+ {
+ start_speaking(sp_priv);
+ }
+
+ }
+ break;
+ case MM_WOM_DONE:
+ {
+ dbg(2, "Wave buffer done\n");
+ WAVEHDR *WaveHeader = (WAVEHDR *)lParam;
+
+ struct speech_priv* sp_priv = (struct speech_priv*)WaveHeader->dwUser;
+ sp_priv->free_buffers = g_list_append(sp_priv->free_buffers, WaveHeader);
+
+ if ( sp_priv->state != state_speaking_phase_3)
+ {
+ fill_buffer(sp_priv);
+ }
+ else if ( g_list_length(sp_priv->free_buffers) == BUFFERS && sp_priv->state == state_speaking_phase_3 )
+ {
+ // remove the spoken phrase from the list
+ char *phrase = g_list_first(sp_priv->phrases)->data;
+ g_free( phrase );
+ sp_priv->phrases = g_list_remove(sp_priv->phrases, phrase);
+
+ if ( sp_priv->phrases )
+ {
+ start_speaking(sp_priv);
+ }
+ else
+ {
+ sp_priv->state = state_available;
+ }
+ }
+ }
+ break;
+ case msg_exit:
+ ExitThread(0);
+ break;
+
+ default:
+ break;
+
+ }
+
+ return TRUE;
+}
+
+static void speech_message_dispatcher( struct speech_priv * sp_priv)
+{
+ BOOL bRet;
+ MSG msg;
+
+ while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
+ {
+ if (bRet == -1)
+ {
+ dbg(0, "Error getting message from queue\n");
+ break;
+ }
+ else
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+}
+
+static void create_buffers(struct speech_priv *sp_priv)
+{
+ int buffer_counter;
+ for (buffer_counter = 0; buffer_counter < BUFFERS; buffer_counter++)
+ {
+ WAVEHDR *WaveHeader = g_new0(WAVEHDR, 1);
+
+ WaveHeader->dwBufferLength = SAMPLES_PER_BUFFER * 2;
+ WaveHeader->lpData = (char *)VirtualAlloc(0, WaveHeader->dwBufferLength, MEM_COMMIT, PAGE_READWRITE);
+ WaveHeader->dwUser = (DWORD)sp_priv;
+ waveOutPrepareHeader(sp_priv->h_wave_out, WaveHeader, sizeof(WAVEHDR));
+
+ sp_priv->free_buffers = g_list_append( sp_priv->free_buffers, WaveHeader );
+ }
+}
+
+static DWORD startThread( LPVOID sp_priv)
+{
+ struct speech_priv *this = (struct speech_priv *) sp_priv;
+ // Create message queue
+ TCHAR *g_szClassName = TEXT("SpeechQueue");
+ WNDCLASS wc;
+ HWND hwnd;
+
+
+ memset(&wc, 0 , sizeof(WNDCLASS));
+ wc.lpfnWndProc = speech_message_handler;
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.lpszClassName = g_szClassName;
+
+ if (!RegisterClass(&wc))
+ {
+ dbg(0, "Window registration for message queue failed\n");
+ return 1;
+ }
+
+ HWND hWndParent = NULL;
+#ifndef HAVE_API_WIN32_CE
+ hWndParent = HWND_MESSAGE;
+#endif
+
+ // create a message only window
+ hwnd = CreateWindow(
+ g_szClassName,
+ TEXT("Navit"),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ hWndParent,
+ NULL,
+ GetModuleHandle(NULL),
+ NULL);
+
+ if (hwnd == NULL)
+ {
+ dbg(0, "Window creation failed: %d\n", GetLastError());
+ return 1;
+ }
+
+ this->h_queue = hwnd;
+ this->phrases = NULL;
+ this->state = state_available;
+
+ if(!waveout_open(this))
+ {
+ dbg(0, "Can't open wave output\n");
+ return 1;
+ }
+
+ this->free_buffers = NULL;
+ create_buffers(this);
+
+ speech_message_dispatcher(this);
+
+ return 0;
+}
+
+static int
+espeak_say(struct speech_priv *this, const char *text)
+{
+ dbg(1, "Speak: '%s'\n", text);
+ char *phrase = g_strdup(text);
+
+ if (!PostMessage(this->h_queue, msg_say, (WPARAM)this, (LPARAM)phrase))
+ {
+ dbg(0, "PostThreadMessage 'say' failed\n");
+ }
+
+ return 0;
+}
+
+static void free_list(gpointer pointer, gpointer this )
+{
+ if ( this )
+ {
+ struct speech_priv *sp_priv = (struct speech_priv *)this;
+ WAVEHDR *WaveHeader = (WAVEHDR *)pointer;
+
+ waveOutUnprepareHeader(sp_priv->h_wave_out, WaveHeader, sizeof(WAVEHDR));
+ VirtualFree(WaveHeader->lpData, WaveHeader->dwBufferLength, MEM_DECOMMIT);
+ }
+ g_free(pointer);
+}
+
+static void
+espeak_destroy(struct speech_priv *this)
+{
+ g_list_foreach( this->free_buffers, free_list, (gpointer)this );
+ g_list_free( this->free_buffers );
+
+ g_list_foreach( this->phrases, free_list, 0 );
+ g_list_free(this->phrases);
+
+ waveout_close(this);
+ g_free(this);
+}
+
+static struct speech_methods espeak_meth = {
+ espeak_destroy,
+ espeak_say,
+};
+
+static struct speech_priv *
+espeak_new(struct speech_methods *meth, struct attr **attrs) {
+ struct speech_priv *this = NULL;
+ struct attr *path;
+ struct attr *language;
+ char *lang_str = "default";
+
+ path=attr_search(attrs, NULL, attr_path);
+ if (! path)
+ {
+ dbg(0, "Missing path to espeak data\n");
+ return NULL;
+ }
+ strcpy(path_home,path->u.str);
+
+ if ( !initialise() )
+ {
+ return NULL;
+ }
+
+ language=attr_search(attrs, NULL, attr_language);
+ if ( language )
+ {
+ lang_str = language->u.str;
+ }
+
+ if(SetVoiceByName(lang_str) != EE_OK)
+ {
+ dbg(0, "Error setting language to: '%s'\n", lang_str);
+ }
+
+ SetParameter(espeakRATE,170,0);
+ SetParameter(espeakVOLUME,100,0);
+ SetParameter(espeakCAPITALS,option_capitals,0);
+ SetParameter(espeakPUNCTUATION,option_punctuation,0);
+ SetParameter(espeakWORDGAP,0,0);
+
+// if(pitch_adjustment != 50)
+// {
+// SetParameter(espeakPITCH,pitch_adjustment,0);
+// }
+ DoVoiceChange(voice);
+
+ this=g_new(struct speech_priv,1);
+ this->h_message_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)startThread, (PVOID)this, 0, NULL);
+
+ *meth=espeak_meth;
+
+
+ return this;
+}
+
+void
+plugin_init(void)
+{
+ plugin_register_speech_type("espeak", espeak_new);
+}