diff options
author | Gonzalo Odiard <godiard@gmail.com> | 2012-05-02 18:34:29 (GMT) |
---|---|---|
committer | Gonzalo Odiard <godiard@gmail.com> | 2012-05-02 20:03:48 (GMT) |
commit | fa52f2684479170e3c8c988ca82f6d4f70ec8c77 (patch) | |
tree | e30a6191a1359ccd8874aa524074a636967ea71d /speechtoolbar.py | |
parent | 628b626b39d7388be2d2c552df18f2b429ad9c02 (diff) |
Add text to speech functionality to Write - SL #3266
Ass discussed with the Learning Team, Write need a inmediate
access to Text to Speech, the global tts feature is too indirect.
Signed-off-by: Gonzalo Odiard <gonzalo@laptop.org>
Diffstat (limited to 'speechtoolbar.py')
-rw-r--r-- | speechtoolbar.py | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/speechtoolbar.py b/speechtoolbar.py new file mode 100644 index 0000000..ca6eae5 --- /dev/null +++ b/speechtoolbar.py @@ -0,0 +1,194 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import simplejson +from gettext import gettext as _ +import logging + +import gtk +import gconf + +from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.toggletoolbutton import ToggleToolButton +from sugar.graphics.combobox import ComboBox +from sugar.graphics.toolcombobox import ToolComboBox + +import speech + + +class SpeechToolbar(gtk.Toolbar): + + def __init__(self, activity): + gtk.Toolbar.__init__(self) + self._activity = activity + if not speech.supported: + return + self.is_paused = False + self._cnf_client = gconf.client_get_default() + self.load_speech_parameters() + + self.sorted_voices = [i for i in speech.voices()] + self.sorted_voices.sort(self.compare_voices) + default = 0 + for voice in self.sorted_voices: + if voice[0] == speech.voice[0]: + break + default = default + 1 + + # Play button + self.play_btn = ToggleToolButton('media-playback-start') + self.play_btn.show() + self.play_btn.connect('toggled', self.play_cb) + self.insert(self.play_btn, -1) + self.play_btn.set_tooltip(_('Play / Pause')) + + # Stop button + self.stop_btn = ToolButton('media-playback-stop') + self.stop_btn.show() + self.stop_btn.connect('clicked', self.stop_cb) + self.stop_btn.set_sensitive(False) + self.insert(self.stop_btn, -1) + self.stop_btn.set_tooltip(_('Stop')) + + self.voice_combo = ComboBox() + for voice in self.sorted_voices: + self.voice_combo.append_item(voice, voice[0]) + self.voice_combo.set_active(default) + + self.voice_combo.connect('changed', self.voice_changed_cb) + combotool = ToolComboBox(self.voice_combo) + self.insert(combotool, -1) + combotool.show() + speech.reset_buttons_cb = self.reset_buttons_cb + speech.end_text_cb = self.reset_buttons_cb + + def compare_voices(self, a, b): + if a[0].lower() == b[0].lower(): + return 0 + if a[0] .lower() < b[0].lower(): + return -1 + if a[0] .lower() > b[0].lower(): + return 1 + + def voice_changed_cb(self, combo): + speech.voice = combo.props.value + speech.say(speech.voice[0]) + self.save_speech_parameters() + + def load_speech_parameters(self): + speech_parameters = {} + data_path = os.path.join(self._activity.get_activity_root(), 'data') + data_file_name = os.path.join(data_path, 'speech_params.json') + if os.path.exists(data_file_name): + f = open(data_file_name, 'r') + try: + speech_parameters = simplejson.load(f) + speech.voice = speech_parameters['voice'] + finally: + f.close() + else: + speech.voice = self.get_default_voice() + logging.error('Default voice %s', speech.voice) + + self._cnf_client.add_dir('/desktop/sugar/speech', + gconf.CLIENT_PRELOAD_NONE) + speech.pitch = self._cnf_client.get_int('/desktop/sugar/speech/pitch') + speech.rate = self._cnf_client.get_int('/desktop/sugar/speech/rate') + self._cnf_client.notify_add('/desktop/sugar/speech/pitch', \ + self.__conf_changed_cb, None) + self._cnf_client.notify_add('/desktop/sugar/speech/rate', \ + self.__conf_changed_cb, None) + + def get_default_voice(self): + """Try to figure out the default voice, from the current locale ($LANG) + Fall back to espeak's voice called Default.""" + voices = speech.get_all_voices() + + locale = os.environ.get('LANG', '') + language_location = locale.split('.', 1)[0].lower() + language = language_location.split('_')[0] + variant = '' + if language_location.find('_') > -1: + variant = language_location.split('_')[1] + # if the language is es but not es_es default to es_la (latin voice) + if language == 'es' and language_location != 'es_es': + language_location = 'es_la' + + best = voices.get(language_location) or voices.get(language) \ + or 'default' + logging.debug('Best voice for LANG %s seems to be %s', + locale, best) + return [best, language, variant] + + def __conf_changed_cb(self, client, connection_id, entry, args): + key = entry.get_key() + value = client.get_int(key) + if key == '/desktop/sugar/speech/pitch': + speech.pitch = value + if key == '/desktop/sugar/speech/rate': + speech.rate = value + + def save_speech_parameters(self): + speech_parameters = {} + speech_parameters['voice'] = speech.voice + data_path = os.path.join(self._activity.get_activity_root(), 'data') + data_file_name = os.path.join(data_path, 'speech_params.json') + f = open(data_file_name, 'w') + try: + simplejson.dump(speech_parameters, f) + finally: + f.close() + + def reset_buttons_cb(self): + logging.error('reset buttons') + self.play_btn.set_named_icon('media-playback-start') + self.stop_btn.set_sensitive(False) + self.play_btn.set_active(False) + self.is_paused = False + + def play_cb(self, widget): + self.stop_btn.set_sensitive(True) + if widget.get_active(): + self.play_btn.set_named_icon('media-playback-pause') + logging.error('Paused %s', self.is_paused) + if not self.is_paused: + # get the text to speech, if there are a selection, + # play selected text, if not, play all + abi = self._activity.abiword_canvas + selection = abi.get_selection('text/plain') + if selection[1] == 0: + # nothing selected + abi.select_all() + text = abi.get_selection('text/plain')[0] + abi.moveto_bod() + else: + text = selection[0] + speech.play(text) + else: + logging.error('Continue play') + speech.continue_play() + else: + self.play_btn.set_named_icon('media-playback-start') + self.is_paused = True + speech.pause() + + def stop_cb(self, widget): + self.stop_btn.set_sensitive(False) + self.play_btn.set_named_icon('media-playback-start') + self.play_btn.set_active(False) + self.is_paused = False + speech.stop() |