Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Minor <j@lux.vu>2008-01-10 09:34:26 (GMT)
committer Joshua Minor <j@lux.vu>2008-01-10 09:34:26 (GMT)
commit824aa0a89e0d424e44f5dbb18124eb8101bf12eb (patch)
tree175a27b0b6e8ab00b05e9cccfbb9aca4b4b560f0
parent36c2ab9023493c3cf38651006f587ba0b9b009ce (diff)
Speak v3
Lots of major performance enhancements.
-rw-r--r--Speak.activity/.DS_Storebin6148 -> 0 bytes
-rw-r--r--Speak.activity/MANIFEST7
-rwxr-xr-xSpeak.activity/activity.py275
-rw-r--r--Speak.activity/activity/activity-speak.svg3
-rw-r--r--Speak.activity/activity/activity.info2
-rw-r--r--Speak.activity/audio.py59
-rw-r--r--Speak.activity/eye.py62
-rw-r--r--Speak.activity/mouth.py170
-rwxr-xr-xbuild1
-rw-r--r--dist/speak-1.xobin0 -> 22274 bytes
-rw-r--r--dist/speak-2.xobin0 -> 27948 bytes
-rw-r--r--dist/speak-2b.xobin0 -> 27920 bytes
-rw-r--r--dist/speak-3.xobin0 -> 27945 bytes
13 files changed, 413 insertions, 166 deletions
diff --git a/Speak.activity/.DS_Store b/Speak.activity/.DS_Store
deleted file mode 100644
index c8c7f65..0000000
--- a/Speak.activity/.DS_Store
+++ /dev/null
Binary files differ
diff --git a/Speak.activity/MANIFEST b/Speak.activity/MANIFEST
index 078d389..4fdf708 100644
--- a/Speak.activity/MANIFEST
+++ b/Speak.activity/MANIFEST
@@ -1,4 +1,9 @@
activity/activity-speak.svg
activity/activity.info
activity.py
-setup.py \ No newline at end of file
+audio.py
+COPYING
+eye.py
+mouth.py
+setup.py
+voice.py \ No newline at end of file
diff --git a/Speak.activity/activity.py b/Speak.activity/activity.py
index 629219d..fbe96ad 100755
--- a/Speak.activity/activity.py
+++ b/Speak.activity/activity.py
@@ -1,23 +1,53 @@
-# coding: UTF8
+# Speak.activity
+# A simple front end to the espeak text-to-speech engine on the XO laptop
+#
+# Copyright (C) 2008 Joshua Minor
+# This file is part of Speak.activity
+#
+# Parts of Speak.activity are based on code from Measure.activity
+# Copyright (C) 2007 Arjun Sarwal - arjun@laptop.org
+#
+# Speak.activity 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 3 of the License, or
+# (at your option) any later version.
+#
+# Foobar 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
-import commands, subprocess
+
+import sys
+import os
+import subprocess
import random
from sugar.activity import activity
from sugar.datastore import datastore
from sugar.presence import presenceservice
import logging
-import sys, os
import gtk
import gobject
import pango
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.toolcombobox import ToolComboBox
+from sugar.graphics.combobox import ComboBox
+
import pygst
pygst.require("0.10")
import gst
import audio
import eye
+import glasses
import mouth
+import fft_mouth
+import waveform_mouth
+import voice
class SpeakActivity(activity.Activity):
def __init__(self, handle):
@@ -25,68 +55,257 @@ class SpeakActivity(activity.Activity):
activity.Activity.__init__(self, handle)
bounds = self.get_allocation()
- toolbox = activity.ActivityToolbox(self)
- self.set_toolbox(toolbox)
- toolbox.show()
+ # pick a voice that espeak supports
+ self.voices = voice.allVoices()
+ #self.voice = random.choice(self.voices.values())
+ self.voice = self.voices["Default"]
+ # 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.entry = gtk.Entry()
self.entry.set_editable(True)
self.entry.connect('activate', self.entry_activate_cb)
self.input_font = pango.FontDescription(str='sans bold 24')
self.entry.modify_font(self.input_font)
- self.eyes = [eye.Eye(), eye.Eye()]
- eyeBox = gtk.HBox()
- eyeBox.pack_start(self.eyes[0])
- eyeBox.pack_start(self.eyes[1])
- map(lambda e: e.set_size_request(300,300), self.eyes)
+ # make an empty box for some eyes
+ self.eyes = None
+ self.eyebox = gtk.HBox()
- self.ACTIVE = True
- self.connect( "notify::active", self._activeCb )
- self.audio = audio.AudioGrab(datastore, self._jobject)
- self.mouth = mouth.Mouth(self.audio)
+ # make an empty box to put the mouth in
+ self.mouth = None
+ self.mouthbox = gtk.HBox()
+ # layout the screen
box = gtk.VBox(homogeneous=False)
- box.pack_start(eyeBox, expand=False)
- box.pack_start(self.mouth)
+ box.pack_start(self.eyebox, expand=False)
+ box.pack_start(self.mouthbox)
box.pack_start(self.entry, expand=False)
self.set_canvas(box)
box.show_all()
+ box.add_events(gtk.gdk.BUTTON_PRESS_MASK |
+ gtk.gdk.POINTER_MOTION_MASK)
+ box.connect("motion_notify_event", self._mouse_moved_cb)
+ box.connect("button_press_event", self._mouse_clicked_cb)
+
+ # make some toolbars
+ toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.show()
+ #activitybar = toolbox.get_activity_toolbar()
+
+ voicebar = self.make_voice_bar()
+ toolbox.add_toolbar("Voice", voicebar)
+ voicebar.show()
+
+ facebar = self.make_face_bar()
+ toolbox.add_toolbar("Face", facebar)
+ facebar.show()
+
+ # make the text box active right away
self.entry.grab_focus()
- gobject.timeout_add(100, self._timeout_cb)
+ # start polling for audio
+ #gobject.timeout_add(100, self._timeout_cb)
+ # say hello to the user
+ self.active = True
presenceService = presenceservice.get_instance()
xoOwner = presenceService.get_owner()
- self.say("Hi %s, my name is Otto. Type something." % xoOwner.props.nick)
+ self.say("Hello %s, my name is XO. Type something." % xoOwner.props.nick)
+
+ def _mouse_moved_cb(self, widget, event):
+ map(lambda w: w.queue_draw(), self.eyes)
+
+ def _mouse_clicked_cb(self, widget, event):
+ pass
+
+ def make_voice_bar(self):
+ voicebar = gtk.Toolbar()
+
+ # button = ToolButton('change-voice')
+ # button.set_tooltip("Change Voice")
+ # button.connect('clicked', self.change_voice_cb)
+ # voicebar.insert(button, -1)
+ # button.show()
+
+ combo = ComboBox()
+ combo.connect('changed', self.voice_changed_cb)
+ voicenames = self.voices.keys()
+ voicenames.sort()
+ for name in voicenames:
+ combo.append_item(self.voices[name], name)
+ combo.set_active(voicenames.index(self.voice.friendlyname))
+ combotool = ToolComboBox(combo)
+ voicebar.insert(combotool, -1)
+ combotool.show()
+
+ self.pitchadj = gtk.Adjustment(50, 0, 99, 1, 10, 0)
+ self.pitchadj.connect("value_changed", self.pitch_adjusted_cb, self.pitchadj)
+ pitchbar = gtk.HScale(self.pitchadj)
+ pitchbar.set_draw_value(False)
+ #pitchbar.set_inverted(True)
+ pitchbar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ pitchbar.set_size_request(240,15)
+ pitchtool = gtk.ToolItem()
+ pitchtool.add(pitchbar)
+ pitchtool.show()
+ voicebar.insert(pitchtool, -1)
+ pitchbar.show()
+
+ self.rateadj = gtk.Adjustment(100, 80, 370, 1, 10, 0)
+ self.rateadj.connect("value_changed", self.rate_adjusted_cb, self.rateadj)
+ ratebar = gtk.HScale(self.rateadj)
+ ratebar.set_draw_value(False)
+ #ratebar.set_inverted(True)
+ ratebar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ ratebar.set_size_request(240,15)
+ ratetool = gtk.ToolItem()
+ ratetool.add(ratebar)
+ ratetool.show()
+ voicebar.insert(ratetool, -1)
+ ratebar.show()
+
+ return voicebar
+
+ def voice_changed_cb(self, combo):
+ self.voice = combo.props.value
+ self.say(self.voice.friendlyname)
+
+ def pitch_adjusted_cb(self, get, data=None):
+ self.say("pitch adjusted")
+
+ def rate_adjusted_cb(self, get, data=None):
+ self.say("rate adjusted")
+
+
+ def make_face_bar(self):
+ facebar = gtk.Toolbar()
+
+ self.numeyesadj = None
+
+ # button = ToolButton('change-voice')
+ # button.set_tooltip("Change Voice")
+ # button.connect('clicked', self.change_voice_cb)
+ # facebar.insert(button, -1)
+ # button.show()
+
+ combo = ComboBox()
+ combo.connect('changed', self.mouth_changed_cb)
+ combo.append_item(mouth.Mouth, "Simple")
+ combo.append_item(waveform_mouth.WaveformMouth, "Waveform")
+ combo.append_item(fft_mouth.FFTMouth, "Frequency")
+ combo.set_active(0)
+ combotool = ToolComboBox(combo)
+ facebar.insert(combotool, -1)
+ combotool.show()
+
+ 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")
+ combotool = ToolComboBox(self.eye_shape_combo)
+ facebar.insert(combotool, -1)
+ combotool.show()
+
+ self.numeyesadj = gtk.Adjustment(2, 1, 5, 1, 1, 0)
+ self.numeyesadj.connect("value_changed", self.eyes_changed_cb, self.numeyesadj)
+ numeyesbar = gtk.HScale(self.numeyesadj)
+ numeyesbar.set_draw_value(False)
+ numeyesbar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ numeyesbar.set_size_request(240,15)
+ numeyestool = gtk.ToolItem()
+ numeyestool.add(numeyesbar)
+ numeyestool.show()
+ facebar.insert(numeyestool, -1)
+ numeyesbar.show()
+
+ self.eye_shape_combo.set_active(0)
+
+ return facebar
+
+ def mouth_changed_cb(self, combo):
+ mouth_class = combo.props.value
+ if self.mouth:
+ self.mouthbox.remove(self.mouth)
+ self.mouth = mouth_class(self.audio)
+ self.mouthbox.add(self.mouth)
+ self.mouth.show()
+ # 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")
+
+ def eyes_changed_cb(self, ignored, ignored2=None):
+ if self.numeyesadj is None:
+ return
+
+ eye_class = self.eye_shape_combo.props.value
+ if self.eyes:
+ for eye in self.eyes:
+ self.eyebox.remove(eye)
+
+ self.eyes = []
+ numberOfEyes = int(self.numeyesadj.value)
+ for i in range(numberOfEyes):
+ eye = eye_class()
+ self.eyes.append(eye)
+ self.eyebox.pack_start(eye)
+ eye.set_size_request(300,300)
+ eye.show()
+
+ # this SegFaults: self.say(self.eye_shape_combo.get_active_text())
+ self.say("eyes changed")
def _timeout_cb(self):
- self.mouth.queue_draw();
+ # make the mouth update with the latest waveform
+ # ideally we would only do this when the audio is actually playing
+ if self.mouth:
+ self.mouth.queue_draw();
return True
def entry_activate_cb(self, entry):
+ # the user pressed Return, say the text and clear it out
text = entry.props.text
if text:
self.say(text)
+ # ideally we would clear it after we finish saying it
+ # so that you would be able to compare the audio to the text
+ # without having to remember what you typed
entry.props.text = ''
- def speak(self, widget, data=None):
- self.say(random.choice(["Let's go to Annas","Hi Opal, how are you?"]))
-
def say(self, something):
+ if self.audio is None or not self.active:
+ return
+ # ideally we would stream the audio instead of writing to disk each time...
+ print self.voice.name, ":", something
wavpath = "/tmp/speak.wav"
- subprocess.call(["espeak", "-w", wavpath, something])
- #subprocess.call(["playwave", wavpath])
+ subprocess.call(["espeak", "-w", wavpath, "-p", str(self.pitchadj.value), "-s", str(self.rateadj.value), "-v", self.voice.name, something], stdout=subprocess.PIPE)
self.audio.playfile(wavpath)
+ # this doesn't seem to work, but would avoid the /tmp/file.wave
+ # if self.proc:
+ # self.proc = None
+ # self.proc = subprocess.Popen(["espeak", "--stdout", "-s", "100", "-v", self.voice.name, something], stdout=subprocess.PIPE)
+ # print self.proc
+ # print self.proc.stdout
+ # print self.proc.stdout.fileno()
+ # self.audio.playfd(self.proc.stdout.fileno())
def _activeCb( self, widget, pspec ):
- if (not self.props.active and self.ACTIVE):
+ # only generate sound when this activity is active
+ if (not self.props.active and self.active):
self.audio.stop_sound_device()
- elif (self.props.active and not self.ACTIVE):
+ elif (self.props.active and not self.active):
self.audio.restart_sound_device()
- self.ACTIVE = self.props.active
+ self.active = self.props.active
def on_quit(self, data=None):
self.audio.on_quit()
diff --git a/Speak.activity/activity/activity-speak.svg b/Speak.activity/activity/activity-speak.svg
index ce92ad4..b99ebdf 100644
--- a/Speak.activity/activity/activity-speak.svg
+++ b/Speak.activity/activity/activity-speak.svg
@@ -5,11 +5,10 @@
<!ENTITY stroke_color "#000000">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
- <rect x="3" y="24" width="44" height="20"
- style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:2"/>
<ellipse fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" cx="12" cy="12" rx="10" ry="10"/>
<ellipse fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" cx="36" cy="12" rx="10" ry="10"/>
<circle fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" cx="16" cy="10" r="2"/>
<circle fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" cx="40" cy="10" r="2"/>
+ <ellipse fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" cx="25" cy="34" rx="20" ry="4"/>
</svg>
diff --git a/Speak.activity/activity/activity.info b/Speak.activity/activity/activity.info
index 3f7c5ee..6029cec 100644
--- a/Speak.activity/activity/activity.info
+++ b/Speak.activity/activity/activity.info
@@ -3,5 +3,5 @@ name = Speak
service_name = vu.lux.olpc.Speak
class = activity.SpeakActivity
icon = activity-speak
-activity_version = 1
+activity_version = 3
show_launcher = yes
diff --git a/Speak.activity/audio.py b/Speak.activity/audio.py
index 484285d..e29ac80 100644
--- a/Speak.activity/audio.py
+++ b/Speak.activity/audio.py
@@ -1,3 +1,27 @@
+# Speak.activity
+# A simple front end to the espeak text-to-speech engine on the XO laptop
+#
+# Copyright (C) 2008 Joshua Minor
+# This file is part of Speak.activity
+#
+# Parts of Speak.activity are based on code from Measure.activity
+# Copyright (C) 2007 Arjun Sarwal - arjun@laptop.org
+#
+# Speak.activity 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 3 of the License, or
+# (at your option) any later version.
+#
+# Foobar 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+
+# This code is a stripped down version of the audio grabber from Measure
+
import pygst
pygst.require("0.10")
import gst
@@ -19,6 +43,8 @@ class AudioGrab(gobject.GObject):
self.pipeline = None
def playfile(self, filename):
+ self.stop_sound_device()
+
# build a pipeline that reads the given file
# and sends it to both the real audio output
# and a fake one that we use to draw from
@@ -44,6 +70,36 @@ class AudioGrab(gobject.GObject):
# how do we detect when the sample has finished playing?
# we should stop the sound device and stop emitting buffers
# to save on CPU and battery usage when there is no audio playing
+
+ def playfd(self, fd):
+ self.stop_sound_device()
+
+ # build a pipeline that reads the given file
+ # and sends it to both the real audio output
+ # and a fake one that we use to draw from
+ if self.pipeline is None:
+ p = 'fdsrc name=fd-source ! wavparse ! tee name=tee tee.! audioconvert ! alsasink tee.! queue ! audioconvert name=conv'
+ self.pipeline = gst.parse_launch(p)
+
+ # make a fakesink to capture audio
+ fakesink = gst.element_factory_make("fakesink", "fakesink")
+ fakesink.connect("handoff",self.on_buffer)
+ fakesink.set_property("signal-handoffs",True)
+ self.pipeline.add(fakesink)
+
+ # attach it to the pipeline
+ conv = self.pipeline.get_by_name("conv")
+ gst.element_link_many(conv, fakesink)
+
+ # set the source file
+ self.pipeline.get_by_name("fd-source").set_property('fd', fd)
+
+ # play
+ self.restart_sound_device()
+
+ # how do we detect when the sample has finished playing?
+ # we should stop the sound device and stop emitting buffers
+ # to save on CPU and battery usage when there is no audio playing
def on_quit(self):
self.pipeline.set_state(gst.STATE_NULL)
@@ -59,7 +115,8 @@ class AudioGrab(gobject.GObject):
return True
def stop_sound_device(self):
- self.pipeline.set_state(gst.STATE_NULL)
+ if self.pipeline is not None:
+ self.pipeline.set_state(gst.STATE_NULL)
def restart_sound_device(self):
self.pipeline.set_state(gst.STATE_PLAYING)
diff --git a/Speak.activity/eye.py b/Speak.activity/eye.py
index 824289c..72d9170 100644
--- a/Speak.activity/eye.py
+++ b/Speak.activity/eye.py
@@ -1,4 +1,24 @@
-#! /usr/bin/python
+# Speak.activity
+# A simple front end to the espeak text-to-speech engine on the XO laptop
+#
+# Copyright (C) 2008 Joshua Minor
+# This file is part of Speak.activity
+#
+# Parts of Speak.activity are based on code from Measure.activity
+# Copyright (C) 2007 Arjun Sarwal - arjun@laptop.org
+#
+# Speak.activity 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 3 of the License, or
+# (at your option) any later version.
+#
+# Foobar 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
import pygtk
import gtk
@@ -12,19 +32,33 @@ class Eye(gtk.DrawingArea):
gtk.DrawingArea.__init__(self)
self.connect("expose_event",self.expose)
self.frame = 0
- # instead of listening for mouse move events we poll to see if the mouse has moved
- # this is so we can react to the mouse even when it isn't directly over this widget
- gobject.timeout_add(100, self._timeout_cb)
- self.mousePosition = self.get_mouse()
+ self.blink = False
+
+ # listen for clicks
+ self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+ self.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
+ self.connect("button_press_event", self._mouse_pressed_cb)
+ self.connect("button_release_event", self._mouse_released_cb)
+
+ # Instead of listening for mouse move events we could poll to see if the mouse has moved
+ # would let us react to the mouse even when it isn't directly over this widget.
+ # Unfortunately that would cause a lot of CPU usage. So instead we rely on our parent to
+ # tell us to redraw when the mouse has moved. We still need to call add_events so that
+ # our parent will get mouse motion events, but we don't connect the callback for them ourselves.
+ self.add_events(gtk.gdk.POINTER_MOTION_MASK)
+ # self.connect("motion_notify_event", self._mouse_moved_cb)
- def _timeout_cb(self):
- # only redraw if the mouse has moved
- newPosition = self.get_mouse()
- if newPosition != self.mousePosition:
- self.queue_draw()
- self.mousePosition = newPosition
- return True
+ def _mouse_moved_cb(self, widget, event):
+ self.queue_draw()
+ def _mouse_pressed_cb(self, widget, event):
+ self.blink = True
+ self.queue_draw()
+
+ def _mouse_released_cb(self, widget, event):
+ self.blink = False
+ self.queue_draw()
+
def get_mouse(self):
display = gtk.gdk.display_get_default()
screen, mouseX, mouseY, modifiers = display.get_pointer()
@@ -34,7 +68,7 @@ class Eye(gtk.DrawingArea):
self.frame += 1
bounds = self.get_allocation()
- mouseX, mouseY = self.mousePosition
+ mouseX, mouseY = self.get_mouse()
eyeSize = min(bounds.width, bounds.height)
outlineWidth = eyeSize/20.0
@@ -77,4 +111,6 @@ class Eye(gtk.DrawingArea):
self.context.set_source_rgb(0,0,0)
self.context.fill()
+ self.blink = False
+
return True
diff --git a/Speak.activity/mouth.py b/Speak.activity/mouth.py
index 2a42833..453675d 100644
--- a/Speak.activity/mouth.py
+++ b/Speak.activity/mouth.py
@@ -1,18 +1,31 @@
-#! /usr/bin/python
+# Speak.activity
+# A simple front end to the espeak text-to-speech engine on the XO laptop
+#
+# Copyright (C) 2008 Joshua Minor
+# This file is part of Speak.activity
+#
+# Parts of Speak.activity are based on code from Measure.activity
+# Copyright (C) 2007 Arjun Sarwal - arjun@laptop.org
+#
+# Speak.activity 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 3 of the License, or
+# (at your option) any later version.
+#
+# Foobar 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+
+# This code is a super-stripped down version of the waveform view from Measure
-import pygst
-pygst.require("0.10")
-import pygtk
import gtk
import cairo
-import gobject
-from time import *
-from struct import *
-import pango
-import os
-import audioop
-from Numeric import *
-from FFT import *
+from struct import unpack
+import numpy.core
class Mouth(gtk.DrawingArea):
def __init__(self, audioSource):
@@ -20,120 +33,28 @@ class Mouth(gtk.DrawingArea):
gtk.DrawingArea.__init__(self)
self.connect("expose_event",self.expose)
self.buffers = []
+ self.buffer_size = 256
self.main_buffers = []
- self.integer_buffer = []
+ self.newest_buffer = []
audioSource.connect("new-buffer", self._new_buffer)
- self.peaks = []
-
- self.y_mag = 0.7
- self.freq_range=70
- self.draw_interval = 1
- self.num_of_points = 105
-
- self.stop=False
-
- self.fft_show = False
- self.fftx = []
-
- self.y_mag_bias_multiplier = 1 #constant to multiply with self.param2 while scaling values
-
- self.scaleX = "10"
- self.scaleY = "10"
-
-
def _new_buffer(self, obj, buf):
- self.integer_buffer = list(unpack( str(int(len(buf))/2)+'h' , buf))
- if(len(self.main_buffers)>6301):
- del self.main_buffers[0:(len(self.main_buffers)-6301)]
- self.main_buffers += self.integer_buffer
+ self.newest_buffer = list(unpack( str(int(len(buf))/2)+'h' , buf))
+ self.main_buffers += self.newest_buffer
+ if(len(self.main_buffers)>self.buffer_size):
+ del self.main_buffers[0:(len(self.main_buffers)-self.buffer_size)]
+ self.queue_draw()
return True
-
def processBuffer(self, bounds):
- self.param1 = bounds.height/65536.0
- self.param2 = bounds.height/2.0
-
- if(self.stop==False):
-
- if(self.fft_show==False):
-
- ######################filtering####################
- weights = [1,2,3,4,3,2,1]
- weights_sum = 16.0
-
- for i in range(3,len(self.integer_buffer)-3):
- self.integer_buffer[i] = (self.integer_buffer[(i-3)]+2*self.integer_buffer[(i-2)] + 3*self.integer_buffer[(i-1)] + 4*self.integer_buffer[(i)]+3*self.integer_buffer[(i+1)] + 2*self.integer_buffer[(i+2)] + self.integer_buffer[(i+3)]) / weights_sum
- ###################################################
-
- self.y_mag_bias_multiplier=1
- self.draw_interval=10
-
- #100hz
- if(self.freq_range==30):
- self.spacing = 60
- self.num_of_points=6300
-
- #1khz
- if(self.freq_range==50):
- self.spacing = 6
- self.num_of_points=630
-
- #4khz
- if(self.freq_range==70):
- self.spacing = 1
- self.num_of_points = 105
-
- self.scaleX = str(self.spacing*.104) + " msec" #.104 = 5/48; 5 points per division and 48 khz sampling
-
- if(len(self.main_buffers)>=self.num_of_points):
- del self.main_buffers[0:len(self.main_buffers)-(self.num_of_points+1)]
- self.buffers=[]
- i=0
- while i<self.num_of_points:
- self.buffers.append(self.main_buffers[i])
- i+=self.spacing
-
- self.scaleY=" "
-
- else:
- ###############fft################
- Fs = 48000
- nfft= 65536
- self.integer_buffer=self.integer_buffer[0:256]
- self.fftx = fft(self.integer_buffer, 256,-1)
-
- self.fftx=self.fftx[0:self.freq_range*2]
- self.draw_interval=bounds.width/(self.freq_range*2)
-
- NumUniquePts = ceil((nfft+1)/2)
- self.buffers=abs(self.fftx)*0.02
- self.y_mag_bias_multiplier=0.1
- self.scaleX = "hz"
- self.scaleY = ""
- ##################################
-
- if(len(self.buffers)==0):
- return False
-
- ###############Scaling the values################
- val = []
- for i in self.buffers:
- temp_val_float = float(self.param1*i*self.y_mag) + self.y_mag_bias_multiplier * self.param2
-
- if(temp_val_float >= bounds.height):
- temp_val_float = bounds.height-25
- if(temp_val_float <= 0):
- temp_val_float = 25
- val.append( temp_val_float )
-
- self.peaks = val
- #################################################
+ if len(self.main_buffers) == 0:
+ self.volume = 0
+ else:
+ self.volume = numpy.core.max(self.main_buffers)# - numpy.core.min(self.main_buffers)
def expose(self, widget, event):
"""This function is the "expose" event handler and does all the drawing."""
-
bounds = self.get_allocation()
self.processBuffer(bounds)
@@ -151,12 +72,21 @@ class Mouth(gtk.DrawingArea):
self.context.rectangle(0,0, bounds.width,bounds.height)
self.context.fill()
- # Draw the waveform
+ # Draw the mouth
+ volume = self.volume / 65535.
+ mouthH = volume * bounds.height
+ mouthW = volume**2 * (bounds.width/2.) + bounds.width/2.
+ # T
+ # L R
+ # B
+ Lx,Ly = bounds.width/2 - mouthW/2, bounds.height/2
+ Tx,Ty = bounds.width/2, bounds.height/2 - mouthH/2
+ Rx,Ry = bounds.width/2 + mouthW/2, bounds.height/2
+ Bx,By = bounds.width/2, bounds.height/2 + mouthH/2
self.context.set_line_width(10.0)
- count = 0
- for peak in self.peaks:
- self.context.line_to(count,bounds.height - peak)
- count += self.draw_interval
+ self.context.move_to(Lx,Ly)
+ self.context.curve_to(Tx,Ty, Tx,Ty, Rx,Ry)
+ self.context.curve_to(Bx,By, Bx,By, Lx,Ly)
self.context.set_source_rgb(0,0,0)
self.context.stroke()
diff --git a/build b/build
index dd0c031..4bcab6a 100755
--- a/build
+++ b/build
@@ -1,3 +1,4 @@
#!/bin/sh
+rm dist/speak.xo
zip -r dist/speak.xo Speak.activity && scp dist/speak.xo luxvu@lux.vu:public_html/olpc/
diff --git a/dist/speak-1.xo b/dist/speak-1.xo
new file mode 100644
index 0000000..a8fb3e3
--- /dev/null
+++ b/dist/speak-1.xo
Binary files differ
diff --git a/dist/speak-2.xo b/dist/speak-2.xo
new file mode 100644
index 0000000..4f9ef59
--- /dev/null
+++ b/dist/speak-2.xo
Binary files differ
diff --git a/dist/speak-2b.xo b/dist/speak-2b.xo
new file mode 100644
index 0000000..e272ca4
--- /dev/null
+++ b/dist/speak-2b.xo
Binary files differ
diff --git a/dist/speak-3.xo b/dist/speak-3.xo
new file mode 100644
index 0000000..152c9d4
--- /dev/null
+++ b/dist/speak-3.xo
Binary files differ