Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/speechtoolbar.py
diff options
context:
space:
mode:
authorGonzalo Odiard <godiard@gmail.com>2012-05-02 18:34:29 (GMT)
committer Gonzalo Odiard <godiard@gmail.com>2012-05-02 20:03:48 (GMT)
commitfa52f2684479170e3c8c988ca82f6d4f70ec8c77 (patch)
treee30a6191a1359ccd8874aa524074a636967ea71d /speechtoolbar.py
parent628b626b39d7388be2d2c552df18f2b429ad9c02 (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.py194
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()