Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Speak.activity
diff options
context:
space:
mode:
authorJoshua Minor <j@lux.vu>2008-03-07 08:15:06 (GMT)
committer Joshua Minor <j@lux.vu>2008-03-07 08:15:06 (GMT)
commitddec556ecbddec904c5b7fdc9062f429b6c4eedf (patch)
tree39af8933370417cd562c8c0b9118e02d06af2851 /Speak.activity
parent99f65d24fb136f6d8a13ac4933766fc6206ed818 (diff)
Speak now tries to pick a default language based on $LANG
Added a first attempt at i18n support. Removed experimental speechd support.
Diffstat (limited to 'Speak.activity')
-rwxr-xr-xSpeak.activity/activity.py57
-rw-r--r--Speak.activity/voice.py98
2 files changed, 115 insertions, 40 deletions
diff --git a/Speak.activity/activity.py b/Speak.activity/activity.py
index d6ad229..fc1275f 100755
--- a/Speak.activity/activity.py
+++ b/Speak.activity/activity.py
@@ -33,12 +33,13 @@ import logging
import gtk
import gobject
import pango
+from gettext import gettext as _
-try:
- sys.path.append('/usr/lib/python2.4/site-packages') # for speechd
- import speechd.client
-except:
- print "Speech-dispatcher not found."
+# try:
+# sys.path.append('/usr/lib/python2.4/site-packages') # for speechd
+# import speechd.client
+# except:
+# print "Speech-dispatcher not found."
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.toolcombobox import ToolComboBox
@@ -62,24 +63,30 @@ class SpeakActivity(activity.Activity):
activity.Activity.__init__(self, handle)
bounds = self.get_allocation()
- try:
- self.synth = speechd.client.SSIPClient("Speak.activity")
- except:
- self.synth = None
- print "Falling back to espeak command line tool."
+ self.synth = None
+ # try:
+ # self.synth = speechd.client.SSIPClient("Speak.activity")
+ # try:
+ # # Try some speechd v0.6.6 features
+ # print "Output modules:", self.synth.list_output_modules()
+ # print "Voices:", self.synth.list_synthesis_voices()
+ # except:
+ # pass
+ # except:
+ # self.synth = None
+ # print "Falling back to espeak command line tool."
# pick a voice that espeak supports
self.voices = voice.allVoices()
+ #print self.voices
#self.voice = random.choice(self.voices.values())
- self.voice = self.voices["Default"]
+ self.voice = voice.defaultVoice()
# make an audio device for playing back and rendering audio
self.active = False
self.connect( "notify::active", self._activeCb )
self.audio = audio.AudioGrab(datastore, self._jobject)
- #self.proc = None
-
# make a box to type into
self.entrycombo = gtk.combo_box_entry_new_text()
self.entrycombo.connect("changed", self._combo_changed_cb)
@@ -164,7 +171,7 @@ class SpeakActivity(activity.Activity):
self.active = True
presenceService = presenceservice.get_instance()
xoOwner = presenceService.get_owner()
- self.say("Hello %s. Type something." % xoOwner.props.nick)
+ self.say(_("Hello %s. Type something.") % xoOwner.props.nick)
def write_file(self, file_path):
f = open(file_path, "w")
@@ -324,10 +331,10 @@ class SpeakActivity(activity.Activity):
self.say(self.voice.friendlyname)
def pitch_adjusted_cb(self, get, data=None):
- self.say("pitch adjusted")
+ self.say(_("pitch adjusted"))
def rate_adjusted_cb(self, get, data=None):
- self.say("rate adjusted")
+ self.say(_("rate adjusted"))
def make_face_bar(self):
facebar = gtk.Toolbar()
@@ -336,9 +343,9 @@ class SpeakActivity(activity.Activity):
self.mouth_shape_combo = ComboBox()
self.mouth_shape_combo.connect('changed', self.mouth_changed_cb)
- self.mouth_shape_combo.append_item(mouth.Mouth, "Simple")
- self.mouth_shape_combo.append_item(waveform_mouth.WaveformMouth, "Waveform")
- self.mouth_shape_combo.append_item(fft_mouth.FFTMouth, "Frequency")
+ self.mouth_shape_combo.append_item(mouth.Mouth, _("Simple"))
+ self.mouth_shape_combo.append_item(waveform_mouth.WaveformMouth, _("Waveform"))
+ self.mouth_shape_combo.append_item(fft_mouth.FFTMouth, _("Frequency"))
self.mouth_shape_combo.set_active(0)
combotool = ToolComboBox(self.mouth_shape_combo)
facebar.insert(combotool, -1)
@@ -346,8 +353,8 @@ class SpeakActivity(activity.Activity):
self.eye_shape_combo = ComboBox()
self.eye_shape_combo.connect('changed', self.eyes_changed_cb)
- self.eye_shape_combo.append_item(eye.Eye, "Round")
- self.eye_shape_combo.append_item(glasses.Glasses, "Glasses")
+ self.eye_shape_combo.append_item(eye.Eye, _("Round"))
+ self.eye_shape_combo.append_item(glasses.Glasses, _("Glasses"))
combotool = ToolComboBox(self.eye_shape_combo)
facebar.insert(combotool, -1)
combotool.show()
@@ -378,7 +385,7 @@ class SpeakActivity(activity.Activity):
# enable mouse move events so we can track the eyes while the mouse is over the mouth
self.mouth.add_events(gtk.gdk.POINTER_MOTION_MASK)
# this SegFaults: self.say(combo.get_active_text())
- self.say("mouth changed")
+ self.say(_("mouth changed"))
def eyes_changed_cb(self, ignored, ignored2=None):
if self.numeyesadj is None:
@@ -399,7 +406,7 @@ class SpeakActivity(activity.Activity):
eye.show()
# this SegFaults: self.say(self.eye_shape_combo.get_active_text())
- self.say("eyes changed")
+ self.say(_("eyes changed"))
def _combo_changed_cb(self, combo):
# when a new item is chosen, make sure the text is selected
@@ -448,8 +455,8 @@ class SpeakActivity(activity.Activity):
# select the whole text
entry.select_region(0,-1)
- def _synth_cb(self, callback_type):
- print "synth callback type:", callback_type
+ def _synth_cb(self, callback_type, index_mark=None):
+ print "synth callback:", callback_type, index_mark
def say(self, something):
if self.audio is None or not self.active:
diff --git a/Speak.activity/voice.py b/Speak.activity/voice.py
index 674ee1b..ac364e0 100644
--- a/Speak.activity/voice.py
+++ b/Speak.activity/voice.py
@@ -22,7 +22,49 @@
import subprocess
-import re
+import re, os
+from gettext import gettext as _
+
+# Lets trick gettext into generating entries for the voice names we expect espeak to have
+# If espeak actually has new or different names then they won't get translated, but they
+# should still show up in the interface.
+expectedVoiceNames = [
+ _("Brazil"),
+ _("Swedish"),
+ _("Icelandic"),
+ _("Romanian"),
+ _("Swahili"),
+ _("Hindi"),
+ _("Dutch"),
+ _("Latin"),
+ _("Hungarian"),
+ _("Macedonian"),
+ _("Welsh"),
+ _("French"),
+ _("Norwegian"),
+ _("Russian"),
+ _("Afrikaans"),
+ _("Finnish"),
+ _("Default"),
+ _("Cantonese"),
+ _("Scottish"),
+ _("Greek"),
+ _("Vietnam"),
+ _("English"),
+ _("Lancashire"),
+ _("Italian"),
+ _("Portugal"),
+ _("German"),
+ _("Whisper"),
+ _("Croatian"),
+ _("Czech"),
+ _("Slovak"),
+ _("Spanish"),
+ _("Polish"),
+ _("Esperanto")
+]
+
+_allVoices = {}
class Voice:
def __init__(self, language, gender, name):
@@ -36,20 +78,46 @@ class Voice:
friendlyname = friendlyname.replace('en-','')
friendlyname = friendlyname.replace('english-wisper','whisper')
friendlyname = friendlyname.capitalize()
- self.friendlyname = friendlyname
-
+ self.friendlyname = _(friendlyname)
def allVoices():
- voices = {}
- result = subprocess.Popen(["espeak", "--voices"], stdout=subprocess.PIPE).communicate()[0]
- for line in result.split('\n'):
- m = re.match(r'\s*\d+\s+([\w-]+)\s+([MF])\s+([\w_-]+)\s+(.+)', line)
- if m:
- language, gender, name, stuff = m.groups()
- if stuff.startswith('mb/') or name in ('en-rhotic','english_rp','english_wmids'):
- # these voices don't produce sound
- continue
- voice = Voice(language, gender, name)
- voices[voice.friendlyname] = voice
- return voices
+ if len(_allVoices) == 0:
+ result = subprocess.Popen(["espeak", "--voices"], stdout=subprocess.PIPE).communicate()[0]
+ for line in result.split('\n'):
+ m = re.match(r'\s*\d+\s+([\w-]+)\s+([MF])\s+([\w_-]+)\s+(.+)', line)
+ if m:
+ language, gender, name, stuff = m.groups()
+ if stuff.startswith('mb/') or name in ('en-rhotic','english_rp','english_wmids'):
+ # these voices don't produce sound
+ continue
+ voice = Voice(language, gender, name)
+ _allVoices[voice.friendlyname] = voice
+ return _allVoices
+
+def defaultVoice():
+ """Try to figure out the default voice, from the current locale ($LANG).
+ Fall back to espeak's voice called Default."""
+
+ def fit(a,b):
+ "Compare two language ids to see if they are similar."
+ as = re.split(r'[^a-z]+', a.lower())
+ bs = re.split(r'[^a-z]+', b.lower())
+ for count in range(0, min(len(as),len(bs))):
+ if as[count] != bs[count]:
+ count -= 1
+ break
+ return count
+ try:
+ lang = os.environ["LANG"]
+ except:
+ lang = ""
+
+ best = _allVoices[_("Default")]
+ for voice in _allVoices.values():
+ voiceMetric = fit(voice.language, lang)
+ bestMetric = fit(best.language, lang)
+ if voiceMetric > bestMetric:
+ best = voice
+ print "Best voice for LANG %s seems to be %s %s" % (lang, best.language, best.friendlyname)
+ return best