From 81c32d12cf7ef1619a518a97a93acc994cba031e Mon Sep 17 00:00:00 2001 From: Joshua Minor Date: Sat, 22 Dec 2007 00:23:11 +0000 Subject: Initial version of Speak.activity --- 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 @@ + + + +]> + + + + + + + + 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= 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 -- cgit v0.9.1