Added RealTTS support by Pali Rohar.
[mstardict] / src / tts.cpp
1 /*
2  *  MStarDict - International dictionary for Maemo.
3  *  Copyright (C) 2010 Roman Moravcik
4  *
5  *  base on code of stardict:
6  *  Copyright (C) 2003-2007 Hu Zheng <huzheng_001@163.com>
7  *
8  *  based on code of sdcv:
9  *  Copyright (C) 2005-2006 Evgeniy <dushistov@mail.ru>
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include <string>
31 #include <vector>
32 #include <memory>
33 #include <list>
34
35 #include <glib.h>
36 #include <glib/gi18n.h>
37
38 #include <gtk/gtk.h>
39 #include <hildon/hildon.h>
40
41 #include "conf.hpp"
42 #include "tts.hpp"
43 #include "mstardict.hpp"
44
45 #include <espeak/speak_lib.h>
46
47 enum {
48     DISPNAME_COLUMN,
49     NAME_COLUMN,
50     N_COLUMNS
51 };
52
53 enum {
54     GENDER_NONE = 0,
55     GENDER_MALE,
56     GENDER_FEMALE
57 };
58
59 static TtsVoice voices[] = {
60     {"afrikaans",               N_("Afrikaans")},
61     {"bosnian",                 N_("Bosnian")},
62     {"catalan",                 N_("Catalan")},
63     {"czech",                   N_("Czech")},
64     {"welsh-test",              N_("Welsh")},
65     {"danish",                  N_("Danish")},
66     {"german",                  N_("German")},
67     {"greek",                   N_("Greek")},
68     {"en-scottish",             N_("English (Scottish)")},
69     {"english",                 N_("English")},
70     {"lancashire",              N_("English (Lancashire)")},
71     {"english_rp",              N_("English (Received Pronunciation)")},
72     {"english_wmids",           N_("English (West Midlands)")},
73     {"english-us",              N_("English (American)")},
74     {"en-westindies",           N_("English (Westindies)")},
75     {"esperanto",               N_("Esperanto")},
76     {"spanish",                 N_("Spanish")},
77     {"spanish-latin-american",  N_("Spanish (Latin American)")},
78     {"finnish",                 N_("Finnish")},
79     {"french",                  N_("French")},
80     {"french (Belgium)",        N_("French (Belgium)")},
81     {"greek-ancient",           N_("Greek (Ancient)")},
82     {"hindi-test",              N_("Hindi")},
83     {"croatian",                N_("Croatian")},
84     {"hungarian",               N_("Hungarian")},
85     {"armenian",                N_("Armenian")},
86     {"armenian-west",           N_("Armenian (West)")},
87     {"indonesian-test",         N_("Indonesian")},
88     {"icelandic-test",          N_("Icelandic")},
89     {"italian",                 N_("Italian")},
90     {"lojban",                  N_("Lojban")},
91     {"kurdish",                 N_("Kurdish")},
92     {"latin",                   N_("Latin")},
93     {"latvian",                 N_("Latvian")},
94     {"macedonian-test",         N_("Macedonian")},
95     {"dutch-test",              N_("Dutch")},
96     {"norwegian",               N_("Norwegian")},
97     {"papiamento-test",         N_("Papiamento")},
98     {"polish",                  N_("Polish")},
99     {"brazil",                  N_("Brazil")},
100     {"portugal",                N_("Portugal")},
101     {"romanian",                N_("Romanian")},
102     {"russian_test",            N_("Russian")},
103     {"slovak",                  N_("Slovak")},
104     {"albanian",                N_("Albanian")},
105     {"serbian",                 N_("Serbian")},
106     {"swedish",                 N_("Swedish")},
107     {"swahihi-test",            N_("Swahihi")},
108     {"tamil",                   N_("Tamil")},
109     {"turkish",                 N_("Turkish")},
110     {"vietnam-test",            N_("Vietnam")},
111     {"Mandarin",                N_("Chinese (Mandarin)")},
112     {"cantonese-test",          N_("Chinese (Cantonese)")},
113     {"default",                 N_("Default")},
114     {"WyabdcRealPeopleTTS",     N_("WyabdcRealPeopleTTS")},
115     {NULL,                      NULL}
116 };
117
118
119 Tts::Tts(MStarDict *mStarDict)
120 {
121     int rate = 0;
122     bool tts_enabled = false;
123     gchar *language = NULL;
124     int gender = GENDER_NONE;
125
126     oStarDict = mStarDict;
127     Enabled = false;
128
129     /* initialize espeak */
130     rate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 512, NULL, 0);
131
132     /* check if tts is enabled */
133     if (!oStarDict->oConf->GetBool("/apps/maemo/mstardict/tts_enabled", &tts_enabled)) {
134         tts_enabled = true;
135     }
136     Enable(tts_enabled);
137
138     /* read configured language */
139     if (!oStarDict->oConf->GetString("/apps/maemo/mstardict/tts_language", &language)) {
140         language = g_strdup("english");
141     }
142
143     /* read configured gender */
144     if (!oStarDict->oConf->GetInt("/apps/maemo/mstardict/tts_gender", &gender)) {
145         gender = GENDER_MALE;
146     }
147     SetVoice(language, gender);
148
149     if (language) {
150         g_free(language);
151     }
152 }
153
154 Tts::~Tts()
155 {
156     /* deinitialize espeak */
157     espeak_Terminate();
158 }
159
160 void
161 Tts::Enable(bool bEnable)
162 {
163     Enabled = bEnable;
164 }
165
166 bool
167 Tts::IsEnabled()
168 {
169     return Enabled;
170 }
171
172 void
173 Tts::SetVoice(const gchar *language, int gender)
174 {
175     if (g_strcmp0(language, "WyabdcRealPeopleTTS") == 0)
176         RealPeople = true;
177     else
178         RealPeople = false;
179
180     espeak_VOICE voice;
181
182     memset(&voice, 0, sizeof(espeak_VOICE));
183     voice.name = language;
184     voice.gender = gender;
185
186     espeak_SetVoiceByProperties(&voice);
187 }
188
189 void
190 Tts::SayText(const gchar *sText)
191 {
192     if (!RealPeople) {
193         espeak_Synth(sText, strlen(sText) + 1, 0, POS_CHARACTER, 0, espeakCHARS_UTF8, NULL, NULL);
194         return;
195     }
196
197     gchar *lower = g_utf8_strdown(sText, -1);
198     gchar *cmd = g_strdup_printf("paplay /home/user/MyDocs/mstardict/WyabdcRealPeopleTTS/%c/%s.wav", lower[0], lower);
199     g_free(lower);
200
201     system(cmd);
202
203     g_free(cmd);
204
205 }
206
207 GtkListStore *
208 Tts::GetVoicesList()
209 {
210     const espeak_VOICE **espeak_voices;
211     GtkListStore *list_store;
212     GtkTreeIter iter;
213     size_t i = 0;
214
215     list_store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
216
217     espeak_voices = espeak_ListVoices(NULL);
218     while (espeak_voices[i]) {
219         gchar *disp_name = NULL;
220
221         for (int j = 0; voices[j].name != NULL; j++) {
222             if (!strcmp(espeak_voices[i]->name, voices[j].name)) {
223                 disp_name = g_strdup(_(voices[j].disp_name));
224                 break;
225             }
226         }
227
228         if (disp_name == NULL)
229             disp_name = g_strdup(espeak_voices[i]->name);
230
231         gtk_list_store_append(list_store, &iter);
232         gtk_list_store_set(list_store,
233                            &iter,
234                            DISPNAME_COLUMN, disp_name,
235                            NAME_COLUMN, espeak_voices[i]->name,
236                            -1);
237
238         if (disp_name)
239             g_free(disp_name);
240
241         i++;
242     }
243     return list_store;
244 }
245
246 gboolean
247 Tts::onTtsEnableButtonClicked(GtkButton *button,
248                               Tts *oTts)
249 {
250     if (hildon_check_button_get_active(HILDON_CHECK_BUTTON(button))) {
251         gtk_widget_set_sensitive(GTK_WIDGET(oTts->gender_male_button), TRUE);
252         gtk_widget_set_sensitive(GTK_WIDGET(oTts->gender_female_button), TRUE);
253         gtk_widget_set_sensitive(GTK_WIDGET(oTts->language_button), TRUE);
254     } else {
255         gtk_widget_set_sensitive(GTK_WIDGET(oTts->gender_male_button), FALSE);
256         gtk_widget_set_sensitive(GTK_WIDGET(oTts->gender_female_button), FALSE);
257         gtk_widget_set_sensitive(GTK_WIDGET(oTts->language_button), FALSE);
258     }
259     return true;
260 }
261
262 gboolean
263 Tts::onTtsGenderButtonClicked(GtkToggleButton *button1,
264                               GtkToggleButton *button2)
265 {
266     if (gtk_toggle_button_get_active(button1))
267         gtk_toggle_button_set_active(button2, FALSE);
268     else
269         gtk_toggle_button_set_active(button2, TRUE);
270     return true;
271 }
272
273 GtkWidget *
274 Tts::CreateTtsConfWidget()
275 {
276     GtkWidget *frame, *vbox, *hbox;
277     GtkWidget *selector;
278     HildonTouchSelectorColumn *column;
279     GtkListStore *voices_list;
280
281     frame = gtk_frame_new(_("Text-To-Speech"));
282
283     vbox = gtk_vbox_new(FALSE, 0);
284     gtk_container_add(GTK_CONTAINER(frame), vbox);
285
286     /* enable tts button */
287     enable_button = hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT);
288     gtk_button_set_label(GTK_BUTTON(enable_button), _("Enable"));
289     gtk_box_pack_start(GTK_BOX(vbox), enable_button, TRUE, TRUE, 0);
290
291     /* gender selection */
292     hbox = gtk_hbox_new(TRUE, 0);
293     gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
294
295     gender_male_button = hildon_gtk_toggle_button_new(HILDON_SIZE_FINGER_HEIGHT);
296     gtk_button_set_label(GTK_BUTTON(gender_male_button), _("Male"));
297     gtk_box_pack_start(GTK_BOX(hbox), gender_male_button, TRUE, TRUE, 0);
298
299     gender_female_button = hildon_gtk_toggle_button_new(HILDON_SIZE_FINGER_HEIGHT);
300     gtk_button_set_label(GTK_BUTTON(gender_female_button), _("Female"));
301     gtk_box_pack_start(GTK_BOX(hbox), gender_female_button, TRUE, TRUE, 0);
302
303     /* language picker button */
304     language_button = hildon_picker_button_new(HILDON_SIZE_FINGER_HEIGHT,
305                                                HILDON_BUTTON_ARRANGEMENT_VERTICAL);
306     hildon_button_set_title(HILDON_BUTTON(language_button), _("Language"));
307     hildon_button_set_alignment(HILDON_BUTTON(language_button), 0.0, 0.5, 1.0, 0.0);
308     hildon_button_set_title_alignment(HILDON_BUTTON(language_button), 0.0, 0.5);
309     gtk_box_pack_start(GTK_BOX(vbox), language_button, TRUE, TRUE, 0);
310
311     /* get list of voices */
312     voices_list = GetVoicesList();
313
314     /* sort list of voices */
315     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(voices_list),
316                                          DISPNAME_COLUMN, GTK_SORT_ASCENDING);
317
318     /* voices selector */
319     selector = hildon_touch_selector_new();
320     column = hildon_touch_selector_append_text_column(HILDON_TOUCH_SELECTOR(selector),
321                                                       GTK_TREE_MODEL(voices_list), TRUE);
322     g_object_set(G_OBJECT(column), "text-column", DISPNAME_COLUMN, NULL);
323
324     hildon_picker_button_set_selector(HILDON_PICKER_BUTTON(language_button),
325                                       HILDON_TOUCH_SELECTOR(selector));
326
327     g_signal_connect(enable_button, "clicked", G_CALLBACK(onTtsEnableButtonClicked), this);
328     g_signal_connect(gender_male_button, "clicked", G_CALLBACK(onTtsGenderButtonClicked), gender_female_button);
329     g_signal_connect(gender_female_button, "clicked", G_CALLBACK(onTtsGenderButtonClicked), gender_male_button);
330
331     return frame;
332 }
333
334 void
335 Tts::TtsConfWidgetLoadConf()
336 {
337     HildonTouchSelector *selector;
338     GtkTreeIter iter;
339     int gender = GENDER_NONE;
340     gchar *language = NULL;
341     GtkTreeModel *model;
342     gboolean iter_valid = TRUE;
343
344     if (IsEnabled()) {
345         hildon_check_button_set_active(HILDON_CHECK_BUTTON(enable_button), TRUE);
346         gtk_widget_set_sensitive(language_button, TRUE);
347     } else {
348         hildon_check_button_set_active(HILDON_CHECK_BUTTON(enable_button), FALSE);
349         gtk_widget_set_sensitive(language_button, FALSE);
350     }
351
352     if (oStarDict->oConf->GetInt("/apps/maemo/mstardict/tts_gender", &gender)) {
353         if (gender == GENDER_MALE)
354             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gender_male_button), TRUE);
355         else
356             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gender_female_button), TRUE);
357     } else {
358             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gender_male_button), TRUE);
359     }
360
361     if (oStarDict->oConf->GetString("/apps/maemo/mstardict/tts_language", &language)) {
362         if (language) {
363             selector = hildon_picker_button_get_selector(HILDON_PICKER_BUTTON(language_button));
364             model = hildon_touch_selector_get_model(HILDON_TOUCH_SELECTOR(selector), DISPNAME_COLUMN);
365             for (iter_valid = gtk_tree_model_get_iter_first(model, &iter); iter_valid; iter_valid = gtk_tree_model_iter_next(model, &iter)) {
366                 const gchar *tmp;
367                 gtk_tree_model_get(model, &iter, NAME_COLUMN, &tmp, -1);
368                 if (strcmp(tmp, language) == 0) {
369                     hildon_touch_selector_select_iter(HILDON_TOUCH_SELECTOR(selector), DISPNAME_COLUMN, &iter, FALSE);
370                     break;
371                 }
372             }
373             g_free(language);
374         }
375     } else {
376         hildon_picker_button_set_active (HILDON_PICKER_BUTTON (language_button), -1);
377     }
378 }
379
380 void
381 Tts::TtsConfWidgetSaveConf()
382 {
383     bool enabled = false;
384     HildonTouchSelector *selector;
385     GtkTreeIter iter;
386     int gender = GENDER_NONE;
387     const gchar *language;
388
389     enabled = hildon_check_button_get_active(HILDON_CHECK_BUTTON(enable_button));
390     if (oStarDict->oConf->SetBool("/apps/maemo/mstardict/tts_enabled", enabled))
391         Enable(enabled);
392
393     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gender_male_button)))
394         gender = GENDER_MALE;
395     else
396         gender = GENDER_FEMALE;
397     oStarDict->oConf->SetInt("/apps/maemo/mstardict/tts_gender", gender);
398
399     if (hildon_picker_button_get_active(HILDON_PICKER_BUTTON(language_button)) > -1) {
400         selector = hildon_picker_button_get_selector(HILDON_PICKER_BUTTON(language_button));
401         if (hildon_touch_selector_get_selected(selector, DISPNAME_COLUMN, &iter)) {
402             gtk_tree_model_get(hildon_touch_selector_get_model(selector, DISPNAME_COLUMN), &iter, NAME_COLUMN, &language, -1);
403
404             /* fixme convert back disp_name */
405             if (oStarDict->oConf->SetString("/apps/maemo/mstardict/tts_language", language))
406                 SetVoice(language, gender);
407         }
408     }
409 }