[Sugar-devel] [Sugar] Implement text to speech in Sugar feature - v2

Gonzalo Odiard gonzalo at laptop.org
Wed Jan 18 09:25:54 EST 2012


Thanks, please read my reply to your review mail
http://lists.sugarlabs.org/archive/sugar-devel/2012-January/035292.html
More below...

On Wed, Jan 18, 2012 at 5:05 AM, Simon Schampijer <simon at schampijer.de>wrote:

> On 17/01/12 22:49, godiard at sugarlabs.org wrote:
>
>> From: Gonzalo Odiard<godiard at gmail.com>
>>
>> Added controls to pause/stop and addressed suggestions
>> in the review process.
>>
>
> Thanks for the update!
>
> - one thing that is not solved yet, is the item that gets created in the
> clipboard tray
>
>
I think is a Write problem, no clue at the moment.


> - when I do not have text selected I can still click on the 'play' icon
>
>
Yes. But does not harm, and I think will overcomplicate.


> - sometimes the button inactive part is out of sync (the stop button is
> not inactive even though I am not playing back). Maybe if it is too
> complicated to get it right we can make them always available and find
> other means of indicating when something is played back, not sure yet
> how/if, just a random thought.
>

Did you paused the spoken text? The stop button is disabled when all the
text was played. Should work ok and I have tested it.


>
> - nitpick: for the copyright, I think it should be either your or OLPC, I
> think OLPC is the one to pick in this case, and welcome to 2012 :)
>
>
Ok, I was confused about how do it. About the year... started the last year
:)


