2 * Navit, a modular navigation system.
3 * Copyright (C) 2005-2008 Navit Team
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * version 2 as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 #define _WIN32_WINNT 0x0500
24 #ifdef HAVE_API_WIN32_BASE
41 #include "support/espeak/speech.h"
42 #include "support/espeak/speak_lib.h"
43 #include "support/espeak/phoneme.h"
44 #include "support/espeak/synthesize.h"
45 #include "support/espeak/voice.h"
46 #include "support/espeak/translate.h"
49 #define SAMPLES_PER_BUFFER 1024
53 // ----- some stuff needed by espeak ----------------------------------
54 char path_home[N_PATH_HOME]; // this is the espeak-data directory
55 int (* uri_callback)(int, const char *, const char *) = NULL;
56 int (* phoneme_callback)(const char *) = NULL;
59 int GetFileLength(const char *filename)
63 if(stat(filename,&statbuf) != 0)
66 if((statbuf.st_mode & S_IFMT) == S_IFDIR)
67 return(-2); // a directory
69 return(statbuf.st_size);
72 void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
78 return g_malloc(size);
86 // --------------------------------------------------------------------
98 state_speaking_phase_1,
99 state_speaking_phase_2,
100 state_speaking_phase_3
107 enum speech_state state;
110 HANDLE h_message_thread;
114 static void waveout_close(struct speech_priv* sp_priv)
116 waveOutClose(sp_priv->h_wave_out);
119 static BOOL waveout_open(struct speech_priv* sp_priv)
124 static WAVEFORMATEX wmTemp;
125 wmTemp.wFormatTag = WAVE_FORMAT_PCM;
126 wmTemp.nChannels = 1;
127 wmTemp.nSamplesPerSec = 22050;
128 wmTemp.wBitsPerSample = 16;
129 wmTemp.nBlockAlign = wmTemp.nChannels * wmTemp.wBitsPerSample / 8;
130 wmTemp.nAvgBytesPerSec = wmTemp.nSamplesPerSec * wmTemp.nBlockAlign;
132 result = waveOutOpen(&hwo, (UINT) WAVE_MAPPER, &wmTemp, (DWORD)sp_priv->h_queue, (DWORD)sp_priv, CALLBACK_WINDOW);
133 sp_priv->h_wave_out = hwo;
135 return (result==MMSYSERR_NOERROR);
138 static int wave_out(struct speech_priv* sp_priv)
140 unsigned char wav_outbuf[SAMPLES_PER_BUFFER * 2];
143 WAVEHDR *WaveHeader = g_list_first(sp_priv->free_buffers)->data;
144 sp_priv->free_buffers = g_list_remove(sp_priv->free_buffers, WaveHeader);
146 out_ptr = out_start = wav_outbuf;
147 out_end = wav_outbuf + sizeof(wav_outbuf);
149 isDone = WavegenFill(0);
151 if ( out_ptr < out_end )
153 memset ( out_ptr, 0, out_end - out_ptr );
155 memcpy(WaveHeader->lpData, wav_outbuf, WaveHeader->dwBufferLength);
156 waveOutWrite(sp_priv->h_wave_out, WaveHeader, sizeof(WAVEHDR));
161 static BOOL initialise(void)
166 WavegenInit(22050,0); // 22050
167 if((result = LoadPhData()) != 1)
171 dbg(0, "Failed to load espeak-data\n");
175 dbg(0, "Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n",result,version_phdata,path_home);
181 for(param=0; param<N_SPEECH_PARAM; param++)
182 param_stack[0].parameter[param] = param_defaults[param];
187 static void fill_buffer(struct speech_priv *this)
189 while ( this->free_buffers && this->state != state_speaking_phase_3 )
191 if(Generate(phoneme_list,&n_phoneme_list,1)==0)
193 if (!SpeakNextClause(NULL,NULL,1))
195 this->state = state_speaking_phase_2;
199 if ( wave_out(this)!= 0 && this->state == state_speaking_phase_2)
201 this->state = state_speaking_phase_3;
206 static void start_speaking(struct speech_priv* sp_priv)
208 char *phrase = g_list_first(sp_priv->phrases)->data;
210 sp_priv->state = state_speaking_phase_1;
212 SpeakNextClause(NULL, phrase,0);
214 fill_buffer(sp_priv);
217 static LRESULT CALLBACK speech_message_handler( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
219 dbg(1, "message_handler called\n");
225 struct speech_priv* sp_priv = (struct speech_priv*)wParam;
226 sp_priv->phrases = g_list_append(sp_priv->phrases, (char*)lParam);
228 if ( sp_priv->state == state_available )
230 start_speaking(sp_priv);
237 dbg(2, "Wave buffer done\n");
238 WAVEHDR *WaveHeader = (WAVEHDR *)lParam;
240 struct speech_priv* sp_priv = (struct speech_priv*)WaveHeader->dwUser;
241 sp_priv->free_buffers = g_list_append(sp_priv->free_buffers, WaveHeader);
243 if ( sp_priv->state != state_speaking_phase_3)
245 fill_buffer(sp_priv);
247 else if ( g_list_length(sp_priv->free_buffers) == BUFFERS && sp_priv->state == state_speaking_phase_3 )
249 // remove the spoken phrase from the list
250 char *phrase = g_list_first(sp_priv->phrases)->data;
252 sp_priv->phrases = g_list_remove(sp_priv->phrases, phrase);
254 if ( sp_priv->phrases )
256 start_speaking(sp_priv);
260 sp_priv->state = state_available;
277 static void speech_message_dispatcher( struct speech_priv * sp_priv)
282 while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
286 dbg(0, "Error getting message from queue\n");
291 TranslateMessage(&msg);
292 DispatchMessage(&msg);
297 static void create_buffers(struct speech_priv *sp_priv)
300 for (buffer_counter = 0; buffer_counter < BUFFERS; buffer_counter++)
302 WAVEHDR *WaveHeader = g_new0(WAVEHDR, 1);
304 WaveHeader->dwBufferLength = SAMPLES_PER_BUFFER * 2;
305 WaveHeader->lpData = (char *)VirtualAlloc(0, WaveHeader->dwBufferLength, MEM_COMMIT, PAGE_READWRITE);
306 WaveHeader->dwUser = (DWORD)sp_priv;
307 waveOutPrepareHeader(sp_priv->h_wave_out, WaveHeader, sizeof(WAVEHDR));
309 sp_priv->free_buffers = g_list_append( sp_priv->free_buffers, WaveHeader );
313 static DWORD startThread( LPVOID sp_priv)
315 struct speech_priv *this = (struct speech_priv *) sp_priv;
316 // Create message queue
317 TCHAR *g_szClassName = TEXT("SpeechQueue");
322 memset(&wc, 0 , sizeof(WNDCLASS));
323 wc.lpfnWndProc = speech_message_handler;
324 wc.hInstance = GetModuleHandle(NULL);
325 wc.lpszClassName = g_szClassName;
327 if (!RegisterClass(&wc))
329 dbg(0, "Window registration for message queue failed\n");
333 HWND hWndParent = NULL;
334 #ifndef HAVE_API_WIN32_CE
335 hWndParent = HWND_MESSAGE;
338 // create a message only window
349 GetModuleHandle(NULL),
354 dbg(0, "Window creation failed: %d\n", GetLastError());
358 this->h_queue = hwnd;
359 this->phrases = NULL;
360 this->state = state_available;
362 if(!waveout_open(this))
364 dbg(0, "Can't open wave output\n");
368 this->free_buffers = NULL;
369 create_buffers(this);
371 speech_message_dispatcher(this);
377 espeak_say(struct speech_priv *this, const char *text)
379 dbg(1, "Speak: '%s'\n", text);
380 char *phrase = g_strdup(text);
382 if (!PostMessage(this->h_queue, msg_say, (WPARAM)this, (LPARAM)phrase))
384 dbg(0, "PostThreadMessage 'say' failed\n");
390 static void free_list(gpointer pointer, gpointer this )
394 struct speech_priv *sp_priv = (struct speech_priv *)this;
395 WAVEHDR *WaveHeader = (WAVEHDR *)pointer;
397 waveOutUnprepareHeader(sp_priv->h_wave_out, WaveHeader, sizeof(WAVEHDR));
398 VirtualFree(WaveHeader->lpData, WaveHeader->dwBufferLength, MEM_DECOMMIT);
404 espeak_destroy(struct speech_priv *this)
406 g_list_foreach( this->free_buffers, free_list, (gpointer)this );
407 g_list_free( this->free_buffers );
409 g_list_foreach( this->phrases, free_list, 0 );
410 g_list_free(this->phrases);
416 static struct speech_methods espeak_meth = {
421 static struct speech_priv *
422 espeak_new(struct speech_methods *meth, struct attr **attrs) {
423 struct speech_priv *this = NULL;
425 struct attr *language;
428 path=attr_search(attrs, NULL, attr_path);
430 strcpy(path_home,path->u.str);
432 sprintf(path_home,"%s/espeak-data",getenv("NAVIT_SHAREDIR"));
433 dbg(0,"path_home set to %s\n",path_home);
440 language=attr_search(attrs, NULL, attr_language);
442 lang_str=g_strdup(language->u.str);
444 char *lang_env=getenv("LANG");
447 char *country,*lang,*lang_full;
450 lang_full=g_strdup(lang_env);
451 strtolower(lang_full,lang_env);
452 lang=g_strdup(lang_full);
453 country=strchr(lang_full,'_');
455 lang[country-lang_full]='\0';
458 file1=g_strdup_printf("%s/voices/%s",path_home,lang_full);
459 file2=g_strdup_printf("%s/voices/%s/%s",path_home,lang,lang_full);
460 dbg(0,"Testing %s and %s\n",file1,file2);
461 if (file_exists(file1) || file_exists(file2))
462 lang_str=g_strdup(lang_full);
464 lang_str=g_strdup(lang);
465 dbg(0,"Language full %s lang %s result %s\n",lang_full,lang,lang_str);
472 if(lang_str && SetVoiceByName(lang_str) != EE_OK)
474 dbg(0, "Error setting language to: '%s',falling back to default\n", lang_str);
478 if(!lang_str && SetVoiceByName("default") != EE_OK) {
479 dbg(0, "Error setting language to default\n");
483 SetParameter(espeakRATE,170,0);
484 SetParameter(espeakVOLUME,100,0);
485 SetParameter(espeakCAPITALS,option_capitals,0);
486 SetParameter(espeakPUNCTUATION,option_punctuation,0);
487 SetParameter(espeakWORDGAP,0,0);
489 // if(pitch_adjustment != 50)
491 // SetParameter(espeakPITCH,pitch_adjustment,0);
493 DoVoiceChange(voice);
495 this=g_new(struct speech_priv,1);
496 this->h_message_thread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)startThread, (PVOID)this, 0, NULL);
507 plugin_register_speech_type("espeak", espeak_new);