Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xJ2JToolbar.py8
-rwxr-xr-xJ2JToolbar.py~319
-rw-r--r--run.py~576
3 files changed, 902 insertions, 1 deletions
diff --git a/J2JToolbar.py b/J2JToolbar.py
index 2e66eaa..54b9b22 100755
--- a/J2JToolbar.py
+++ b/J2JToolbar.py
@@ -21,7 +21,7 @@ import pygame
import olpcgames
from gettext import gettext as _
from sugar.graphics.toolbutton import ToolButton
-from sugar.graphics.toolcombobox import ToolComboBox
+
import logging
log = logging.getLogger('City run')
@@ -31,6 +31,12 @@ ImagePath = os.path.dirname(os.path.abspath(__file__)) + "/City/Images"
GST_PIPE = ['v4l2src', 'ffmpegcolorspace', 'pngenc']
+
+
+from sugar.graphics.toolcombobox import ToolComboBox
+
+
+
class readScenes(object):
def __init__(self, scpath):
self.scpath = scpath
diff --git a/J2JToolbar.py~ b/J2JToolbar.py~
new file mode 100755
index 0000000..2e66eaa
--- /dev/null
+++ b/J2JToolbar.py~
@@ -0,0 +1,319 @@
+#This python module is part of the Jam2Jam XO Activity, March, 2010
+#
+#Copyright (C) 2010 Thorin Kerr & Andrew Brown
+#
+#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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import gtk, gst, thread, tempfile, time, os
+import pygame
+import olpcgames
+from gettext import gettext as _
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.toolcombobox import ToolComboBox
+import logging
+
+log = logging.getLogger('City run')
+log.setLevel(logging.DEBUG)
+
+ImagePath = os.path.dirname(os.path.abspath(__file__)) + "/City/Images"
+
+GST_PIPE = ['v4l2src', 'ffmpegcolorspace', 'pngenc']
+
+class readScenes(object):
+ def __init__(self, scpath):
+ self.scpath = scpath
+ self.scene_names = [dir for dir in os.listdir(self.scpath) if os.path.isdir(self.scpath + '/' + dir)]
+ self.scene_data = []
+ for n in self.scene_names:
+ fp = self.scpath + "/" + n
+ mdfile = [open(fp + '/' + f) for f in os.listdir(fp) if os.path.isfile(fp + "/" + f) and f.startswith(n)]
+ if mdfile:
+ result = {"Name":n}
+ defaults = {}
+ for line in mdfile[0]:
+ if line.startswith('#') or line.startswith('\n'):
+ pass
+ else:
+ keyvals = line.split('=')
+ if len(keyvals) == 2:
+ key = keyvals[0].upper()
+ val = (keyvals[1][:-1] if keyvals[1][-1] == '\n' else keyvals[1])
+ if key.startswith('TEMPO'):
+ result['Tempo'] = val.replace(' ','')
+ elif key.startswith('KEY'):
+ result['Key'] = val.replace(' ','')
+ elif key.startswith('MODE'):
+ result['Mode'] = val.replace(' ','')
+ else:
+ pass
+ else:
+ raise IOError, "Bad Scene Meta Data file: %s" %keyvals
+ result['Defaults'] = {}
+ self.scene_data.append(result)
+ else:
+ raise IOError, "Can't find Meta Data file in %s Scene" %n
+ def scene_instruct(self, name):
+ "returns a list of strings suitable to give to a ScenePlayer object for creating a scene"
+ for scd in self.scene_data:
+ if scd['Name'] == name:
+ collected = [name]
+ for k in ['Key', 'Mode', 'Tempo', 'Defaults']:
+ try:
+ collected.append(str(scd[k]))
+ except KeyError:
+ collected.append('None')
+ return collected
+ def get_scene_list(self):
+ "returns a list of scene strings for the toolbar, with City as the default"
+ ordered_names = self.scene_names[:]
+ if 'City' in ordered_names:
+ ordered_names.insert(0,ordered_names.pop(ordered_names.index('City')))
+ return [self.scene_instruct(s) for s in ordered_names]
+
+class CameraSnap(object):
+ """A class representing the OLPC camera."""
+ def __init__(self):
+ log.info("CameraSnap init")
+ snap_file, self.snap_path = tempfile.mkstemp(suffix = '.png')
+ pipe = GST_PIPE + ['filesink location=%s' % self.snap_path]
+ self.pipe = gst.parse_launch('!'.join(pipe))
+ self.bus = self.pipe.get_bus()
+ log.info("tempfile is %s " %self.snap_path)
+ def Snap(self):
+ """Take a snapshot."""
+ log.info("about to set pipe state to PLAY")
+ self.pipe.set_state(gst.STATE_PLAYING)
+ log.info("about to poll")
+ thread.start_new_thread(self.bus.poll, (gst.MESSAGE_EOS, -1))
+ for i in xrange(60):
+ time.sleep(0.18)
+ if os.path.getsize(self.snap_path) > 0: break
+ else: raise IOError, "Error writing camera snap to file"
+ return self.snap_path
+ def Stop(self):
+ self.pipe.set_state(gst.STATE_NULL)
+
+
+class Jam2JamToolBar(gtk.Toolbar):
+ def __init__(self, activity):
+ gtk.Toolbar.__init__(self)
+ self.activity = activity
+ self.parameters = ['Density', 'Pitch', 'Length', 'Timbre', 'Volume'] # no tempo here.
+ scene_stuff = readScenes(self.activity._ScenePath)
+ self.scenes = scene_stuff.get_scene_list()
+ print "SCENE DATA IS ", self.scenes
+ #self.scenes = [['City', 'A', 'minor pentatonic'], ['City', 'G#', 'major']] #this data needs to be obtained from directories
+ self.play_pause_state = 'Playing'
+ self.scene_init = True
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ self.insert(separator, -1)
+
+ #Horizontal Parameter control combobox
+ self._add_widget(gtk.Label(_('Horizontal:')))
+ self._Hparameter_combo = ToolComboBox()
+ for i, f in enumerate(self.parameters):
+ self._Hparameter_combo.combo.append_item(i, f)
+ self._Hparameter_combo.combo.connect('changed', self._Hparameter_change_cb)
+ self._add_widget(self._Hparameter_combo)
+ self._Hparameter_combo.combo.set_active(0)
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+ #Vertical Parameter control combobox
+ self._add_widget(gtk.Label(_('Vertical:')))
+ self._Vparameter_combo = ToolComboBox()
+ for j, k in enumerate(self.parameters):
+ self._Vparameter_combo.combo.append_item(j, k)
+ self._Vparameter_combo.combo.connect('changed', self._Vparameter_change_cb)
+ self._add_widget(self._Vparameter_combo)
+ self._Vparameter_combo.combo.set_active(1)
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+
+ #Scene Selection control combobox
+ self._add_widget(gtk.Label(_('Scene:')))
+ self._Scene_combo = ToolComboBox()
+ for l, m in enumerate(self.scenes):
+ self._Scene_combo.combo.append_item(l, m[0])
+ self._Scene_combo.combo.connect('changed', self._Scene_change_cb)
+ self._add_widget(self._Scene_combo)
+ #ought to do this safely somehow.
+ self._Scene_combo.combo.set_active(0)
+ self.scene_init = False
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+
+ #Camera Button
+ self.camera_ready = True
+ camera_icon = ImagePath + "/camera-external.svg"
+ camera_busy_icon = ImagePath + "/camera-busy.svg"
+ self.camera_image, self.camera_busy_image = gtk.Image(), gtk.Image()
+ self.camera_image.set_from_file(camera_icon)
+ self.camera_busy_image.set_from_file(camera_busy_icon)
+ self.camera_image.show()
+ #camera_busy_image.show()
+ self._cameraButton = ToolButton()
+ self._cameraButton.set_icon_widget(self.camera_image)
+ self._cameraButton.connect('clicked', self._cameraSnap_cb)
+ self._cameraButton.set_tooltip(_('Snapshot'))
+ self.insert(self._cameraButton, -1)
+ self._cameraButton.show()
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+ #Play/Pause Button
+ pause_icon = ImagePath + "/media-playback-pause.svg"
+ play_icon = ImagePath + "/media-playback-start.svg"
+ self.pause_image = gtk.Image()
+ self.pause_image.set_from_file(pause_icon)
+
+ self.play_image = gtk.Image()
+ self.play_image.set_from_file(play_icon)
+
+ self._pauseButton = ToolButton()
+ self._pauseButton.connect('clicked', self._pause_cb)
+ self.pause_image.show()
+ self._pauseButton.set_icon_widget(self.pause_image)
+ self._pauseButton.set_tooltip(_('Pause'))
+ #self._toggleplay_pause()
+ self.insert(self._pauseButton, -1)
+ self._pauseButton.show()
+
+ # Separator
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+
+ def _add_widget(self, widget, expand=False):
+ tool_item = gtk.ToolItem()
+ tool_item.set_expand(expand)
+ tool_item.add(widget)
+ widget.show()
+ self.insert(tool_item, -1)
+ tool_item.show()
+
+ def _toggleplay_pause(self):
+ if self.play_pause_state == "Playing":
+ self.activity.jamScene.music_player.pause()
+ self.play_image.show()
+ self._pauseButton.set_icon_widget(self.play_image)
+ self._pauseButton.set_tooltip(_('Play'))
+ self.play_pause_state = "Paused"
+ else:
+ self.activity.jamScene.music_player.resume()
+ self.pause_image.show()
+ self._pauseButton.set_icon_widget(self.pause_image)
+ self._pauseButton.set_tooltip(_('Pause'))
+ self.play_pause_state = "Playing"
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def _show_busy_camera(self):
+ self.camera_ready = False
+ self.camera_busy_image.show()
+ self._cameraButton.set_icon_widget(self.camera_busy_image)
+ self._cameraButton.set_tooltip(_('Please wait...'))
+
+ def _show_active_camera(self):
+ self.camera_image.show()
+ self._cameraButton.set_icon_widget(self.camera_image)
+ self._cameraButton.set_tooltip(_('Snap'))
+ self.camera_ready = True
+
+ def _Hparameter_change_cb(self, widget):
+ param = "Parameter|Horizontal|" + self.parameters[self._Hparameter_combo.combo.get_active()]
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=param))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def _Vparameter_change_cb(self, widget):
+ param = "Parameter|Vertical|" + self.parameters[self._Vparameter_combo.combo.get_active()]
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=param))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ def _Scene_change_cb(self, widget):
+ if self.scene_init:
+ pass
+ else:
+ selection = self.scenes[self._Scene_combo.combo.get_active()]
+ scene = "Reload|" + '|'.join(map(lambda x: str(x), selection))
+ olpcgames.eventwrap.post(olpcgames.eventwrap.Event(pygame.USEREVENT, action=scene))
+ try:
+ self.activity._pgc.grab_focus()
+ except AttributeError:
+ pass
+
+ ### functions to assist calls from pygame
+ def deactivate_scene_change(self):
+ self._Scene_combo.set_sensitive(False)
+ def reactivate_scene_change(self):
+ self._Scene_combo.set_sensitive(True)
+ def set_horizontal_parameter(self, param):
+ ndx = self.parameters.index(param)
+ self._Hparameter_combo.combo.set_active(ndx)
+ def set_vertical_parameter(self, param):
+ ndx = self.parameters.index(param)
+ self._Vparameter_combo.combo.set_active(ndx)
+
+ def _cameraSnap_cb(self, widget):
+ "Here I could wrap a camera event..."
+ def snaptime():
+ snap = CameraSnap()
+ self.activity.cameras_loaded.append(snap)
+ picpath = snap.Snap()
+ self.activity.load_image(picpath)
+ snap.Stop()
+ self._show_active_camera()
+ self.activity._pgc.grab_focus()
+ if self.camera_ready:
+ self._show_busy_camera()
+ thread.start_new_thread(snaptime, ())
+ else:
+ log.info('Ignoring request to use camera, as camera is currently busy')
+
+ def _pause_cb(self, widget):
+ self._toggleplay_pause()
+ log.info("Play/Pause Button pressed")
+
diff --git a/run.py~ b/run.py~
new file mode 100644
index 0000000..242ad90
--- /dev/null
+++ b/run.py~
@@ -0,0 +1,576 @@
+#! /usr/bin/env python
+
+#This python module is part of the Jam2Jam XO Activity, March, 2010
+#
+#Copyright (C) 2010 Thorin Kerr & Andrew Brown
+#
+#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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import subprocess
+import logging, olpcgames
+import olpcgames.pausescreen as pausescreen
+import olpcgames.mesh as mesh
+from olpcgames import camera
+from sugar.presence import presenceservice
+from threading import Timer
+from math import ceil, sqrt
+
+
+from City.CsHelpers import *
+from City.Parameters import Instrument
+import City.City as City
+
+log = logging.getLogger( 'City run' )
+log.setLevel( logging.DEBUG )
+
+def buildInstruments(names, imgpath, screensize, scale):
+ "returns a list of Instrument objects, loaded with images"
+ Instruments = [Instrument(names[i]) for i in range(len(names))]
+ imagefiles = ResourceList(imgpath, '.png')
+ startx = 0
+ for i in Instruments:
+ for f in imagefiles:
+ if i.name.startswith(f[:4]):
+ i.loadImage(ImagePath+'/'+f, scale)
+ i.x = screensize[0] * 0.8 - startx
+ i.y = (screensize[1] - i.image.get_size()[1]) * 0.5
+ i.Touch = True
+ startx = startx + (screensize[0] * 0.8) / len(Instruments)
+ return Instruments
+
+def getInstrumentParameters(scene, inm):
+ "return a list of parameters values for the instrument, in order of PNAMES"
+ pobj = scene.Params
+ result = []
+ for pnm in PNAMES:
+ result.append(pobj.getValue(pnm, inm))
+ return result
+
+def setInstrumentParameters(scene, inm, vlst):
+ "sets parameters for an instrument"
+ pobj = scene.Params
+ for pnm,val in zip(PNAMES, vlst):
+ pobj.setValue(pnm, inm, val)
+ return True
+
+KEYCODES = {276:"Nudge|Left", 275:"Nudge|Right", 274:"Nudge|Down", 273:"Nudge|Up",
+ 260:"Nudge|Left", 262:"Nudge|Right", 258:"Nudge|Down", 264:"Nudge|Up",
+ 263: "Instrument|Bass", 257:"Instrument|Chords", 265:"Instrument|Lead", 259:"Instrument|Drums",
+ 49: "Instrument|Bass", 50:"Instrument|Chords", 51:"Instrument|Lead", 52:"Instrument|Drums",
+ 112: "Parameter|Pitch", 118:"Parameter|Volume", 100:"Parameter|Density", 108:"Parameter|Length", 116:"Parameter|Timbre",
+ 304: "Modifier|Shift"}
+
+
+class jamScene( object ):
+ def __init__(self, screen, scene = 'City', key = 'A', mode = 'minor', tempo = 120, initial_parameters = {}):
+ self.scene = City.ScenePlayer(scene, key, mode, tempo, initial_parameters)
+ self.music_player = City.makePlayer(self.scene)
+ self.beatEstimator = beatEstimator(self.music_player.tempoMult, 0.17, self.music_player.beatlimit)
+ self.pending_instrument_assignment = []
+ self.latency_counter = 0
+ self.latency = [0.07]
+ self.latency_time_ID = {}
+ self._syncloop_running = 0
+ global schedEvent, now
+ schedEvent = self.scene.TimeQueue.schedEvent
+ now = self.scene.cs.perfTime
+ self.screen = screen
+ screenRect = screen.get_rect()
+ self.screenSize = screen.get_size()
+ self.playArea = pygame.Rect(screenRect.left,screenRect.top, screenRect.width, screenRect.height * 0.8)
+ if olpcgames.ACTIVITY:
+ olpcgames.ACTIVITY.playArea = self.playArea
+ olpcgames.ACTIVITY.jamScene = self
+ self.panelArea = pygame.Rect(screenRect.left,screenRect.height * 0.8, screenRect.width, screenRect.height * 0.2)
+ self.TemplateInstruments = buildInstruments(INAMES, ImagePath, self.playArea.size, 2)
+ for oni in self.TemplateInstruments:
+ oni.activate()
+ self.PanelInstruments = buildInstruments(INAMES, ImagePath, self.panelArea.size, 1.5)
+ for pnl in self.PanelInstruments:
+ pnl.Touch = True
+ pnl.activate()
+ imagesize = self.TemplateInstruments[0].image.get_size()
+ self.panelSize = (self.screenSize[0], imagesize[1] + 10)
+ #movement limits
+ self.xmin = self.playArea.left + imagesize[0] * 0.5
+ self.xmax = self.playArea.right - imagesize[0] * 0.5
+ self.ymax = self.playArea.bottom - imagesize[1] * 0.5
+ self.ymin = self.playArea.top + imagesize[1] * 0.5
+ #interface key codes
+ self.keycode = KEYCODES
+ #various states
+ self.keyActions = []
+ self.selectedInstrument = self.TemplateInstruments[0]
+ self.occupiedInstruments = {self.selectedInstrument.name: None}
+ self.myself = None
+ self.sharer = False
+ self.connected = False
+ self.timeTally = []
+ self.running = True
+ self.Vparam = "Pitch"
+ self.Hparam = "Density"
+ #interface controls
+ self.movingInstrument = False
+ #initial draw
+ panelColour = (0,0,0)
+ self.snap_store = (olpcgames.ACTIVITY.snap_store if platform == 'Sugar' else [])
+ self.feedbackgroundImage = None
+ if self.screenSize == (1200, 780):
+ self.setbackgroundImage(pygame.image.load(ImagePath + "/jam2jamXO_2.png").convert())
+ else:
+ bgi = pygame.image.load(ImagePath + "/jam2jamXO_2.png").convert()
+ bgi_scaled = pygame.transform.scale(bgi, self.playArea.size)
+ self.setbackgroundImage(bgi_scaled)
+ self.panel = pygame.Surface((self.panelArea.width, self.panelArea.height))
+ self.panel.fill(panelColour)
+ self.screen.blit(self.panel, self.panelArea)
+ pygame.display.flip()
+ for pnl in self.PanelInstruments: pnl.y = pnl.y() + self.playArea.height
+ def setbackgroundImage(self, img):
+ self.backgroundImage = img
+ self.screen.blit(self.backgroundImage, (0,0), self.playArea)
+ self.selectedInstrument.Touch = True
+ def updatePanel(self):
+ "redraw panel icons"
+ for pi in self.PanelInstruments:
+ if not pi.Touch:
+ pass
+ else:
+ if pi.name in self.occupiedInstruments:
+ pi.deactivate()
+ else:
+ pi.activate()
+ self.screen.blit(pi.image, pi.Rect)
+ pi.Touch = False
+ def runloop(self):
+ "main game loop"
+ clock = pygame.time.Clock()
+ imgcnt = 0
+ self.music_player.playLoop(now())
+ while self.running:
+ events = (pausescreen.get_events(sleep_timeout = 43200) if platform == 'Sugar' else pygame.event.get())
+ for event in events:
+ self.eventAction(event)
+ for act in self.keyActions:
+ self.interfaceAction(act)
+ if self.feedbackgroundImage:
+ self.setbackgroundImage(self.feedbackgroundImage)
+ self.feedbackgroundImage = None
+ self.updateInspos()
+ self.updatePanel()
+ if platform == 'Sugar': currentcnt = len(self.snap_store)
+ else: currentcnt = 0
+ if imgcnt == currentcnt:
+ pass
+ else:
+ self.music_player.picture_cycle = [self, True]
+ imgcnt = currentcnt
+ pygame.display.flip()
+ clock.tick(25)
+ def updateInspos(self):
+ "animate selected instrument."
+ ins = self.selectedInstrument
+ if ins.Touch:
+ xval = self.scene.Params.getValue(self.Hparam, ins.name)
+ yval = self.scene.Params.getValue(self.Vparam, ins.name)
+ xpos = rescale(xval, 0,1,self.xmin, self.xmax)
+ ypos = rescale(yval, 0,1,self.ymax, self.ymin)
+ self.screen.blit(self.backgroundImage, ins.Rect, ins.Rect)
+ ins.ctr = (xpos, ypos)
+ ins.Touch = False
+ self.screen.blit(ins.image, ins.Rect)
+ def sendSync(self):
+ "Tell audio loop to broadcast time and beat messages"
+ if self._syncloop_running:
+ self.music_player.sendSync = True
+ log.info("sent sync")
+ schedEvent(now() + 10.7, self.sendSync)
+ def setselectedInstrument(self, ins):
+ "select the instrument onscreen"
+ self.selectedInstrument = ins
+ if ins.name not in self.occupiedInstruments:
+ self.occupiedInstruments.update({ins.name:str(self.myself)})
+ self.selectedInstrument.Touch = True
+ def eventAction(self, event):
+ "detect events, and select action"
+ if event.type == pygame.QUIT:
+ self.music_player.freeze()
+ self.running = False
+ elif event.type == pygame.USEREVENT:
+ if hasattr(event, "action"):
+ if event.action.startswith("Parameter"):
+ args = event.action.split('|')
+ if args[1] == "Horizontal":
+ self.Hparam = args[2]
+ self.selectedInstrument.Touch = True
+ elif args[1] == "Vertical":
+ self.Vparam = args[2]
+ self.selectedInstrument.Touch = True
+ else:
+ raise ValueError, 'Unknown Parameter Action %s' %args
+ elif event.action.startswith('Reload'):
+ #should look always like this: "Reload|name|key:mode|tempo|defaults"
+ args = event.action.split('|')
+ name = args[1]
+ key = ('E' if args[2] == 'None' else args[2])
+ mode = ('minor' if args[3] == 'None' else args[3])
+ tempo = (117 if args[4] == 'None' else int(args[4]))
+ d = eval(args[5])
+ defaults = (d if d else {})
+ self.load_scene(name, key, mode, tempo, defaults) #this call blocks
+ if self.pending_instrument_assignment: #now check if we are waiting to assign instruments and params.
+ self.receiveMessage("AuthorisedInstrument|%s|%s" %(self.pending_instrument_assignment[0], self.pending_instrument_assignment[1]), self.myself)
+ elif event.action.startswith("Shared"):
+ self.sharer = "Pending"
+ log.info("Sharing activity")
+ elif event.action.startswith("Joined"):
+ log.info("Joined Activity")
+ else:
+ log.debug("unknown parameter change: %s", event.action)
+ else: log.debug("ignoring USEREVENT %s", event)
+ elif event.type == pygame.MOUSEBUTTONDOWN:
+ x,y = event.pos
+ Ins = self.selectedInstrument
+ if Ins.Rect.collidepoint(x,y):
+ self.movingInstrument = Ins
+ else:
+ for Panndx in range(len(self.PanelInstruments)):
+ Pan = self.PanelInstruments[Panndx]
+ if Pan.Rect.collidepoint(x,y):
+ if Pan.active: self.requestInstrument(Pan.name)
+ break
+ elif event.type == pygame.MOUSEMOTION:
+ if self.movingInstrument:
+ insname = self.movingInstrument.name
+ self.scene.Params.setValue(self.Hparam, insname, rescale(event.pos[0], self.playArea.left, self.playArea.right, 0, 1))
+ self.scene.Params.setValue(self.Vparam, insname, limit(rescale(event.pos[1], self.playArea.bottom, self.playArea.top, 0, 1), 0,1))
+ self.movingInstrument.Touch = True
+ elif event.type == pygame.MOUSEBUTTONUP:
+ self.movingInstrument = False
+ elif platform == 'Sugar' and event.type == mesh.CONNECT:
+ log.info( """Connected to the mesh!| %s""", event )
+ self.connected = True
+ self.music_player.resetBeat()
+ elif event.type == pygame.KEYDOWN:
+ try:
+ iaction = self.keycode[event.key]
+ self.keyActions.append(iaction)
+ except KeyError:
+ pass
+ elif event.type == pygame.KEYUP:
+ try:
+ self.keyActions.remove(self.keycode[event.key])
+ except ValueError: pass
+ except KeyError: pass
+ elif self.connected and event.type == mesh.PARTICIPANT_ADD:
+ if not self.myself: self.myself = mesh.my_handle()
+ if event.handle == self.myself:
+ if self.sharer == "Pending": self.sharer = self.myself
+ elif len(self.occupiedInstruments) == 4:
+ pass
+ else:
+ if self.sharer == self.myself:
+ giveupInstrument = [p for p in self.PanelInstruments if p.active][0].name
+ giveupparameters = getInstrumentParameters(self.scene, giveupInstrument)
+ mesh.send_to(event.handle, "Welcome|%s|%s|%s" %(self.scene.scene_name, giveupInstrument, giveupparameters))
+ self.stealInstrument(giveupInstrument, handle = event.handle)
+ if self.connected: mesh.broadcast('Occupied|%s' %self.occupiedInstruments)
+ olpcgames.ACTIVITY.J2JToolbar.deactivate_scene_change()
+ if len(self.occupiedInstruments) >= 2 and not self._syncloop_running:
+ self._syncloop_running = True
+ self.sendSync()
+ else:
+ self.latency_checker()
+ log.info("Waiting to be assigned instrument from sharer")
+ elif self.connected and event.type == mesh.PARTICIPANT_REMOVE:
+ "return instrument to the sharer if a jammer leaves."
+ try:
+ relname = [n for n in self.occupiedInstruments if self.occupiedInstruments[n] == str(event.handle)][0]
+ relpanel = [p for p in self.PanelInstruments if p.name == relname][0]
+ del self.occupiedInstruments[relname]
+ relpanel.Touch = True
+ if self.sharer == self.myself:
+ self.music_player.mutelist.remove(relname)
+ if len(self.occupiedInstruments) == 1:
+ olpcgames.ACTIVITY.J2JToolbar.reactivate_scene_change()
+ if len(self.occupiedInstruments) <= 1:
+ self._syncloop_running = False
+ except IndexError: log.debug("Index error while removing jammer %s occ = %s" %(str(event.handle), self.occupiedInstruments))
+ except KeyError: pass
+ except ValueError: pass
+ if self.sharer == self.myself: mesh.broadcast('Occupied|%s' %self.occupiedInstruments)
+ log.info( """Removed jammer| %s""", event )
+ elif self.connected and (event.type == mesh.MESSAGE_MULTI or event.type == mesh.MESSAGE_UNI):
+ if event.handle == self.myself:
+ pass
+ else:
+ self.receiveMessage(event.content, event.handle)
+ def interfaceAction(self, iaction):
+ if iaction.startswith("Nudge"):
+ direction = iaction.split("|")[1]
+ insname = self.selectedInstrument.name
+ if direction == "Left" or direction == "Right":
+ param = self.Hparam
+ else:
+ param = self.Vparam
+ currentValue = self.scene.Params.getValue(param, insname)
+ newvalue = limit((currentValue - 0.03 if direction == "Left" or direction == "Down" else currentValue + 0.03), 0, 1)
+ self.scene.Params.setValue(param, insname, newvalue)
+ self.selectedInstrument.Touch = True
+ if newvalue == 0 or newvalue == 1:
+ try:
+ self.keyActions.remove(iaction)
+ except ValueError: pass
+ elif iaction.startswith("Instrument"):
+ ins = iaction.split('|')[1]
+ self.requestInstrument(ins)
+ try:
+ self.keyActions.remove(iaction)
+ except ValueError: pass
+ elif iaction.startswith("Parameter"):
+ pm = iaction.split('|')[1]
+ if "Modifier|Shift" in self.keyActions:
+ print "shift key is on"
+ if olpcgames.ACTIVITY:
+ olpcgames.ACTIVITY.J2JToolbar.set_vertical_parameter(pm)
+ else:
+ self.Vparam = pm
+ else:
+ print "shift key is off"
+ if olpcgames.ACTIVITY:
+ olpcgames.ACTIVITY.J2JToolbar.set_horizontal_parameter(pm)
+ else:
+ self.Hparam = pm
+ try:
+ self.keyActions.remove(iaction)
+ except ValueError: pass
+ else: pass
+ def stealInstrument(self, stealname, releasename = False, handle = False):
+ "attempts to deactivate an instrument, and make it unavailable for selection"
+ if not handle: handle = self.myself
+ if stealname == self.selectedInstrument.name:
+ log.info("ignoring request to steal %s: already active" %stealname)
+ return False
+ elif stealname in self.occupiedInstruments and not releasename:
+ log.info ("ignoring request to steal %s: already occupied and no release instrument provided" %stealname)
+ return False
+ else:
+ paneli = [pnli for pnli in self.PanelInstruments if pnli.name == stealname][0]
+ self.occupiedInstruments.update({stealname:str(handle)})
+ self.music_player.mutelist.append(stealname)
+ if releasename:
+ relname = releasename
+ relpanel = [p for p in self.PanelInstruments if p.name == relname][0]
+ try:
+ del self.occupiedInstruments[relname]
+ relpanel.Touch = True
+ self.music_player.mutelist.remove(relname)
+ except KeyError: pass
+ except ValueError: pass
+ paneli.Touch = True
+ return True
+ def requestInstrument(self, name):
+ "instrument selections should go through this first. To request an instrument, you need to give one up"
+ if name in self.occupiedInstruments:
+ log.info('failed instrument selection, as instrument currently occupied')
+ else:
+ if self.connected and (self.sharer != self.myself):
+ releasename = self.selectedInstrument.name
+ iparams = getInstrumentParameters(self.scene, releasename)
+ requestname = name
+ mesh.send_to(self.sharer, 'JammerRequest|%s|%s|%s' %(releasename, requestname, iparams))
+ else:
+ self.reselectInstruments(name)
+ if self.connected: mesh.broadcast('Occupied|%s' %self.occupiedInstruments)
+ def receiveMessage(self, instruction, handle):
+ if instruction.startswith("Welcome"):
+ messages = instruction.split("|")
+ self.sharer = handle
+ jam_scene = messages[1]
+ self.pending_instrument_assignment = [messages[2],messages[3]]
+ self.select_activity_scene(jam_scene)
+ if self.sharer != self.myself:
+ olpcgames.ACTIVITY.J2JToolbar.deactivate_scene_change()
+ elif instruction.startswith("Beat"):
+ splitvals = instruction.split('|')
+ receivedBeat = int(splitvals[1])
+ time_now = now()
+ self.beatEstimator.addBeat(receivedBeat, time_now)
+ if abs(receivedBeat - self.beatEstimator.beat_match(time_now)) > 0.17:
+ pass
+ else:
+ latency = (sum(self.latency) / len(self.latency))
+ tmult = self.music_player.tempoMult
+ latency = latency * 0.25 + 0.04 #this might be XO 1.0 specific
+ beatadvance = int(ceil(latency * 1/tmult))
+ scheduled_time = now() + ((beatadvance * tmult) - latency)
+ self.music_player.Cease()
+ self.music_player.playLoop(scheduled_time, (receivedBeat + beatadvance) % self.music_player.beatlimit)
+ elif instruction.startswith("JammerRequest"):
+ "In theory only the sharer ever gets this message"
+ split = instruction.split('|')
+ releasename = split[1]
+ requestname = split[2]
+ iparams = eval(split[3])
+ stealresult = self.stealInstrument(requestname, releasename, handle)
+ if stealresult:
+ setInstrumentParameters(self.scene, releasename, iparams)
+ rqparams = getInstrumentParameters(self.scene, requestname)
+ mesh.send_to(handle, "AuthorisedInstrument|%s|%s" %(requestname, rqparams))
+ mesh.broadcast('Occupied|%s' %self.occupiedInstruments)
+ else:
+ mesh.send_to(handle, "DeniedInstrument|%s")
+ elif instruction.startswith("AuthorisedInstrument"):
+ "In theory only a 'joiner' receives this message"
+ msg = instruction.split('|')
+ ai = msg[1]
+ params = eval(msg[2])
+ setInstrumentParameters(self.scene, ai, params)
+ self.reselectInstruments(ai)
+ elif instruction.startswith("DeniedInstrument"):
+ di = instruction.split('|')[1]
+ log.info("Instrument request for %s was denied by sharer." %di)
+ elif instruction.startswith("Occupied"):
+ insdict = eval(instruction.split('|')[1])
+ self.occupiedInstruments = insdict
+ for pni in self.PanelInstruments:
+ if pni.name in insdict:
+ pni.deactivate()
+ else:
+ pni.activate()
+ elif instruction.startswith("LateReq"):
+ id = instruction.split('|')[1]
+ mesh.send_to(handle, "LateResp|%s" %id)
+ elif instruction.startswith("LateResp"):
+ id = int(instruction.split('|')[1])
+ try:
+ t = self.latency_time_ID[id]
+ del self.latency_time_ID[id]
+ result = (now() - t) / 2
+ avglat = sum(self.latency) / len(self.latency)
+ diffs = [(val - avglat) ** 2 for val in self.latency]
+ stddev = sqrt(sum(diffs) / len(diffs))
+ if id == 0:
+ del self.latency[0]
+ self.latency.append(result)
+ elif result > (avglat + stddev):
+ pass
+ elif result < (avglat - stddev) and len(self.latency) > 6:
+ pass
+ elif len(self.latency) > 12:
+ del self.latency[0]
+ self.latency.append(result)
+ else:
+ self.latency.append(result)
+ except KeyError:
+ log.info('Unmatched time ID %s' %id)
+ else:
+ log.debug("UNKNOWN INSTRUCTION RECEIVED :%s", instruction)
+ def reselectInstruments(self, name):
+ "Swaps the instrument on screen and selects the active Panel Instruments available to this user"
+ oldInstrument = self.selectedInstrument
+ oldname = oldInstrument.name
+ if (self.sharer == self.myself) or not self.connected:
+ del self.occupiedInstruments[oldname]
+ self.occupiedInstruments.update({name:str(self.myself)})
+ self.screen.blit(self.backgroundImage, oldInstrument.Rect, oldInstrument.Rect)
+ oldInstrument.Touch = False
+ self.setselectedInstrument([i for i in self.TemplateInstruments if i.name == name][0])
+ for w in self.PanelInstruments:
+ if w.name == name or w.name == oldname:
+ w.Touch = True
+ if self.connected:
+ if self.sharer == self.myself:
+ self.music_player.mutelist = [j for j in self.occupiedInstruments if j != self.selectedInstrument.name]
+ else:
+ self.music_player.mutelist = [k.name for k in self.TemplateInstruments if k.name != self.selectedInstrument.name]
+ def load_scene(self, name, key, mode, tempo, defaults = {}):
+ self.music_player.freeze()
+ self.music_player.cs.perf.Stop()
+ self.music_player.cs.perf.Join()
+ self.music_player.cs.csound.cleanup()
+ sc = City.ScenePlayer(name, key, mode, tempo, defaults)
+ mp = City.makePlayer(sc)
+ global schedEvent, now
+ schedEvent = sc.TimeQueue.schedEvent
+ now = sc.cs.perfTime
+ self.scene = sc
+ self.music_player = mp
+ self.music_player.playLoop(now())
+ self.selectedInstrument.Touch = True
+ self.music_player.picture_cycle = [self, True]
+
+ def select_activity_scene(self, scene_name, key = None, mode = None, tempo = None, defaults = None):
+ current_scene_name = self.scene.scene_name
+ if scene_name == current_scene_name:
+ if self.pending_instrument_assignment:
+ self.receiveMessage("AuthorisedInstrument|%s|%s" %(self.pending_instrument_assignment[0], self.pending_instrument_assignment[1]), self.myself)
+ elif not olpcgames.ACTIVITY:
+ k = (key if key else 'G')
+ m = (mode if mode else 'major')
+ t = (int(tempo) if tempo else 164)
+ d = (eval(defaults) if defaults else {})
+ self.load_scene(scene_name, k,m,t,d)
+ else:
+ toolbar = olpcgames.ACTIVITY.J2JToolbar
+ try:
+ ndx = [n[0] for n in toolbar.scenes].index(scene_name)
+ toolbar._Scene_combo.combo.set_active(ndx)
+ except ValueError:
+ log.info('request to change to unknown scene: %s', scene_name)
+ def latency_checker(self):
+ if self.sharer:
+ self.latency_time_ID[self.latency_counter] = now()
+ mesh.send_to(self.sharer, "LateReq|%s" %self.latency_counter)
+ self.latency_counter += 1
+ if self.latency_counter < 10:
+ Timer(3.5, self.latency_checker, ()).start()
+ elif self.latency_counter < 50:
+ Timer(6.75, self.latency_checker, ()).start()
+ else:
+ log.info('turning off latency checking')
+
+#pygame main loop
+def main():
+ # check automatic power management
+ try:
+ sugar_pm_check = subprocess.Popen("sugar-control-panel -g automatic_pm", shell=True, stdout=subprocess.PIPE)
+ sugar_pm_result = sugar_pm_check.communicate()[0]
+ if sugar_pm_result.startswith("on"):
+ subprocess.Popen("sugar-control-panel -s automatic_pm off", shell=True)
+ _spm_off = True
+ else:
+ _spm_off = False
+ except OSError:
+ _spm_off = False
+ log.info("Failed to detect and set automatic power management")
+ screenSize_X, screenSize_Y = (olpcgames.ACTIVITY.game_size if platform=="Sugar" else (1024,640))
+ toolbarheight = 45
+ screen = pygame.display.set_mode((screenSize_X, screenSize_Y - toolbarheight))
+ a_,b_,c_,d_ = pygame.cursors.load_xbm("arrow40b.xbm", "arrow40b-mask.xbm")
+ pygame.mouse.set_cursor(a_,b_,c_,d_)
+ jam = jamScene(screen, tempo = 120)
+ jam.runloop()
+ pygame.quit()
+ jam.music_player.freeze()
+ jam.music_player.cs.perf.Stop()
+ jam.music_player.cs.csound.cleanup()
+ if _spm_off: subprocess.Popen("sugar-control-panel -s automatic_pm on", shell=True)
+
+if __name__ == '__main__':
+ logging.basicConfig()
+ print "running as main"
+ main()
+