> Regards,
>   Simon
>
>
>
>  ---
>>  data/sugar.schemas.in             |   28 ++++
>>  extensions/deviceicon/**Makefile.am |    1 +
>>  extensions/deviceicon/speech.**py   |  204 ++++++++++++++++++++++++++++
>>  extensions/globalkey/Makefile.**am  |    1 +
>>  extensions/globalkey/speech.py    |   24 ++++
>>  src/jarabe/model/Makefile.am      |    1 +
>>  src/jarabe/model/speech.py        |  266 ++++++++++++++++++++++++++++++*
>> *+++++++
>>  src/jarabe/view/keyhandler.py     |   29 +----
>>  8 files changed, 526 insertions(+), 28 deletions(-)
>>  create mode 100644 extensions/deviceicon/speech.**py
>>  create mode 100644 extensions/globalkey/speech.py
>>  create mode 100644 src/jarabe/model/speech.py
>>
>> diff --git a/data/sugar.schemas.in b/data/sugar.schemas.in
>> index 8b3e1ad..66d3391 100644
>> --- a/data/sugar.schemas.in
>> +++ b/data/sugar.schemas.in
>> @@ -368,5 +368,33 @@
>>        </locale>
>>      </schema>
>>
>> +<schema>
>> +<key>/schemas/desktop/sugar/**speech/pitch</key>
>> +<applyto>/desktop/sugar/**speech/pitch</applyto>
>> +<owner>sugar</owner>
>> +<type>int</type>
>> +<default>50</default>
>> +<locale name="C">
>> +<short>Default pitch to the speech sugar service</short>
>> +<long>Pitch value used by the speech service in Sugar,
>> +        can be changed by the user, with controls in a icon
>> +        in the frame</long>
>> +</locale>
>> +</schema>
>> +
>> +<schema>
>> +<key>/schemas/desktop/sugar/**speech/rate</key>
>> +<applyto>/desktop/sugar/**speech/rate</applyto>
>> +<owner>sugar</owner>
>> +<type>int</type>
>> +<default>170</default>
>> +<locale name="C">
>> +<short>Default rate to the speech sugar service</short>
>> +<long>Rate value used by the speech service in Sugar,
>> +        can be changed by the user, with controls in a icon
>> +        in the frame</long>
>> +</locale>
>> +</schema>
>> +
>>    </schemalist>
>>  </gconfschemafile>
>> diff --git a/extensions/deviceicon/**Makefile.am b/extensions/deviceicon/
>> **Makefile.am
>> index 118d866..7ed1f77 100644
>> --- a/extensions/deviceicon/**Makefile.am
>> +++ b/extensions/deviceicon/**Makefile.am
>> @@ -5,5 +5,6 @@ sugar_PYTHON =          \
>>        battery.py      \
>>        network.py      \
>>        speaker.py      \
>> +       speech.py       \
>>        touchpad.py     \
>>        volume.py
>> diff --git a/extensions/deviceicon/**speech.py b/extensions/deviceicon/**
>> speech.py
>> new file mode 100644
>> index 0000000..4528e98
>> --- /dev/null
>> +++ b/extensions/deviceicon/**speech.py
>> @@ -0,0 +1,204 @@
>> +# Copyright (C) 2011 One Laptop Per Child
>> +# Copyright (C) 2011 Gonzalo Odiard
>> +#
>> +# 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
>> +
>> +from gettext import gettext as _
>> +import gconf
>> +
>> +import glib
>> +import gtk
>> +
>> +from sugar.graphics.icon import Icon
>> +from sugar.graphics.menuitem import MenuItem
>> +from sugar.graphics.tray import TrayIcon
>> +from sugar.graphics.palette import Palette
>> +from sugar.graphics.xocolor import XoColor
>> +from sugar.graphics.toolbutton import ToolButton
>> +
>> +from jarabe.frame.frameinvoker import FrameWidgetInvoker
>> +from jarabe.model import speech
>> +
>> +
>> +_ICON_NAME = 'microphone'
>> +
>> +
>> +class SpeechDeviceView(TrayIcon):
>> +
>> +    FRAME_POSITION_RELATIVE = 105
>> +
>> +    def __init__(self):
>> +        client = gconf.client_get_default()
>> +        self._color = XoColor(client.get_string('/**
>> desktop/sugar/user/color'))
>> +
>> +        TrayIcon.__init__(self, icon_name=_ICON_NAME,
>> xo_color=self._color)
>> +
>> +        self.set_palette_invoker(**FrameWidgetInvoker(self))
>> +
>> +        self._manager = speech.get_speech_manager()
>> +
>> +        self.connect('expose-event', self.__expose_event_cb)
>> +
>> +        self._icon_widget.connect('**button-release-event',
>> +                                  self.__button_release_event_**cb)
>> +
>> +    def create_palette(self):
>> +        label = glib.markup_escape_text(_('**Speech'))
>> +        palette = SpeechPalette(label, manager=self._manager)
>> +        palette.set_group_id('frame')
>> +        return palette
>> +
>> +    def __button_release_event_cb(**self, widget, event):
>> +        if event.button != 1:
>> +            return False
>> +
>> +        self.palette_invoker.notify_**right_click()
>> +        return True
>> +
>> +    def __expose_event_cb(self, *args):
>> +        self._update_info()
>> +
>> +
>> +class SpeechPalette(Palette):
>> +
>> +    def __init__(self, primary_text, manager):
>> +        Palette.__init__(self, label=primary_text)
>> +
>> +
>> +
>> +        self._manager = manager
>> +        self._manager.connect('play', self._set_buttons_state, 'play')
>> +        self._manager.connect('stop', self._set_buttons_state, 'stop')
>> +        self._manager.connect('pause', self._set_buttons_state, 'pause')
>> +
>> +        vbox = gtk.VBox()
>> +        self.set_content(vbox)
>> +
>> +        """
>> +        self._play_item = MenuItem('Say selected text')
>> +        self._play_icon = Icon(icon_name='player_play',
>> +                icon_size=gtk.ICON_SIZE_MENU)
>> +        self._pause_icon = Icon(icon_name='player_pause',
>> +                icon_size=gtk.ICON_SIZE_MENU)
>> +        self._play_item.set_image(**self._play_icon)
>> +        self.menu.append(self._play_**item)
>> +        self._play_item.show()
>> +        self._play_item.connect('**activate', self.__play_clicked_cb)
>> +
>> +        self._stop_item = MenuItem('Stop talking')
>> +        self._stop_icon = Icon(icon_name='player_stop',
>> +                icon_size=gtk.ICON_SIZE_MENU)
>> +        self._stop_item.set_image(**self._stop_icon)
>> +        self.menu.append(self._stop_**item)
>> +        self._stop_item.show()
>> +        self._stop_item.connect('**activate', self.__stop_clicked_cb)
>> +        self._stop_item.set_sensitive(**False)
>> +        """
>> +        hbox_play_pause = gtk.HBox()
>> +        self._play_pause_button = ToolButton(icon_name='player_**play')
>> +        self._play_pause_button.**connect('clicked',
>> self.__play_clicked_cb)
>> +        hbox_play_pause.pack_start(**self._play_pause_button)
>> +        self._stop_button = ToolButton(icon_name='player_**stop')
>> +        self._stop_button.connect('**clicked', self.__stop_clicked_cb)
>> +        self._stop_button.set_**sensitive(False)
>> +        hbox_play_pause.pack_start(**self._stop_button)
>> +        hbox_play_pause.pack_start(**gtk.Label(_('Say selected text')))
>> +        vbox.add(hbox_play_pause)
>> +
>> +        vbox.add(gtk.HSeparator())
>> +
>> +        pitch_step = 10
>> +        self._adj_pitch = gtk.Adjustment(value=self._**
>> manager.get_pitch(),
>> +                                          lower=self._manager.MIN_PITCH,
>> +                                          upper=self._manager.MAX_PITCH,
>> +                                          step_incr=pitch_step,
>> +                                          page_incr=pitch_step,
>> +                                          page_size=pitch_step)
>> +        self._hscale_pitch = gtk.HScale(self._adj_pitch)
>> +        self._hscale_pitch.set_digits(**0)
>> +        self._hscale_pitch.set_draw_**value(False)
>> +
>> +        hbox_pitch = gtk.HBox()
>> +        hbox_pitch.pack_start(gtk.**Label(_('Pitch')))
>> +        hbox_pitch.pack_start(self._**hscale_pitch)
>> +        vbox.add(hbox_pitch)
>> +
>> +        rate_step = 10
>> +        self._adj_rate = gtk.Adjustment(value=self._**
>> manager.get_rate(),
>> +                                          lower=self._manager.MIN_RATE,
>> +                                          upper=self._manager.MAX_RATE,
>> +                                          step_incr=rate_step,
>> +                                          page_incr=rate_step,
>> +                                          page_size=rate_step)
>> +        self._hscale_rate = gtk.HScale(self._adj_rate)
>> +        self._hscale_rate.set_digits(**0)
>> +        self._hscale_rate.set_draw_**value(False)
>> +
>> +        hbox_rate = gtk.HBox()
>> +        hbox_rate.pack_start(gtk.**Label(_('Rate')))
>> +        hbox_rate.pack_start(self._**hscale_rate)
>> +        vbox.add(hbox_rate)
>> +        vbox.show_all()
>> +
>> +        self._adj_pitch.connect('**value_changed',
>> self.__adj_pitch_changed_cb)
>> +        self._adj_rate.connect('value_**changed',
>> self.__adj_rate_changed_cb)
>> +
>> +    def __adj_pitch_changed_cb(self, adjustement):
>> +        self._manager.set_pitch(int(**adjustement.value))
>> +
>> +    def __adj_rate_changed_cb(self, adjustement):
>> +        self._manager.set_rate(int(**adjustement.value))
>> +
>> +    def __play_clicked_cb(self, widget):
>> +        if self._manager.is_paused:
>> +            self._manager.restart()
>> +        else:
>> +            if not self._manager.is_playing:
>> +                self._manager.say_selected_**text()
>> +            else:
>> +                self._manager.pause()
>> +
>> +    def __stop_clicked_cb(self, widget):
>> +        self._manager.stop()
>> +
>> +    def _set_buttons_state(self, manager, signal):
>> +        if signal == 'play':
>> +            """
>> +            self._play_item.set_image(**self._pause_icon)
>> +            self._play_item.set_label('**Pause talking selected text')
>> +            self._stop_item.set_sensitive(**True)
>> +            """
>> +            self._play_pause_button.set_**icon('player_pause')
>> +            self._stop_button.set_**sensitive(True)
>> +
>> +        elif signal == 'pause':
>> +            """
>> +            self._play_item.set_image(**self._play_icon)
>> +            self._play_item.set_label('Say selected text')
>> +            self._stop_item.set_sensitive(**True)
>> +            """
>> +            self._play_pause_button.set_**icon('player_play')
>> +            self._stop_button.set_**sensitive(True)
>> +
>> +        elif signal == 'stop':
>> +            """
>> +            self._play_item.set_image(**self._play_icon)
>> +            self._stop_item.set_sensitive(**False)
>> +            """
>> +            self._play_pause_button.set_**icon('player_play')
>> +            self._stop_button.set_**sensitive(False)
>> +
>> +def setup(tray):
>> +    tray.add_device(**SpeechDeviceView())
>> diff --git a/extensions/globalkey/**Makefile.am b/extensions/globalkey/**
>> Makefile.am
>> index 69afac2..b6cbbd6 100644
>> --- a/extensions/globalkey/**Makefile.am
>> +++ b/extensions/globalkey/**Makefile.am
>> @@ -3,4 +3,5 @@ sugardir = $(pkgdatadir)/extensions/**globalkey
>>  sugar_PYTHON =                \
>>        __init__.py     \
>>        screenshot.py   \
>> +       speech.py       \
>>        viewsource.py
>> diff --git a/extensions/globalkey/speech.**py
>> b/extensions/globalkey/speech.**py
>> new file mode 100644
>> index 0000000..1e7ea66
>> --- /dev/null
>> +++ b/extensions/globalkey/speech.**py
>> @@ -0,0 +1,24 @@
>> +# Copyright (C) 2011 One Laptop Per Child
>> +# Copyright (C) 2011 Gonzalo Odiard
>> +#
>> +# 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
>> +
>> +from jarabe.model import speech
>> +
>> +BOUND_KEYS = ['<alt>s']
>> +
>> +
>> +def handle_key_press(key):
>> +    speech.get_speech_manager().**say_selected_text()
>> diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am
>> index 92e8712..2fc6b1c 100644
>> --- a/src/jarabe/model/Makefile.am
>> +++ b/src/jarabe/model/Makefile.am
>> @@ -16,4 +16,5 @@ sugar_PYTHON =                        \
>>        screen.py               \
>>          session.py            \
>>        sound.py                \
>> +       speech.py               \
>>        telepathyclient.py
>> diff --git a/src/jarabe/model/speech.py b/src/jarabe/model/speech.py
>> new file mode 100644
>> index 0000000..6330b0f
>> --- /dev/null
>> +++ b/src/jarabe/model/speech.py
>> @@ -0,0 +1,266 @@
>> +# Copyright (C) 2011 One Laptop Per Child
>> +# Copyright (C) 2011 Gonzalo Odiard
>> +#
>> +# 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 gconf
>> +
>> +import gst
>> +import gtk
>> +import gobject
>> +
>> +import os
>> +from gettext import gettext as _
>> +import logging
>> +
>> +# TRANS: The language pitch (range [0 - 99], default 50 for English)
>> +# Look at http://espeak.sourceforge.net/**commands.html<http://espeak.sourceforge.net/commands.html>for details
>> +DEFAULT_PITCH = int(_('50'))
>> +
>> +
>> +# TRANS: The diction speed, in average words per minute (range [80 -
>> 390],
>> +# default 170 for English).
>> +# Look at http://espeak.sourceforge.net/**commands.html<http://espeak.sourceforge.net/commands.html>for details
>> +DEFAULT_RATE = int(_('170'))
>> +
>> +_speech_manager = None
>> +
>> +
>> +class SpeechManager(gobject.GObject)**:
>> +
>> +    __gtype_name__ = 'SpeechManager'
>> +
>> +    __gsignals__ = {
>> +        'play': (gobject.SIGNAL_RUN_FIRST, None, []),
>> +        'pause': (gobject.SIGNAL_RUN_FIRST, None, []),
>> +        'stop': (gobject.SIGNAL_RUN_FIRST, None, [])
>> +    }
>> +
>> +    MIN_PITCH = 0
>> +    MAX_PITCH = 99
>> +
>> +    MIN_RATE = 80
>> +    MAX_RATE = 390
>> +
>> +    def __init__(self, **kwargs):
>> +        gobject.GObject.__init__(self, **kwargs)
>> +        self._player = AudioGrabGst()
>> +        self._player.connect('play', self._emit_signal, 'play')
>> +        self._player.connect('stop', self._emit_signal, 'stop')
>> +        self._player.connect('pause', self._emit_signal, 'pause')
>> +        logging.debug('SpeechManager setting default parameters')
>> +        self._voice_name = self._player.get_default_**voice()
>> +        self._pitch = DEFAULT_PITCH
>> +        self._rate = DEFAULT_RATE
>> +        self._is_playing = False
>> +        self._is_paused = False
>> +
>> +        try:
>> +            self._loading = True
>> +            self.restore()
>> +            self._loading = False
>> +        except:
>> +            pass
>> +
>> +    def _emit_signal(self, player, signal):
>> +        self._is_playing = (signal == 'play')
>> +        self._is_paused = (signal == 'pause')
>> +        self.emit(signal)
>> +
>> +    def get_is_playing(self):
>> +        return self._is_playing
>> +
>> +    is_playing = gobject.property(type=bool, getter=get_is_playing,
>> +            setter=None, default=False)
>> +
>> +    def get_is_paused(self):
>> +        return self._is_paused
>> +
>> +    is_paused = gobject.property(type=bool, getter=get_is_paused,
>> +            setter=None, default=False)
>> +
>> +    def get_pitch(self):
>> +        return self._pitch
>> +
>> +    def get_rate(self):
>> +        return self._rate
>> +
>> +    def set_pitch(self, pitch):
>> +        self._pitch = pitch
>> +        if not self._loading:
>> +            self.save()
>> +
>> +    def set_rate(self, rate):
>> +        self._rate = rate
>> +        if not self._loading:
>> +            self.save()
>> +
>> +    def say_text(self, text):
>> +        if text:
>> +            self._player.speak(self._**pitch, self._rate,
>> self._voice_name, text)
>> +
>> +    def say_selected_text(self):
>> +        clipboard = gtk.clipboard_get(selection='**PRIMARY')
>> +        clipboard.request_text(self._**primary_selection_cb)
>> +
>> +    def pause(self):
>> +        self._player.pause_sound_**device()
>> +
>> +    def restart(self):
>> +        self._player.restart_sound_**device()
>> +
>> +    def stop(self):
>> +        self._player.stop_sound_**device()
>> +
>> +    def _primary_selection_cb(self, clipboard, text, user_data):
>> +        logging.debug('SpeechManager._**primary_selection_cb: %r', text)
>> +        self.say_text(text)
>> +
>> +    def save(self):
>> +        client = gconf.client_get_default()
>> +        client.set_int('/desktop/**sugar/speech/pitch',
>> self.get_pitch())
>> +        client.set_int('/desktop/**sugar/speech/rate',
>> +                self.get_rate())
>> +        logging.debug('saving speech configuration pitch %s rate %s' %
>> +                (self._pitch, self._rate))
>> +
>> +    def restore(self):
>> +        client = gconf.client_get_default()
>> +        self.set_pitch(client.get_int(**'/desktop/sugar/speech/pitch')**
>> )
>> +        self.set_rate(client.get_int('**/desktop/sugar/speech/rate'))
>> +        logging.debug('loading speech configuration pitch %s rate %s' %
>> +                (self._pitch, self._rate))
>> +
>> +
>> +class AudioGrabGst(gobject.GObject):
>> +
>> +    __gsignals__ = {
>> +        'play': (gobject.SIGNAL_RUN_FIRST, None, []),
>> +        'pause': (gobject.SIGNAL_RUN_FIRST, None, []),
>> +        'stop': (gobject.SIGNAL_RUN_FIRST, None, [])
>> +    }
>> +
>> +    def __init__(self):
>> +        gobject.GObject.__init__(self)
>> +        self._pipeline = None
>> +        self._quiet = True
>> +
>> +    def restart_sound_device(self):
>> +        if self._pipeline is None:
>> +            return
>> +
>> +        self._quiet = False
>> +        self._pipeline.set_state(gst.**STATE_PLAYING)
>> +        self.emit('play')
>> +
>> +    def pause_sound_device(self):
>> +        if self._pipeline is None:
>> +            return
>> +
>> +        self._pipeline.set_state(gst.**STATE_PAUSED)
>> +        self.emit('pause')
>> +        self._quiet = True
>> +
>> +    def stop_sound_device(self):
>> +        if self._pipeline is None:
>> +            return
>> +
>> +        self._pipeline.set_state(gst.**STATE_NULL)
>> +        self.emit('stop')
>> +
>> +        self._quiet = True
>> +
>> +    def make_pipeline(self, cmd):
>> +        if self._pipeline is not None:
>> +            self.stop_sound_device()
>> +            del self._pipeline
>> +
>> +        self._pipeline = gst.parse_launch(cmd)
>> +
>> +        bus = self._pipeline.get_bus()
>> +        bus.add_signal_watch()
>> +        bus.connect('message::element'**, self.__pipe_message_cb)
>> +
>> +    def __pipe_message_cb(self, bus, message):
>> +        logging.error('Gst message %s' % message)
>> +        if message.structure.get_name() == 'espeak-mark' and \
>> +                message.structure['mark'] == 'end':
>> +            self.emit('stop')
>> +
>> +    def speak(self, pitch, rate, voice_name, text):
>> +        # XXX workaround for http://bugs.sugarlabs.org/**ticket/1801<http://bugs.sugarlabs.org/ticket/1801>
>> +        if not [i for i in text if i.isalnum()]:
>> +            return
>> +        text = text + '<mark name="end>"></mark>'
>> +
>> +        self.make_pipeline('espeak name=espeak ! autoaudiosink')
>> +        src = self._pipeline.get_by_name('**espeak')
>> +
>> +        logging.debug('pitch=%d rate=%d voice=%s text=%s' % (pitch, rate,
>> +                voice_name, text))
>> +
>> +        src.props.text = text
>> +        src.props.pitch = pitch
>> +        src.props.rate = rate
>> +        src.props.voice = voice_name
>> +        src.props.track = 2  # track for marks
>> +
>> +        self.restart_sound_device()
>> +
>> +    def get_all_voices(self):
>> +        all_voices = {}
>> +        for i in gst.element_factory_make('**espeak').props.voices:
>> +            name, language, dialect = i
>> +            #if name in ('en-rhotic','english_rp','**english_wmids'):
>> +                # these voices don't produce sound
>> +             #   continue
>> +            all_voices[language] = name
>> +        return all_voices
>> +
>> +    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 = self.get_all_voices()
>> +
>> +        try:
>> +            lang = os.environ['LANG']
>> +            if lang.find('.')>  --1:
>> +                lang = lang[0:lang.find('.')]
>> +                lang = lang.replace('_', '-').lower()
>> +        except:
>> +            lang = ""
>> +
>> +        best = "default"
>> +
>> +        try:
>> +            best = voices[lang]
>> +        except:
>> +            try:
>> +                lang = lang[0:lang.find('-')]
>> +                best = voices[lang]
>> +            except:
>> +                pass
>> +
>> +        logging.debug('Best voice for LANG %s seems to be %s' %
>> +                (lang, best))
>> +        return best
>> +
>> +
>> +def get_speech_manager():
>> +    global _speech_manager
>> +
>> +    if _speech_manager == None:
>> +        _speech_manager = SpeechManager()
>> +    return _speech_manager
>> diff --git a/src/jarabe/view/keyhandler.**py
>> b/src/jarabe/view/keyhandler.**py
>> index d79bfe6..a71f260 100644
>> --- a/src/jarabe/view/keyhandler.**py
>> +++ b/src/jarabe/view/keyhandler.**py
>> @@ -60,13 +60,9 @@ _actions_table = {
>>      '<alt><shift>f': 'frame',
>>      '<alt><shift>q': 'quit_emulator',
>>      'XF86Search': 'open_search',
>> -    '<alt><shift>o': 'open_search',
>> -    '<alt><shift>s': 'say_text',
>> +    '<alt><shift>o': 'open_search'
>>  }
>>
>> -SPEECH_DBUS_SERVICE = 'org.laptop.Speech'
>> -SPEECH_DBUS_PATH = '/org/laptop/Speech'
>> -SPEECH_DBUS_INTERFACE = 'org.laptop.Speech'
>>
>>  _instance = None
>>
>> @@ -77,7 +73,6 @@ class KeyHandler(object):
>>          self._key_pressed = None
>>          self._keycode_pressed = 0
>>          self._keystate_pressed = 0
>> -        self._speech_proxy = None
>>
>>          self._key_grabber = KeyGrabber()
>>          self._key_grabber.connect('**key-pressed',
>> @@ -114,28 +109,6 @@ class KeyHandler(object):
>>          sound.set_volume(volume)
>>          sound.set_muted(volume == 0)
>>
>> -    def _get_speech_proxy(self):
>> -        if self._speech_proxy is None:
>> -            bus = dbus.SessionBus()
>> -            speech_obj = bus.get_object(SPEECH_DBUS_**SERVICE,
>> SPEECH_DBUS_PATH,
>> -                                        follow_name_owner_changes=**
>> True)
>> -            self._speech_proxy = dbus.Interface(speech_obj,
>> -                                                SPEECH_DBUS_INTERFACE)
>> -        return self._speech_proxy
>> -
>> -    def _on_speech_err(self, ex):
>> -        logging.error('An error occurred with the ESpeak service: %r',
>> ex)
>> -
>> -    def _primary_selection_cb(self, clipboard, text, user_data):
>> -        logging.debug('KeyHandler._**primary_selection_cb: %r', text)
>> -        if text:
>> -            self._get_speech_proxy().**SayText(text,
>> reply_handler=lambda: None,
>> -                error_handler=self._on_speech_**err)
>> -
>> -    def handle_say_text(self, event_time):
>> -        clipboard = gtk.clipboard_get(selection='**PRIMARY')
>> -        clipboard.request_text(self._**primary_selection_cb)
>> -
>>      def handle_previous_window(self, event_time):
>>          self._tabbing_handler.**previous_activity(event_time)
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.sugarlabs.org/archive/sugar-devel/attachments/20120118/fee0bb2a/attachment-0001.html>


More information about the Sugar-devel mailing list