Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Minor <j@lux.vu>2007-12-22 00:23:11 (GMT)
committer Joshua Minor <j@lux.vu>2007-12-22 00:23:11 (GMT)
commit81c32d12cf7ef1619a518a97a93acc994cba031e (patch)
treee8d5574a97cad8f90e668a791af0dc4e26cfebda
Initial version of Speak.activity
-rw-r--r--Speak.activity/.DS_Storebin0 -> 6148 bytes
-rw-r--r--Speak.activity/MANIFEST4
-rwxr-xr-xSpeak.activity/activity.py96
-rw-r--r--Speak.activity/activity/activity-speak.svg15
-rw-r--r--Speak.activity/activity/activity.info7
-rw-r--r--Speak.activity/audio.py147
-rw-r--r--Speak.activity/eye.py80
-rw-r--r--Speak.activity/mouth.py172
-rwxr-xr-xSpeak.activity/setup.py5
-rwxr-xr-xbuild3
-rw-r--r--dist/speak.xobin0 -> 7823 bytes
11 files changed, 529 insertions, 0 deletions
diff --git a/Speak.activity/.DS_Store b/Speak.activity/.DS_Store
new file mode 100644
index 0000000..c8c7f65
--- /dev/null
+++ b/Speak.activity/.DS_Store
Binary files differ
diff --git a/Speak.activity/MANIFEST b/Speak.activity/MANIFEST
new file mode 100644
index 0000000..078d389
--- /dev/null
+++ b/Speak.activity/MANIFEST
@@ -0,0 +1,4 @@
+activity/activity-speak.svg
+activity/activity.info
+activity.py
+setup.py \ No newline at end of file
diff --git a/Speak.activity/activity.py b/Speak.activity/activity.py
new file mode 100755
index 0000000..250c9a7
--- /dev/null
+++ b/Speak.activity/activity.py
@@ -0,0 +1,96 @@
+# coding: UTF8
+
+import commands, 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
+
+import pygst
+pygst.require("0.10")
+import gst
+
+import audio
+import eye
+import mouth
+
+class SpeakActivity(activity.Activity):
+ def __init__(self, handle):
+
+ activity.Activity.__init__(self, handle)
+ bounds = self.get_allocation()
+
+ toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.show()
+
+ 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)
+
+ self.ACTIVE = True
+ self.connect( "notify::active", self._activeCb )
+ self.audio = audio.AudioGrab(datastore, self._jobject)
+ self.mouth = mouth.Mouth(self.audio)
+ self.audio.set_wave_copy(self.mouth)
+
+ box = gtk.VBox(homogeneous=False)
+ box.pack_start(eyeBox, expand=False)
+ box.pack_start(self.mouth)
+ box.pack_start(self.entry, expand=False)
+
+ self.set_canvas(box)
+ box.show_all()
+
+ self.entry.grab_focus()
+
+ gobject.timeout_add(100, self._timeout_cb)
+
+ presenceService = presenceservice.get_instance()
+ xoOwner = presenceService.get_owner()
+ self.say("Hi %s, my name is Otto. Type something." % xoOwner.props.nick)
+
+ def _timeout_cb(self):
+ self.mouth.queue_draw();
+ return True
+
+ def entry_activate_cb(self, entry):
+ text = entry.props.text
+ if text:
+ self.say(text)
+ 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):
+ wavpath = "/tmp/speak.wav"
+ subprocess.call(["espeak", "-w", wavpath, something])
+ #subprocess.call(["playwave", wavpath])
+ self.audio.playfile(wavpath)
+
+ def _activeCb( self, widget, pspec ):
+ if (not self.props.active and self.ACTIVE):
+ self.audio.stop_sound_device()
+ elif (self.props.active and not self.ACTIVE):
+ self.audio.restart_sound_device()
+ self.ACTIVE = self.props.active
+
+ def on_quit(self, data=None):
+ self.audio.on_quit()
+
+# activate gtk threads when this module loads
+gtk.gdk.threads_init()
diff --git a/Speak.activity/activity/activity-speak.svg b/Speak.activity/activity/activity-speak.svg
new file mode 100644
index 0000000..ce92ad4
--- /dev/null
+++ b/Speak.activity/activity/activity-speak.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY fill_color "#FFFFFF">
+ <!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"/>
+</svg>
+
diff --git a/Speak.activity/activity/activity.info b/Speak.activity/activity/activity.info
new file mode 100644
index 0000000..3f7c5ee
--- /dev/null
+++ b/Speak.activity/activity/activity.info
@@ -0,0 +1,7 @@
+[Activity]
+name = Speak
+service_name = vu.lux.olpc.Speak
+class = activity.SpeakActivity
+icon = activity-speak
+activity_version = 1
+show_launcher = yes
diff --git a/Speak.activity/audio.py b/Speak.activity/audio.py
new file mode 100644
index 0000000..278f9a6
--- /dev/null
+++ b/Speak.activity/audio.py
@@ -0,0 +1,147 @@
+import pygst
+pygst.require("0.10")
+import gst
+import pygtk
+import gtk, gobject
+import signal, os
+
+import time
+
+import dbus
+
+import audioop
+
+from struct import *
+
+
+
+class AudioGrab(gobject.GObject):
+ __gsignals__ = {
+ 'new-buffer': (gobject.SIGNAL_RUN_FIRST, None, [gobject.TYPE_PYOBJECT, gobject.TYPE_BOOLEAN, gobject.TYPE_PYOBJECT])
+ }
+
+ def __init__(self, datastore, _jobject):
+ gobject.GObject.__init__(self)
+
+ self.wave_copy= None
+ self.electrical_ui_copy= None
+
+ self.datastore = datastore
+ self._jobject = _jobject
+
+ self.pipeline = None
+ self.fakesink = None
+
+ self.logging_status = False
+ self.count1 = 48000/9600
+ self.final_count = 0
+
+ self.count_temp = 0
+ self.entry_count = 0
+
+ self.draw_graph_status = False
+ self.f = None
+
+ self.temp_buffer = []
+ self.snapshot_buffer = []
+
+ def playfile(self, filename):
+ p = 'filesrc name=file-source ! decodebin ! tee name=tee tee.! audioconvert ! alsasink tee.! queue ! audioconvert name=conv'
+ self.pipeline = gst.parse_launch(p)
+
+ self.fakesink = gst.element_factory_make("fakesink", "fakesink")
+ self.fakesink.connect("handoff",self.on_buffer)
+ self.fakesink.set_property("signal-handoffs",True)
+ self.pipeline.add(self.fakesink)
+
+ conv = self.pipeline.get_by_name("conv")
+ gst.element_link_many(conv, self.fakesink)
+
+ self.pipeline.get_by_name("file-source").set_property('location', filename)
+ self.pipeline.set_state(gst.STATE_PLAYING)
+
+ def disable_handoff_signal(self):
+ self.fakesink.set_property("signal-handoffs",False)
+
+ def enable_handoff_signal(self):
+ self.fakesink.set_property("signal-handoffs",True)
+
+ def on_quit(self):
+ self.fakesink.set_property("signal-handoffs",False)
+ self.pipeline.set_state(gst.STATE_NULL)
+
+ def _new_buffer(self, buf):
+ # on the main thread
+
+ if(self.logging_status == True):
+
+ if(self.final_count==777700): #777700 is a special value for indicating 'Snapshot'
+ #buffer_temp = unpack( str(int(len(self.temp_buffer))/2)+'h' , self.temp_buffer)
+ for val in self.snapshot_buffer:
+ self.f.write(str(val)+'\n') #Write the latest buffer
+ self.f.write("stop")
+ self.f.close()
+ self.datastore.write(self._jobject)
+ self.logging_status=False
+
+ else:
+ self.count_temp+=1
+
+ if(self.count_temp==self.final_count):
+ self.count_temp=0
+ write_buffer = unpack( str(int(len(buf))/2)+'h' , buf)
+ self.f.write(str(write_buffer[int(len(buf)/4.0)])+'\n') #Writing the middle value of the buffer available at each logging time
+ self.entry_count+=1
+
+
+ self.emit("new-buffer", buf, self.draw_graph_status, self.f)
+ return False
+
+
+ def on_buffer(self,element,buffer,pad):
+ # could be in a different thread
+ #print len(str(buffer))
+ gobject.timeout_add(30, self._new_buffer, str(buffer))
+ #gobject.idle_add(self._new_buffer, str(buffer))
+ return True
+
+
+ def set_logging_status(self, status, f, multiplier):
+ self.logging_status = status
+
+ if(status==True):
+ self.f = f
+ self.final_count = (multiplier*(48000/960)*2)
+ self.count_temp=0
+ self.entry_count=0
+
+ if(status==False):
+ self.f.write("stop")
+ self.f.close()
+ self.datastore.write(self._jobject)
+ self.logging_status=False
+
+
+ def start_drawing_graph(self, status, f):
+ self.draw_graph_status = status
+ if(self.draw_graph_status==True):
+ self.f = f
+ return
+
+ def snapshot(self):
+ self.snapshot_buffer = self.wave_copy.buffers
+ return
+
+ def set_wave_copy(self, wave):
+ self.wave_copy = wave
+ return
+
+ def set_reference_electrical(self,electrical_ui):
+ self.electrical_ui_copy=electrical_ui
+
+ def stop_sound_device(self):
+ 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
new file mode 100644
index 0000000..824289c
--- /dev/null
+++ b/Speak.activity/eye.py
@@ -0,0 +1,80 @@
+#! /usr/bin/python
+
+import pygtk
+import gtk
+import gtk.gdk
+import gobject
+import cairo
+import math
+
+class Eye(gtk.DrawingArea):
+ def __init__(self):
+ 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()
+
+ 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 get_mouse(self):
+ display = gtk.gdk.display_get_default()
+ screen, mouseX, mouseY, modifiers = display.get_pointer()
+ return mouseX, mouseY
+
+ def expose(self, widget, event):
+ self.frame += 1
+ bounds = self.get_allocation()
+
+ mouseX, mouseY = self.mousePosition
+
+ eyeSize = min(bounds.width, bounds.height)
+ outlineWidth = eyeSize/20.0
+ pupilSize = eyeSize/10.0
+ pupilX = max(min(mouseX - bounds.x, bounds.width), 0)
+ pupilY = max(min(mouseY - bounds.y, bounds.height), 0)
+ dX = pupilX - bounds.width/2.
+ dY = pupilY - bounds.height/2.
+ distance = math.sqrt(dX*dX + dY*dY)
+ limit = eyeSize/2 - outlineWidth*2 - pupilSize
+ if distance > limit:
+ pupilX = bounds.width/2 + dX*limit/distance
+ pupilY = bounds.height/2 + dY*limit/distance
+
+ self.context = widget.window.cairo_create()
+ #self.context.set_antialias(cairo.ANTIALIAS_NONE)
+
+ #set a clip region for the expose event. This reduces redrawing work (and time)
+ self.context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
+ self.context.clip()
+
+ # background
+ self.context.set_source_rgb(.5,.5,.5)
+ self.context.rectangle(0,0,bounds.width,bounds.height)
+ self.context.fill()
+
+ # eye ball
+ self.context.arc(bounds.width/2,bounds.height/2, eyeSize/2-outlineWidth/2, 0,360)
+ self.context.set_source_rgb(1,1,1)
+ self.context.fill()
+
+ # outline
+ self.context.set_line_width(outlineWidth)
+ self.context.arc(bounds.width/2,bounds.height/2, eyeSize/2-outlineWidth/2, 0,360)
+ self.context.set_source_rgb(0,0,0)
+ self.context.stroke()
+
+ # pupil
+ self.context.arc(pupilX,pupilY,pupilSize,0,360)
+ self.context.set_source_rgb(0,0,0)
+ self.context.fill()
+
+ return True
diff --git a/Speak.activity/mouth.py b/Speak.activity/mouth.py
new file mode 100644
index 0000000..94d156d
--- /dev/null
+++ b/Speak.activity/mouth.py
@@ -0,0 +1,172 @@
+#! /usr/bin/python
+
+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 *
+
+class Mouth(gtk.DrawingArea):
+ def __init__(self, audioSource):
+
+ gtk.DrawingArea.__init__(self)
+ self.connect("expose_event",self.expose)
+ self.buffers = []
+ self.str_buffer=''
+ self.integer_buffer=[]
+
+ audioSource.connect("new-buffer", self._new_buffer)
+
+ self.peaks = []
+ self.main_buffers = []
+
+ self.y_mag = 0.7
+ self.freq_range=70
+ self.draw_interval = 1
+ self.num_of_points = 105
+
+ self.details_show = False
+ self.logging_status=False
+ self.f=None
+
+ 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, status, f):
+ self.str_buffer = 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.logging_status=status
+ self.f=f
+ 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
+ #################################################
+
+ def expose(self, widget, event):
+ """This function is the "expose" event handler and does all the drawing."""
+
+ bounds = self.get_allocation()
+
+ self.processBuffer(bounds)
+
+ #Create context, disable antialiasing
+ self.context = widget.window.cairo_create()
+ self.context.set_antialias(cairo.ANTIALIAS_NONE)
+
+ #set a clip region for the expose event. This reduces redrawing work (and time)
+ self.context.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
+ self.context.clip()
+
+
+ # background
+ self.context.set_source_rgb(.5,.5,.5)
+ self.context.rectangle(0,0, bounds.width,bounds.height)
+ self.context.fill()
+
+ # Draw the waveform
+ 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.set_source_rgb(0,0,0)
+ self.context.stroke()
+
+ return True
diff --git a/Speak.activity/setup.py b/Speak.activity/setup.py
new file mode 100755
index 0000000..d200a8a
--- /dev/null
+++ b/Speak.activity/setup.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+# coding: UTF8
+from sugar.activity import bundlebuilder
+bundlebuilder.start()
+
diff --git a/build b/build
new file mode 100755
index 0000000..dd0c031
--- /dev/null
+++ b/build
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+zip -r dist/speak.xo Speak.activity && scp dist/speak.xo luxvu@lux.vu:public_html/olpc/
diff --git a/dist/speak.xo b/dist/speak.xo
new file mode 100644
index 0000000..65b0dfd
--- /dev/null
+++ b/dist/speak.xo
Binary files differ