Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/TamTamJam.activity
diff options
context:
space:
mode:
Diffstat (limited to 'TamTamJam.activity')
-rw-r--r--TamTamJam.activity/Jam/Block.py895
-rw-r--r--TamTamJam.activity/Jam/Desktop.py466
-rw-r--r--TamTamJam.activity/Jam/Fillin.py116
-rw-r--r--TamTamJam.activity/Jam/GenRythm.py80
-rw-r--r--TamTamJam.activity/Jam/JamMain.py1112
-rw-r--r--TamTamJam.activity/Jam/Parasite.py349
-rw-r--r--TamTamJam.activity/Jam/Picker.py422
-rw-r--r--TamTamJam.activity/Jam/Popup.py1397
-rw-r--r--TamTamJam.activity/Jam/RythmGenerator.py77
-rw-r--r--TamTamJam.activity/Jam/Toolbars.py129
-rw-r--r--TamTamJam.activity/Jam/__init__.py0
-rw-r--r--TamTamJam.activity/NEWS71
-rw-r--r--TamTamJam.activity/TamTam.py1229
-rw-r--r--TamTamJam.activity/actvity/activity-tamtamjam.svg24
-rw-r--r--TamTamJam.activity/actvity/activity.info6
l---------TamTamJam.activity/common1
-rw-r--r--TamTamJam.activity/icons/XYBut.svg14
-rw-r--r--TamTamJam.activity/icons/XYButDown.svg14
-rw-r--r--TamTamJam.activity/icons/XYButDownClick.svg12
-rwxr-xr-xTamTamJam.activity/icons/accept.svg18
-rwxr-xr-xTamTamJam.activity/icons/cancel.svg11
-rw-r--r--TamTamJam.activity/icons/preset1.svg12
-rw-r--r--TamTamJam.activity/icons/preset10.svg15
-rw-r--r--TamTamJam.activity/icons/preset2.svg14
-rw-r--r--TamTamJam.activity/icons/preset3.svg16
-rw-r--r--TamTamJam.activity/icons/preset4.svg14
-rw-r--r--TamTamJam.activity/icons/preset5.svg13
-rw-r--r--TamTamJam.activity/icons/preset6.svg17
-rw-r--r--TamTamJam.activity/icons/preset7.svg13
-rw-r--r--TamTamJam.activity/icons/preset8.svg17
-rw-r--r--TamTamJam.activity/icons/preset9.svg17
-rw-r--r--TamTamJam.activity/icons/tam-help.svg18
-rw-r--r--TamTamJam.activity/icons/tempo1.svg8
-rw-r--r--TamTamJam.activity/icons/tempo2.svg9
-rw-r--r--TamTamJam.activity/icons/tempo3.svg8
-rw-r--r--TamTamJam.activity/icons/tempo4.svg9
-rw-r--r--TamTamJam.activity/icons/tempo5.svg9
-rw-r--r--TamTamJam.activity/icons/tempo6.svg11
-rw-r--r--TamTamJam.activity/icons/tempo7.svg9
-rw-r--r--TamTamJam.activity/icons/tempo8.svg10
-rw-r--r--TamTamJam.activity/icons/volume0.svg10
-rw-r--r--TamTamJam.activity/icons/volume1.svg12
-rw-r--r--TamTamJam.activity/icons/volume2.svg14
-rw-r--r--TamTamJam.activity/icons/volume3.svg16
-rw-r--r--TamTamJam.activity/setup.py21
45 files changed, 6755 insertions, 0 deletions
diff --git a/TamTamJam.activity/Jam/Block.py b/TamTamJam.activity/Jam/Block.py
new file mode 100644
index 0000000..f0789fd
--- /dev/null
+++ b/TamTamJam.activity/Jam/Block.py
@@ -0,0 +1,895 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import random
+
+import Config
+
+from Util.NoteDB import PARAMETER
+
+#::: NOTE:
+# All the graphics resources are loaded in Desktop and referenced here as necessary
+#:::
+
+class Block():
+
+ WIDTH = 100
+ HEIGHT = 100
+
+ SNAP = 15
+
+ PAD = 4
+
+ KEYSIZE = 26
+ KEYMASK_START = 309
+
+ def __init__( self, owner, data ):
+ self.owner = owner
+ self.gc = owner.gc
+
+ self.data = {}
+ for key in data.keys():
+ self.data[key] = data[key]
+
+ self.type = Block
+
+ self.width = Block.WIDTH
+ self.height = Block.HEIGHT
+
+ self.parent = None
+ self.canChild = False
+ self.child = None
+ self.canParent = False
+
+ self.canSubstitute = False
+
+ self.parentOffest = 0
+
+ self.dragging = False # is currently dragging
+ self.placed = False # has been placed on the desktop at least once
+
+ self.firstLoc = True
+ self.x = -1
+ self.y = -1
+
+ self.active = False
+
+ def dumpToStream( self, ostream, child = False ):
+ ostream.block_add( ClassToStr[ self.type ], self.active, self.x + self.width//2, self.y + self.height//2, child, self.data )
+ if self.child:
+ self.child.dumpToStream( ostream, True )
+
+ def destroy( self ):
+ if self.child:
+ self.child.destroy()
+ self.child = None
+ self.invalidate_rect( not self.dragging )
+
+ def isPlaced( self ):
+ return self.placed
+
+ def setPlaced( self, placed ):
+ self.placed = placed
+
+ def getLoc( self ):
+ return ( self.x, self.y )
+
+ def setLoc( self, x, y ):
+ if x == self.x and y == self.y: return
+
+ if self.firstLoc:
+ self.firstLoc = False
+ else:
+ self.invalidate_rect( not self.dragging )
+
+ self.x = int(x)
+ self.y = int(y)
+ self.endX = self.x + self.width
+ self.endY = self.y + self.height
+
+ self.invalidate_rect( not self.dragging )
+
+ if self.child:
+ self.child.snapToParentLoc( self.getChildAnchor() )
+
+ def resetLoc( self ):
+ if self.oldParent != None:
+ self.oldParent.addChild( self )
+ return False
+ else:
+ self.setLoc( self.oldLoc[0], self.oldLoc[1] )
+ return True
+
+ def getParentAnchor( self ):
+ return ( self.x + self.parentOffset, self.y )
+
+ def getChildAnchor( self ):
+ return ( self.endX, self.y )
+
+ def snapToParentLoc( self, loc ):
+ self.setLoc( loc[0] - self.parentOffset, loc[1] )
+
+ def substitute( self, block ):
+ pass # override in subclasses
+
+ def testSubstitute( self, block ):
+ if self.child:
+ return self.child.testSubstitute( block )
+
+ def testChild( self, loc ):
+
+ if not self.canParent:
+ return False
+
+ if self.child:
+ return self.child.testChild( loc )
+ elif abs( self.endX - loc[0] ) < Block.SNAP and abs( self.y - loc[1] ) < Block.SNAP:
+ return self
+
+ return False
+
+ def addChild( self, child ):
+ c = self.child
+ if self.child:
+ self.removeChild()
+
+ self.child = child
+ child._addParent( self )
+ child.snapToParentLoc( self.getChildAnchor() )
+
+ if c:
+ child.addChild( c )
+
+ def removeChild( self ):
+ self.child._removeParent()
+ self.child = None
+
+ def _addParent( self, parent ):
+ self.parent = parent
+
+ def _removeParent( self ):
+ self.parent = None
+
+ def getRoot( self ):
+ if self.parent: return self.parent.getRoot()
+ return self
+
+ def isActive( self ):
+ return self.active
+
+ def setActive( self, state ):
+ self.active = state
+ self.invalidate_rect( not self.dragging )
+
+ def getData( self, key ):
+ return self.data[ key ]
+
+ def setData( self, key, value ):
+ self.data[ key ] = value
+
+ def testMouseOver( self, event ):
+ if self.child:
+ ret = self.child.testMouseOver( event )
+ if ret: return ret
+
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if 0 <= x <= self.width and 0 <= y <= self.height:
+ return -1
+
+ return False
+
+ def button_press( self, event ):
+
+ if event.y < self.y or event.y > self.endY:
+ return False
+
+ return self._button_pressB( event )
+
+ def _button_pressB( self, event ):
+
+ if event.x < self.x:
+ return False
+
+ if event.x > self.endX:
+ if self.child:
+ return self.child._button_pressB( event )
+ else:
+ return False
+
+ self.oldParent = self.parent
+ self.oldLoc = ( self.x, self.y )
+ self.dragOffset = ( event.x - self.x, event.y - self.y )
+
+ self._doButtonPress( event )
+
+ return self
+
+ def _doButtonPress( self, event ):
+ pass # override in subclasses
+
+ def button_release( self, event ):
+ if self.dragging:
+ self.dragging = False
+ self.placed = True
+ self.invalidateBranch()
+
+ def motion_notify( self, event ):
+
+ removeFromBlocks = not self.dragging and not self.parent
+
+ if not self.dragging:
+ self.dragging = True
+ self.invalidate_rect()
+
+ if self.parent:
+ self.parent.removeChild()
+
+ self.setLoc( event.x - self.dragOffset[0], event.y - self.dragOffset[1] )
+
+ return removeFromBlocks
+
+ def _beginDrag( self ):
+ self.dragging = True
+ self.dragOffset = ( self.width//2, self.height//2 )
+
+ def invalidateBranch( self, base = True ):
+ self.invalidate_rect( base )
+ if self.child:
+ self.child.invalidateBranch( base )
+
+ def invalidate_rect( self, base = True ):
+ self.owner.invalidate_rect( self.x, self.y, self.width, self.height, base )
+
+ def draw( self, startX, startY, stopX, stopY, pixmap ):
+ if stopY <= self.y or startY >= self.endY:
+ return False
+
+ self._drawB( startX, startY, stopX, stopY, pixmap )
+
+ def _drawB( self, startX, startY, stopX, stopY, pixmap ):
+
+ if stopX <= self.x:
+ return False
+
+ if self.child:
+ self.child._drawB( startX, startY, stopX, stopY, pixmap )
+
+ if startX >= self.endX:
+ return False
+
+ self._doDraw( startX, startY, stopX, stopY, pixmap )
+
+ return True
+
+ def _doDraw( self, startX, startY, stopX, stopY, pixmap ):
+ pass # override in subclasses
+
+ def drawHighlight( self, startX, startY, stopX, stopY, pixmap ):
+ pass # override in subclasses
+
+class Instrument(Block):
+
+ MASK_START = 0
+
+ #::: data format:
+ # { "name": name, "id": instrumentId [, "volume": 0-1, "pan": 0-1, "reverb": 0-1 ] }
+ #:::
+ def __init__( self, owner, data ):
+ Block.__init__( self, owner, data )
+
+ self.type = Instrument
+
+ self.canParent = True
+ self.canSubstitute = True
+
+ if not "volume" in self.data.keys():
+ self.data["volume"] = 0.5
+ if not "pan" in self.data.keys():
+ self.data["pan"] = 0.5
+ if not "reverb" in self.data.keys():
+ self.data["reverb"] = 0
+
+ self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
+ self.owner.getInstrumentImage( self.data["id"], True ) ]
+
+ def setData( self, key, value ):
+ self.data[ key ] = value
+ if self.active:
+ self.owner.updateInstrument( self )
+ if self.child and self.child.active:
+ self.owner.updateLoop( self.child )
+
+ def substitute( self, block ):
+ self.data["id"] = block.data["id"]
+ self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
+ self.owner.getInstrumentImage( self.data["id"], True ) ]
+ self.invalidate_rect( True )
+
+ if self.child and self.child.active:
+ self.owner.updateLoop( self.child )
+
+ def testSubstitute( self, block ):
+ ret = Block.testSubstitute( self, block )
+ if ret:
+ return ret
+
+ if block.type == Loop:
+ return False
+
+ if abs( self.x - block.x ) < Block.SNAP and abs( self.y - block.y ) < Block.SNAP:
+ return self
+
+ return False
+
+ def _doButtonPress( self, event ): # we were hit with a button press
+ pass
+
+ def button_release( self, event ):
+ if not self.dragging:
+ self.owner.activateInstrument( self )
+ Block.button_release( self, event )
+
+ def _doDraw( self, startX, startY, stopX, stopY, pixmap ):
+ x = max( startX, self.x )
+ y = max( startY, self.y )
+ endX = min( stopX, self.endX )
+ endY = min( stopY, self.endY )
+ width = endX - x
+ height = endY - y
+
+ # draw border
+ if self.active: self.gc.foreground = self.owner.colors["Border_Active"]
+ else: self.gc.foreground = self.owner.colors["Border_Inactive"]
+ self.gc.set_clip_origin( self.x-Instrument.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, height )
+
+ # draw block
+ self.gc.set_clip_origin( self.x-Instrument.MASK_START, self.y-self.height )
+ pixmap.draw_drawable( self.gc, self.img[self.active], x-self.x, y-self.y, x, y, width, height )
+
+ def drawHighlight( self, startX, startY, stopX, stopY, pixmap ):
+ self.gc.foreground = self.owner.colors["Border_Highlight"]
+ self.gc.set_clip_origin( self.x-Instrument.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, self.x, self.y, self.width, self.height )
+
+class Drum(Block):
+
+ MASK_START = 100
+
+ KEYRECT = [ Block.PAD - 1, Block.HEIGHT + 1 - Block.PAD - Block.KEYSIZE, Block.KEYSIZE, Block.KEYSIZE ]
+ KEYRECT += [ KEYRECT[0]+KEYRECT[2], KEYRECT[1]+KEYRECT[3] ]
+
+ #::: data format:
+ # { "name": name, "id": instrumentId [ , "page": pageId, "volume": 0-1, "reverb": 0-1, "beats": 2-12, "regularity": 0-1, "key": shortcut ] }
+ #:::
+ def __init__( self, owner, data ):
+ Block.__init__( self, owner, data )
+
+ self.type = Drum
+
+ self.canSubstitute = True
+
+ if not "page" in self.data.keys():
+ self.data["page"] = -1
+ if not "volume" in self.data.keys():
+ self.data["volume"] = 0.5
+ if not "reverb" in self.data.keys():
+ self.data["reverb"] = 0.0
+ if not "beats" in self.data.keys():
+ self.data["beats"] = random.randint(2, 12)
+ if not "regularity" in self.data.keys():
+ self.data["regularity"] = random.random()
+ if "key" not in self.data.keys():
+ self.data["key"] = None
+
+ self.owner.mapKey( self.data["key"], self )
+ self.keyImage = [ self.owner.getKeyImage( self.data["key"], False ),
+ self.owner.getKeyImage( self.data["key"], True ) ]
+
+
+ self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
+ self.owner.getInstrumentImage( self.data["id"], True ) ]
+
+ if self.data["page"] == -1:
+ self.regenerate()
+
+ def destroy( self ):
+ self.owner.mapKey( None, self, self.data["key"] )
+ self.owner.noteDB.deletePages( [ self.data["page"] ] )
+ Block.destroy( self )
+
+ def setData( self, key, value ):
+ if key == "beats":
+ self.data["beats"] = value
+ self.owner.noteDB.updatePage( self.data["page"], PARAMETER.PAGE_BEATS, value )
+
+ elif key == "key":
+ oldKey = self.data["key"]
+ self.data["key"] = value
+ self.keyImage = [ self.owner.getKeyImage( value, False ),
+ self.owner.getKeyImage( value, True ) ]
+ self.invalidate_rect()
+ self.owner.mapKey( value, self, oldKey )
+
+ else:
+ self.data[key] = value
+
+ if self.active:
+ self.owner.updateDrum( self )
+
+ def substitute( self, block ):
+ self.data["name"] = block.data["name"]
+ self.data["id"] = block.data["id"]
+
+ self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
+ self.owner.getInstrumentImage( self.data["id"], True ) ]
+
+ self.invalidate_rect( True )
+
+ if self.active:
+ self.owner.updateDrum()
+
+ def testSubstitute( self, block ):
+ ret = Block.testSubstitute( self, block )
+ if ret:
+ return ret
+
+ if block.type == Loop:
+ return False
+
+ if Config.INSTRUMENTSID[block.data["id"]].kit == None:
+ return False
+
+ if abs( self.x - block.x ) < Block.SNAP and abs( self.y - block.y ) < Block.SNAP:
+ return self
+
+ return False
+
+ def testMouseOver( self, event ):
+ ret = self.testWithinKey( event )
+ if ret: return ret
+
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if 0 <= x <= self.width and 0 <= y <= self.height:
+ return -1
+
+ return False
+
+ def testWithinKey( self, event ):
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if Drum.KEYRECT[0] <= x <= Drum.KEYRECT[4] and Drum.KEYRECT[1] <= y <= Drum.KEYRECT[5]:
+ return self
+
+ return False
+
+ def _doButtonPress( self, event ): # we were hit with a button press
+ pass
+
+ def button_release( self, event ):
+ if not self.dragging:
+ if self.active:
+ self.owner.deactivateDrum( self )
+ else:
+ self.owner.activateDrum( self )
+ Block.button_release( self, event )
+
+ def _doDraw( self, startX, startY, stopX, stopY, pixmap ):
+ x = max( startX, self.x )
+ y = max( startY, self.y )
+ endX = min( stopX, self.endX )
+ endY = min( stopY, self.endY )
+ width = endX - x
+ height = endY - y
+
+ # draw border
+ if self.active: self.gc.foreground = self.owner.colors["Border_Active"]
+ else: self.gc.foreground = self.owner.colors["Border_Inactive"]
+ self.gc.set_clip_origin( self.x-Drum.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, height )
+
+ # draw block
+ self.gc.set_clip_origin( self.x-Drum.MASK_START, self.y-self.height )
+ pixmap.draw_drawable( self.gc, self.img[self.active], x-self.x, y-self.y, x, y, width, height )
+
+ # draw key
+ self.gc.set_clip_origin( self.x+Drum.KEYRECT[0]-Block.KEYMASK_START, self.y+Drum.KEYRECT[1] )
+ pixmap.draw_drawable( self.gc, self.keyImage[ self.active ], 0, 0, self.x+Drum.KEYRECT[0], self.y+Drum.KEYRECT[1], Block.KEYSIZE, Block.KEYSIZE )
+
+
+ def drawHighlight( self, startX, startY, stopX, stopY, pixmap ):
+ self.gc.foreground = self.owner.colors["Border_Highlight"]
+ self.gc.set_clip_origin( self.x-Drum.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, self.x, self.y, self.width, self.height )
+
+ def drawKeyHighlight( self, pixmap ):
+ self.gc.foreground = self.owner.colors["Border_Highlight"]
+ self.gc.set_clip_origin( self.x+Drum.KEYRECT[0]-Block.KEYMASK_START, self.y+Drum.KEYRECT[1]-Block.KEYSIZE )
+ pixmap.draw_rectangle( self.gc, True, self.x+Drum.KEYRECT[0], self.y+Drum.KEYRECT[1], Block.KEYSIZE, Block.KEYSIZE )
+
+ def regenerate( self ):
+ self.data["page"] = self.owner.owner._generateDrumLoop( self.data["id"], self.data["beats"], self.data["regularity"], self.data["reverb"], self.data["page"] )
+ if self.active:
+ self.owner.updateDrum( self )
+
+ def clear( self ):
+ self.owner.noteDB.deleteNotesByTrack( [ self.data["page"] ], [ 0 ] )
+
+class Loop(Block):
+
+ HEAD = 13
+ BEAT = 23
+ TAIL = BEAT + Block.PAD
+
+ WIDTH = [ HEAD + BEAT*(n-1) + TAIL for n in range(Config.MAXIMUM_BEATS+1) ]
+
+ BEAT_MUL3 = BEAT*3
+
+ MASK_START = 200
+ MASK_BEAT = MASK_START + HEAD
+ MASK_TAIL = MASK_START + HEAD + BEAT*3
+
+ KEYRECT = [ HEAD + Block.PAD, Block.HEIGHT - 2*Block.PAD - Block.KEYSIZE, Block.KEYSIZE, Block.KEYSIZE ]
+ KEYRECT += [ KEYRECT[0]+KEYRECT[2], KEYRECT[1]+KEYRECT[3] ]
+
+ #::: data format:
+ # { "name": name, "id": pageId [, "beats": 2-12, "regularity": 0-1, "key": shortcut ] }
+ #:::
+ def __init__( self, owner, data ):
+ Block.__init__( self, owner, data )
+
+ self.type = Loop
+
+ self.canParent = True
+ self.canChild = True
+ self.canSubstitute = True
+
+ self.parentOffset = Loop.HEAD - 4
+
+ self.data["beats"] = self.owner.noteDB.getPage(self.data["id"]).beats
+ self.width = Loop.WIDTH[ self.data["beats"] ]
+
+ if "regularity" not in self.data.keys():
+ self.data["regularity"] = random.random()
+ if "key" not in self.data.keys():
+ self.data["key"] = None
+
+ self.keyActive = False
+ self.keyImage = [ self.owner.getKeyImage( self.data["key"], False ),
+ self.owner.getKeyImage( self.data["key"], True ) ]
+
+ self.img = [ self.owner.getLoopImage( self.data["id"], False ),
+ self.owner.getLoopImage( self.data["id"], True ) ]
+
+ def destroy( self ):
+ if self.keyActive:
+ self.owner.mapKey( None, self, self.data["key"] )
+ self.owner.noteDB.deletePages( [ self.data["id"] ] )
+ Block.destroy( self )
+
+ def _updateWidth( self ):
+ self.invalidateBranch( True )
+
+ oldWidth = self.width
+
+ self.width = Loop.WIDTH[self.data["beats"]]
+ self.endX = self.x + self.width
+
+ if self.child:
+ self.child.snapToParentLoc( self.getChildAnchor() )
+
+ if oldWidth < self.width: # or block.child:
+ self.invalidateBranch( True )
+
+ def updateLoop( self ):
+ self.updateImage()
+ self.invalidate_rect()
+
+ if self.active:
+ self.owner.updateLoop( self.getRoot().child )
+
+ def updateImage( self ):
+ self.owner.updateLoopImage( self.data["id"] )
+ self.img = [ self.owner.getLoopImage( self.data["id"], False ),
+ self.owner.getLoopImage( self.data["id"], True ) ]
+
+ def setData( self, key, value ):
+
+ if key == "beats":
+ self.owner.noteDB.updatePage( self.data["id"], PARAMETER.PAGE_BEATS, value )
+ self._updateWidth()
+ self.updateLoop()
+ self.data["beats"] = value
+
+ elif key == "key":
+ oldKey = self.data["key"]
+ self.data["key"] = value
+ self.keyImage = [ self.owner.getKeyImage( value, False ),
+ self.owner.getKeyImage( value, True ) ]
+ self.invalidate_rect()
+ if self.keyActive:
+ self.owner.mapKey( value, self, oldKey )
+
+ else:
+ self.data[key] = value
+
+ def substitute( self, block ):
+ self.invalidateBranch( True )
+
+ oldWidth = self.width
+
+ newid = self.owner.noteDB.duplicatePages( [ block.data["id"] ] )[block.data["id"]]
+ self.data["id"] = newid
+ self.data["beats"] = self.owner.noteDB.getPage(self.data["id"]).beats
+
+ self.updateImage()
+ self._updateWidth()
+
+ if False: # don't substitute children
+ if block.child:
+ c = block.child
+ after = self
+ while c:
+ data = {}
+ for key in c.data.keys():
+ data[key] = c.data[key]
+
+ newid = self.owner.noteDB.duplicatePages( [ data["id"] ] )[data["id"]]
+ self.owner.updateLoopImage( newid )
+ data["id"] = newid
+
+ copy = Loop( self.owner, self.gc, data )
+ after.addChild( copy )
+ after = copy
+ c = c.child
+ elif self.child:
+ self.child.snapToParentLoc( self.getChildAnchor() )
+
+ if self.active:
+ self.owner.updateLoop( self.getRoot().child )
+
+ def testSubstitute( self, block ):
+ ret = Block.testSubstitute( self, block )
+ if ret:
+ return ret
+
+ if block.type != Loop:
+ return False
+
+ if abs( self.x - block.x ) < Block.SNAP and abs( self.y - block.y ) < Block.SNAP:
+ return self
+
+ return False
+
+ def setActive( self, state ):
+ Block.setActive( self, state )
+
+ if self.child:
+ self.child.setActive( state )
+
+ def addChild( self, child ):
+ Block.addChild( self, child )
+ if self.active:
+ child.setActive( True )
+ self.owner.updateLoop( self.getRoot().child )
+
+ def _addParent( self, parent ):
+ Block._addParent( self, parent )
+
+ if self.parent.type == Instrument:
+ self.keyActive = True
+ self.owner.mapKey( self.data["key"], self )
+ else:
+ root = self.getRoot()
+ if root.type == Instrument:
+ root = root.child
+ if root.getData("key") == None:
+ root.setData( "key", self.data["key"] )
+ self.setData( "key", None )
+
+ def _removeParent( self ):
+ if self.active:
+ loopRoot = self.getRoot().child
+ parent = self.parent
+ else:
+ loopRoot = None
+
+ self.keyActive = False
+ self.owner.mapKey( None, self, self.data["key"] )
+
+ Block._removeParent( self )
+
+ if loopRoot == self:
+ self.owner.deactivateLoop( loopRoot )
+ elif loopRoot != None:
+ self.setActive( False )
+ parent.child = None # disconnect us before updating
+ self.owner.updateLoop( loopRoot )
+
+ def testMouseOver( self, event ):
+ ret = self.testWithinKey( event )
+ if ret: return ret
+
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if 0 <= x <= self.width and 0 <= y <= self.height:
+ return -1
+
+ return False
+
+ def testWithinKey( self, event ):
+ if not self.keyActive:
+ return False
+
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if Loop.KEYRECT[0] <= x <= Loop.KEYRECT[4] and Loop.KEYRECT[1] <= y <= Loop.KEYRECT[5]:
+ return self
+
+ return False
+
+ def _doButtonPress( self, event ): # we were hit with a button press
+ pass
+
+ def button_release( self, event ):
+ if not self.dragging:
+ if self.active:
+ root = self.getRoot()
+ self.owner.deactivateLoop( root.child )
+ else:
+ root = self.getRoot()
+ if root.type == Instrument: # must be attached to an instrument
+ self.owner.activateLoop( root.child )
+ Block.button_release( self, event )
+
+ def _doDraw( self, startX, startY, stopX, stopY, pixmap ):
+ y = max( startY, self.y )
+ endY = min( stopY, self.endY )
+ height = endY - y
+
+ loop = self.img[ self.active ]
+ if self.active: self.gc.foreground = self.owner.colors["Border_Active"]
+ else: self.gc.foreground = self.owner.colors["Border_Inactive"]
+
+ #-- draw head -----------------------------------------
+
+ if self.x + Loop.HEAD > startX:
+ x = max( startX, self.x )
+ endX = min( stopX, self.x + Loop.HEAD )
+ width = endX - x
+
+ # draw border
+ self.gc.set_clip_origin( self.x-Loop.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, height )
+
+ # draw block
+ self.gc.set_clip_origin( self.x-Loop.MASK_START, self.y-self.height )
+ pixmap.draw_drawable( self.gc, loop, x-self.x, y-self.y, x, y, width, height )
+
+ #-- draw beats ----------------------------------------
+
+ beats = self.owner.noteDB.getPage(self.data["id"]).beats - 1 # last beat is drawn with the tail
+ curx = self.x + Loop.HEAD
+ while beats > 3:
+ if curx >= stopX:
+ break
+ elif curx + Loop.BEAT_MUL3 > startX:
+ x = max( startX, curx )
+ endX = min( stopX, curx + Loop.BEAT_MUL3 )
+ width = endX - x
+
+ # draw border
+ self.gc.set_clip_origin( curx-Loop.MASK_BEAT, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, height )
+
+ # draw block
+ self.gc.set_clip_origin( curx-Loop.MASK_BEAT, self.y-self.height )
+ pixmap.draw_drawable( self.gc, loop, x-self.x, y-self.y, x, y, width, height )
+
+ curx += Loop.BEAT_MUL3
+ beats -= 3
+ if beats and curx < stopX:
+ endX = curx + Loop.BEAT*beats
+ if endX > startX:
+ x = max( startX, curx )
+ endX = min( stopX, endX )
+ width = endX - x
+
+ # draw border
+ self.gc.set_clip_origin( curx-Loop.MASK_BEAT, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, height )
+
+ # draw block
+ self.gc.set_clip_origin( curx-Loop.MASK_BEAT, self.y-self.height )
+ pixmap.draw_drawable( self.gc, loop, x-self.x, y-self.y, x, y, width, height )
+
+ curx += Loop.BEAT*beats
+
+
+ #-- draw tail -----------------------------------------
+
+ if curx < stopX:
+ x = max( startX, curx )
+ endX = min( stopX, self.endX )
+ width = endX - x
+
+ # draw border
+ self.gc.set_clip_origin( curx-Loop.MASK_TAIL, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, height )
+
+ # draw block
+ self.gc.set_clip_origin( curx-Loop.MASK_TAIL, self.y-self.height )
+ pixmap.draw_drawable( self.gc, loop, x-self.x, y-self.y, x, y, width, height )
+
+ #-- draw key ------------------------------------------
+ if self.keyActive:
+ self.gc.set_clip_origin( self.x+Loop.KEYRECT[0]-Block.KEYMASK_START, self.y+Loop.KEYRECT[1] )
+ pixmap.draw_drawable( self.gc, self.keyImage[ self.active ], 0, 0, self.x+Loop.KEYRECT[0], self.y+Loop.KEYRECT[1], Block.KEYSIZE, Block.KEYSIZE )
+
+ def drawHighlight( self, startX, startY, stopX, stopY, pixmap ):
+ self.gc.foreground = self.owner.colors["Border_Highlight"]
+
+ #-- draw head -----------------------------------------
+
+ self.gc.set_clip_origin( self.x-Loop.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, self.x, self.y, Loop.HEAD, self.height )
+
+ #-- draw beats ----------------------------------------
+
+ beats = self.owner.noteDB.getPage(self.data["id"]).beats - 1 # last beat is drawn with the tail
+ x = self.x + Loop.HEAD
+ while beats > 3:
+ self.gc.set_clip_origin( x-Loop.MASK_BEAT, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, self.y, Loop.BEAT_MUL3, self.height )
+
+ x += Loop.BEAT_MUL3
+ beats -= 3
+ if beats:
+ width = Loop.BEAT*beats
+
+ self.gc.set_clip_origin( x-Loop.MASK_BEAT, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, self.y, width, self.height )
+
+ x += width
+
+ #-- draw tail -----------------------------------------
+
+ self.gc.set_clip_origin( x-Loop.MASK_TAIL, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, self.y, Loop.TAIL, self.height )
+
+ def drawKeyHighlight( self, pixmap ):
+ self.gc.foreground = self.owner.colors["Border_Highlight"]
+ self.gc.set_clip_origin( self.x+Loop.KEYRECT[0]-Block.KEYMASK_START, self.y+Loop.KEYRECT[1]-Block.KEYSIZE )
+ pixmap.draw_rectangle( self.gc, True, self.x+Loop.KEYRECT[0], self.y+Loop.KEYRECT[1], Block.KEYSIZE, Block.KEYSIZE )
+
+ def clear( self ):
+ self.owner.noteDB.deleteNotesByTrack( [ self.data["id"] ], [ 0 ] )
+
+ self.updateImage()
+
+ self.invalidate_rect()
+
+ if self.active:
+ self.owner.updateLoop( self.getRoot().child )
+
+
+StrToClass = {
+ "Instrument": Instrument,
+ "Drum": Drum,
+ "Loop": Loop
+ }
+
+ClassToStr = {
+ Instrument: "Instrument",
+ Drum: "Drum",
+ Loop: "Loop"
+ }
diff --git a/TamTamJam.activity/Jam/Desktop.py b/TamTamJam.activity/Jam/Desktop.py
new file mode 100644
index 0000000..7b1acba
--- /dev/null
+++ b/TamTamJam.activity/Jam/Desktop.py
@@ -0,0 +1,466 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import Config
+
+from gettext import gettext as _
+
+from Jam import Block
+from Jam import Popup
+
+class Desktop( gtk.EventBox ):
+
+ def __init__( self, owner ):
+ gtk.EventBox.__init__( self )
+
+ self.owner = owner
+
+ self.drawingArea = gtk.DrawingArea()
+ self.add( self.drawingArea )
+
+ # take drawing setup from owner
+ self.gc = owner.gc
+ self.colors = owner.colors
+ self.blockMask = owner.blockMask
+
+ self.noteDB = owner.noteDB
+
+ self.dirtyRectToAdd = gtk.gdk.Rectangle() # used by the invalidate_rect function
+ self.screenBuf = None
+ self.screenBufDirty = False
+ self.screenBufDirtyRect = gtk.gdk.Rectangle()
+
+ self.blocks = [] # items on the desktop
+ self.activeInstrument = None
+ self.activeDrum = None
+
+ self.loops = {} # dict of playing loops by loop root
+ self.drums = [] # list of active drums
+
+ self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK)
+
+ self.connect( "size-allocate", self.on_size_allocate )
+ self.connect( "button-press-event", self.on_button_press )
+ self.connect( "button-release-event", self.on_button_release )
+ self.connect( "motion-notify-event", self.on_motion_notify )
+ self.drawingArea.connect( "expose-event", self.on_expose )
+
+ self.clickedBlock = None
+ self.possibleParent = None
+ self.possibleSubstitute = None
+ self.dragging = False
+ self.possibleDelete = False
+ self.overKey = False
+
+ #-- Popups --------------------------------------------
+ self.rightClicked = False
+
+ self.popup = {}
+ self.popup[Popup.Instrument] = Popup.Instrument( _("Instrument Properties"), self.owner )
+ self.popup[Popup.Drum] = Popup.Drum( _("Drum Kit Properties"), self.owner )
+ self.popup[Popup.Loop] = Popup.Loop( _("Loop Properties"), self.owner )
+ self.popup[Popup.Shortcut] = Popup.Shortcut( _("Assign Key"), self.owner )
+
+ def dumpToStream( self, ostream ):
+ for b in self.blocks:
+ b.dumpToStream( ostream )
+
+ def on_size_allocate( self, widget, allocation ):
+ if self.screenBuf == None or self.alloc.width != allocation.width or self.alloc.height != allocation.height:
+ win = gtk.gdk.get_default_root_window()
+ self.screenBuf = gtk.gdk.Pixmap( win, allocation.width, allocation.height )
+ self.invalidate_rect( 0, 0, allocation.width, allocation.height )
+ self.alloc = allocation
+ self.absoluteLoc = [0,0]
+ parent = self.get_parent()
+ while parent:
+ alloc = parent.get_allocation()
+ self.absoluteLoc[0] += alloc.x
+ self.absoluteLoc[1] += alloc.y
+ parent = parent.get_parent()
+ return False
+
+ def getLoopIds( self ):
+ return [ self.loops[loop] for loop in self.loops ]
+
+ #==========================================================
+ # Blocks
+
+ def addBlock( self, blockClass, blockData, loc = (-1,-1), drag = False ):
+
+ block = blockClass( self, blockData )
+
+ if loc[0] != -1: x = loc[0]
+ else: x = self.alloc.width//2
+ if loc[1] != -1: y = loc[1]
+ elif drag: y = self.alloc.height - block.height//2
+ else: y = self.alloc.height//2
+
+ if drag:
+ win = gtk.gdk.get_default_root_window()
+ display = win.get_display()
+ screen = display.get_default_screen()
+ display.warp_pointer( screen, int(self.absoluteLoc[0] + x), int(self.absoluteLoc[1] + y) )
+ self._beginDrag( block )
+ block.setLoc( x - block.width//2, y - block.height//2 )
+ else:
+ self.blocks.append( block )
+ block.setLoc( x - block.width//2, y - block.height//2 )
+
+ if blockClass == Block.Instrument:
+ pass
+ elif blockClass == Block.Drum:
+ pass
+ elif blockClass == Block.Loop:
+ pass
+
+ return block
+
+ def deleteBlock( self, block ):
+ if block.type == Block.Instrument:
+ if block == self.activeInstrument:
+ self.activeInstrument = None
+ for i in range(len(self.blocks)-1, -1, -1):
+ if self.blocks[i].type == Block.Instrument:
+ self.activateInstrument( self.blocks[i] )
+ break
+
+ elif block.type == Block.Drum:
+ if block == self.activeDrum:
+ self.deactivateDrum()
+
+ elif block.type == Block.Loop:
+ pass
+
+ if block in self.blocks:
+ block.invalidate_rect( True )
+ self.blocks.remove( block )
+
+ block.destroy()
+
+ def _clearDesktop( self ):
+ for i in range( len(self.blocks)-1, -1, -1 ):
+ self.deleteBlock( self.blocks[i] )
+
+ def mapKey( self, key, block, oldKey = None ):
+ self.owner.mapKey( key, block, oldKey )
+
+ def getInstrumentImage( self, id, active = False ):
+ return self.owner.getInstrumentImage( id, active )
+
+ def getKeyImage( self, key, active = False ):
+ return self.owner.getKeyImage( key, active )
+
+ def getLoopImage( self, id, active = False ):
+ return self.owner.getLoopImage( id, active )
+
+ def updateLoopImage( self, id ):
+ self.owner.updateLoopImage( id )
+
+ #==========================================================
+ # State
+
+ def activateInstrument( self, block ):
+ if self.activeInstrument:
+ self.activeInstrument.setActive( False )
+
+ block.setActive( True )
+ self.activeInstrument = block
+
+ self.updateInstrument( block )
+
+ def updateInstrument( self, block ):
+ data = block.data
+ self.owner._updateInstrument( data["id"], data["volume"], data["pan"], data["reverb"] )
+
+ def activateDrum( self, block ):
+ for drum in self.drums:
+ self.deactivateDrum( drum )
+
+ block.setActive( True )
+ self.drums.append( block )
+
+ self.updateDrum( block, True )
+
+ def deactivateDrum( self, block ):
+ self.owner._stopDrum( self.loops[block] )
+ del self.loops[block]
+
+ block.setActive( False )
+ self.drums.remove( block )
+
+ def updateDrum( self, block, firstTime = False ):
+ data = block.data
+
+ if firstTime:
+ loopId = None
+ else:
+ loopId = self.loops[block]
+
+ self.loops[block] = self.owner._playDrum( data["id"], data["page"], data["volume"], data["reverb"], data["beats"], data["regularity"], loopId )
+
+ def activateLoop( self, block ):
+ block.setActive( True )
+
+ self.updateLoop( block, True )
+
+ def deactivateLoop( self, block ):
+ block.setActive( False )
+
+ self.owner._stopLoop( self.loops[block] )
+ del self.loops[block]
+
+ def updateLoop( self, block, firstTime = False ):
+ inst = block.parent.data
+
+ tune = []
+ itr = block
+ while itr != None:
+ tune.append( itr.data["id"] )
+ itr = itr.child
+
+ if firstTime:
+ loopId = None
+ else:
+ loopId = self.loops[block]
+
+ self.loops[block] = self.owner._playLoop( inst["id"], inst["volume"], inst["reverb"], tune, loopId )
+
+ #==========================================================
+ # Mouse
+
+ def on_button_press( self, widget, event ):
+
+ if event.button == 3:
+ self.rightClicked = True
+
+ hit = False
+ for i in range(len(self.blocks)-1, -1, -1):
+ hit = self.blocks[i].button_press( event )
+ if hit:
+ self.clickedBlock = hit
+ break
+
+ def on_button_release( self, widget, event ):
+
+ if event.button == 3: # Right Click
+ if self.clickedBlock:
+ if self.clickedBlock.type == Block.Instrument:
+ self.popup[Popup.Instrument].setBlock( self.clickedBlock )
+ elif self.clickedBlock.type == Block.Drum:
+ self.popup[Popup.Drum].setBlock( self.clickedBlock )
+ elif self.clickedBlock.type == Block.Loop:
+ self.popup[Popup.Loop].setBlock( self.clickedBlock )
+
+ self.clickedBlock = None
+ self.rightClicked = False
+ self.overKey = False # just in case
+ return
+
+ if self.overKey:
+ self.popup[Popup.Shortcut].setBlock( self.clickedBlock )
+ self.overKey.invalidate_rect( False )
+ self.overKey = False
+ self.clickedBlock = None
+ return
+
+ if self.possibleDelete:
+ self.possibleDelete = False
+ self.deleteBlock( self.clickedBlock )
+ self.clickedBlock = None
+ self.possibleParent = None
+ self.possibleSubstitute = None
+ self.dragging = False
+
+ if self.dragging:
+ self.dragging = False
+
+ if self.possibleParent:
+ self.possibleParent.addChild( self.clickedBlock )
+ root = self.possibleParent.getRoot()
+ self.blocks.remove(root)
+ self.blocks.append(root)
+ root.invalidateBranch( True )
+ self.possibleParent = None
+ elif self.possibleSubstitute:
+ self.possibleSubstitute.substitute( self.clickedBlock )
+ if self.clickedBlock.isPlaced():
+ if self.clickedBlock.resetLoc():
+ self.blocks.append( self.clickedBlock )
+ else:
+ self.deleteBlock( self.clickedBlock )
+ self.clickedBlock = None
+ if self.possibleSubstitute.type == Block.Instrument:
+ self.activateInstrument( self.possibleSubstitute )
+ self.possibleSubstitute = None
+ else:
+ self.blocks.append( self.clickedBlock )
+
+ if self.clickedBlock:
+ self.clickedBlock.button_release( event )
+ self.clickedBlock = None
+
+
+ def on_motion_notify( self, widget, event ):
+
+ if self.rightClicked:
+ return
+
+ if self.clickedBlock and self.overKey:
+ return
+
+ if event.is_hint or widget != self:
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ blockCount = len(self.blocks)
+
+ if not self.clickedBlock:
+ if self.popup[Popup.Shortcut].is_up():
+ return
+ for i in range(blockCount-1, -1, -1):
+ over = self.blocks[i].testMouseOver( event )
+ if over == -1:
+ break # over block but no hotspot
+ if over:
+ if self.overKey != self.blocks[i]:
+ if self.overKey:
+ self.overKey.invalidate_rect( False )
+ self.overKey = over
+ self.overKey.invalidate_rect( False )
+ return
+ if self.overKey:
+ self.overKey.invalidate_rect( False )
+ self.overKey = False
+ return
+
+ self.dragging = True
+
+ if self.clickedBlock.motion_notify( event ): # first drag of root block, remove from self.blocks
+ self.blocks.remove( self.clickedBlock )
+ blockCount = len(self.blocks)
+
+ if event.y < 0 or event.y > self.alloc.height:
+ self.possibleDelete = True
+ return
+ else:
+ self.possibleDelete = False
+
+ if self.clickedBlock.canChild and blockCount:
+ for i in range(blockCount-1, -1, -1):
+ handled = self.blocks[i].testChild( self.clickedBlock.getParentAnchor() )
+ if handled:
+ if self.possibleParent != handled:
+ if self.possibleParent:
+ self.possibleParent.invalidate_rect( False )
+ self.possibleParent = handled
+ self.possibleParent.invalidate_rect( False )
+ break
+ if not handled and self.possibleParent:
+ self.possibleParent.invalidate_rect( False )
+ self.possibleParent = None
+
+ if self.clickedBlock.canSubstitute and blockCount:
+ for i in range(blockCount-1, -1, -1):
+ handled = self.blocks[i].testSubstitute( self.clickedBlock )
+ if handled:
+ if self.possibleSubstitute != handled:
+ if self.possibleSubstitute:
+ self.possibleSubstitute.invalidate_rect( False )
+ self.possibleSubstitute = handled
+ self.possibleSubstitute.invalidate_rect( False )
+ break
+ if not handled and self.possibleSubstitute:
+ self.possibleSubstitute.invalidate_rect( False )
+ self.possibleSubstitute = None
+
+ def _beginDrag( self, block ):
+ block._beginDrag()
+ self.clickedBlock = block
+ self.dragging = True
+
+ #==========================================================
+ # Drawing
+
+ def draw( self ):
+
+ startX = self.screenBufDirtyRect.x
+ startY = self.screenBufDirtyRect.y
+ stopX = startX + self.screenBufDirtyRect.width
+ stopY = startY + self.screenBufDirtyRect.height
+
+ self.gc.set_clip_rectangle( self.screenBufDirtyRect )
+
+ # draw background
+ self.gc.foreground = self.colors["bg"]
+ self.screenBuf.draw_rectangle( self.gc, True, startX, startY, self.screenBufDirtyRect.width, self.screenBufDirtyRect.height )
+
+ # draw blocks
+ self.gc.set_clip_mask( self.blockMask )
+ for block in self.blocks:
+ block.draw( startX, startY, stopX, stopY, self.screenBuf )
+
+ self.screenBufDirty = False
+
+ def on_expose( self, DA, event ):
+
+ if self.screenBufDirty:
+ self.draw()
+
+ self.drawingAreaDirty = False
+
+ startX = event.area.x
+ startY = event.area.y
+ stopX = event.area.x + event.area.width
+ stopY = event.area.y + event.area.height
+
+ self.gc.set_clip_rectangle( event.area )
+
+ # draw base
+ DA.window.draw_drawable( self.gc, self.screenBuf, startX, startY, startX, startY, event.area.width, event.area.height )
+
+ if self.possibleDelete:
+ return
+
+ self.gc.set_clip_mask( self.blockMask )
+
+ # draw possible parent
+ if self.possibleParent:
+ self.possibleParent.drawHighlight( startX, startY, stopX, stopY, DA.window )
+
+ # draw dragged objects
+ if self.dragging:
+ self.clickedBlock.draw( startX, startY, stopX, stopY, DA.window )
+
+ # draw possible substitute
+ if self.possibleSubstitute:
+ self.possibleSubstitute.drawHighlight( startX, startY, stopX, stopY, DA.window )
+
+ # draw key highlight
+ if self.overKey:
+ self.overKey.drawKeyHighlight( DA.window )
+
+ def invalidate_rect( self, x, y, width, height, base = True ):
+ self.dirtyRectToAdd.x = x
+ self.dirtyRectToAdd.y = y
+ self.dirtyRectToAdd.width = width
+ self.dirtyRectToAdd.height = height
+
+ #print "dirty %d %d %d %d %d %d" % (x, y, width, height, x+width, y+height)
+ if base: # the base image has been dirtied
+ if not self.screenBufDirty:
+ self.screenBufDirtyRect.x = x
+ self.screenBufDirtyRect.y = y
+ self.screenBufDirtyRect.width = width
+ self.screenBufDirtyRect.height = height
+ else:
+ self.screenBufDirtyRect = self.screenBufDirtyRect.union( self.dirtyRectToAdd )
+ self.screenBufDirty = True
+ if self.drawingArea.window != None:
+ self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True )
+ self.drawingAreaDirty = True
+
diff --git a/TamTamJam.activity/Jam/Fillin.py b/TamTamJam.activity/Jam/Fillin.py
new file mode 100644
index 0000000..72e2ae3
--- /dev/null
+++ b/TamTamJam.activity/Jam/Fillin.py
@@ -0,0 +1,116 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+import gobject
+
+from RythmGenerator import *
+from Util.CSoundClient import new_csound_client
+from Util.NoteDB import Note
+import Config
+
+class Fillin:
+ def __init__( self, nbeats, tempo, instrument, reverb, volume ):
+ self.notesList = []
+ self.barCount = 0
+ self.gate = 0
+ self.nbeats = nbeats
+ self.tempo = tempo
+ self.instrument = instrument
+ self.reverb = reverb
+ self.volume = volume
+ self.onsets = []
+ self.pitchs = []
+ self.playBackTimeout = None
+ self.loopId = 0
+ self.csnd = new_csound_client()
+
+ def reset( self ):
+ self.barCount = 0
+ self.gate = 0
+
+ def setLoopId( self, id ):
+ self.loopId = id
+
+ def setProperties( self, tempo, instrument, volume, beats, reverb ):
+ self.setTempo( tempo )
+ self.setInstrument( instrument )
+ self.setVolume( volume )
+ self.setBeats( beats )
+ self.setReverb( reverb )
+
+ def setInstrument( self, instrument ):
+ self.instrument = instrument
+
+ def setBeats( self, nbeats ):
+ if self.playBackTimeout != None:
+ gobject.source_remove( self.playBackTimeout )
+
+ self.nbeats = nbeats
+ self.clear()
+ self.reset()
+
+ def setTempo( self, tempo ):
+ self.tempo = tempo
+ if self.playBackTimeout != None:
+ gobject.source_remove( self.playBackTimeout )
+ self.play()
+
+ def setReverb( self, reverb ):
+ self.reverb = reverb
+
+ def setVolume( self, volume ):
+ self.volume = volume
+
+ def play( self ):
+ if self.playBackTimeout == None:
+ self.playBackTimeout = gobject.timeout_add( int(60000/self.tempo/8), self.handleClock )
+ self.handleClock()
+
+ def stop( self ):
+ if self.playBackTimeout != None:
+ gobject.source_remove( self.playBackTimeout )
+ self.clear()
+
+ def clear( self ):
+ if self.notesList:
+ for n in self.notesList:
+ self.csnd.loopDelete(n, self.loopId)
+ self.notesList = []
+
+ def handleClock( self ):
+ tick = self.csnd.loopGetTick( self.loopId )
+ if tick < ( Config.TICKS_PER_BEAT / 2 + 1 ):
+ if self.gate == 0:
+ self.gate = 1
+ self.barCount += 1
+ self.barCount %= 4
+ if self.barCount == 1:
+ self.clear()
+
+ if tick > ( ( Config.TICKS_PER_BEAT * self.nbeats ) - ( Config.TICKS_PER_BEAT / 2 ) - 1 ):
+ if self.gate == 1:
+ self.gate = 0
+ if self.barCount == 3:
+ self.regenerate()
+ return True
+
+ def unavailable( self, onsets, pitchs ):
+ self.onsets = onsets
+ self.pitchs = pitchs
+
+ def regenerate(self):
+ def flatten(ll):
+ rval = []
+ for l in ll:
+ rval += l
+ return rval
+ i = 500
+ self.notesList= []
+ for x in flatten( generator(self.instrument, self.nbeats, 0.4, 0.1, self.reverb) ):
+ if x.onset not in self.onsets or x.pitch not in self.pitchs:
+ x.amplitude = x.amplitude*self.volume
+ n = Note(0, x.trackId, i, x)
+ self.notesList.append(n)
+ i += 1
+ self.csnd.loopPlay(n,1, loopId = self.loopId ) #add as active
+
diff --git a/TamTamJam.activity/Jam/GenRythm.py b/TamTamJam.activity/Jam/GenRythm.py
new file mode 100644
index 0000000..d0e23e3
--- /dev/null
+++ b/TamTamJam.activity/Jam/GenRythm.py
@@ -0,0 +1,80 @@
+import random
+import Config
+
+from Generation.GenerationConstants import GenerationConstants
+from Generation.Utils import *
+
+class GenRythm:
+ def drumRythmSequence(self, instrumentName, nbeats, density, regularity ):
+ rythmSequence = []
+ binSelection = []
+ downBeats = []
+ upBeats = []
+ beats = []
+ countDown = 0
+ onsetTime = None
+
+ if Config.INSTRUMENTS[instrumentName].instrumentRegister == Config.PUNCH:
+ registerDensity = 0.5
+ downBeatRecurence = 4
+ downBeats = [x for x in GenerationConstants.DRUM_PUNCH_ACCENTS[ nbeats ]]
+ for downBeat in downBeats:
+ upBeats.append( downBeat + Config.TICKS_PER_BEAT / 2 )
+
+ if Config.INSTRUMENTS[instrumentName].instrumentRegister == Config.LOW:
+ registerDensity =1
+ downBeatRecurence = 4
+ downBeats = [x for x in GenerationConstants.DRUM_LOW_ACCENTS[ nbeats ]]
+ for downBeat in downBeats:
+ upBeats.append( downBeat + Config.TICKS_PER_BEAT / 2 )
+
+ if Config.INSTRUMENTS[instrumentName].instrumentRegister == Config.MID:
+ registerDensity = .75
+ downBeatRecurence = 1
+ downBeats = [x for x in GenerationConstants.DRUM_MID_ACCENTS[ nbeats ]]
+ for downBeat in downBeats:
+ upBeats.append( downBeat + Config.TICKS_PER_BEAT / 4 )
+
+ if Config.INSTRUMENTS[instrumentName].instrumentRegister == Config.HIGH:
+ registerDensity = 1.5
+ downBeatRecurence = 1
+ downBeats = [x for x in GenerationConstants.DRUM_HIGH_ACCENTS[ nbeats ]]
+ for downBeat in downBeats:
+ upBeats.append( downBeat + Config.TICKS_PER_BEAT / 4 )
+
+ realDensity = density * registerDensity
+ if realDensity > 1.:
+ realDensity = 1.
+
+ list = range( int( realDensity * len( downBeats ) ) )
+ for i in list:
+ if random.random() < ( regularity * downBeatRecurence ) and binSelection.count( 1 ) < len( downBeats ):
+ binSelection.append( 1 )
+ else:
+ if binSelection.count( 0 ) < len( downBeats ):
+ binSelection.append( 0 )
+ else:
+ binSelection.append( 1 )
+
+ countDown = binSelection.count( 1 )
+
+ length = len(downBeats) - 1
+ for i in range( countDown ):
+ ran1 = random.randint(0, length)
+ ran2 = random.randint(0, length)
+ randMin = min(ran1, ran2)
+ onsetTime = downBeats.pop(randMin)
+ rythmSequence.append( onsetTime )
+ length -= 1
+
+ length = len(upBeats) - 1
+ for i in range( len( binSelection ) - countDown ):
+ ran1 = random.randint(0, length)
+ ran2 = random.randint(0, length)
+ randMin = min(ran1, ran2)
+ onsetTime = upBeats.pop(randMin)
+ rythmSequence.append( onsetTime )
+ length -= 1
+
+ rythmSequence.sort()
+ return rythmSequence
diff --git a/TamTamJam.activity/Jam/JamMain.py b/TamTamJam.activity/Jam/JamMain.py
new file mode 100644
index 0000000..c339ae6
--- /dev/null
+++ b/TamTamJam.activity/Jam/JamMain.py
@@ -0,0 +1,1112 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+import pango
+
+from SubActivity import SubActivity
+
+import os, sys, shutil
+
+import Config
+from gettext import gettext as _
+import sugar.graphics.style as style
+
+from Jam.Desktop import Desktop
+import Jam.Picker as Picker
+import Jam.Block as Block
+from Jam.Toolbars import JamToolbar, DesktopToolbar
+
+
+from Util.CSoundNote import CSoundNote
+from Util.CSoundClient import new_csound_client
+import Util.InstrumentDB as InstrumentDB
+from Util import NoteDB
+
+from Fillin import Fillin
+from RythmGenerator import generator
+from Generation.GenerationConstants import GenerationConstants
+from Util.NoteDB import Note, Page
+
+from Util import ControlStream
+
+import xdrlib
+import time
+import gobject
+import Util.Network as Net
+from sugar.presence import presenceservice
+from sugar.graphics.xocolor import XoColor
+
+from math import sqrt
+
+class JamMain(SubActivity):
+
+ def __init__(self, activity, set_mode):
+ SubActivity.__init__(self, set_mode)
+
+ self.activity = activity
+
+ self.instrumentDB = InstrumentDB.getRef()
+ self.noteDB = NoteDB.NoteDB()
+
+ #-- initial settings ----------------------------------
+ self.tempo = Config.PLAYER_TEMPO
+ self.beatDuration = 60.0/self.tempo
+ self.ticksPerSecond = Config.TICKS_PER_BEAT*self.tempo/60.0
+ self.volume = 0.5
+
+ self.csnd = new_csound_client()
+ for i in range(0,9):
+ self.csnd.setTrackVolume( 100, i )
+ self.csnd.setMasterVolume( self.volume*100 ) # csnd expects a range 0-100 for now
+ self.csnd.setTempo( self.tempo )
+
+ self.paused = False
+
+ presenceService = presenceservice.get_instance()
+ self.xoOwner = presenceService.get_owner()
+
+ #-- Drawing -------------------------------------------
+ def darken( colormap, hex ):
+ hexToDec = { "0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "A":10, "B":11, "C":12, "D":13, "E":14, "F":15, "a":10, "b":11, "c":12, "d":13, "e":14, "f":15 }
+ r = int( 0.7*(16*hexToDec[hex[1]] + hexToDec[hex[2]]) )
+ g = int( 0.7*(16*hexToDec[hex[3]] + hexToDec[hex[4]]) )
+ b = int( 0.7*(16*hexToDec[hex[5]] + hexToDec[hex[6]]) )
+ return colormap.alloc_color( r*256, g*256, b*256 )
+ def lighten( colormap, hex ):
+ hexToDec = { "0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "A":10, "B":11, "C":12, "D":13, "E":14, "F":15, "a":10, "b":11, "c":12, "d":13, "e":14, "f":15 }
+ r = 255 - int( 0.7*(255-(16*hexToDec[hex[1]] + hexToDec[hex[2]])) )
+ g = 255 - int( 0.7*(255-(16*hexToDec[hex[3]] + hexToDec[hex[4]])) )
+ b = 255 - int( 0.7*(255-(16*hexToDec[hex[5]] + hexToDec[hex[6]])) )
+ return colormap.alloc_color( r*256, g*256, b*256 )
+
+ xoColorKey = self.xoOwner.props.color
+ if not xoColorKey:
+ xoColorKey = ( "#8D8D8D,#FFDDEA" )
+ xoColor = XoColor( xoColorKey )
+
+ win = gtk.gdk.get_default_root_window()
+ self.gc = gtk.gdk.GC( win )
+ colormap = gtk.gdk.colormap_get_system()
+ self.colors = { "bg": colormap.alloc_color( Config.PANEL_BCK_COLOR ),
+ "black": colormap.alloc_color( style.COLOR_BLACK.get_html() ),
+ #"Picker_Bg": colormap.alloc_color( "#404040" ),
+ #"Picker_Bg_Inactive": colormap.alloc_color( "#808080" ),
+ "Picker_Bg": colormap.alloc_color( style.COLOR_TOOLBAR_GREY.get_html() ),
+ "Picker_Bg_Inactive": colormap.alloc_color( style.COLOR_BUTTON_GREY.get_html() ),
+ "Picker_Fg": colormap.alloc_color( style.COLOR_WHITE.get_html() ),
+ "Border_Active": colormap.alloc_color( xoColor.get_stroke_color() ), #colormap.alloc_color( "#590000" ),
+ "Border_Inactive": colormap.alloc_color( "#8D8D8D" ),
+ "Border_Highlight": colormap.alloc_color( "#FFFFFF" ),
+ "Bg_Active": colormap.alloc_color( xoColor.get_fill_color() ), #colormap.alloc_color( "#FFDDEA" ),
+ "Bg_Inactive": colormap.alloc_color( "#DBDBDB" ),
+ "Preview_Note_Fill": colormap.alloc_color( Config.BG_COLOR ),
+ "Preview_Note_Border": colormap.alloc_color( Config.FG_COLOR ),
+ "Preview_Note_Selected": colormap.alloc_color( style.COLOR_WHITE.get_html() ),
+ "Note_Fill_Active": lighten( colormap, "#590000" ), # base "Border_Active"
+ "Note_Fill_Inactive": lighten( colormap, "#8D8D8D" ), # base "Border_Inactive"
+ "Beat_Line": colormap.alloc_color( "#959595" ) }
+ self.colors[ "Note_Border_Active"] = self.colors["Border_Active"]
+ self.colors[ "Note_Border_Inactive"] = self.colors["Border_Inactive"]
+
+
+ if True: # load block clipmask
+ pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'jam-blockMask.png')
+ pixels = pix.get_pixels()
+ stride = pix.get_rowstride()
+ channels = pix.get_n_channels()
+ bitmap = ""
+ byte = 0
+ shift = 0
+ for j in range(pix.get_height()):
+ offset = stride*j
+ for i in range(pix.get_width()):
+ r = pixels[i*channels+offset]
+ if r != "\0": byte += 1 << shift
+ shift += 1
+ if shift > 7:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ if shift > 0:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ self.blockMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() )
+
+ pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+"sampleBG.png" )
+ self.sampleBg = gtk.gdk.Pixmap( win, pix.get_width(), pix.get_height() )
+ self.sampleBg.draw_pixbuf( self.gc, pix, 0, 0, 0, 0, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
+ self.sampleBg.endOffset = pix.get_width()-5
+ self.sampleNoteHeight = 7
+ if True: # load sample note clipmask
+ pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'sampleNoteMask.png')
+ pixels = pix.get_pixels()
+ stride = pix.get_rowstride()
+ channels = pix.get_n_channels()
+ bitmap = ""
+ byte = 0
+ shift = 0
+ for j in range(pix.get_height()):
+ offset = stride*j
+ for i in range(pix.get_width()):
+ r = pixels[i*channels+offset]
+ if r != "\0": byte += 1 << shift
+ shift += 1
+ if shift > 7:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ if shift > 0:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ self.sampleNoteMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() )
+ self.sampleNoteMask.endOffset = pix.get_width()-3
+
+ self.loopPitchOffset = 4
+ self.loopTickOffset = 13
+ self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES-1) / (Block.Loop.HEIGHT - 2*self.loopPitchOffset - self.sampleNoteHeight)
+ self.pixelsPerPitch = float(Block.Loop.HEIGHT - 2*self.loopPitchOffset - self.sampleNoteHeight)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ self.pixelsPerTick = Block.Loop.BEAT/float(Config.TICKS_PER_BEAT)
+ self.ticksPerPixel = 1.0/self.pixelsPerTick
+
+ #-- Instrument Images ---------------------------------
+ self.instrumentImage = {}
+ self.instrumentImageActive = {}
+ for inst in self.instrumentDB.getSet( "All" ):
+ self.prepareInstrumentImage( inst.id, inst.img )
+
+ #-- Loop Images ---------------------------------------
+ self.loopImage = {} # get filled in through updateLoopImage
+ self.loopImageActive = {} #
+
+ #-- Key Images ----------------------------------------
+ self.keyImage = {}
+ self.keyImageActive = {}
+ # use hardware key codes to work on any keyboard layout (hopefully)
+ self.valid_shortcuts = { 18:"9", 19:"0", 20:"-", 21:"=",
+ 32:"O", 33:"P", 34:"[", 35:"]",
+ 47:";", 48:"'", 51:"\\",
+ 60:".", 61:"/",
+ None:" " }
+ for key in self.valid_shortcuts.keys():
+ self.prepareKeyImage( key )
+
+ #-- Toolbars ------------------------------------------
+ self.activity.activity_toolbar.keep.show()
+
+ self.jamToolbar = JamToolbar( self )
+ self.activity.toolbox.add_toolbar( _("Jam"), self.jamToolbar )
+
+ self.desktopToolbar = DesktopToolbar( self )
+ self.activity.toolbox.add_toolbar( _("Desktop"), self.desktopToolbar )
+
+ #-- GUI -----------------------------------------------
+ if True: # GUI
+ self.modify_bg( gtk.STATE_NORMAL, self.colors["bg"] ) # window bg
+
+ self.GUI = {}
+ self.GUI["mainVBox"] = gtk.VBox()
+ self.add( self.GUI["mainVBox"] )
+
+ #-- Desktop -------------------------------------------
+ self.desktop = self.GUI["desktop"] = Desktop( self )
+ self.GUI["mainVBox"].pack_start( self.GUI["desktop"] )
+
+ #-- Bank ----------------------------------------------
+ separator = gtk.Label( " " )
+ separator.set_size_request( -1, style.TOOLBOX_SEPARATOR_HEIGHT )
+ self.GUI["mainVBox"].pack_start( separator, False )
+ self.GUI["notebook"] = gtk.Notebook()
+ self.GUI["notebook"].set_scrollable( True )
+ self.GUI["notebook"].modify_bg( gtk.STATE_NORMAL, self.colors["Picker_Bg"] ) # active tab
+ self.GUI["notebook"].modify_bg( gtk.STATE_ACTIVE, self.colors["Picker_Bg_Inactive"] ) # inactive tab
+ self.GUI["notebook"].props.tab_vborder = style.TOOLBOX_TAB_VBORDER
+ self.GUI["notebook"].props.tab_hborder = style.TOOLBOX_TAB_HBORDER
+ self.GUI["notebook"].set_size_request( -1, 160 )
+ self.GUI["notebook"].connect( "switch-page", self.setPicker )
+ self.GUI["mainVBox"].pack_start( self.GUI["notebook"], False, False )
+ self.pickers = {}
+ self.pickerScroll = {}
+ for type in [ Picker.Instrument, Picker.Drum, Picker.Loop ]:
+ self.pickers[type] = type( self )
+
+ def prepareLabel( name ):
+ label = gtk.Label( _(name) )
+ label.set_alignment( 0.0, 0.5 )
+ label.modify_fg( gtk.STATE_NORMAL, self.colors["Picker_Fg"] )
+ label.modify_fg( gtk.STATE_ACTIVE, self.colors["Picker_Fg"] )
+ return label
+
+ self.GUI["notebook"].append_page( self.pickers[Picker.Drum], prepareLabel("Drum Kits") )
+ self.GUI["notebook"].append_page( self.pickers[Picker.Loop], prepareLabel("Loops") )
+
+ sets = self.instrumentDB.getLabels()[:]
+ sets.sort()
+ for set in sets:
+ page = gtk.HBox()
+ page.set = set
+ self.GUI["notebook"].append_page( page, prepareLabel( set ) )
+
+ self.show_all()
+
+ self.GUI["notebook"].set_current_page( 0 )
+
+ #-- Keyboard ------------------------------------------
+ self.key_dict = {}
+ self.nextTrack = 1
+ self.keyboardListener = None
+ self.recordingNote = None
+
+ self.keyMap = {}
+
+ # default instrument
+ self._updateInstrument( Config.INSTRUMENTS["kalimba"].instrumentId, 0.5 )
+ self.instrumentStack = []
+
+ #-- Drums ---------------------------------------------
+ self.drumLoopId = None
+ # use dummy values for now
+ self.drumFillin = Fillin( 2, 100, Config.INSTRUMENTS["drum1kit"].instrumentId, 0, 1 )
+
+ #-- Desktops ------------------------------------------
+ self.curDesktop = None
+ # copy preset desktops
+ path = Config.TAM_TAM_ROOT+"/Resources/Desktops/"
+ filelist = os.listdir( path )
+ for file in filelist:
+ shutil.copyfile( path+file, Config.SCRATCH_DIR+file )
+
+ #-- Network -------------------------------------------
+ self.network = Net.Network()
+ self.network.addWatcher( self.networkStatusWatcher )
+ self.network.connectMessage( Net.HT_SYNC_REPLY, self.processHT_SYNC_REPLY )
+ self.network.connectMessage( Net.HT_TEMPO_UPDATE, self.processHT_TEMPO_UPDATE )
+ self.network.connectMessage( Net.PR_SYNC_QUERY, self.processPR_SYNC_QUERY )
+ self.network.connectMessage( Net.PR_TEMPO_QUERY, self.processPR_TEMPO_QUERY )
+ self.network.connectMessage( Net.PR_REQUEST_TEMPO_CHANGE, self.processPR_REQUEST_TEMPO_CHANGE )
+
+ # sync
+ self.syncQueryStart = {}
+ self.syncTimeout = None
+ self.heartbeatLoop = self.csnd.loopCreate()
+ self.csnd.loopSetNumTicks( Config.TICKS_PER_BEAT, self.heartbeatLoop )
+ self.heartbeatStart = time.time()
+ self.csnd.loopStart( self.heartbeatLoop )
+
+ # data packing classes
+ self.packer = xdrlib.Packer()
+ self.unpacker = xdrlib.Unpacker("")
+
+ # handle forced networking
+ if self.network.isHost():
+ self.updateSync()
+ self.syncTimeout = gobject.timeout_add( 1000, self.updateSync )
+ elif self.network.isPeer():
+ self.sendTempoQuery()
+ self.syncTimeout = gobject.timeout_add( 1000, self.updateSync )
+
+ #-- Final Set Up --------------------------------------
+ self.setVolume( self.volume )
+ self.setTempo( self.tempo )
+ self.activity.toolbox.set_current_toolbar(1) # JamToolbar
+ self.setDesktop( 0, True )
+
+
+ #==========================================================
+ # SubActivity Handlers
+
+ def onActivate( self, arg ):
+ SubActivity.onActivate( self, arg )
+
+ def onDeactivate( self ):
+ SubActivity.onDeactivate( self )
+
+ def onDestroy( self ):
+ SubActivity.onDestroy( self )
+
+ # clear up scratch folder
+ path = Config.SCRATCH_DIR
+ filelist = os.listdir( path )
+ for file in filelist:
+ os.remove( path+file )
+
+
+ #==========================================================
+ # Playback
+
+ def onKeyPress( self, widget, event ):
+ key = event.hardware_keycode
+
+ if key in self.keyMap.keys():
+ activate = True
+ for block in self.keyMap[key]:
+ if block.isActive():
+ activate = False
+ break
+ if activate:
+ for block in self.keyMap[key]:
+ if not block.isActive():
+ if block.type == Block.Drum: self.desktop.activateDrum( block )
+ elif block.type == Block.Loop: self.desktop.activateLoop( block )
+ else:
+ for block in self.keyMap[key]:
+ if block.isActive():
+ if block.type == Block.Drum: self.desktop.deactivateDrum( block )
+ elif block.type == Block.Loop: self.desktop.deactivateLoop( block )
+ return
+
+ if self.key_dict.has_key( key ): # repeated press
+ return
+
+ if Config.KEY_MAP_PIANO.has_key( key ):
+ pitch = Config.KEY_MAP_PIANO[key]
+ inst = Config.INSTRUMENTSID[self.instrument["id"]]
+
+ if inst.kit: # drum kit
+ if pitch in GenerationConstants.DRUMPITCH:
+ pitch = GenerationConstants.DRUMPITCH[pitch]
+ csnote = self._playNote( key,
+ 36,
+ self.instrument["amplitude"]*0.5, # trackVol*noteVol
+ self.instrument["pan"],
+ 100,
+ inst.kit[pitch].instrumentId,
+ self.instrument["reverb"] )
+ else:
+ if event.state == gtk.gdk.MOD1_MASK:
+ pitch += 5
+
+ if inst.csoundInstrumentId == Config.INST_PERC: #Percussions resonance
+ duration = 60
+ else:
+ duration = -1
+
+ csnote = self._playNote( key,
+ pitch,
+ self.instrument["amplitude"]*0.5, # trackVol*noteVol
+ self.instrument["pan"],
+ duration,
+ self.instrument["id"],
+ self.instrument["reverb"] )
+
+ if self.keyboardListener:
+ self.keyboardListener.recordNote( csnote.pitch )
+ self.recordingNote = True
+
+ def onKeyRelease( self, widget, event ):
+ key = event.hardware_keycode
+
+ if self.key_dict.has_key( key ):
+ self._stopNote( key )
+
+ if self.recordingNote:
+ if self.keyboardListener:
+ self.keyboardListener.finishNote()
+ self.recordingNote = False
+
+ def _playNote( self, key, pitch, amplitude, pan, duration, instrumentId, reverb ):
+ self.key_dict[key] = CSoundNote( 0, # onset
+ pitch,
+ amplitude,
+ pan,
+ duration,
+ self.nextTrack,
+ instrumentId,
+ reverbSend = reverb,
+ tied = True,
+ mode = 'mini' )
+ self.nextTrack += 1
+ if self.nextTrack > 8:
+ self.nextTrack = 1
+ self.csnd.play(self.key_dict[key], 0.3)
+
+ return self.key_dict[key]
+
+ def _stopNote( self, key ):
+ csnote = self.key_dict[key]
+ if Config.INSTRUMENTSID[ csnote.instrumentId ].csoundInstrumentId == Config.INST_TIED:
+ csnote.duration = .5
+ csnote.decay = 0.7
+ csnote.tied = False
+ self.csnd.play(csnote, 0.3)
+ del self.key_dict[key]
+
+ def _updateInstrument( self, id, volume, pan = 0, reverb = 0 ):
+ self.instrument = { "id": id,
+ "amplitude": volume,
+ "pan": pan,
+ "reverb": reverb }
+
+ def pushInstrument( self, instrument ):
+ self.instrumentStack.append( self.instrument )
+ self.instrument = instrument
+
+ def popInstrument( self ):
+ self.instrument = self.instrumentStack.pop()
+
+ def _playDrum( self, id, pageId, volume, reverb, beats, regularity, loopId = None ):
+
+ if loopId == None: # create new loop
+ startTick = 0
+ firstTime = True
+ else: # update loop
+ startTick = self.csnd.loopGetTick( loopId )
+ self.csnd.loopDestroy( loopId )
+ firstTime = False
+
+ loopId = self.csnd.loopCreate()
+
+ # TODO update track volume
+
+ noteOnsets = []
+ notePitchs = []
+ for n in self.noteDB.getNotesByTrack( pageId, 0 ):
+ n.pushState()
+ noteOnsets.append( n.cs.onset )
+ notePitchs.append( n.cs.pitch )
+ n.cs.amplitude = volume * n.cs.amplitude # TODO remove me once track volume is working
+ n.cs.reverbSend = reverb
+ self.csnd.loopPlay( n, 1, loopId = loopId ) #add as active
+ n.popState()
+
+ ticks = self.noteDB.getPage( pageId ).ticks
+
+ self.csnd.loopSetNumTicks( ticks, loopId )
+
+ self.drumFillin.setLoopId( loopId )
+ self.drumFillin.setProperties( self.tempo, Config.INSTRUMENTSID[id].name, volume, beats, reverb )
+ self.drumFillin.unavailable( noteOnsets, notePitchs )
+
+ self.drumFillin.play()
+
+ # sync to heartbeat
+ if False: # firstTime: # always force first note to play rather than snaping to nearest beat.. good idea?
+ startTick = ticks - Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+ else:
+ while startTick > ticks: # align with last beat
+ startTick -= Config.TICKS_PER_BEAT
+ beatTick = int(startTick) % Config.TICKS_PER_BEAT
+ heartTick = self.csnd.loopGetTick( self.heartbeatLoop )
+ if beatTick > heartTick:
+ if beatTick - heartTick < heartTick + Config.TICKS_PER_BEAT - beatTick:
+ startTick = (int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+ else:
+ startTick = (1 + int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+ else:
+ if heartTick - beatTick < beatTick + Config.TICKS_PER_BEAT - heartTick:
+ startTick = (int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+ else:
+ startTick = (-1 + int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+
+ if startTick >= ticks:
+ startTick -= ticks
+ elif startTick < 0:
+ startTick += ticks
+
+ self.csnd.loopSetTick( startTick, loopId )
+
+ if not self.paused:
+ self.csnd.loopStart( loopId )
+
+ return loopId
+
+ def _stopDrum( self, loopId ):
+ self.drumFillin.stop()
+ self.csnd.loopDestroy( loopId )
+
+ def _playLoop( self, id, volume, reverb, tune, loopId = None, force = False ):
+ if loopId == None: # create new loop
+ startTick = 0
+ firstTime = True
+ else: # update loop
+ startTick = self.csnd.loopGetTick( loopId )
+ self.csnd.loopDestroy( loopId )
+ firstTime = False
+
+ loopId = self.csnd.loopCreate()
+
+ # TODO update track volume
+
+ inst = Config.INSTRUMENTSID[id]
+
+ offset = 0
+ for page in tune:
+ for n in self.noteDB.getNotesByTrack( page, 0 ):
+ n.pushState()
+ n.cs.instrumentId = id
+ n.cs.amplitude = volume * n.cs.amplitude # TODO remove me once track volume is working
+ n.cs.reverbSend = reverb
+ if inst.kit: # drum kit
+ if n.cs.pitch in GenerationConstants.DRUMPITCH:
+ n.cs.pitch = GenerationConstants.DRUMPITCH[n.cs.pitch]
+ n.cs.onset += offset
+ self.csnd.loopPlay( n, 1, loopId = loopId )
+ n.popState()
+ offset += self.noteDB.getPage(page).ticks
+
+
+ self.csnd.loopSetNumTicks( offset, loopId )
+
+ # sync to heartbeat
+ if False: # firstTime: # always force first note to play rather than snaping to nearest beat.. good idea?
+ startTick = offset - Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+ else:
+ while startTick > offset: # align with last beat
+ startTick -= Config.TICKS_PER_BEAT
+ beatTick = int(startTick) % Config.TICKS_PER_BEAT
+ heartTick = self.csnd.loopGetTick( self.heartbeatLoop )
+ if beatTick > heartTick:
+ if beatTick - heartTick < heartTick + Config.TICKS_PER_BEAT - beatTick:
+ startTick = (int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+ else:
+ startTick = (1 + int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+ else:
+ if heartTick - beatTick < beatTick + Config.TICKS_PER_BEAT - heartTick:
+ startTick = (int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+ else:
+ startTick = (-1 + int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
+
+ if startTick >= offset:
+ startTick -= offset
+ elif startTick < 0:
+ startTick += offset
+
+
+ self.csnd.loopSetTick( startTick, loopId )
+
+ if not self.paused or force:
+ self.csnd.loopStart( loopId )
+
+ return loopId
+
+ def _stopLoop( self, loopId ):
+ self.csnd.loopDestroy( loopId )
+
+ def setPaused( self, paused ):
+ if self.paused == paused:
+ return
+
+ loops = self.desktop.getLoopIds()
+
+ if self.paused: # unpause
+ self.paused = False
+ for loop in loops:
+ self.csnd.loopStart( loop )
+ else: # pause
+ self.paused = True
+ for loop in loops:
+ self.csnd.loopPause( loop )
+
+ #==========================================================
+ # Generate
+
+ def _generateDrumLoop( self, instrumentId, beats, regularity, reverb, pageId = -1 ):
+ def flatten(ll):
+ rval = []
+ for l in ll:
+ rval += l
+ return rval
+
+ notes = flatten( generator( Config.INSTRUMENTSID[instrumentId].name, beats, 0.8, regularity, reverb) )
+
+ if pageId == -1:
+ page = Page( beats )
+ pageId = self.noteDB.addPage( -1, page )
+ else:
+ self.noteDB.deleteNotesByTrack( [ pageId ], [ 0 ] )
+
+ if len(notes):
+ self.noteDB.addNotes( [ pageId, 0, len(notes) ] + notes + [-1] )
+
+ return pageId
+
+ def _generateTrack( self, instrumentId, page, track, parameters, algorithm ):
+ dict = { track: { page: self.noteDB.getCSNotesByTrack( page, track ) } }
+ instruments = { page: [ Config.INSTRUMENTSID[instrumentId].name for i in range(Config.NUMBER_OF_TRACKS) ] }
+ beatsOfPages = { page: self.noteDB.getPage(page).beats }
+
+ algorithm( parameters,
+ [ 0.5 for i in range(Config.NUMBER_OF_TRACKS) ],
+ instruments,
+ self.tempo,
+ beatsOfPages,
+ [ track ],
+ [ page ],
+ dict,
+ 4)
+
+ # filter & fix input ...WTF!?
+ for track in dict:
+ for page in dict[track]:
+ for note in dict[track][page]:
+ intdur = int(note.duration)
+ note.duration = intdur
+ note.pageId = page
+ note.trackId = track
+
+ # prepare the new notes
+ newnotes = []
+ for tid in dict:
+ for pid in dict[tid]:
+ newnotes += dict[tid][pid]
+
+ # delete the notes and add the new
+ self.noteDB.deleteNotesByTrack( [ page ], [ track ] )
+
+ self.noteDB.addNotes(
+ [ page, track, len(dict[track][page]) ]
+ + dict[track][page]
+ + [ -1 ] )
+
+
+ #==========================================================
+ # Get/Set
+
+ def getVolume( self ):
+ return self.volume
+
+ def setVolume( self, volume ):
+ self.jamToolbar.volumeSlider.set_value( volume )
+
+ def _setVolume( self, volume ):
+ self.volume = volume
+ self.csnd.setMasterVolume( self.volume*100 ) # csnd expects a range 0-100 for now
+
+ def getTempo( self ):
+ return self.tempo
+
+ def setTempo( self, tempo ):
+ self.jamToolbar.tempoSlider.set_value( tempo )
+
+ def _setTempo( self, tempo ):
+ if self.network.isHost() or self.network.isOffline():
+ t = time.time()
+ percent = self.heartbeatElapsed() / self.beatDuration
+
+ self.tempo = tempo
+ self.beatDuration = 60.0/self.tempo
+ self.ticksPerSecond = Config.TICKS_PER_BEAT*self.tempo/60.0
+ self.csnd.setTempo( self.tempo )
+
+ if self.network.isHost() or self.network.isOffline():
+ self.heatbeatStart = t - percent*self.beatDuration
+ self.updateSync()
+ self.sendTempoUpdate()
+
+ def getInstrument( self ):
+ return self.instrument
+
+ def getDesktop( self ):
+ return self.desktop
+
+ def _clearDesktop( self, save = True ):
+ if self.curDesktop == None:
+ return
+
+ if save:
+ self._saveDesktop()
+
+ self.desktop._clearDesktop()
+
+ self.curDesktop = None
+
+ def setDesktop( self, desktop, force = False ):
+ radiobtn = self.desktopToolbar.getDesktopButton( desktop )
+ if force and radiobtn.get_active():
+ self._setDesktop( desktop )
+ else:
+ radiobtn.set_active( True )
+
+ def _setDesktop( self, desktop ):
+ self._clearDesktop()
+
+ self.curDesktop = desktop
+
+ TTTable = ControlStream.TamTamTable( self.noteDB, jam = self )
+
+ filename = self.getDesktopScratchFile( self.curDesktop )
+ try:
+ stream = open( filename, "r" )
+ TTTable.parseFile( stream )
+ stream.close()
+ except IOError, (errno, strerror):
+ if Config.DEBUG > 3: print "IOError:: _setDesktop:", errno, strerror
+
+ def getInstrumentImage( self, id, active = False ):
+ if active: return self.instrumentImageActive[id]
+ else: return self.instrumentImage[id]
+
+ def getKeyImage( self, key, active = False ):
+ if active: return self.keyImageActive[key]
+ else: return self.keyImage[key]
+
+ def getLoopImage( self, id, active = False ):
+ if active: return self.loopImageActive[id]
+ else: return self.loopImage[id]
+
+ def setPicker( self, widget, pagePointer, page_num ):
+ page = self.GUI["notebook"].get_nth_page( page_num )
+ if page == self.pickers[Picker.Drum]:
+ pass
+ elif page == self.pickers[Picker.Loop]:
+ pass
+ else:
+ self.pickers[Picker.Instrument].setFilter( ( page.set ) )
+ parent = self.pickers[Picker.Instrument].get_parent()
+ if parent != page:
+ if parent != None:
+ parent.remove( self.pickers[Picker.Instrument] )
+ page.add( self.pickers[Picker.Instrument] )
+
+ def setKeyboardListener( self, listener ):
+ self.keyboardListener = listener
+
+ def mapKey( self, key, block, oldKey = None ):
+ if oldKey != None and block in self.keyMap[oldKey]:
+ self.keyMap[oldKey].remove( block )
+
+ if key == None:
+ return
+
+ if key not in self.keyMap.keys():
+ self.keyMap[key] = []
+
+ if block not in self.keyMap[key]:
+ self.keyMap[key].append( block )
+
+ #==========================================================
+ # Pixmaps
+
+ def prepareInstrumentImage( self, id, img_path ):
+ try:
+ win = gtk.gdk.get_default_root_window()
+ pix = gtk.gdk.pixbuf_new_from_file( img_path )
+ x = (Block.Block.WIDTH-pix.get_width())//2
+ y = (Block.Block.HEIGHT-pix.get_height())//2
+ img = gtk.gdk.Pixmap( win, Block.Block.WIDTH, Block.Block.HEIGHT )
+ self.gc.foreground = self.colors["Bg_Inactive"]
+ img.draw_rectangle( self.gc, True, 0, 0, Block.Block.WIDTH, Block.Block.HEIGHT )
+ img.draw_pixbuf( self.gc, pix, 0, 0, x, y, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
+ self.instrumentImage[id] = img
+ img = gtk.gdk.Pixmap( win, Block.Block.WIDTH, Block.Block.HEIGHT )
+ self.gc.foreground = self.colors["Bg_Active"]
+ img.draw_rectangle( self.gc, True, 0, 0, Block.Block.WIDTH, Block.Block.HEIGHT )
+ img.draw_pixbuf( self.gc, pix, 0, 0, x, y, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
+ self.instrumentImageActive[id] = img
+ except:
+ if Config.DEBUG >= 5: print "JamMain:: file does not exist: " + img_path
+ img = gtk.gdk.Pixmap( win, Block.Block.WIDTH, Block.Block.HEIGHT )
+ self.gc.foreground = self.colors["Bg_Inactive"]
+ img.draw_rectangle( self.gc, True, 0, 0, Block.Block.WIDTH, Block.Block.HEIGHT )
+ self.instrumentImage[id] = img
+ img = gtk.gdk.Pixmap( win, Block.Block.WIDTH, Block.Block.HEIGHT )
+ self.gc.foreground = self.colors["Bg_Active"]
+ img.draw_rectangle( self.gc, True, 0, 0, Block.Block.WIDTH, Block.Block.HEIGHT )
+ self.instrumentImageActive[id] = img
+
+ def _drawNotes( self, pixmap, beats, notes, active ):
+ self.gc.set_clip_mask( self.sampleNoteMask )
+ for note in notes: # draw N notes
+ x = self.ticksToPixels( note.cs.onset )
+ endX = self.ticksToPixels( note.cs.onset + note.cs.duration ) - 3 # include end cap offset
+ width = endX - x
+ if width < 5:
+ width = 5
+ endX = x + width
+ y = self.pitchToPixels( note.cs.pitch )
+ # draw fill
+ if active: self.gc.foreground = self.colors["Note_Fill_Active"]
+ else: self.gc.foreground = self.colors["Note_Fill_Inactive"]
+ self.gc.set_clip_origin( x, y-self.sampleNoteHeight )
+ pixmap.draw_rectangle( self.gc, True, x+1, y+1, width+1, self.sampleNoteHeight-2 )
+ # draw border
+ if active: self.gc.foreground = self.colors["Note_Border_Active"]
+ else: self.gc.foreground = self.colors["Note_Border_Inactive"]
+ self.gc.set_clip_origin( x, y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, self.sampleNoteHeight )
+ self.gc.set_clip_origin( endX-self.sampleNoteMask.endOffset, y )
+ pixmap.draw_rectangle( self.gc, True, endX, y, 3, self.sampleNoteHeight )
+
+ def prepareKeyImage( self, key ):
+ win = gtk.gdk.get_default_root_window()
+ pangolayout = self.create_pango_layout( _(self.valid_shortcuts[key]) )
+ fontDesc = pango.FontDescription( "bold" )
+ pangolayout.set_font_description( fontDesc )
+ extents = pangolayout.get_pixel_extents()
+ x = ( Block.Block.KEYSIZE - extents[1][2] ) // 2
+ y = ( Block.Block.KEYSIZE - extents[1][3] ) // 2
+
+ pixmap = gtk.gdk.Pixmap( win, Block.Block.KEYSIZE, Block.Block.KEYSIZE )
+ self.gc.foreground = self.colors["Border_Inactive"]
+ pixmap.draw_rectangle( self.gc, True, 0, 0, Block.Block.KEYSIZE, Block.Block.KEYSIZE )
+ self.gc.foreground = self.colors["Bg_Inactive"]
+ pixmap.draw_layout( self.gc, x, y, pangolayout )
+ self.keyImage[key] = pixmap
+
+ pixmap = gtk.gdk.Pixmap( win, Block.Block.KEYSIZE, Block.Block.KEYSIZE )
+ self.gc.foreground = self.colors["Border_Active"]
+ pixmap.draw_rectangle( self.gc, True, 0, 0, Block.Block.KEYSIZE, Block.Block.KEYSIZE )
+ self.gc.foreground = self.colors["Bg_Active"]
+ pixmap.draw_layout( self.gc, x, y, pangolayout )
+ self.keyImageActive[key] = pixmap
+
+ def updateLoopImage( self, id ):
+ page = self.noteDB.getPage( id )
+
+ win = gtk.gdk.get_default_root_window()
+ width = Block.Loop.WIDTH[page.beats]
+ height = Block.Loop.HEIGHT
+
+ self.gc.set_clip_rectangle( gtk.gdk.Rectangle( 0, 0, width, height ) )
+
+ pixmap = gtk.gdk.Pixmap( win, width, height )
+ self.gc.foreground = self.colors["Bg_Inactive"]
+ pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
+ self._drawNotes( pixmap, page.beats, self.noteDB.getNotesByTrack( id, 0 ), False )
+ self.loopImage[id] = pixmap
+
+ self.gc.set_clip_rectangle( gtk.gdk.Rectangle( 0, 0, width, height ) )
+
+ pixmap = gtk.gdk.Pixmap( win, width, height )
+ self.gc.foreground = self.colors["Bg_Active"]
+ pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
+ self._drawNotes( pixmap, page.beats, self.noteDB.getNotesByTrack( id, 0 ), True )
+ self.loopImageActive[id] = pixmap
+
+ def ticksToPixels( self, ticks ):
+ return self.loopTickOffset + int(round( ticks * self.pixelsPerTick ))
+ def pitchToPixels( self, pitch ):
+ return self.loopPitchOffset + int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+
+ #==========================================================
+ # Load/Save
+
+ def _saveDesktop( self ):
+ if self.curDesktop == None:
+ return
+
+ filename = self.getDesktopScratchFile( self.curDesktop )
+ if os.path.isfile( filename ):
+ os.remove( filename )
+
+ try:
+ scratch = open( filename, "w" )
+ stream = ControlStream.TamTamOStream(scratch)
+
+ self.noteDB.dumpToStream( stream, True )
+ self.desktop.dumpToStream( stream )
+
+ scratch.close()
+ except IOError, (errno, strerror):
+ if Config.DEBUG > 3: print "IOError:: _saveDesktop:", errno, strerror
+
+ def getDesktopScratchFile( self, i ):
+ return Config.SCRATCH_DIR+"desktop%d" % i
+
+ def handleJournalLoad( self, filepath ):
+
+ self._clearDesktop( False )
+
+ TTTable = ControlStream.TamTamTable( self.noteDB, jam = self )
+
+ try:
+ stream = open( filepath, "r" )
+ TTTable.parseFile( stream )
+ stream.close()
+
+ self.setVolume( TTTable.masterVolume )
+ self.setTempo( TTTable.tempo )
+
+ except IOError, (errno, strerror):
+ if Config.DEBUG > 3: print "IOError:: handleJournalLoad:", errno, strerror
+
+ def handleJournalSave( self, filepath ):
+
+ self._saveDesktop()
+
+ try:
+ streamF = open( filepath, "w" )
+ stream = ControlStream.TamTamOStream( streamF )
+
+ for i in range(10):
+ desktop_file = self.getDesktopScratchFile( i )
+ stream.desktop_store( desktop_file, i )
+
+ stream.desktop_set( self.curDesktop )
+
+ stream.master_vol( self.volume )
+ stream.tempo( self.tempo )
+
+ streamF.close()
+
+ except IOError, (errno, strerror):
+ if Config.DEBUG > 3: print "IOError:: handleJournalSave:", errno, strerror
+
+ #==========================================================
+ # Network
+
+ #-- Activity ----------------------------------------------
+
+ def shared( self, activity ):
+ if Config.DEBUG: print "miniTamTam:: successfully shared, start host mode"
+ self.activity._shared_activity.connect( "buddy-joined", self.buddy_joined )
+ self.activity._shared_activity.connect( "buddy-left", self.buddy_left )
+ self.network.setMode( Net.MD_HOST )
+ self.updateSync()
+ self.syncTimeout = gobject.timeout_add( 1000, self.updateSync )
+
+ def joined( self, activity ):
+ if Config.DEBUG:
+ print "miniTamTam:: joined activity!!"
+ for buddy in self.activity._shared_activity.get_joined_buddies():
+ print buddy.props.ip4_address
+
+ def buddy_joined( self, activity, buddy ):
+ if Config.DEBUG:
+ print "buddy joined " + str(buddy)
+ try:
+ print buddy.props.ip4_address
+ except:
+ print "bad ip4_address"
+ if self.network.isHost():
+ if buddy == self.xoOwner:
+ return
+ if buddy.props.ip4_address:
+ self.network.introducePeer( buddy.props.ip4_address )
+ else:
+ print "miniTamTam:: new buddy does not have an ip4_address!!"
+
+ def buddy_left( self, activity, buddy):
+ if Config.DEBUG: print "buddy left"
+
+ #def joined( self, activity ):
+ # if Config.DEBUG: print "miniTamTam:: successfully joined, wait for host"
+ # self.net.waitForHost()
+
+ #-- Senders -----------------------------------------------
+
+ def sendSyncQuery( self ):
+ self.packer.pack_float(random.random())
+ hash = self.packer.get_buffer()
+ self.packer.reset()
+ self.syncQueryStart[hash] = time.time()
+ self.network.send( Net.PR_SYNC_QUERY, hash)
+
+ def sendTempoUpdate( self ):
+ self.packer.pack_int(self.tempo)
+ self.network.sendAll( Net.HT_TEMPO_UPDATE, self.packer.get_buffer() )
+ self.packer.reset()
+
+ def sendTempoQuery( self ):
+ self.network.send( Net.PR_TEMPO_QUERY )
+
+ def requestTempoChange( self, val ):
+ self.packer.pack_int(val)
+ self.network.send( Net.PR_REQUEST_TEMPO_CHANGE, self.packer.get_buffer() )
+ self.packer.reset()
+
+ #-- Handlers ----------------------------------------------
+
+ def networkStatusWatcher( self, mode ):
+ if mode == Net.MD_OFFLINE:
+ if self.syncTimeout:
+ gobject.source_remove( self.syncTimeout )
+ self.syncTimeout = None
+ if mode == Net.MD_PEER:
+ self.updateSync()
+ if not self.syncTimeout:
+ self.syncTimeout = gobject.timeout_add( 1000, self.updateSync )
+ self.sendTempoQuery()
+
+ def processHT_SYNC_REPLY( self, sock, message, data ):
+ t = time.time()
+ hash = data[0:4]
+ latency = t - self.syncQueryStart[hash]
+ self.unpacker.reset(data[4:8])
+ nextBeat = self.unpacker.unpack_float()
+ #print "mini:: got sync: next beat in %f, latency %d" % (nextBeat, latency*1000)
+ self.heartbeatStart = t + nextBeat - self.beatDuration - latency/2
+ self.correctSync()
+ self.syncQueryStart.pop(hash)
+
+ def processHT_TEMPO_UPDATE( self, sock, message, data ):
+ self.unpacker.reset(data)
+ val = self.unpacker.unpack_int()
+ if self.tempoSliderActive:
+ self.delayedTempo = val
+ return
+ self.tempoAdjustment.handler_block( self.tempoAdjustmentHandler )
+ self.tempoAdjustment.set_value( val )
+ self._updateTempo( val )
+ self.tempoAdjustment.handler_unblock( self.tempoAdjustmentHandler )
+ self.sendSyncQuery()
+
+ def processPR_SYNC_QUERY( self, sock, message, data ):
+ self.packer.pack_float(self.nextHeartbeat())
+ self.network.send( Net.HT_SYNC_REPLY, data + self.packer.get_buffer(), sock )
+ self.packer.reset()
+
+ def processPR_TEMPO_QUERY( self, sock, message, data ):
+ self.packer.pack_int(self.tempo)
+ self.network.send( Net.HT_TEMPO_UPDATE, self.packer.get_buffer(), to = sock )
+ self.packer.reset()
+
+ def processPR_REQUEST_TEMPO_CHANGE( self, sock, message, data ):
+ if self.tempoSliderActive:
+ return
+ self.unpacker.reset(data)
+ val = self.unpacker.unpack_int()
+ self.tempoAdjustment.set_value( val )
+
+ #==========================================================
+ # Sync
+
+ def nextHeartbeat( self ):
+ delta = time.time() - self.heartbeatStart
+ return self.beatDuration - (delta % self.beatDuration)
+
+ def nextHeartbeatInTicks( self ):
+ delta = time.time() - self.heartbeatStart
+ next = self.beatDuration - (delta % self.beatDuration)
+ return self.ticksPerSecond*next
+
+ def heartbeatElapsed( self ):
+ delta = time.time() - self.heartbeatStart
+ return delta % self.beatDuration
+
+ def heartbeatElapsedTicks( self ):
+ delta = time.time() - self.heartbeatStart
+ return self.ticksPerSecond*(delta % self.beatDuration)
+
+ def updateSync( self ):
+ if self.network.isOffline():
+ return False
+ elif self.network.isWaiting():
+ return True
+ elif self.network.isHost():
+ self.correctSync()
+ else:
+ self.sendSyncQuery()
+ return True
+
+ def correctSync( self ):
+ curTick = self.csnd.loopGetTick( self.heartbeatLoop )
+ curTicksIn = curTick % Config.TICKS_PER_BEAT
+ ticksIn = self.heartbeatElapsedTicks()
+ err = curTicksIn - ticksIn
+ if err > Config.TICKS_PER_BEAT_DIV2:
+ err -= Config.TICKS_PER_BEAT
+ elif err < -Config.TICKS_PER_BEAT_DIV2:
+ err += Config.TICKS_PER_BEAT
+ correct = curTick - err
+ if correct > Config.TICKS_PER_BEAT:
+ correct -= Config.TICKS_PER_BEAT
+ elif correct < 0:
+ correct += Config.TICKS_PER_BEAT
+ #print "correct:: %f ticks, %f ticks in, %f expected, %f err, correct %f" % (curTick, curTicksIn, ticksIn, err, correct)
+ if abs(err) > 0.25:
+ self.csnd.adjustTick(-err)
+
+
diff --git a/TamTamJam.activity/Jam/Parasite.py b/TamTamJam.activity/Jam/Parasite.py
new file mode 100644
index 0000000..084d092
--- /dev/null
+++ b/TamTamJam.activity/Jam/Parasite.py
@@ -0,0 +1,349 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import Config
+
+from Util.NoteDB import PARAMETER
+from Util.CSoundClient import new_csound_client
+
+class LoopParasite:
+
+ def __init__( self, noteDB, owner, note ):
+ self.noteDB = noteDB
+ self.owner = owner
+ self.note = note
+
+ self.firstTransform = True
+ self.x = 0
+ self.y = 0
+ self.width = 1
+ self.height = Config.NOTE_HEIGHT
+
+ self.selected = False
+ self.potentialDeselect = False
+
+ self.oldOnset = -1
+ self.oldEnd = -1
+ self.oldPitch = -1
+ self.oldAmplitude = -1
+ self.oldBeats = -1
+ self.lastDragO = 0
+ self.lastDragP = 0
+ self.lastDragD = 0
+
+ self.gc = self.owner.gc
+ self.colors = self.owner.colors
+
+ self.updateParameter( None, None )
+
+ def attach( self ):
+ return self
+
+ def destroy( self ):
+ if self.selected:
+ self.owner.deselectNotes( { self.note.track: [self] } )
+ else: # if we were deselected above the rect has already been invalidated
+ self.owner.invalidatePreview( self.x, self.y, self.width, self.height, self.note.page, True )
+
+ def updateParameter( self, parameter, value ):
+ self.end = self.note.cs.onset + self.note.cs.duration
+
+ self.updateTransform()
+
+ def getId( self ):
+ return self.note.id
+
+ def getStartTick( self ):
+ return self.note.cs.onset
+
+ def getEndTick( self ):
+ return self.end
+
+ def testOnset( self, start, stop ):
+ return self.note.cs.onset >= start and self.note.cs.onset < stop
+
+ def getPitch( self ):
+ return self.note.cs.pitch
+
+ def updateTransform( self, force = False ):
+ if self.note.page == self.owner.getPage():
+ if not self.firstTransform:
+ oldX = self.x
+ oldY = self.y
+ oldEndX = self.x + self.width
+ dirty = True
+ else:
+ dirty = False
+
+ beats = self.noteDB.getPage( self.note.page ).beats
+ if force or self.note.cs.onset != self.oldOnset or beats != self.oldBeats:
+ self.x = self.owner.ticksToPixels( beats, self.note.cs.onset )
+ self.oldOnset = self.note.cs.onset
+ if force or self.end != self.oldEnd or self.note.cs.onset != self.oldOnset or beats != self.oldBeats:
+ self.width = self.owner.ticksToPixels( beats, self.end ) - self.x
+ self.oldEnd = self.end
+ if force or self.note.cs.pitch != self.oldPitch:
+ self.y = self.owner.pitchToPixels( self.note.cs.pitch )
+ self.oldPitch = self.note.cs.pitch
+ self.oldBeats = beats
+
+ if dirty:
+ if self.firstTransform:
+ self.owner.invalidatePreview( self.x, self.y, self.width, self.height, self.note.page, True )
+ else:
+ x = min( self.x, oldX )
+ y = min( self.y, oldY )
+ endx = max( self.x + self.width, oldEndX )
+ endy = max( self.y, oldY ) + self.height
+ self.owner.invalidatePreview( x, y, endx-x, endy-y, self.note.page, True )
+
+ self.firstTransform = False
+
+ def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound, maxRightBound ):
+ left = leftBound - self.note.cs.onset
+ right = rightBound - self.note.cs.duration - self.note.cs.onset
+ up = Config.MAXIMUM_PITCH - self.note.cs.pitch
+ down = Config.MINIMUM_PITCH - self.note.cs.pitch
+ short = Config.MINIMUM_NOTE_DURATION - self.note.cs.duration
+ long = widthBound - self.note.cs.duration - self.note.cs.onset
+
+ if dragLimits[0][0] < left: dragLimits[0][0] = left
+ if dragLimits[0][1] > right: dragLimits[0][1] = right
+ if dragLimits[1][0] < down: dragLimits[1][0] = down
+ if dragLimits[1][1] > up: dragLimits[1][1] = up
+ if dragLimits[2][0] < short: dragLimits[2][0] = short
+ if dragLimits[2][1] > long: dragLimits[2][1] = long
+
+ # store the current loc as a reference point
+ self.baseOnset = self.note.cs.onset
+ self.basePitch = self.note.cs.pitch
+ self.baseDuration = self.note.cs.duration
+
+ def playSampleNote( self, full=True ):
+ secs_per_tick = 0.025
+ csnd = new_csound_client()
+
+ if full:
+ onset = self.note.cs.onset
+ instrumentId = self.note.cs.instrumentId
+ self.note.cs.onset = 0
+ self.note.cs.instrumentId = self.owner.instrument["id"]
+ csnd.play( self.note.cs, 0.024)
+ self.note.cs.onset = onset
+ self.note.cs.instrumentId = instrumentId
+ else:
+ onset = self.note.cs.onset
+ duration = self.note.cs.duration
+ instrumentId = self.note.cs.instrumentId
+ self.note.cs.onset = 0
+ self.note.cs.duration = 10
+ self.note.cs.instrumentId = self.owner.instrument["id"]
+ csnd.play( self.note.cs, 0.024)
+ self.note.cs.onset = onset
+ self.note.cs.duration = duration
+ self.note.cs.instrumentId = instrumentId
+
+ #=======================================================
+ # Events
+
+ # handleButtonPress returns:
+ # -2, not a hit but there was X overlap
+ # -1, event occurs before us so don't bother checking any later notes
+ # 0, event didn't hit
+ # 1, event was handled
+ def handleButtonPress( self, emitter, event ):
+ eX = event.x - self.x
+ if eX < 0:
+ return -1 # event occurs before us, no point in checking further
+ if eX > self.width:
+ return 0 # no X overlap
+
+ eY = event.y - self.y
+ if eY < 0 or eY > self.height:
+ return -2 # not a hit, but it was in our X range
+
+ if event.button == 3:
+ print "Show some note parameters!?!"
+ #self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters )
+ return 1 # handled
+
+ playSample = False
+
+ if event.type == gtk.gdk._2BUTTON_PRESS: # select bar
+ self.potentialDeselect = False
+ start = 0
+ check = self.note.cs.onset - Config.TICKS_PER_BEAT
+ while start <= check: start += Config.TICKS_PER_BEAT
+ stop = start + Config.TICKS_PER_BEAT
+ check += self.note.cs.duration
+ while stop < check: stop += Config.TICKS_PER_BEAT
+ emitter.selectNotesByBar( self.note.track, start, stop )
+ elif event.type == gtk.gdk._3BUTTON_PRESS: # select track
+ self.potentialDeselect = False
+ emitter.selectNotesByTrack( self.note.track )
+ else:
+ if self.selected: # we already selected, might want to delected
+ self.potentialDeselect = True
+ else:
+ emitter.selectNotes( { self.note.track: [ self ] } )
+ playSample = True
+
+ percent = eX/self.width
+ if percent < 0.3: emitter.setCurrentAction( "note-drag-onset", self )
+ elif percent > 0.7: emitter.setCurrentAction( "note-drag-duration", self )
+ else:
+ emitter.setCurrentAction( "note-drag-pitch", self )
+ if playSample: self.playSampleNote()
+
+ return 1
+
+ def handleButtonRelease( self, emitter, event, buttonPressCount ):
+
+ if self.potentialDeselect:
+ self.potentialDeselect = False
+ emitter.deselectNotes( { self.note.track: [ self ] } )
+
+ emitter.doneCurrentAction()
+
+ return True
+
+ def noteDragOnset( self, do, stream ):
+ self.potentialDeselect = False
+ if do != self.lastDragO:
+ self.lastDragO = do
+ stream += [ self.note.id, self.baseOnset + do ]
+
+ def noteDragPitch( self, dp, stream ):
+ self.potentialDeselect = False
+ if dp != self.lastDragP:
+ self.lastDragP = dp
+ stream += [ self.note.id, self.basePitch + dp ]
+
+ def noteDragDuration( self, dd, stream ):
+ self.potentialDeselect = False
+ if dd != self.lastDragD:
+ self.lastDragD = dd
+ stream += [ self.note.id, self.baseDuration + dd ]
+
+ def doneNoteDrag( self, emitter ):
+ self.baseOnset = self.note.cs.onset
+ self.basePitch = self.note.cs.pitch
+ self.baseDuration = self.note.cs.duration
+
+ self.lastDragO = 0
+ self.lastDragP = 0
+ self.lastDragD = 0
+
+ def noteDecOnset( self, step, leftBound, stream ):
+ if self.selected:
+ if leftBound < self.note.cs.onset:
+ onset = max( self.note.cs.onset+step, leftBound )
+ stream += [ self.note.id, onset ]
+ return onset + self.note.cs.duration
+ return self.end
+
+ def noteIncOnset( self, step, rightBound, stream ):
+ if self.selected:
+ if rightBound > self.end:
+ onset = min( self.end+step, rightBound ) - self.note.cs.duration
+ stream += [ self.note.id, onset ]
+ return onset
+ return self.note.cs.onset
+
+ def noteDecPitch( self, step, stream ):
+ if self.note.cs.pitch > Config.MINIMUM_PITCH:
+ stream += [ self.note.id, max( self.note.cs.pitch+step, Config.MINIMUM_PITCH ) ]
+
+ def noteIncPitch( self, step, stream ):
+ if self.note.cs.pitch < Config.MAXIMUM_PITCH:
+ stream += [ self.note.id, min( self.note.cs.pitch+step, Config.MAXIMUM_PITCH ) ]
+
+ def noteDecDuration( self, step, stream ):
+ if self.note.cs.duration > Config.MINIMUM_NOTE_DURATION:
+ stream += [ self.note.id, max( self.note.cs.duration+step, Config.MINIMUM_NOTE_DURATION ) ]
+
+ def noteIncDuration( self, step, rightBound, stream ):
+ if self.selected:
+ if self.end < rightBound:
+ stream += [ self.note.id, min( self.end+step, rightBound ) - self.note.cs.onset ]
+
+ def noteDecVolume( self, step, stream ):
+ if self.note.cs.amplitude > 0:
+ stream += [ self.note.id, max( self.note.cs.amplitude+step, 0 ) ]
+
+ def noteIncVolume( self, step, stream ):
+ if self.note.cs.amplitude < 1:
+ stream += [ self.note.id, min( self.note.cs.amplitude+step, 1 ) ]
+
+ def handleMarqueeSelect( self, emitter, start, stop ):
+ intersectionY = [ max(start[1],self.y), min(stop[1],self.y+self.height) ]
+ if intersectionY[0] > intersectionY[1]:
+ return False
+
+ intersectionX = [ max(start[0],self.x), min(stop[0],self.x+self.width) ]
+ if intersectionX[0] > intersectionX[1]:
+ return False
+
+ return True
+
+ # updateTooltip returns:
+ # -2, not a hit but there was X overlap
+ # -1, event occurs before us so don't bother checking any later notes
+ # 0, event didn't hit
+ # 1, event was handled
+ def updateTooltip( self, emitter, event ):
+ eX = event.x - self.x
+ if eX < 0:
+ return -1 # event occurs before us, no point in checking further
+ if eX > self.width:
+ return 0 # no X overlap
+
+ eY = event.y - self.y
+ if eY < 0 or eY > self.height:
+ return -2 # not a hit, but it was in our X range
+
+ percent = eX/self.width
+ if percent < 0.3: emitter.setCursor("drag-onset")
+ elif percent > 0.7: emitter.setCursor("drag-duration")
+ else: emitter.setCursor("drag-pitch")
+
+ return 1 # we handled it
+
+ #=======================================================
+ # Selection
+
+ def setSelected( self, state ):
+ if self.selected != state:
+ self.selected = state
+ self.owner.invalidatePreview( self.x, self.y, self.width, self.height, self.note.page )
+ return True # state changed
+ return False # state is the same
+
+ def getSelected( self ):
+ return self.selected
+
+ #=======================================================
+ # Draw
+
+ def draw( self, win, gc, startX, stopX ):
+ if stopX < self.x: return False # we don't need to draw and no one after us will draw
+ if startX > self.x + self.width: return True # we don't need to draw, but maybe a later note does
+
+ # draw fill
+ self.gc.foreground = self.colors["Preview_Note_Fill"]
+ self.gc.set_clip_origin( self.x, self.y-self.owner.sampleNoteHeight )
+ self.owner.previewBuffer.draw_rectangle( self.gc, True, self.x+1, self.y+1, self.width-2, self.owner.sampleNoteHeight-2 )
+ # draw border
+ if self.selected:
+ self.gc.foreground = self.colors["Preview_Note_Selected"]
+ else:
+ self.gc.foreground = self.colors["Preview_Note_Border"]
+ self.gc.set_clip_origin( self.x, self.y )
+ endX = self.x + self.width - 3
+ self.owner.previewBuffer.draw_rectangle( self.gc, True, self.x, self.y, self.width-3, self.owner.sampleNoteHeight )
+ self.gc.set_clip_origin( endX-self.owner.sampleNoteMask.endOffset, self.y )
+ self.owner.previewBuffer.draw_rectangle( self.gc, True, endX, self.y, 3, self.owner.sampleNoteHeight )
+
+ return True # we drew something
+
diff --git a/TamTamJam.activity/Jam/Picker.py b/TamTamJam.activity/Jam/Picker.py
new file mode 100644
index 0000000..b69a73f
--- /dev/null
+++ b/TamTamJam.activity/Jam/Picker.py
@@ -0,0 +1,422 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import os
+
+import random #TEMP
+import sets
+
+import Config
+from gettext import gettext as _
+
+from sugar.graphics.palette import Palette, WidgetInvoker
+
+from Util import ControlStream
+from Util import InstrumentDB
+
+from Jam import Block
+
+class Picker( gtk.HBox ):
+
+ def __init__( self, owner, filter = None ):
+ gtk.HBox.__init__( self )
+
+ self.owner = owner
+
+ # take drawing setup from owner
+ self.gc = owner.gc
+ self.colors = owner.colors
+ self.blockMask = owner.blockMask
+
+ self.filter = filter
+
+ self.desktop = owner.getDesktop()
+
+ self.scrollLeft = gtk.Button( "<" )
+ self.scrollLeft.connect( "clicked", self.doScroll, "left" )
+ self.pack_start( self.scrollLeft, False, False )
+
+ self.scrolledWindow = gtk.ScrolledWindow()
+ self.scrolledWindow.set_policy( gtk.POLICY_ALWAYS, gtk.POLICY_NEVER )
+ self.pack_start( self.scrolledWindow )
+ self.hadjustment = self.scrolledWindow.get_hadjustment()
+ self.hadjustment.connect( "changed", self.scrollChanged )
+ self.hadjustment.connect( "value-changed", self.scrollChanged )
+
+ self.scrollRight = gtk.Button( ">" )
+ self.scrollRight.connect( "clicked", self.doScroll, "right" )
+ self.pack_start( self.scrollRight, False, False )
+
+ self.pickerBox = gtk.HBox()
+ self.scrolledWindow.add_with_viewport( self.pickerBox )
+ self.pickerBox.get_parent().modify_bg( gtk.STATE_NORMAL, self.colors["Picker_Bg"] )
+
+ # spacers
+ self.pickerBox.pack_start( gtk.Label(" "), True, True )
+ self.pickerBox.pack_end( gtk.Label(" "), True, True )
+
+ self.show_all()
+
+ self.scroll = {}
+ self.scroll[filter] = 0
+
+ self.blocks = []
+
+ def addBlock( self, data, name, block = None ):
+ if block == None:
+ block = gtk.Button( name )
+
+ # tooltip
+ block._palette = Palette( name )
+ block._palette.props.invoker = WidgetInvoker(block)
+ block._palette.props.invoker._position_hint = WidgetInvoker.AT_CURSOR
+
+ block.add_events( gtk.gdk.BUTTON_PRESS_MASK
+ | gtk.gdk.BUTTON_RELEASE_MASK
+ | gtk.gdk.ENTER_NOTIFY_MASK
+ | gtk.gdk.LEAVE_NOTIFY_MASK
+ | gtk.gdk.POINTER_MOTION_MASK
+ | gtk.gdk.POINTER_MOTION_HINT_MASK )
+ block.connect( "button-press-event", self.on_button_press )
+ block.connect( "button-release-event", self.on_button_release )
+ block.connect( "motion-notify-event", self.on_motion_notify )
+ block.data = data
+
+ self.blocks.append( block )
+
+ if self._testAgainstFilter( block ):
+ self.pickerBox.pack_start( block, False, False, 3 )
+
+ block.show_all()
+
+ return block
+
+ def getFilter( self ):
+ return filter
+
+ def setFilter( self, filter ):
+ if filter == self.filter:
+ return
+
+ self.scroll[self.filter] = self.hadjustment.get_value()
+
+ self.filter = filter
+
+ for block in self.pickerBox.get_children()[1:-1]: # outside children are spacers
+ self.pickerBox.remove( block )
+
+ for block in self.blocks:
+ if self._testAgainstFilter( block ):
+ self.pickerBox.pack_start( block, False, False, 3 )
+
+ if self.scroll.has_key( filter ):
+ self.hadjustment.set_value( self.scroll[filter] )
+ else:
+ self.hadjustment.set_value( 0 )
+ self.scroll[filter] = 0
+
+ def _testAgainstFilter( self, block ):
+ return True
+
+ def doScroll( self, widget, data ):
+ if data == "left":
+ val = max( self.hadjustment.get_property("lower"), self.hadjustment.get_value() - self.hadjustment.get_property("page_increment") )
+ else:
+ val = min( self.hadjustment.get_property("upper") - self.hadjustment.get_property("page_size"), self.hadjustment.get_value() + self.hadjustment.get_property("page_increment") )
+
+ self.hadjustment.set_value( val )
+
+ def scrollChanged( self, widget ):
+ val = self.hadjustment.get_value()
+ if val == 0:
+ self.scrollLeft.set_sensitive( False )
+ else:
+ self.scrollLeft.set_sensitive( True )
+
+ if val >= self.hadjustment.get_property( "upper" ) - self.hadjustment.get_property("page_size"):
+ self.scrollRight.set_sensitive( False )
+ else:
+ self.scrollRight.set_sensitive( True )
+
+ def on_button_press( self, widget, event ):
+ pass
+
+ def on_button_release( self, widget, event ):
+ self.desktop.on_button_release( widget, event )
+
+ def on_motion_notify( self, widget, event ):
+ self.desktop.on_motion_notify( widget, event )
+
+
+class Instrument( Picker ):
+
+ def __init__( self, owner, filter = ( "All" ) ):
+ Picker.__init__( self, owner, filter )
+
+ self.type = Instrument
+
+ self.instrumentDB = InstrumentDB.getRef()
+
+ for inst in self.instrumentDB.getSet( "All" ):
+ self.addBlock( inst.id )
+
+ def addBlock( self, id ):
+ # match data structure of Block.Instrument
+ data = { "name": _(Config.INSTRUMENTSID[id].name),
+ "id": id }
+
+ win = gtk.gdk.get_default_root_window()
+ width = Block.Instrument.WIDTH
+ height = Block.Instrument.HEIGHT
+ pixmap = gtk.gdk.Pixmap( win, width, height )
+
+ self.gc.set_clip_rectangle( gtk.gdk.Rectangle( 0, 0, width, height ) )
+
+ # draw bg
+ self.gc.foreground = self.colors["Picker_Bg"]
+ pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
+
+ self.gc.set_clip_mask( self.blockMask )
+
+ # draw border
+ self.gc.foreground = self.colors["Border_Inactive"]
+ self.gc.set_clip_origin( -Block.Instrument.MASK_START, 0 )
+ pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
+
+ # draw block
+ inst = self.owner.getInstrumentImage( data["id"] )
+ self.gc.set_clip_origin( -Block.Instrument.MASK_START, -height )
+ pixmap.draw_drawable( self.gc, inst, 0, 0, 0, 0, width, height )
+
+ image = gtk.Image()
+ image.set_from_pixmap( pixmap, None )
+
+ block = gtk.EventBox()
+ block.modify_bg( gtk.STATE_NORMAL, self.colors["Picker_Bg"] )
+ block.add( image )
+
+ Picker.addBlock( self, data, data["name"], block )
+
+ def _testAgainstFilter( self, block ):
+ if "All" in self.filter:
+ return True
+
+ for label in self.instrumentDB.getInstrument( block.data["id"] ).labels:
+ if label in self.filter:
+ return True
+
+ return False
+
+ def on_button_press( self, widget, event ):
+ walloc = widget.get_allocation()
+ salloc = self.scrolledWindow.get_allocation()
+ loc = ( walloc.x + salloc.x + event.x - self.hadjustment.get_value(), -1 )
+
+ block = self.desktop.addBlock( Block.Instrument, widget.data, loc, True )
+ self.desktop.activateInstrument( block )
+
+
+class Drum( Picker ):
+
+ def __init__( self, owner, filter = None ):
+ Picker.__init__( self, owner, filter )
+
+ self.type = Drum
+
+ self.instrumentDB = InstrumentDB.getRef()
+
+ for inst in self.instrumentDB.getSet( "kit" ):
+ self.addBlock( inst.id )
+
+ def addBlock( self, id ):
+ # match data structure of Block.Drum
+ data = { "name": _(Config.INSTRUMENTSID[id].name),
+ "id": id }
+
+ win = gtk.gdk.get_default_root_window()
+ width = Block.Drum.WIDTH
+ height = Block.Drum.HEIGHT
+ pixmap = gtk.gdk.Pixmap( win, width, height )
+
+ self.gc.set_clip_rectangle( gtk.gdk.Rectangle( 0, 0, width, height ) )
+
+ # draw bg
+ self.gc.foreground = self.colors["Picker_Bg"]
+ pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
+
+ self.gc.set_clip_mask( self.blockMask )
+
+ # draw border
+ self.gc.foreground = self.colors["Border_Inactive"]
+ self.gc.set_clip_origin( -Block.Drum.MASK_START, 0 )
+ pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
+
+ # draw block
+ inst = self.owner.getInstrumentImage( data["id"] )
+ self.gc.set_clip_origin( -Block.Drum.MASK_START, -height )
+ pixmap.draw_drawable( self.gc, inst, 0, 0, 0, 0, width, height )
+
+ image = gtk.Image()
+ image.set_from_pixmap( pixmap, None )
+
+ block = gtk.EventBox()
+ block.modify_bg( gtk.STATE_NORMAL, self.colors["Picker_Bg"] )
+ block.add( image )
+
+ Picker.addBlock( self, data, data["name"], block )
+
+ def on_button_press( self, widget, event ):
+ walloc = widget.get_allocation()
+ salloc = self.scrolledWindow.get_allocation()
+ loc = ( walloc.x + salloc.x + event.x - self.hadjustment.get_value(), -1 )
+ self.desktop.addBlock( Block.Drum, widget.data, loc, True )
+
+
+class Loop( Picker ):
+
+ def __init__( self, owner, filter = None ):
+ Picker.__init__( self, owner, filter )
+
+ self.type = Loop
+
+ self.presetLoops = self._scanDirectory( Config.FILES_DIR+"/Loops" )
+
+ def _loadFile( self, fullpath, filename ):
+ if filename[-4:] != ".ttl":
+ if Config.DEBUG >= 3: print "WARNING: incorrect extension on loop file: " + filename
+ return -1
+ try:
+ oldPages = sets.Set( self.owner.noteDB.getTune() )
+
+ ifile = open( fullpath, 'r' )
+ ttt = ControlStream.TamTamTable ( self.owner.noteDB )
+ ttt.parseFile( ifile )
+ ifile.close()
+
+ curPages = sets.Set( self.owner.noteDB.getTune() )
+ newPages = curPages.difference( oldPages )
+
+ if len(newPages) != 1:
+ print "ERROR: bad loop file, contains more than one page (or none)"
+ return -1
+
+ id = newPages.pop() # new pageId
+
+ self.owner.noteDB.getPage( id ).setLocal( False ) # flag as a global page
+
+ self.addBlock( id, filename[:-4] )
+
+ return id
+
+ except OSError,e:
+ print 'ERROR: failed to open file %s for reading\n' % ofilename
+ return -1
+
+ def _scanDirectory( self, path ):
+ dirlist = os.listdir( path )
+ ids = []
+ for fpath in dirlist:
+ id = self._loadFile( path+"/"+fpath, fpath )
+ if id != -1: ids.append(id)
+ return ids
+
+ def addBlock( self, id, name ):
+ # match data structure of Block.Loop
+ data = { "name": _(name),
+ "id": id }
+
+ self.owner.updateLoopImage( data["id"] )
+ loop = self.owner.getLoopImage( data["id"] )
+
+ page = self.owner.noteDB.getPage( id )
+
+ win = gtk.gdk.get_default_root_window()
+ width = Block.Loop.WIDTH[page.beats]
+ height = Block.Loop.HEIGHT
+ pixmap = gtk.gdk.Pixmap( win, width, height )
+
+ self.gc.set_clip_rectangle( gtk.gdk.Rectangle( 0, 0, width, height ) )
+
+ # draw bg
+ self.gc.foreground = self.colors["Picker_Bg"]
+ pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
+
+ self.gc.set_clip_mask( self.blockMask )
+ self.gc.foreground = self.owner.colors["Border_Inactive"]
+
+ #-- draw head -----------------------------------------
+
+ # draw border
+ self.gc.set_clip_origin( -Block.Loop.MASK_START, 0 )
+ pixmap.draw_rectangle( self.gc, True, 0, 0, Block.Loop.HEAD, height )
+
+ # draw block
+ self.gc.set_clip_origin( -Block.Loop.MASK_START, -height )
+ pixmap.draw_drawable( self.gc, loop, 0, 0, 0, 0, Block.Loop.HEAD, height )
+
+ #-- draw beats ----------------------------------------
+
+ beats = page.beats - 1 # last beat is drawn with the tail
+ curx = Block.Loop.HEAD
+ while beats > 3:
+ # draw border
+ self.gc.set_clip_origin( curx-Block.Loop.MASK_BEAT, 0 )
+ pixmap.draw_rectangle( self.gc, True, curx, 0, Block.Loop.BEAT_MUL3, height )
+
+ # draw block
+ self.gc.set_clip_origin( curx-Block.Loop.MASK_BEAT, -height )
+ pixmap.draw_drawable( self.gc, loop, curx, 0, curx, 0, Block.Loop.BEAT_MUL3, height )
+
+ curx += Block.Loop.BEAT_MUL3
+ beats -= 3
+
+ if beats:
+ w = Block.Loop.BEAT*beats
+
+ # draw border
+ self.gc.set_clip_origin( curx-Block.Loop.MASK_BEAT, 0 )
+ pixmap.draw_rectangle( self.gc, True, curx, 0, w, height )
+
+ # draw block
+ self.gc.set_clip_origin( curx-Block.Loop.MASK_BEAT, -height )
+ pixmap.draw_drawable( self.gc, loop, curx, 0, curx, 0, w, height )
+
+ curx += w
+
+ #-- draw tail -----------------------------------------
+
+ # draw border
+ self.gc.set_clip_origin( curx-Block.Loop.MASK_TAIL, 0 )
+ pixmap.draw_rectangle( self.gc, True, curx, 0, Block.Loop.TAIL, height )
+
+ # draw block
+ self.gc.set_clip_origin( curx-Block.Loop.MASK_TAIL, -height )
+ pixmap.draw_drawable( self.gc, loop, curx, 0, curx, 0, Block.Loop.TAIL, height )
+
+ image = gtk.Image()
+ image.set_from_pixmap( pixmap, None )
+
+ block = gtk.EventBox()
+ block.modify_bg( gtk.STATE_NORMAL, self.colors["Picker_Bg"] )
+ block.add( image )
+
+ Picker.addBlock( self, data, data["name"], block )
+
+ def on_button_press( self, widget, event ):
+ walloc = widget.get_allocation()
+ salloc = self.scrolledWindow.get_allocation()
+ loc = ( walloc.x + salloc.x + event.x - self.hadjustment.get_value(), -1 )
+
+ data = {}
+ for key in widget.data.keys():
+ data[key] = widget.data[key]
+
+ newid = self.owner.noteDB.duplicatePages( [ data["id"] ] )[data["id"]]
+ self.owner.updateLoopImage( newid )
+ data["id"] = newid
+
+ block = self.desktop.addBlock( Block.Loop, data, loc, True )
+
+
diff --git a/TamTamJam.activity/Jam/Popup.py b/TamTamJam.activity/Jam/Popup.py
new file mode 100644
index 0000000..3d21e7f
--- /dev/null
+++ b/TamTamJam.activity/Jam/Popup.py
@@ -0,0 +1,1397 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import Config
+
+from gettext import gettext as _
+from sugar.graphics import style
+from sugar.graphics.palette import Palette, Invoker, _palette_observer
+import gobject
+
+import Jam.Block as Block
+from Util.NoteDB import PARAMETER
+from Util.CSoundNote import CSoundNote
+from Util.CSoundClient import new_csound_client
+from Jam.Parasite import LoopParasite
+
+from Generation.Generator import generator1, GenerationParameters
+
+class SELECTNOTES:
+ ALL = -1
+ NONE = 0
+ ADD = 1
+ REMOVE = 2
+ FLIP = 3
+ EXCLUSIVE = 4
+
+class NoneInvoker( Invoker ):
+
+ def __init__( self ):
+ Invoker.__init__( self )
+ self._position_hint = Invoker.AT_CURSOR
+
+ def get_rect( self ):
+ return gtk.gdk.Rectangle( 0, 0, 0, 0 )
+
+ def get_toplevel( self ):
+ return None
+
+class Popup( Palette ):
+
+ def __init__( self, label, owner ):
+ Palette.__init__( self, label )
+
+ self.owner = owner
+
+ self.block = None
+
+ self.props.invoker = NoneInvoker()
+ self.set_group_id( "TamTamPopup" )
+
+ self._set_state( Palette.SECONDARY ) # skip to fully exposed
+
+ self.connect( "key-press-event", self.on_key_press )
+ self.connect( "key-release-event", self.on_key_release )
+
+ self.connect( "focus_out_event", self.closePopup )
+
+ def destroy( self ):
+ pass
+
+ def _leave_notify_event_cb( self, widget, event ):
+ return # don't popdown()
+
+ def _show( self ):
+ Palette._show( self )
+
+ if self._palette_popup_sid != None:
+ _palette_observer.disconnect( self._palette_popup_sid ) # don't hide when other palettes pop
+ self._palette_popup_sid = None
+
+ def popup( self, immediate = False ):
+ self.owner.activity.handler_block(self.owner.activity.focusOutHandler)
+ self.owner.activity.handler_block(self.owner.activity.focusInHandler)
+
+ Palette.popup( self, immediate )
+
+ def popdown( self, immediate = False ):
+ self.block = None
+
+ Palette.popdown( self, immediate )
+
+ self.owner.activity.handler_unblock(self.owner.activity.focusOutHandler)
+ self.owner.activity.handler_unblock(self.owner.activity.focusInHandler)
+
+ def updatePosition( self ):
+ self.props.invoker._cursor_x = -1
+ self.props.invoker._cursor_y = -1
+ self._update_position()
+
+ def closePopup( self, widget, event ):
+ self.popdown( True )
+
+ def on_key_press( self, widget, event ):
+ self.owner.onKeyPress( widget, event )
+
+ def on_key_release( self, widget, event ):
+ self.owner.onKeyRelease( widget, event )
+
+ def setBlock( self, block ):
+ if self.is_up():
+ self.updatePosition()
+ else:
+ self.popup( True )
+
+
+class Instrument( Popup ):
+
+ def __init__( self, label, owner ):
+ Popup.__init__( self, label, owner )
+
+ self.settingBlock = False
+
+ self.GUI = {}
+
+ self.GUI["mainBox"] = gtk.VBox()
+ self.set_content( self.GUI["mainBox"] )
+
+ #-- Volume --------------------------------------------
+ self.GUI["volumeBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["volumeBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["volumeLabel"] = gtk.Label( _("Volume:") )
+ self.GUI["volumeLabel"].set_size_request( 100, -1 )
+ self.GUI["volumeLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["volumeBox"].pack_start( self.GUI["volumeLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["volumeAdjustment"] = gtk.Adjustment( 0.5, 0.0, 1.0, 0.1, 0.1, 0 )
+ self.GUI["volumeAdjustment"].connect( 'value-changed', self.handleVolume )
+ self.GUI["volumeSlider"] = gtk.HScale( adjustment = self.GUI["volumeAdjustment"] )
+ self.GUI["volumeSlider"].set_size_request( 250, -1 )
+ self.GUI["volumeSlider"].set_draw_value( False )
+ self.GUI["volumeBox"].pack_start( self.GUI["volumeSlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["volumeImage"] = gtk.Image()
+ self.GUI["volumeBox"].pack_start( self.GUI["volumeImage"], False, padding = style.DEFAULT_PADDING )
+
+ #-- Pan -----------------------------------------------
+ self.GUI["panBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["panBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["panLabel"] = gtk.Label( _("Pan:") )
+ self.GUI["panLabel"].set_size_request( 100, -1 )
+ self.GUI["panLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["panBox"].pack_start( self.GUI["panLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["panAdjustment"] = gtk.Adjustment( 0.5, 0, 1.0, 0.1, 0.1, 0 )
+ self.GUI["panAdjustment"].connect( 'value-changed', self.handlePan )
+ self.GUI["panSlider"] = gtk.HScale( adjustment = self.GUI["panAdjustment"] )
+ self.GUI["panSlider"].set_size_request( 250, -1 )
+ self.GUI["panSlider"].set_draw_value( False )
+ self.GUI["panBox"].pack_start( self.GUI["panSlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["panImage"] = gtk.Image()
+ self.GUI["panBox"].pack_start( self.GUI["panImage"], False, padding = style.DEFAULT_PADDING )
+
+ #-- Reverb --------------------------------------------
+ self.GUI["reverbBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["reverbBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["reverbLabel"] = gtk.Label( _("Reverb:") )
+ self.GUI["reverbLabel"].set_size_request( 100, -1 )
+ self.GUI["reverbLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["reverbBox"].pack_start( self.GUI["reverbLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["reverbAdjustment"] = gtk.Adjustment( 0.5, 0, 1.0, 0.1, 0.1, 0 )
+ self.GUI["reverbAdjustment"].connect( 'value-changed', self.handleReverb )
+ self.GUI["reverbSlider"] = gtk.HScale( adjustment = self.GUI["reverbAdjustment"] )
+ self.GUI["reverbSlider"].set_size_request( 250, -1 )
+ self.GUI["reverbSlider"].set_draw_value( False )
+ self.GUI["reverbBox"].pack_start( self.GUI["reverbSlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["reverbImage"] = gtk.Image()
+ self.GUI["reverbBox"].pack_start( self.GUI["reverbImage"], False, padding = style.DEFAULT_PADDING )
+
+ if False: # TEMP quote out
+ self.GUI["separator"] = gtk.HSeparator()
+ self.GUI["mainBox"].pack_start( self.GUI["separator"], padding = style.DEFAULT_PADDING )
+
+ #-- Export --------------------------------------------
+ self.GUI["exportBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["exportBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["exportEntry"] = gtk.Entry()
+ self.GUI["exportEntry"].modify_fg( gtk.STATE_NORMAL, self.owner.colors["black"] )
+ self.GUI["exportEntry"].modify_fg( gtk.STATE_ACTIVE, self.owner.colors["black"] )
+ self.GUI["exportBox"].pack_start( self.GUI["exportEntry"], padding = style.DEFAULT_PADDING )
+ self.GUI["exportButton"] = gtk.Button( "Export" )
+ self.GUI["exportBox"].pack_start( self.GUI["exportButton"], False, padding = style.DEFAULT_PADDING )
+
+ self.GUI["mainBox"].show_all()
+
+ def setBlock( self, block ):
+ self.settingBlock = True
+
+ self.block = block
+ self.GUI["volumeAdjustment"].set_value( block.getData( "volume" ) )
+ self.GUI["panAdjustment"].set_value( block.getData( "pan" ) )
+ self.GUI["reverbAdjustment"].set_value( block.getData( "reverb" ) )
+ #self.GUI["exportEntry"].set_text( block.getData( "name" ) )
+
+ self.settingBlock = False
+
+ Popup.setBlock( self, block )
+
+ def handleVolume( self, widget ):
+ if not self.settingBlock:
+ self.block.setData( "volume", widget.get_value() )
+
+ def handlePan( self, widget ):
+ if not self.settingBlock:
+ self.block.setData( "pan", widget.get_value() )
+
+ def handleReverb( self, widget ):
+ if not self.settingBlock:
+ self.block.setData( "reverb", widget.get_value() )
+
+
+class Drum( Popup ):
+
+ def __init__( self, label, owner ):
+ Popup.__init__( self, label, owner )
+
+ self.settingBlock = False
+
+ self.GUI = {}
+
+ self.GUI["mainBox"] = gtk.VBox()
+ self.set_content( self.GUI["mainBox"] )
+
+ #-- Volume --------------------------------------------
+ self.GUI["volumeBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["volumeBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["volumeLabel"] = gtk.Label( _("Volume:") )
+ self.GUI["volumeLabel"].set_size_request( 130, -1 )
+ self.GUI["volumeLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["volumeBox"].pack_start( self.GUI["volumeLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["volumeAdjustment"] = gtk.Adjustment( 0.5, 0.0, 1.0, 0.1, 0.1, 0 )
+ self.GUI["volumeAdjustment"].connect( 'value-changed', self.handleVolume )
+ self.GUI["volumeSlider"] = gtk.HScale( adjustment = self.GUI["volumeAdjustment"] )
+ self.GUI["volumeSlider"].set_size_request( 250, -1 )
+ self.GUI["volumeSlider"].set_draw_value( False )
+ self.GUI["volumeBox"].pack_start( self.GUI["volumeSlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["volumeImage"] = gtk.Image()
+ self.GUI["volumeBox"].pack_start( self.GUI["volumeImage"], False, padding = style.DEFAULT_PADDING )
+
+ #-- Reverb --------------------------------------------
+ self.GUI["reverbBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["reverbBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["reverbLabel"] = gtk.Label( _("Reverb:") )
+ self.GUI["reverbLabel"].set_size_request( 130, -1 )
+ self.GUI["reverbLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["reverbBox"].pack_start( self.GUI["reverbLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["reverbAdjustment"] = gtk.Adjustment( 0.5, 0, 1.0, 0.1, 0.1, 0 )
+ self.GUI["reverbAdjustment"].connect( 'value-changed', self.handleReverb )
+ self.GUI["reverbSlider"] = gtk.HScale( adjustment = self.GUI["reverbAdjustment"] )
+ self.GUI["reverbSlider"].set_size_request( 250, -1 )
+ self.GUI["reverbSlider"].set_draw_value( False )
+ self.GUI["reverbBox"].pack_start( self.GUI["reverbSlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["reverbImage"] = gtk.Image()
+ self.GUI["reverbBox"].pack_start( self.GUI["reverbImage"], False, padding = style.DEFAULT_PADDING )
+
+ self.GUI["generationSeparator"] = gtk.HSeparator()
+ self.GUI["mainBox"].pack_start( self.GUI["generationSeparator"], padding = style.DEFAULT_PADDING )
+
+ #-- Beats ---------------------------------------------
+ self.GUI["beatsBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["beatsBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["beatsLabel"] = gtk.Label( _("Beats:") )
+ self.GUI["beatsLabel"].set_size_request( 130, -1 )
+ self.GUI["beatsLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["beatsBox"].pack_start( self.GUI["beatsLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["beatsAdjustment"] = gtk.Adjustment( 4, 2, Config.MAXIMUM_BEATS, 1, 1, 0 )
+ self.GUI["beatsAdjustment"].connect( 'value-changed', self.handleBeats )
+ self.GUI["beatsSlider"] = gtk.HScale( adjustment = self.GUI["beatsAdjustment"] )
+ self.GUI["beatsSlider"].set_size_request( 250, -1 )
+ self.GUI["beatsSlider"].set_draw_value( False )
+ self.GUI["beatsBox"].pack_start( self.GUI["beatsSlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["beatsImage"] = gtk.Image()
+ self.GUI["beatsBox"].pack_start( self.GUI["beatsImage"], False, padding = style.DEFAULT_PADDING )
+
+ #-- Regularity ----------------------------------------
+ self.GUI["regularityBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["regularityBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["regularityLabel"] = gtk.Label( _("Regularity:") )
+ self.GUI["regularityLabel"].set_size_request( 130, -1 )
+ self.GUI["regularityLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["regularityBox"].pack_start( self.GUI["regularityLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["regularityAdjustment"] = gtk.Adjustment( 0.5, 0.0, 1.0, 0.1, 0.1, 0 )
+ self.GUI["regularityAdjustment"].connect( 'value-changed', self.handleRegularity )
+ self.GUI["regularitySlider"] = gtk.HScale( adjustment = self.GUI["regularityAdjustment"] )
+ self.GUI["regularitySlider"].set_size_request( 250, -1 )
+ self.GUI["regularitySlider"].set_draw_value( False )
+ self.GUI["regularityBox"].pack_start( self.GUI["regularitySlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["regularityImage"] = gtk.Image()
+ self.GUI["regularityBox"].pack_start( self.GUI["regularityImage"], False, padding = style.DEFAULT_PADDING )
+
+ #-- Generate ------------------------------------------
+ self.GUI["generateBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["generateBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["regenerateButton"] = gtk.Button( "Regenerate" )
+ self.GUI["regenerateButton"].connect( "clicked", self.handleRegenerate )
+ self.GUI["generateBox"].pack_start( self.GUI["regenerateButton"], True, False, padding = style.DEFAULT_PADDING )
+ self.GUI["clearButton"] = gtk.Button( "Clear" )
+ self.GUI["clearButton"].connect( "clicked", self.handleClear )
+ self.GUI["generateBox"].pack_start( self.GUI["clearButton"], True, False, padding = style.DEFAULT_PADDING )
+
+ self.GUI["mainBox"].show_all()
+
+ def setBlock( self, block ):
+ self.settingBlock = True
+
+ self.block = block
+ self.GUI["volumeAdjustment"].set_value( block.getData( "volume" ) )
+ self.GUI["reverbAdjustment"].set_value( block.getData( "reverb" ) )
+ self.GUI["beatsAdjustment"].set_value( block.getData( "beats" ) )
+ self.GUI["regularityAdjustment"].set_value( block.getData( "regularity" ) )
+
+ self.settingBlock = False
+
+ Popup.setBlock( self, block )
+
+ def handleVolume( self, widget ):
+ if not self.settingBlock:
+ self.block.setData( "volume", widget.get_value() )
+
+ def handleReverb( self, widget ):
+ if not self.settingBlock:
+ self.block.setData( "reverb", widget.get_value() )
+
+ def handleBeats( self, widget ):
+ # snap to 0 decimal places
+ val = widget.get_value()
+ if round( val ) != val:
+ widget.set_value( round( val ) )
+ return
+
+ if not self.settingBlock:
+ self.block.setData( "beats", int(round( widget.get_value() )) )
+
+ def handleRegularity( self, widget ):
+ if not self.settingBlock:
+ self.block.setData( "regularity", widget.get_value() )
+
+ def handleRegenerate( self, widget ):
+ self.block.regenerate()
+
+ def handleClear( self, widget ):
+ self.block.clear()
+
+class Loop( Popup ):
+
+ def __init__( self, label, owner ):
+ Popup.__init__( self, label, owner )
+
+ self.settingBlock = False
+
+ self.gc = self.owner.gc
+ self.colors = self.owner.colors
+ self.sampleNoteMask = self.owner.sampleNoteMask
+
+ self.noteDB = self.owner.noteDB
+ self.csnd = new_csound_client()
+
+ self.GUI = {}
+
+ self.GUI["mainBox"] = gtk.VBox()
+ self.set_content( self.GUI["mainBox"] )
+
+ #-- Beats ---------------------------------------------
+ self.GUI["beatsBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["beatsBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["beatsLabel"] = gtk.Label( _("Beats:") )
+ self.GUI["beatsLabel"].set_size_request( 130, -1 )
+ self.GUI["beatsLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["beatsBox"].pack_start( self.GUI["beatsLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["beatsAdjustment"] = gtk.Adjustment( 4, 2, Config.MAXIMUM_BEATS, 1, 1, 0 )
+ self.GUI["beatsAdjustment"].connect( 'value-changed', self.handleBeats )
+ self.GUI["beatsSlider"] = gtk.HScale( adjustment = self.GUI["beatsAdjustment"] )
+ self.GUI["beatsSlider"].set_size_request( 250, -1 )
+ self.GUI["beatsSlider"].set_draw_value( False )
+ self.GUI["beatsBox"].pack_start( self.GUI["beatsSlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["beatsImage"] = gtk.Image()
+ self.GUI["beatsBox"].pack_start( self.GUI["beatsImage"], False, padding = style.DEFAULT_PADDING )
+
+ #-- Regularity ----------------------------------------
+ self.GUI["regularityBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["regularityBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["regularityLabel"] = gtk.Label( _("Regularity:") )
+ self.GUI["regularityLabel"].set_size_request( 130, -1 )
+ self.GUI["regularityLabel"].set_alignment( 0.0, 0.5 )
+ self.GUI["regularityBox"].pack_start( self.GUI["regularityLabel"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["regularityAdjustment"] = gtk.Adjustment( 0.5, 0.0, 1.0, 0.1, 0.1, 0 )
+ self.GUI["regularityAdjustment"].connect( 'value-changed', self.handleRegularity )
+ self.GUI["regularitySlider"] = gtk.HScale( adjustment = self.GUI["regularityAdjustment"] )
+ self.GUI["regularitySlider"].set_size_request( 250, -1 )
+ self.GUI["regularitySlider"].set_draw_value( False )
+ self.GUI["regularityBox"].pack_start( self.GUI["regularitySlider"], False, padding = style.DEFAULT_PADDING )
+ self.GUI["regularityImage"] = gtk.Image()
+ self.GUI["regularityBox"].pack_start( self.GUI["regularityImage"], False, padding = style.DEFAULT_PADDING )
+
+ #-- Generate ------------------------------------------
+ self.GUI["generateBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["generateBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["regenerateButton"] = gtk.Button( "Regenerate" )
+ self.GUI["regenerateButton"].connect( "clicked", self.handleRegenerate )
+ self.GUI["generateBox"].pack_start( self.GUI["regenerateButton"], True, False, padding = style.DEFAULT_PADDING )
+ self.GUI["clearButton"] = gtk.Button( "Clear" )
+ self.GUI["clearButton"].connect( "clicked", self.handleClear )
+ self.GUI["generateBox"].pack_start( self.GUI["clearButton"], True, False, padding = style.DEFAULT_PADDING )
+ self.GUI["recordButton"] = gtk.ToggleButton( "Record" )
+ self.GUI["recordButton"].connect( "toggled", self.handleRecord )
+ self.GUI["generateBox"].pack_start( self.GUI["recordButton"], True, False, padding = style.DEFAULT_PADDING )
+
+ #-- Preview -------------------------------------------
+ self.GUI["previewBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["previewBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["previewEventBox"] = gtk.EventBox()
+ self.GUI["previewEventBox"].add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK)
+ self.GUI["previewEventBox"].connect( "button-press-event", self.handlePreviewPress )
+ self.GUI["previewEventBox"].connect( "button-release-event", self.handlePreviewRelease )
+ self.GUI["previewEventBox"].connect( "motion-notify-event", self.handlePreviewMotion )
+ self.GUI["previewEventBox"].connect( "leave-notify-event", self.handlePreviewLeave )
+ self.GUI["previewBox"].pack_start( self.GUI["previewEventBox"], True, padding = style.DEFAULT_PADDING )
+ self.previewDA = self.GUI["previewDA"] = gtk.DrawingArea()
+ self.GUI["previewDA"].connect( "size-allocate", self.handlePreviewAlloc )
+ self.GUI["previewDA"].connect( "expose-event", self.handlePreviewExpose )
+ self.GUI["previewEventBox"].add( self.GUI["previewDA"] )
+
+ self.GUI["mainBox"].show_all()
+
+ self.previewDA.alloced = False
+ self.previewDirty = False
+ self.previewDirtyRect = gtk.gdk.Rectangle( 0, 0, 0, 0 )
+ self.dirtyRectToAdd = gtk.gdk.Rectangle( 0, 0, 0, 0 )
+
+ self.sampleBg = self.owner.sampleBg
+ self.GUI["previewDA"].set_size_request( -1, self.sampleBg.get_size()[1] )
+ self.sampleNoteHeight = self.owner.sampleNoteHeight
+ self.sampleNoteMask = self.owner.sampleNoteMask
+
+ self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.sampleBg.get_size()[1] - self.sampleNoteHeight)
+ self.pixelsPerPitch = float(self.sampleBg.get_size()[1] - self.sampleNoteHeight)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ # Temporary Initialization
+ self.pixelsPerTick = [0] + [ 1 for i in range(1,Config.MAXIMUM_BEATS+1) ]
+ self.ticksPerPixel = [0] + [ 1 for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.cursor = { \
+ "default": None, \
+ "drag-onset": gtk.gdk.Cursor(gtk.gdk.SB_RIGHT_ARROW), \
+ "drag-pitch": gtk.gdk.Cursor(gtk.gdk.BOTTOM_SIDE), \
+ "drag-duration": gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE), \
+ "drag-playhead": gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW), \
+ "pencil": gtk.gdk.Cursor(gtk.gdk.PENCIL), \
+ "paste": gtk.gdk.Cursor(gtk.gdk.CENTER_PTR), \
+ "error": None }
+
+ self.recording = False
+ self.recordLoop = None
+ self.recordingNote = None
+
+ self.owner.noteDB.addListener( self, LoopParasite )
+
+ def destroy( self ):
+ self.owner.noteDB.deleteListener( self )
+
+ Popup.destroy()
+
+ def setBlock( self, block ):
+ self.settingBlock = True
+
+ if self.GUI["recordButton"].get_active():
+ self.GUI["recordButton"].set_active( False )
+
+ self.block = block
+ self.GUI["beatsAdjustment"].set_value( block.getData( "beats" ) )
+ self.GUI["regularityAdjustment"].set_value( block.getData( "regularity" ) )
+
+ root = block.getRoot()
+ if root.type == Block.Instrument:
+ self.instrument = { "id": root.getData( "id" ),
+ "amplitude": root.getData( "volume" ),
+ "pan": root.getData( "pan" ),
+ "reverb": root.getData( "reverb" ) }
+ else:
+ self.instrument = self.owner.getInstrument()
+
+ self.curPage = block.getData("id")
+ self.curBeats = block.getData("beats")
+
+ self.selectedNotes = [ [] for i in range(Config.NUMBER_OF_TRACKS) ]
+
+ self.curAction = False # stores the current mouse action
+ self.curActionObject = False # stores the object that in handling the action
+
+ self.lastDO = self.lastDP = self.lastDrumDP = self.lastDD = None
+
+ self.clickButton = 0 # used in release and motion events to make sure we where actually the widget originally clicked. (hack for popup windows)
+ self.buttonPressCount = 1 # used on release events to indicate double/triple releases
+ self.clickLoc = [0,0] # location of the last click
+ self.marqueeLoc = False # current drag location of the marquee
+ self.marqueeRect = [[0,0],[0,0]]
+
+ self.playheadT = 0
+ self.playheadX = Config.TRACK_SPACING_DIV2
+
+ self.settingBlock = False
+
+ if self.previewDA.alloced:
+ self.invalidatePreview( 0, 0, self.previewDA.width, self.previewDA.height, -1, True )
+
+ Popup.setBlock( self, block )
+
+ def popdown( self, immediate = False ):
+ self.applyNoteSelection( SELECTNOTES.NONE, 0, [], self.curPage )
+
+ if self.GUI["recordButton"].get_active():
+ self.GUI["recordButton"].set_active( False )
+
+ Popup.popdown( self, immediate )
+
+ def getPage( self ):
+ if self.block != None:
+ return self.block.getData("id")
+ else:
+ return -1
+
+ #=======================================================
+ # Handelers
+
+ def handleBeats( self, widget ):
+ # snap to 0 decimal places
+ val = widget.get_value()
+ if round( val ) != val:
+ widget.set_value( round( val ) )
+ return
+
+ if not self.settingBlock:
+ self.curBeats = int(round( widget.get_value() ))
+ self.block.setData( "beats", self.curBeats )
+ for n in self.owner.noteDB.getNotesByTrack( self.getPage(), 0, self ):
+ n.updateTransform( True )
+ self.invalidatePreview( 0, 0, self.previewDA.width, self.previewDA.height )
+
+ if self.recordLoop:
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], self.recordLoop, force = True )
+
+ def handleRegularity( self, widget ):
+ if not self.settingBlock:
+ self.block.setData( "regularity", widget.get_value() )
+
+ def handleRegenerate( self, widget ):
+ parameters = GenerationParameters(
+ rythmRegularity = self.block.getData( "regularity" ),
+ pitchRegularity = self.block.getData( "regularity" ) )
+
+ self.owner._generateTrack( self.instrument["id"], self.curPage, 0, parameters, generator1 )
+
+ self.block.updateLoop()
+
+ if self.recordLoop:
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], self.recordLoop, force = True )
+
+ def handleClear( self, widget ):
+ self.block.clear()
+
+ if self.recordLoop:
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], self.recordLoop, force = True )
+
+ def handleRecord( self, widget ):
+ if widget.get_active():
+ self.startRecording()
+ else:
+ self.stopRecording()
+
+ def handlePreviewPress( self, widget, event ):
+ if event.button != 1:
+ return
+
+ self.clickButton = event.button
+
+ if event.type == gtk.gdk._2BUTTON_PRESS: self.buttonPressCount = 2
+ elif event.type == gtk.gdk._3BUTTON_PRESS: self.buttonPressCount = 3
+ else: self.buttonPressCount = 1
+
+ self.clickLoc = [ int(event.x), int(event.y) ]
+
+ page = self.block.getData("id")
+ beats = self.block.getData("beats")
+
+ notes = self.noteDB.getNotesByTrack( page, 0, self )
+ last = len(notes)-1
+ handled = 0
+ for n in range(last+1):
+ handled = notes[n].handleButtonPress( self, event )
+ if handled == 0:
+ continue
+ elif handled == 1:
+ if not self.curAction: self.curAction = True # it was handled but no action was declared, set curAction to True anyway
+ return
+ else: # all other options mean we can stop looking
+ break
+
+ if not handled or handled == -1: # event didn't overlap any notes, so we can draw
+ pitch = min( self.pixelsToPitchFloor( self.clickLoc[1] - self.previewDA.height + self.sampleNoteHeight//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ onset = self.pixelsToTicksFloor( beats, self.clickLoc[0] )
+ cs = CSoundNote( onset,
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ 0,
+ instrumentId = self.instrument["id"] )
+ cs.pageId = page
+ id = self.noteDB.addNote( -1, page, 0, cs )
+ n = self.noteDB.getNote( page, 0, id, self )
+ self.selectNotes( { 0:[n] }, True )
+ n.playSampleNote( False )
+
+ noteS = self.noteDB.getNotesByTrack( page, 0 )
+ for note in noteS:
+ if note.cs.onset < onset and (note.cs.onset + note.cs.duration) > onset:
+ self.noteDB.updateNote(self.curPage, 0, note.id, PARAMETER.DURATION, onset - note.cs.onset)
+
+ self.updateDragLimits()
+ self.clickLoc[0] += self.ticksToPixels( beats, 1 )
+ self.setCurrentAction( "note-drag-duration", n )
+ self.setCursor("drag-duration")
+
+ def handlePreviewRelease( self, widget, event ):
+ if not self.clickButton: return # we recieved this event but were never clicked! (probably a popup window was open)
+ self.clickButton = 0
+
+ if event.button != 1:
+ return
+
+ if not self.curAction:
+ self.applyNoteSelection( SELECTNOTES.NONE, 0, [], self.curPage )
+ return
+
+ if not self.curActionObject: # there was no real action to carry out
+ self.curAction = False
+ return
+
+ if self.curActionObject != self:
+ self.curActionObject.handleButtonRelease( self, event, self.buttonPressCount )
+ self.updateTooltip( event )
+ else:
+ # we're doing the action ourselves
+ if self.curAction == "marquee": self.doneMarquee( event )
+ self.updateTooltip( event )
+
+ def handlePreviewMotion( self, widget, event ):
+ if event.is_hint:
+ x, y, state = self.previewDA.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ if not self.clickButton: # we recieved this event but were never clicked! (probably a popup window was open)
+ self.updateTooltip( event )
+ return
+
+ if event.state & gtk.gdk.BUTTON1_MASK:
+ if not self.curAction: # no action is in progress yet we're dragging, start a marquee
+ self.setCurrentAction( "marquee", self )
+
+ if self.curAction == "note-drag-onset":
+ self.noteDragOnset( event )
+
+ elif self.curAction == "note-drag-duration":
+ self.noteDragDuration( event )
+
+ elif self.curAction == "note-drag-pitch":
+ self.noteDragPitch( event )
+
+ #elif self.curAction == "note-drag-pitch-drum":
+ # self.noteDragPitch( event, True )
+
+ elif self.curAction == "marquee":
+ self.updateMarquee( event )
+ else:
+ self.updateTooltip( event )
+
+ def handlePreviewLeave( self, widget, event ):
+ self.setCursor("default")
+
+ def handlePreviewAlloc( self, widget, allocation ):
+ self.previewDA.alloced = True
+ win = gtk.gdk.get_default_root_window()
+ self.previewDA.width = allocation.width
+ self.previewDA.height = allocation.height
+ self.previewBuffer = gtk.gdk.Pixmap( win, allocation.width, allocation.height )
+ self.clearClipMask = gtk.gdk.Rectangle( 0, 0, allocation.width, allocation.height )
+
+ self.pixelsPerTick = [0] + [ self.previewDA.width/float(i*Config.TICKS_PER_BEAT) for i in range(1,Config.MAXIMUM_BEATS+1) ]
+ self.ticksPerPixel = [0] + [ 1.0/self.pixelsPerTick[i] for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.beatSpacing = [[0]]
+ for i in range(1,Config.MAXIMUM_BEATS+1):
+ self.beatSpacing.append( [ self.ticksToPixels( i, Config.TICKS_PER_BEAT*j ) for j in range(i) ] )
+
+ for n in self.owner.noteDB.getNotes( self ):
+ n.updateTransform( True )
+
+ self.invalidatePreview( 0, 0, allocation.width, allocation.height, -1, True )
+
+ def on_key_press( self, widget, event ):
+ keyval = event.keyval
+
+ # backspace and del keys
+ if keyval == gtk.keysyms.Delete or keyval == gtk.keysyms.BackSpace:
+ if len( self.selectedNotes[0] ):
+ self.owner.noteDB.deleteNotes(
+ [ self.curPage, 0, len( self.selectedNotes[0] ) ]
+ + [ n.note.id for n in self.selectedNotes[0] ]
+ + [ -1 ] )
+ self.block.updateLoop()
+ else:
+ self.owner.onKeyPress( widget, event )
+
+ #=======================================================
+ # Drawing
+
+ def previewDraw( self ):
+ startX = self.previewDirtyRect.x
+ startY = self.previewDirtyRect.y
+ stopX = self.previewDirtyRect.x + self.previewDirtyRect.width
+ stopY = self.previewDirtyRect.y + self.previewDirtyRect.height
+
+ page = self.block.getData("id")
+ beats = self.owner.noteDB.getPage(page).beats
+
+ self.gc.set_clip_rectangle( self.previewDirtyRect )
+
+ # draw background
+ self.previewBuffer.draw_drawable( self.gc, self.sampleBg, 0, 0, 0, 0, self.previewDA.width-5, self.previewDA.height )
+ self.previewBuffer.draw_drawable( self.gc, self.sampleBg, self.sampleBg.endOffset, 0, self.previewDA.width-5, 0, 5, self.previewDA.height )
+
+ # draw beat lines
+ self.gc.set_line_attributes( Config.BEAT_LINE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.colors["Beat_Line"]
+ for i in range(1,beats):
+ x = self.beatSpacing[beats][i]
+ self.previewBuffer.draw_line( self.gc, x, 1, x, self.previewDA.height-1 )
+
+ # draw notes
+ self.gc.set_clip_mask( self.sampleNoteMask )
+ notes = self.owner.noteDB.getNotesByTrack( page, 0, self )
+ for n in notes:
+ if not n.draw( self.previewBuffer, self.gc, startX, stopX ): break
+
+ self.previewDirty = False
+
+ def handlePreviewExpose( self, widget, event ):
+ if self.previewDirty:
+ self.previewDraw()
+
+ self.gc.set_clip_rectangle( event.area )
+
+ # draw base
+ widget.window.draw_drawable( self.gc, self.previewBuffer, event.area.x, event.area.y, event.area.x, event.area.y, event.area.width, event.area.height )
+
+ if self.marqueeLoc: # draw the selection rect
+ self.gc.set_line_attributes( Config.MARQUEE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.colors["Preview_Note_Selected"]
+ widget.window.draw_rectangle( self.gc, False, self.marqueeRect[0][0], self.marqueeRect[0][1], self.marqueeRect[1][0], self.marqueeRect[1][1] )
+
+ if self.recording: # draw playhead
+ self.gc.set_line_attributes( Config.PLAYHEAD_SIZE, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.colors["black"]
+ widget.window.draw_line( self.gc, self.playheadX, event.area.y, self.playheadX, event.area.y + event.area.height )
+
+ def invalidatePreview( self, x, y, width, height, page = -1, base = True ):
+ if page != -1 and page != self.getPage():
+ return
+
+ self.dirtyRectToAdd.x = x
+ self.dirtyRectToAdd.y = y
+ self.dirtyRectToAdd.width = width
+ self.dirtyRectToAdd.height = height
+
+ if base: # the base image has been dirtied
+ if not self.previewDirty:
+ self.previewDirtyRect.x = x
+ self.previewDirtyRect.y = y
+ self.previewDirtyRect.width = width
+ self.previewDirtyRect.height = height
+ else:
+ self.previewDirtyRect = self.previewDirtyRect.union( self.dirtyRectToAdd )
+ self.previewDirty = True
+
+ if self.previewDA.window != None:
+ self.previewDA.window.invalidate_rect( self.dirtyRectToAdd, True )
+
+ #=======================================================
+ # Recording
+
+ def startRecording( self ):
+ if self.recording:
+ return
+
+ self.owner.setPaused( True )
+ self.owner.pushInstrument( self.instrument )
+ self.owner.setKeyboardListener( self )
+
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], force = True )
+ self.updatePlayhead()
+ self.recordTimeout = gobject.timeout_add( 20, self._record_timeout )
+ self.recording = True
+
+ def stopRecording( self ):
+ if not self.recording:
+ return
+
+ self.owner.setPaused( False )
+ self.owner.popInstrument()
+ self.owner.setKeyboardListener( None )
+
+ gobject.source_remove( self.recordTimeout )
+ self.recording = False
+
+ if self.recordingNote:
+ self.finishNote()
+
+ self.owner._stopLoop( self.recordLoop )
+ self.recordLoop = None
+ self.clearPlayhead()
+
+ def recordNote( self, pitch ):
+ onset = self.csnd.loopGetTick( self.recordLoop )
+ #onset = Config.DEFAULT_GRID * int(onset / Config.DEFAULT_GRID + 0.5)
+
+ cs = CSoundNote( onset,
+ pitch,
+ 0.75,
+ 0.5,
+ Config.DEFAULT_GRID,
+ 0,
+ instrumentId = self.instrument["id"] )
+ cs.pageId = self.curPage
+
+ for n in self.noteDB.getNotesByTrack( self.curPage, 0 ):
+ if onset < n.cs.onset:
+ break
+ if onset >= n.cs.onset + n.cs.duration:
+ continue
+ if onset < n.cs.onset + n.cs.duration - 2:
+ self.noteDB.deleteNote( n.page, n.track, n.id )
+ elif onset - n.cs.onset < 1:
+ self.noteDB.deleteNote( n.page, n.track, n.id )
+ else:
+ self.noteDB.updateNote( n.page, n.track, n.id, PARAMETER.DURATION, onset - n.cs.onset )
+ break
+
+ self.recordingNote = self.noteDB.addNote( -1, self.curPage, 0, cs )
+
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], self.recordLoop, force = True )
+
+ def finishNote( self ):
+ self.recordingNote = None
+
+ self.block.updateLoop()
+
+ def _updateNote( self ):
+ tick = self.csnd.loopGetTick( self.recordLoop )
+ #tick = Config.DEFAULT_GRID * int(tick / Config.DEFAULT_GRID + 0.5)
+
+ note = self.noteDB.getNote( self.curPage, 0, self.recordingNote )
+
+ if tick < note.cs.onset:
+ tick = self.noteDB.getPage( self.curPage ).ticks
+ self.noteDB.updateNote( note.page, note.track, note.id, PARAMETER.DURATION, tick - note.cs.onset )
+ for n in self.noteDB.getNotesByTrack( self.curPage, 0 ):
+ if n.cs.onset <= note.cs.onset:
+ continue
+ if n.cs.onset > note.cs.onset and n.cs.onset < note.cs.onset + note.cs.duration:
+ self.noteDB.deleteNote( n.page, n.track, n.id )
+ else:
+ break
+ self.finishNote()
+ elif tick > note.cs.onset + note.cs.duration:
+ self.noteDB.updateNote( note.page, note.track, note.id, PARAMETER.DURATION, tick - note.cs.onset )
+ for n in self.noteDB.getNotesByTrack( self.curPage, 0 ):
+ if n.cs.onset <= note.cs.onset:
+ continue
+ if n.cs.onset > note.cs.onset and n.cs.onset < note.cs.onset + note.cs.duration:
+ self.noteDB.deleteNote( n.page, n.track, n.id )
+ else:
+ break
+
+ def _record_timeout( self ):
+ self.updatePlayhead()
+ if self.recordingNote:
+ self._updateNote()
+ return True
+
+ def updatePlayhead( self ):
+ ticks = self.csnd.loopGetTick( self.recordLoop )
+ if self.playheadT != ticks:
+ self.invalidatePreview( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.previewDA.height, self.curPage, False )
+ self.playheadX = self.ticksToPixels( self.curBeats, ticks )
+ self.invalidatePreview( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.previewDA.height, self.curPage, False )
+ self.playheadT = ticks
+
+ return True
+
+ def clearPlayhead( self ):
+ self.invalidatePreview( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.previewDA.height, self.curPage, False )
+
+ #=======================================================
+ # Actions
+
+ def setCurrentAction( self, action, obj = None ):
+ if self.curAction:
+ self.doneCurrentAction()
+
+ self.curAction = action
+ self.curActionObject = obj
+
+ if action == "note-drag-onset": self.updateDragLimits()
+ elif action == "note-drag-duration": self.updateDragLimits()
+ elif action == "note-drag-pitch": self.updateDragLimits()
+ #elif action == "note-drag-pitch-drum": self.updateDragLimits()
+
+ def doneCurrentAction( self ):
+ if not self.curAction: return
+ action = self.curAction
+ self.curAction = False
+
+ if action == "note-drag-onset": self.doneNoteDrag( action )
+ elif action == "note-drag-duration": self.doneNoteDrag( action )
+ elif action == "note-drag-pitch": self.doneNoteDrag( action )
+ #elif action == "note-drag-pitch-drum": self.doneNoteDrag( action )
+
+ def selectionChanged( self ):
+ if self.curAction == "note-drag-onset": self.updateDragLimits()
+ elif self.curAction == "note-drag-duration": self.updateDragLimits()
+ elif self.curAction == "note-drag-pitch": self.updateDragLimits()
+ #elif self.curAction == "note-drag-pitch-drum": self.updateDragLimits()
+
+ def applyNoteSelection( self, mode, trackN, which, page = -1 ):
+ if page == -1: page = self.curPage
+ if mode == SELECTNOTES.ALL:
+ track = self.noteDB.getNotesByTrack( page, trackN, self )
+ map( lambda note:note.setSelected( True ), track )
+ self.selectedNotes[trackN] = []
+ map( lambda note:self.selectedNotes[trackN].append(note), track )
+ elif mode == SELECTNOTES.NONE:
+ track = self.selectedNotes[trackN] #self.noteDB.getNotesByTrack( page, trackN, self )
+ map( lambda note:note.setSelected( False ), track )
+ self.selectedNotes[trackN] = []
+ elif mode == SELECTNOTES.ADD:
+ for note in which:
+ if note.setSelected( True ):
+ self.selectedNotes[trackN].append( note )
+ elif mode == SELECTNOTES.REMOVE:
+ for note in which:
+ if note.setSelected( False ):
+ self.selectedNotes[trackN].remove( note )
+ elif mode == SELECTNOTES.FLIP:
+ for note in which:
+ if note.getSelected():
+ note.setSelected( False )
+ self.selectedNotes[trackN].remove( note )
+ else:
+ note.setSelected( True )
+ self.selectedNotes[trackN].append( note )
+ elif mode == SELECTNOTES.EXCLUSIVE:
+ notes = self.noteDB.getNotesByTrack( page, trackN, self )
+ for n in range(len(notes)):
+ if notes[n] in which:
+ if notes[n].setSelected( True ):
+ self.selectedNotes[trackN].append( notes[n] )
+ else:
+ if notes[n].setSelected( False ):
+ self.selectedNotes[trackN].remove( notes[n] )
+
+ def selectNotesByBar( self, trackN, start, stop, page = -1 ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == trackN:
+ notes = []
+ track = self.noteDB.getNotesByTrack( self.curPage, trackN, self )
+ for n in range(len(track)):
+ if track[n].testOnset( start, stop ): notes.append(track[n])
+ if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, trackN, notes, page )
+ else: self.applyNoteSelection( SELECTNOTES.ADD, trackN, notes, page )
+ else:
+ if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def selectNotesByTrack( self, trackN, page = -1 ):
+ if Config.ModKeys.ctrlDown:
+ self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page )
+ else:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == trackN: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page )
+ else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def selectNotes( self, noteDic, ignoreCtrl = False, page = -1 ):
+ if Config.ModKeys.ctrlDown and not ignoreCtrl:
+ for i in noteDic:
+ self.applyNoteSelection( SELECTNOTES.FLIP, i, noteDic[i], page )
+ else:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i in noteDic: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, i, noteDic[i], page )
+ else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def deselectNotes( self, noteDic, page = -1 ):
+ for i in noteDic:
+ self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i], page )
+ self.selectionChanged()
+
+ def clearSelectedNotes( self, page = -1 ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def updateDragLimits( self ):
+ self.dragLimits = [ [-9999,9999], [-9999,9999], [-9999,9999] ] # initialize to big numbers!
+ maxRightBound = self.noteDB.getPage(self.curPage).ticks
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ leftBound = 0
+ skip = True # skip the first note
+ for n in range(len(track)):
+ if skip:
+ skip = False
+ thisNote = track[n]
+ continue
+ nextNote = track[n]
+ if not thisNote.getSelected():
+ leftBound = thisNote.getEndTick()
+ else:
+ if not nextNote.getSelected():
+ rightBound = min( nextNote.getStartTick(), maxRightBound )
+ widthBound = rightBound
+ else:
+ rightBound = maxRightBound
+ widthBound = min( nextNote.getStartTick(), maxRightBound )
+ thisNote.updateDragLimits( self.dragLimits, leftBound, rightBound, widthBound, maxRightBound )
+ thisNote = nextNote
+ # do the last note
+ if thisNote.getSelected():
+ thisNote.updateDragLimits( self.dragLimits, leftBound, maxRightBound, maxRightBound, maxRightBound )
+
+ def noteDragOnset( self, event ):
+ do = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] )
+ do = min( self.dragLimits[0][1], max( self.dragLimits[0][0], do ) )
+
+ if do != self.lastDO:
+ self.lastDO = do
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragOnset( do, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.ONSET, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteDragDuration( self, event ):
+ dd = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] )
+ dd = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dd ) )
+
+ if dd != self.lastDD:
+ self.lastDD = dd
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragDuration( dd, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.DURATION, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteDragPitch( self, event, drum = False ):
+ if not drum: dp = self.pixelsToPitch( event.y - self.clickLoc[1] )
+ else: dp = self.pixelsToPitchDrum( event.y - self.clickLoc[1] )
+ dp = min( self.dragLimits[1][1], max( self.dragLimits[1][0], dp ) )
+
+ if dp != self.lastDP:
+ self.lastDP = dp
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragPitch( dp, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.PITCH, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ self.curActionObject.playSampleNote( True )
+
+ def doneNoteDrag( self, action ):
+ # if action == "note-drag-pitch" or action == "note-drag-pitch-drum":
+ # self.curActionObject.playSampleNote()
+
+ self.lastDO = self.lastDP = self.lastDrumDP = self.lastDD = None
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ for note in self.selectedNotes[i]:
+ note.doneNoteDrag( self )
+
+ self.block.updateLoop()
+
+ def noteStepOnset( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ if step < 0: # moving to the left, iterate forwards
+ leftBound = 0
+ for n in range(len(track)):
+ leftBound = track[n].noteDecOnset( step, leftBound, tstream )
+ else: # moving to the right, iterate backwards
+ rightBound = self.noteDB.getPage(self.curPage).ticks
+ for n in range(len(track)-1, -1, -1 ):
+ rightBound = track[n].noteIncOnset( step, rightBound, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.ONSET, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepPitch( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecPitch( step, tstream )
+ else:
+ for n in self.selectedNotes[i]:
+ n.noteIncPitch( step, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.PITCH, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepDuration( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecDuration( step, tstream )
+ else:
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ for j in range(len(track)-1):
+ track[j].noteIncDuration( step, track[j+1].getStartTick(), tstream )
+ track[len(track)-1].noteIncDuration( step, self.noteDB.getPage(self.curPage).ticks, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.DURATION, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepVolume( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecVolume( step, tstream )
+ else:
+ for n in self.selectedNotes[i]:
+ n.noteIncVolume( step, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.AMPLITUDE, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def updateMarquee( self, event ):
+ if self.marqueeLoc:
+ oldX = self.marqueeRect[0][0]
+ oldEndX = self.marqueeRect[0][0] + self.marqueeRect[1][0]
+ oldY = self.marqueeRect[0][1]
+ oldEndY = self.marqueeRect[0][1] + self.marqueeRect[1][1]
+ else:
+ oldX = oldEndX = self.clickLoc[0]
+ oldY = oldEndY = self.clickLoc[1]
+
+ self.marqueeLoc = [ int(event.x), int(event.y) ]
+ if self.marqueeLoc[0] < 0: self.marqueeLoc[0] = 0
+ elif self.marqueeLoc[0] > self.previewDA.width: self.marqueeLoc[0] = self.previewDA.width
+ if self.marqueeLoc[1] < 0: self.marqueeLoc[1] = 0
+ elif self.marqueeLoc[1] > self.previewDA.height: self.marqueeLoc[1] = self.previewDA.height
+
+ if self.marqueeLoc[0] > self.clickLoc[0]:
+ self.marqueeRect[0][0] = self.clickLoc[0]
+ self.marqueeRect[1][0] = self.marqueeLoc[0] - self.clickLoc[0]
+ else:
+ self.marqueeRect[0][0] = self.marqueeLoc[0]
+ self.marqueeRect[1][0] = self.clickLoc[0] - self.marqueeLoc[0]
+ if self.marqueeLoc[1] > self.clickLoc[1]:
+ self.marqueeRect[0][1] = self.clickLoc[1]
+ self.marqueeRect[1][1] = self.marqueeLoc[1] - self.clickLoc[1]
+ else:
+ self.marqueeRect[0][1] = self.marqueeLoc[1]
+ self.marqueeRect[1][1] = self.clickLoc[1] - self.marqueeLoc[1]
+
+ x = min( self.marqueeRect[0][0], oldX )
+ width = max( self.marqueeRect[0][0] + self.marqueeRect[1][0], oldEndX ) - x
+ y = min( self.marqueeRect[0][1], oldY )
+ height = max( self.marqueeRect[0][1] + self.marqueeRect[1][1], oldEndY ) - y
+ self.invalidatePreview( x-1, y-1, width+2, height+2, self.curPage, False )
+
+ def doneMarquee( self, event ):
+ if self.marqueeLoc:
+ stop = [ self.marqueeRect[0][0] + self.marqueeRect[1][0], self.marqueeRect[0][1] + self.marqueeRect[1][1] ]
+
+ select = {}
+
+ intersectionY = [ self.marqueeRect[0][1], stop[1] ]
+
+ notes = []
+ track = self.noteDB.getNotesByTrack( self.getPage(), 0, self )
+ for n in range(len(track)):
+ hit = track[n].handleMarqueeSelect( self,
+ [ self.marqueeRect[0][0], intersectionY[0] ], \
+ [ stop[0], intersectionY[1] ] )
+ if hit: notes.append(track[n])
+
+ if len(notes): select[0] = notes
+
+ self.selectNotes( select )
+
+ self.marqueeLoc = False
+ self.doneCurrentAction()
+
+ self.invalidatePreview( self.marqueeRect[0][0]-1, self.marqueeRect[0][1]-1, self.marqueeRect[1][0]+2, self.marqueeRect[1][1]+2, self.getPage(), False )
+
+ def updateTooltip( self, event ):
+
+ notes = self.noteDB.getNotesByTrack( self.getPage(), 0, self )
+ handled = 0
+ for n in range(len(notes)):
+ handled = notes[n].updateTooltip( self, event )
+ if handled == 0: continue
+ elif handled == 1: return # event was handled
+ else: break
+
+ if handled == -2: # event X overlapped with a note
+ self.setCursor("default")
+ return
+
+ self.setCursor("pencil")
+
+ def setCursor( self, cursor ):
+ self.window.set_cursor(self.cursor[cursor])
+
+ def ticksToPixels( self, beats, ticks ):
+ return int(round( ticks * self.pixelsPerTick[beats] ))
+ def pixelsToTicks( self, beats, pixels ):
+ return int(round( pixels * self.ticksPerPixel[beats] ))
+ def pitchToPixels( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+ def ticksToPixelsFloor( self, beats, ticks ):
+ return int( ticks * self.pixelsPerTick[beats] )
+ def pixelsToTicksFloor( self, beats, pixels ):
+ return int( pixels * self.ticksPerPixel[beats] )
+ def pixelsToPitch( self, pixels ):
+ return int(round(-pixels*self.pitchPerPixel))
+ def pitchToPixelsFloor( self, pitch ):
+ return int(( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch )
+ def pixelsToPitchFloor( self, pixels ):
+ return int(-pixels*self.pitchPerPixel)
+
+
+class Shortcut( Popup ):
+
+ def __init__( self, label, owner ):
+ Popup.__init__( self, label, owner )
+
+ self.gc = self.owner.gc
+
+ self.GUI = {}
+
+ self.GUI["mainBox"] = gtk.VBox()
+ self.set_content( self.GUI["mainBox"] )
+
+ #-- Keys ----------------------------------------------
+ # match keycodes from JamMain.valid_shortcuts
+ layout = [ [ 0.0, [ 18, 19, 20, 21 ] ],
+ [ 0.3, [ 32, 33, 34, 35 ] ],
+ [ 1.7, [ 47, 48, 51 ] ],
+ [ 1.1, [ 60, 61 ] ] ]
+
+ self.GUI["keyBox"] = gtk.VBox()
+ self.GUI["mainBox"].pack_start( self.GUI["keyBox"], padding = style.DEFAULT_PADDING - 2 )
+
+ for row in layout:
+ offset = row[0]
+ hbox = gtk.HBox()
+ self.GUI["keyBox"].pack_start( hbox, padding = 2 )
+ separator = gtk.Label("")
+ separator.set_size_request( int(Block.Block.KEYSIZE*row[0]) + style.DEFAULT_PADDING, -1 )
+ hbox.pack_start( separator, False )
+ separator = gtk.Label("")
+ separator.set_size_request( style.DEFAULT_PADDING, -1 )
+ hbox.pack_end( separator, False )
+ for key in row[1]:
+ self.GUI[key] = gtk.ToggleButton()
+ self.GUI[key].connect( "expose-event", self.keyExpose )
+ self.GUI[key].connect( "toggled", self.keyToggled )
+ self.GUI[key].set_size_request( Block.Block.KEYSIZE, Block.Block.KEYSIZE )
+ self.GUI[key].key = key
+ self.GUI[key].image = [ self.owner.getKeyImage( key, False ),
+ self.owner.getKeyImage( key, True ) ]
+ hbox.pack_start( self.GUI[key], False, padding = 2 )
+
+ #-- None ----------------------------------------------
+ self.GUI["noneBox"] = gtk.HBox()
+ self.GUI["mainBox"].pack_start( self.GUI["noneBox"], padding = style.DEFAULT_PADDING )
+ self.GUI["noneButton"] = gtk.Button( _("None") )
+ self.GUI["noneButton"].connect( "clicked", self.handleNone )
+ self.GUI["noneBox"].pack_start( self.GUI["noneButton"], True, False, padding = style.DEFAULT_PADDING )
+
+ self.GUI["mainBox"].show_all()
+
+ self.key = None
+
+ def setBlock( self, block ):
+ self.ignoreToggle = True
+
+ self.block = block
+ self.key = self.block.getData( "key" )
+
+ if self.key != None:
+ self.GUI[self.key].set_active( True )
+
+ self.ignoreToggle = False
+
+ Popup.setBlock( self, block )
+
+ def on_key_press( self, widget, event ):
+ key = event.hardware_keycode
+ if key in self.owner.valid_shortcuts.keys():
+ self.block.setData( "key", key )
+ if self.key != None: # clear old key
+ self.ignoreToggle = True
+ self.GUI[self.key].set_active( False )
+ self.key = None
+ self.ignoreToggle = False
+ self.popdown( True )
+ else:
+ self.owner.onKeyPress( widget, event )
+
+ def keyExpose( self, widget, event ):
+ self.gc.set_clip_mask( self.owner.blockMask )
+ self.gc.set_clip_origin( event.area.x - Block.Block.KEYMASK_START, event.area.y )
+ widget.window.draw_drawable( self.gc, widget.image[widget.get_active()], 0, 0, event.area.x, event.area.y, event.area.width, event.area.height )
+ return True
+
+ def keyToggled( self, widget ):
+ if self.ignoreToggle:
+ return
+
+ if widget.get_active():
+ self.block.setData( "key", widget.key )
+
+ self.ignoreToggle = True
+
+ if self.key != None: # clear old key
+ self.GUI[self.key].set_active( False )
+ self.key = None
+
+ widget.set_active( False )
+
+ self.ignoreToggle = False
+
+ self.popdown( True )
+
+ def handleNone( self, widget ):
+ if self.key != None:
+ self.ignoreToggle = True
+ self.GUI[self.key].set_active( False )
+ self.key = None
+ self.ignoreToggle = False
+
+ self.block.setData( "key", None )
+
+ self.popdown( True )
diff --git a/TamTamJam.activity/Jam/RythmGenerator.py b/TamTamJam.activity/Jam/RythmGenerator.py
new file mode 100644
index 0000000..4740160
--- /dev/null
+++ b/TamTamJam.activity/Jam/RythmGenerator.py
@@ -0,0 +1,77 @@
+import random
+
+import Config
+from Util.CSoundNote import CSoundNote
+from Generation.GenerationConstants import GenerationConstants
+from GenRythm import GenRythm
+
+def generator( instrument, nbeats, density, regularity, reverbSend ):
+
+ makeRythm = GenRythm()
+
+ noteDuration = GenerationConstants.DOUBLE_TICK_DUR / 2
+ trackId = 5
+ pan = 0.5
+ attack = 0.005
+ decay = 0.095
+ filterType = 0
+ filterCutoff = 1000
+ tied = False
+ mode = 'mini'
+
+ def makePitchSequence(length, drumPitch):
+ pitchSequence = []
+ append = pitchSequence.append
+ list = range(length)
+ max = len(drumPitch) - 1
+ for i in list:
+ append(drumPitch[ random.randint( 0, max ) ] )
+ return pitchSequence
+
+ def makeGainSequence( onsetList ):
+ gainSequence = []
+ append = gainSequence.append
+ for onset in onsetList:
+ if onset == 0:
+ gain = random.uniform(GenerationConstants.GAIN_MID_MAX_BOUNDARY, GenerationConstants.GAIN_MAX_BOUNDARY)
+ elif ( onset % Config.TICKS_PER_BEAT) == 0:
+ gain = random.uniform(GenerationConstants.GAIN_MID_MIN_BOUNDARY, GenerationConstants.GAIN_MID_MAX_BOUNDARY)
+ else:
+ gain = random.uniform(GenerationConstants.GAIN_MIN_BOUNDARY, GenerationConstants.GAIN_MID_MIN_BOUNDARY)
+ append(gain)
+ return gainSequence
+
+ def pageGenerate( regularity, drumPitch ):
+ barLength = Config.TICKS_PER_BEAT * nbeats
+
+ #print 'pageGenerate drumPitch[0] ', drumPitch[0]
+ currentInstrument = Config.INSTRUMENTS[ instrument ].kit[ drumPitch[0] ].name
+
+ rythmSequence = makeRythm.drumRythmSequence(currentInstrument, nbeats, density, regularity)
+ pitchSequence = makePitchSequence(len(rythmSequence), drumPitch )
+ gainSequence = makeGainSequence(rythmSequence)
+
+ trackNotes = []
+ list = range(len(rythmSequence))
+ for i in list:
+ trackNotes.append( CSoundNote( rythmSequence[i], pitchSequence[i], gainSequence[i],
+ pan, noteDuration, trackId,
+ Config.INSTRUMENTS[instrument].instrumentId, attack,
+ decay, reverbSend, filterType, filterCutoff, tied, mode))
+ return trackNotes
+
+##################################################################################
+ # begin generate()
+ if regularity > 0.75:
+ streamOfPitch = GenerationConstants.DRUM_COMPLEXITY1
+ elif regularity > 0.5:
+ streamOfPitch = GenerationConstants.DRUM_COMPLEXITY2
+ elif regularity > 0.25:
+ streamOfPitch = GenerationConstants.DRUM_COMPLEXITY3
+ else:
+ streamOfPitch = GenerationConstants.DRUM_COMPLEXITY4
+
+ trackNotes = []
+ for drumPitch in streamOfPitch:
+ trackNotes.append(pageGenerate( regularity, drumPitch ))
+ return trackNotes
diff --git a/TamTamJam.activity/Jam/Toolbars.py b/TamTamJam.activity/Jam/Toolbars.py
new file mode 100644
index 0000000..8a45c01
--- /dev/null
+++ b/TamTamJam.activity/Jam/Toolbars.py
@@ -0,0 +1,129 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from gettext import gettext as _
+
+from sugar.graphics.palette import Palette, WidgetInvoker
+from sugar.graphics.radiotoolbutton import RadioToolButton
+
+import Config
+
+
+class JamToolbar( gtk.Toolbar ):
+
+ def __init__( self, owner ):
+ gtk.Toolbar.__init__( self )
+
+ self.owner = owner
+
+ self.toolItem = {}
+
+ self.volumeImg = gtk.Image()
+
+ self.volumeAdjustment = gtk.Adjustment( 0.0, 0, 1.0, 0.1, 0.1, 0 )
+ self.volumeAdjustment.connect( 'value-changed', self.handleVolume )
+ self.volumeSlider = gtk.HScale( adjustment = self.volumeAdjustment )
+ self.volumeSlider.set_size_request( 450, -1 )
+ self.volumeSlider.set_draw_value( False )
+ self._add_tooltip( self.volumeSlider, _("Master Volume") )
+ self._insert_widget( self.volumeSlider, -1 )
+ self._insert_widget( self.volumeImg, -1 )
+
+ self._insert_separator( True )
+
+ self.tempoImg = gtk.Image()
+
+ self.tempoAdjustment = gtk.Adjustment( Config.PLAYER_TEMPO_LOWER, Config.PLAYER_TEMPO_LOWER, Config.PLAYER_TEMPO_UPPER+1, 10, 10, 0 )
+ self.tempoAdjustment.connect( 'value-changed', self.handleTempo )
+ self.tempoSlider = gtk.HScale( adjustment = self.tempoAdjustment )
+ self.tempoSlider.set_size_request( 450, -1 )
+ self.tempoSlider.set_draw_value( False )
+ self._add_tooltip( self.tempoSlider, _("Tempo") )
+ self._insert_widget( self.tempoSlider, -1 )
+ self._insert_widget( self.tempoImg, -1 )
+
+ self.show_all()
+
+ #def _add_palette( self, widget, palette, position = Palette.DEFAULT ):
+ def _add_palette( self, widget, palette ):
+ widget._palette = palette
+ widget._palette.props.invoker = WidgetInvoker( widget )
+ #widget._palette.set_property( "position", position )
+
+ def _add_tooltip( self, widget, tooltip ):
+ #self._add_palette( widget, Palette( tooltip ), Palette.DEFAULT )
+ self._add_palette( widget, Palette( tooltip ) )
+
+ def _insert_widget( self, widget, pos ):
+ self.toolItem[ widget ] = gtk.ToolItem()
+ self.toolItem[ widget ].add( widget )
+ self.insert( self.toolItem[ widget ], pos )
+
+ def _insert_separator( self, expand = False ):
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw( False )
+ separator.set_expand( expand )
+ self.insert( separator, -1 )
+
+ def mapRange( self, value, ilower, iupper, olower, oupper ):
+ if value == iupper:
+ return oupper
+ return olower + int( (oupper-olower+1)*(value-ilower)/float(iupper-ilower) )
+
+
+ def handleVolume( self, widget ):
+ self.owner._setVolume( widget.get_value() )
+
+ img = self.mapRange( widget.value, widget.lower, widget.upper, 0, 3 )
+ self.volumeImg.set_from_file(Config.TAM_TAM_ROOT + '/icons/volume' + str(img) + '.svg')
+
+ def handleTempo( self, widget ):
+ self.owner._setTempo( widget.get_value() )
+
+ img = self.mapRange( widget.value, widget.lower, widget.upper, 1, 8 )
+ self.tempoImg.set_from_file(Config.TAM_TAM_ROOT + '/icons/tempo' + str(img) + '.svg')
+
+
+class DesktopToolbar( gtk.Toolbar ):
+
+ def __init__( self, owner ):
+ gtk.Toolbar.__init__( self )
+
+ self.owner = owner
+
+ self._insert_separator( True )
+
+ self.desktop = []
+
+ btn = RadioToolButton( 'preset1', group = None )
+ btn.connect( 'toggled', self.setDesktop, 0 )
+ btn.set_tooltip( _('Desktop 1') )
+ self.insert( btn, -1 )
+ self.desktop.append( btn )
+
+ for i in range(2,11):
+ btn = RadioToolButton( 'preset%d'%i, group = self.desktop[0] )
+ btn.connect( 'toggled', self.setDesktop, i-1 )
+ btn.set_tooltip( _('Desktop %d'%i) )
+ self.insert( btn, -1 )
+ self.desktop.append( btn )
+
+ self._insert_separator( True )
+
+ self.show_all()
+
+ def _insert_separator( self, expand = False ):
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw( False )
+ separator.set_expand( expand )
+ self.insert( separator, -1 )
+
+ def getDesktopButton( self, which ):
+ return self.desktop[which]
+
+ def setDesktop( self, widget, which ):
+ if widget.get_active():
+ self.owner._setDesktop( which )
+
diff --git a/TamTamJam.activity/Jam/__init__.py b/TamTamJam.activity/Jam/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TamTamJam.activity/Jam/__init__.py
diff --git a/TamTamJam.activity/NEWS b/TamTamJam.activity/NEWS
new file mode 100644
index 0000000..8120a07
--- /dev/null
+++ b/TamTamJam.activity/NEWS
@@ -0,0 +1,71 @@
+39
+
+* New activity : TamTamJam (lync)
+* Activities are now separate (TamTamJam, TamTamEdit, TamTamSynthLab)
+* No sound when set to language other than english fixed (#3165) (J5, marcopg, tomeu, Flipo)
+
+38
+
+* Adapt to new Palette API (Flipo)
+
+37
+
+* Small layout change in edit (Flipo)
+* Temporary fix for Palettes causing the audio to stop (Flipo)
+
+36
+
+* Major image cleanup, 700k saved (Flipo)
+* Tune generation back (see leftmost icon on generate tab) (Olipet)
+
+35
+
+* TamTam Edit Toolbars enhancements (Flipo, Olipet)
+* TamTam Edit layout changes (lync)
+* Dual instruments for tracks in Edit (Olipet, lync)
+
+34
+
+* Synthlab sugar integration (olipet, Flipo)
+* Toolbars in edit draft, hit alt-t to show the toolbar, alt-y to hide (Flipo)
+* Bug that prevented sound from working fixed (LeJames)
+
+33
+
+* Added Sugar Toolbar to miniTamTam and Synthlab (Flipo)
+* Network now working in miniTamTam, use the share button on the activity toolbar (lync)
+* Added help subsystem (ethrop, LeJames)
+
+32
+
+* Important microphone fix (Olipet)
+
+31
+
+* Working network (create a FORCE_SHARE file in the / dir) (lync)
+* New tune generator in Edit, click the dice in the tune view (Olipet)
+* Fine tuning of the algorythmic generator and orchestras (Olipet)
+* TamTamJam (miniTamTam) integration with the journal (Flipo)
+* Title according to subactivity displayed in the journal (Flipo)
+
+30
+
+* Clicking the close button in Edit and Synthlab goes back to Sugar. (Flipo)
+* Fix to the Journal load and save. (Flipo, lync)
+* Ogg player on the welcome page. (lync)
+* Demo tune instruments and tempo are randomly generated (Olipet)
+* lab placeholder files were reduced in size by 95% (Olipet)
+
+29
+
+* WelcomeScreen: Removed Typing Game icon. (Flipo)
+* General: Added sounds. (ethrop, Olipet)
+* General: Journal integration. (Flipo, lync)
+* TamTamJam: Microphone semi-working. (James)
+* TamTamEdit: Beats per page setting. (lync, Olipet)
+* TamTamEdit: Mute/Solo controls on tracks. (Flipo)
+* TamTamEdit: Paint tool. (Olipet)
+* TamTamEdit: Properties automation. (Olipet)
+* TamTamEdit: Pages can have different colors. (lync)
+* TamTamEdit: Realtime keyboard recording. (Olipet)
+* TamTamEdit: Export song to ogg file. (Olipet) \ No newline at end of file
diff --git a/TamTamJam.activity/TamTam.py b/TamTamJam.activity/TamTam.py
new file mode 100644
index 0000000..e282df9
--- /dev/null
+++ b/TamTamJam.activity/TamTam.py
@@ -0,0 +1,1229 @@
+import locale
+locale.setlocale(locale.LC_NUMERIC, 'C')
+import signal , time , sys , os, shutil
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import gobject
+import time
+
+import Config
+from Util.CSoundClient import new_csound_client
+from Util.Profiler import TP
+
+from Util.InstrumentPanel import InstrumentPanel
+from miniTamTam.miniTamTamMain import miniTamTamMain
+from Jam.JamMain import JamMain
+from Edit.MainWindow import MainWindow
+from Welcome import Welcome
+from SynthLab.SynthLabWindow import SynthLabWindow
+from Util.Trackpad import Trackpad
+from gettext import gettext as _
+#from Util.KeyboardWindow import KeyboardWindow
+import commands
+
+if __name__ != '__main__':
+ try:
+ from sugar.activity.activity import Activity
+ from sugar.activity import activity
+ FAKE_ACTIVITY = False
+ if Config.DEBUG: print 'using sugar Activity'
+ except ImportError:
+ from FActivity import FakeActivity as Activity
+ FAKE_ACTIVITY = True
+ if Config.DEBUG: print 'using fake activity'
+else:
+ from FActivity import FakeActivity as Activity
+ if Config.DEBUG: print 'using fake activity'
+
+
+class TamTam(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='welcome'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ if self._shared_activity: # if we're joining a shared activity force mini
+ self.set_mode("mini")
+ else:
+ self.set_mode(mode)
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ #if len( self.preloadList ):
+ # self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ #print "-----", event.keyval, event.string, event.hardware_keycode
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ subactivity_name = self.metadata['tamtam_subactivity']
+ if subactivity_name == 'edit' \
+ or subactivity_name == 'synth' \
+ or subactivity_name == 'jam':
+ self.set_mode(subactivity_name)
+ self.modeList[subactivity_name].handleJournalLoad(file_path)
+ elif subactivity_name == 'mini':
+ self.set_mode(subactivity_name)
+ else:
+ return
+
+ def write_file(self,file_path):
+ if self.mode == 'edit':
+ self.metadata['tamtam_subactivity'] = self.mode
+ self.modeList[self.mode].handleJournalSave(file_path)
+ elif self.mode == 'synth':
+ self.metadata['tamtam_subactivity'] = self.mode
+ self.modeList[self.mode].handleJournalSave(file_path)
+ elif self.mode == 'mini':
+ self.metadata['tamtam_subactivity'] = self.mode
+ f = open(file_path,'w')
+ f.close()
+ elif self.mode == 'jam':
+ self.metadata['tamtam_subactivity'] = self.mode
+ self.modeList[self.mode].handleJournalSave(file_path)
+
+class TamTamJam(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='welcome'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam Jam')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ self.set_mode("jam")
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ if len( self.preloadList ):
+ self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ self.modeList['jam'].handleJournalLoad(file_path)
+
+ def write_file(self,file_path):
+ self.modeList['jam'].handleJournalSave(file_path)
+
+class TamTamEdit(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='edit'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam Edit')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ self.set_mode("edit")
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ if len( self.preloadList ):
+ self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ self.modeList['edit'].handleJournalLoad(file_path)
+
+ def write_file(self,file_path):
+ self.modeList['edit'].handleJournalSave(file_path)
+
+class TamTamSynthLab(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='synth'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam SynthLab')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ self.set_mode("synth")
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ if len( self.preloadList ):
+ self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ self.modeList['synth'].handleJournalLoad(file_path)
+
+ def write_file(self,file_path):
+ self.modeList['synth'].handleJournalSave(file_path)
+
+class TamTamMini(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='mini'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam Mini')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ self.set_mode("mini")
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ if len( self.preloadList ):
+ self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ self.metadata['tamtam_subactivity'] = 'mini'
+
+ def write_file(self,file_path):
+ f = open(file_path,'w')
+ f.close()
+
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1 :
+ mainwin = TamTam(None, sys.argv[1])
+ else:
+ mainwin = TamTam(None, 'welcome')
+
+ gtk.gdk.threads_init()
+ gtk.main()
+
+ sys.exit(0)
+
+
+
+
+
+
+
+
+ def run_edit_mode():
+ tamtam = MainWindow()
+ mainwin = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ mainwin.set_title('TamTam Player')
+ display = mainwin.get_display()
+ screen = gtk.gdk.Display.get_default_screen(display)
+ mainwin.set_geometry_hints( None, screen.get_width(), screen.get_height(), screen.get_width(), screen.get_height(), screen.get_width(), screen.get_height() )
+ #mainwin.fullscreen() # don't need to specify full screen, it seem to sit properly anyway
+ mainwin.set_resizable(False)
+ mainwin.connect('destroy' , tamtam.destroy )
+ #mainwin.connect( "configure-event", tamtam.handleConfigureEvent )
+ mainwin.connect( "key-press-event", tamtam.onKeyPress )
+ mainwin.connect( "key-release-event", tamtam.onKeyRelease )
+ mainwin.connect( "delete_event", tamtam.delete_event )
+ mainwin.add(tamtam)
+ tamtam.show()
+ mainwin.show()
+ gtk.main()
diff --git a/TamTamJam.activity/actvity/activity-tamtamjam.svg b/TamTamJam.activity/actvity/activity-tamtamjam.svg
new file mode 100644
index 0000000..563df7b
--- /dev/null
+++ b/TamTamJam.activity/actvity/activity-tamtamjam.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#010101">
+ <!ENTITY fill_color "#FFFFFF">
+]><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="activity-tamtam">
+
+ <path d=" M9.653,18.199h35.54c0.772,0,1.4,0.628,1.4,1.403v2.362c0,0.771-0.628,1.401-1.4,1.401H9.653c-0.775,0-1.403-0.628-1.403-1.401 v-2.361C8.25,18.826,8.877,18.199,9.653,18.199z" display="inline" fill="&stroke_color;" id="path8" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
+
+ <rect display="inline" fill="&fill_color;" height="22.128" id="rect10" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" width="33.139" x="10.852" y="23.365"/>
+
+ <path d=" M9.653,45.493h35.54c0.772,0,1.4,0.627,1.4,1.401v2.362c0,0.775-0.628,1.405-1.4,1.405H9.653c-0.775,0-1.403-0.629-1.403-1.405 v-2.362C8.25,46.124,8.877,45.493,9.653,45.493z" display="inline" fill="&stroke_color;" id="path17" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
+
+ <path d=" M10.851,45.491l8.544-22.126l7.896,22.125l7.926-22.126" display="inline" fill="none" id="path19" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
+
+ <path d=" M43.987,45.491l-8.77-22.126" display="inline" fill="none" id="path21" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5"/>
+
+ <line display="inline" fill="none" id="line27" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" x1="33.69" x2="48.336" y1="13.149" y2="5.948"/>
+
+ <line display="inline" fill="none" id="line29" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" x1="6.042" x2="20.658" y1="2.213" y2="9.48"/>
+
+ <path d=" M25.721,9.802c0.55,1.451-0.171,3.066-1.608,3.612c-1.437,0.545-3.049-0.188-3.599-1.639c-0.547-1.45,0.174-3.067,1.609-3.613 C23.561,7.62,25.172,8.352,25.721,9.802z" display="inline" fill="&fill_color;" id="path23" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.25"/>
+
+ <path d=" M33.338,16.442c-1.04,1.125-2.798,1.189-3.928,0.144c-1.128-1.044-1.197-2.801-0.155-3.927c1.041-1.125,2.8-1.19,3.926-0.146 C34.31,13.558,34.381,15.316,33.338,16.442z" display="inline" fill="&fill_color;" id="path25" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.25"/>
+</g></svg>
+
diff --git a/TamTamJam.activity/actvity/activity.info b/TamTamJam.activity/actvity/activity.info
new file mode 100644
index 0000000..423ad09
--- /dev/null
+++ b/TamTamJam.activity/actvity/activity.info
@@ -0,0 +1,6 @@
+[Activity]
+name = TamTamJam
+service_name = org.laptop.TamTamJam
+icon = activity-tamtam
+class = TamTam.TamTamJam
+activity_version = 40
diff --git a/TamTamJam.activity/common b/TamTamJam.activity/common
new file mode 120000
index 0000000..60d3b0a
--- /dev/null
+++ b/TamTamJam.activity/common
@@ -0,0 +1 @@
+../common \ No newline at end of file
diff --git a/TamTamJam.activity/icons/XYBut.svg b/TamTamJam.activity/icons/XYBut.svg
new file mode 100644
index 0000000..54d7563
--- /dev/null
+++ b/TamTamJam.activity/icons/XYBut.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
+<g>
+ <circle fill="#808284" cx="15" cy="15" r="9.376"/>
+ <g>
+ <path fill="#FFFFFF" stroke="#808284" stroke-width="2.25" d="M21.5,14.998c0,3.584-2.915,6.502-6.499,6.502
+ c-3.584,0-6.501-2.918-6.501-6.502S11.417,8.5,15.001,8.5C18.585,8.5,21.5,11.413,21.5,14.998z M15.003,2
+ C7.832,2,2,7.831,2,14.998C2,22.165,7.832,28,15.003,28C22.168,28,28,22.165,28,14.998C28,7.831,22.168,2,15.003,2z"/>
+ </g>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/XYButDown.svg b/TamTamJam.activity/icons/XYButDown.svg
new file mode 100644
index 0000000..201df5f
--- /dev/null
+++ b/TamTamJam.activity/icons/XYButDown.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
+<g>
+ <circle cx="15" cy="15" r="6.933"/>
+ <g>
+ <path fill="none" stroke="#808284" stroke-width="2.25" d="M21.5,14.998c0,3.585-2.915,6.502-6.499,6.502
+ c-3.584,0-6.501-2.917-6.501-6.502c0-3.584,2.917-6.498,6.501-6.498C18.585,8.5,21.5,11.414,21.5,14.998z M15.003,2
+ C7.832,2,2,7.831,2,14.998C2,22.165,7.832,28,15.003,28C22.169,28,28,22.165,28,14.998C28,7.831,22.169,2,15.003,2z"/>
+ </g>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/XYButDownClick.svg b/TamTamJam.activity/icons/XYButDownClick.svg
new file mode 100644
index 0000000..9e0d222
--- /dev/null
+++ b/TamTamJam.activity/icons/XYButDownClick.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
+<circle fill="none" cx="15" cy="15" r="6.933"/>
+<g>
+ <path fill="none" stroke="#808284" stroke-width="2.25" d="M21.5,14.998c0,3.585-2.915,6.502-6.499,6.502
+ c-3.584,0-6.501-2.917-6.501-6.502c0-3.584,2.917-6.498,6.501-6.498C18.585,8.5,21.5,11.414,21.5,14.998z M15.003,2
+ C7.832,2,2,7.831,2,14.998C2,22.165,7.832,28,15.003,28C22.169,28,28,22.165,28,14.998C28,7.831,22.169,2,15.003,2z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/accept.svg b/TamTamJam.activity/icons/accept.svg
new file mode 100755
index 0000000..a2f9e28
--- /dev/null
+++ b/TamTamJam.activity/icons/accept.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="45px" height="45px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="
+ M5.78,28.079c0-12.43,10.071-22.499,22.499-22.499c12.431,0,22.501,10.069,22.501,22.499c0,12.431-10.07,22.501-22.501,22.501
+ C15.851,50.58,5.78,40.51,5.78,28.079z"/>
+</g>
+<g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" x1="16.788" y1="26.691" x2="25.781" y2="38.706"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" x1="25.781" y1="38.706" x2="39.773" y2="18.704"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/cancel.svg b/TamTamJam.activity/icons/cancel.svg
new file mode 100755
index 0000000..59512eb
--- /dev/null
+++ b/TamTamJam.activity/icons/cancel.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="45px" height="45px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="
+ M5.78,28.08c0-12.431,10.071-22.5,22.5-22.5c12.432,0,22.5,10.069,22.5,22.5c0,12.432-10.068,22.5-22.5,22.5
+ C15.851,50.58,5.78,40.512,5.78,28.08z"/>
+<line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" x1="20.155" y1="19.956" x2="37.029" y2="36.83"/>
+<line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" x1="37.029" y1="19.956" x2="20.155" y2="36.83"/>
+</svg>
diff --git a/TamTamJam.activity/icons/preset1.svg b/TamTamJam.activity/icons/preset1.svg
new file mode 100644
index 0000000..9673b82
--- /dev/null
+++ b/TamTamJam.activity/icons/preset1.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,6.769 10.793,49.501 44.012,49.501 44.126,15.444
+ 35.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,7.043 35.334,15.753 44.168,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M26.749,26.727h-0.036L24.48,27.79l-0.45-2.053l3.097-1.44h2.27V36h-2.647V26.727z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/preset10.svg b/TamTamJam.activity/icons/preset10.svg
new file mode 100644
index 0000000..28e7082
--- /dev/null
+++ b/TamTamJam.activity/icons/preset10.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,6.769 10.793,49.501 44.012,49.501 44.126,15.444
+ 35.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,7.042 35.334,15.753 44.166,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M20.719,26.395h-0.036l-2.232,1.062l-0.449-2.053l3.097-1.439h2.27v11.704h-2.646v-9.273H20.719z"/>
+ <path fill="#FFFFFF" d="M35.858,29.762c0,3.619-1.458,6.104-4.447,6.104c-3.023,0-4.356-2.719-4.375-6.031
+ c0-3.386,1.44-6.067,4.467-6.067C34.635,23.766,35.858,26.557,35.858,29.762z M29.791,29.834c-0.018,2.683,0.631,3.962,1.692,3.962
+ c1.062,0,1.639-1.333,1.639-3.998c0-2.593-0.559-3.961-1.655-3.961C30.457,25.836,29.773,27.114,29.791,29.834z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/preset2.svg b/TamTamJam.activity/icons/preset2.svg
new file mode 100644
index 0000000..f376c67
--- /dev/null
+++ b/TamTamJam.activity/icons/preset2.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,6.769 10.793,49.501 44.012,49.501 44.126,15.444
+ 35.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,7.042 35.334,15.753 44.166,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M22.922,35.168v-1.656l1.513-1.368c2.557-2.287,3.799-3.602,3.835-4.97c0-0.955-0.576-1.711-1.927-1.711
+ c-1.008,0-1.891,0.504-2.502,0.973l-0.774-1.963c0.882-0.666,2.251-1.206,3.835-1.206c2.646,0,4.105,1.548,4.105,3.673
+ c0,1.963-1.423,3.529-3.115,5.042l-1.08,0.9v0.035h4.411v2.251H22.922z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/preset3.svg b/TamTamJam.activity/icons/preset3.svg
new file mode 100644
index 0000000..a22bb3c
--- /dev/null
+++ b/TamTamJam.activity/icons/preset3.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,6.769 10.793,49.501 44.012,49.501 44.126,15.444
+ 35.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,7.042 35.334,15.753 44.166,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M23.909,32.65c0.486,0.252,1.604,0.72,2.72,0.72c1.423,0,2.143-0.684,2.143-1.565
+ c0-1.152-1.152-1.674-2.358-1.674h-1.115v-1.963h1.062c0.918-0.019,2.088-0.36,2.088-1.352c0-0.702-0.576-1.224-1.729-1.224
+ c-0.954,0-1.962,0.414-2.448,0.702l-0.559-1.981c0.701-0.45,2.106-0.882,3.618-0.882c2.504,0,3.891,1.313,3.891,2.917
+ c0,1.243-0.702,2.215-2.144,2.719v0.035c1.404,0.253,2.538,1.314,2.538,2.846c0,2.07-1.817,3.583-4.789,3.583
+ c-1.513,0-2.791-0.396-3.475-0.828L23.909,32.65z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/preset4.svg b/TamTamJam.activity/icons/preset4.svg
new file mode 100644
index 0000000..e6cf5e9
--- /dev/null
+++ b/TamTamJam.activity/icons/preset4.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,6.769 10.793,49.501 44.012,49.501 44.126,15.444
+ 35.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,7.042 35.334,15.753 44.166,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M27.1,35.835v-2.791h-5.186v-1.782l4.431-7.13h3.349v6.859h1.404v2.054h-1.404v2.791L27.1,35.835
+ L27.1,35.835z M27.1,30.991v-2.592c0-0.703,0.037-1.422,0.09-2.179h-0.07c-0.379,0.756-0.685,1.439-1.082,2.179l-1.565,2.557v0.035
+ H27.1z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/preset5.svg b/TamTamJam.activity/icons/preset5.svg
new file mode 100644
index 0000000..53f5b08
--- /dev/null
+++ b/TamTamJam.activity/icons/preset5.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,5.769 10.793,49.834 44.012,49.834 44.126,14.444
+ 35.45,5.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,6.042 35.334,14.753 44.166,14.753 "/>
+<path fill="#FFFFFF" d="M31.025,25.407h-4.537l-0.252,1.8c0.252-0.035,0.468-0.035,0.756-0.035c1.117,0,2.251,0.252,3.08,0.846
+ c0.883,0.594,1.422,1.566,1.422,2.936c0,2.179-1.873,4.105-5.022,4.105c-1.423,0-2.611-0.324-3.259-0.666l0.485-2.053
+ c0.522,0.252,1.585,0.575,2.647,0.575c1.134,0,2.34-0.541,2.34-1.782c0-1.207-0.955-1.945-3.295-1.945
+ c-0.648,0-1.098,0.037-1.584,0.109l0.773-6.142h6.445V25.407z"/>
+</svg>
diff --git a/TamTamJam.activity/icons/preset6.svg b/TamTamJam.activity/icons/preset6.svg
new file mode 100644
index 0000000..4c2493c
--- /dev/null
+++ b/TamTamJam.activity/icons/preset6.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,6.769 10.793,49.501 44.012,49.501 44.126,15.444
+ 35.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,7.043 35.334,15.753 44.168,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M30.562,26.575c-0.307,0-0.63,0-1.062,0.036c-2.431,0.198-3.511,1.44-3.815,2.811h0.055
+ c0.576-0.596,1.386-0.938,2.484-0.938c1.963,0,3.619,1.387,3.619,3.817c0,2.32-1.782,4.229-4.321,4.229
+ c-3.115,0-4.646-2.321-4.646-5.112c0-2.196,0.812-4.033,2.07-5.203c1.171-1.062,2.686-1.64,4.521-1.729
+ c0.505-0.036,0.828-0.036,1.099-0.018L30.562,26.575L30.562,26.575z M27.501,34.516c0.952,0,1.564-0.883,1.564-2.07
+ c0-1.081-0.576-2.018-1.747-2.018c-0.738,0-1.351,0.451-1.62,1.045c-0.07,0.145-0.106,0.357-0.106,0.684
+ c0.054,1.242,0.646,2.359,1.891,2.359H27.501z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/preset7.svg b/TamTamJam.activity/icons/preset7.svg
new file mode 100644
index 0000000..d6de26c
--- /dev/null
+++ b/TamTamJam.activity/icons/preset7.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,6.769 10.793,49.501 44.012,49.501 44.126,15.444
+ 35.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,7.042 35.334,15.753 44.166,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M32.146,25.296v1.729l-4.825,9.977h-2.898l4.824-9.417v-0.036h-5.365v-2.251L32.146,25.296L32.146,25.296z"
+ />
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/preset8.svg b/TamTamJam.activity/icons/preset8.svg
new file mode 100644
index 0000000..5c4a17f
--- /dev/null
+++ b/TamTamJam.activity/icons/preset8.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="11.793,6.769 11.793,49.501 45.012,49.501 45.126,15.444
+ 36.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="36.334,7.042 36.334,15.753 45.166,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M26.164,29.784c-1.135-0.575-1.691-1.529-1.691-2.557c0-1.98,1.782-3.295,4.123-3.295
+ c2.735,0,3.871,1.584,3.871,3.007c0,1.008-0.54,1.999-1.692,2.574v0.055c1.134,0.433,2.144,1.404,2.144,2.916
+ c0,2.125-1.783,3.548-4.503,3.548c-2.971,0-4.339-1.675-4.339-3.259c0-1.404,0.812-2.377,2.089-2.936L26.164,29.784L26.164,29.784z
+ M30.126,32.684c0-1.027-0.757-1.639-1.838-1.928c-0.898,0.253-1.422,0.899-1.422,1.747c-0.019,0.847,0.63,1.639,1.656,1.639
+ C29.495,34.142,30.126,33.512,30.126,32.684z M27.046,27.138c0,0.792,0.721,1.296,1.655,1.584c0.631-0.18,1.188-0.756,1.188-1.494
+ c0-0.72-0.414-1.44-1.422-1.44C27.532,25.787,27.046,26.399,27.046,27.138z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/preset9.svg b/TamTamJam.activity/icons/preset9.svg
new file mode 100644
index 0000000..4b803e9
--- /dev/null
+++ b/TamTamJam.activity/icons/preset9.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<polygon fill="none" stroke="#FFFFFF" stroke-width="2" points="10.793,6.769 10.793,49.501 44.012,49.501 44.126,15.444
+ 35.45,6.996 "/>
+<polyline fill="#E6E3E1" stroke="#5D6060" stroke-width="2" points="35.334,7.042 35.334,15.753 44.166,15.753 "/>
+<g>
+ <path fill="#FFFFFF" d="M24.793,34.164c0.343,0.036,0.647,0.036,1.188,0c0.828-0.055,1.675-0.287,2.305-0.721
+ c0.758-0.521,1.262-1.276,1.478-2.16l-0.054-0.018c-0.522,0.539-1.278,0.846-2.342,0.846c-1.979,0-3.654-1.387-3.654-3.655
+ c0-2.286,1.837-4.213,4.41-4.213c3.008,0,4.431,2.306,4.431,5.042c0,2.431-0.774,4.213-2.053,5.384
+ c-1.116,1.009-2.646,1.565-4.466,1.639c-0.469,0.036-0.937,0.018-1.242,0V34.164z M28.033,26.241c-0.918,0-1.603,0.828-1.584,2.053
+ c0,1.009,0.54,1.873,1.656,1.873c0.738,0,1.261-0.36,1.513-0.774c0.091-0.162,0.145-0.342,0.145-0.684
+ c0-1.243-0.468-2.468-1.71-2.468H28.033z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/tam-help.svg b/TamTamJam.activity/icons/tam-help.svg
new file mode 100644
index 0000000..2923bb3
--- /dev/null
+++ b/TamTamJam.activity/icons/tam-help.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="45.395px" height="45.395px" viewBox="0 0 45.395 45.395" enable-background="new 0 0 45.395 45.395" xml:space="preserve">
+<circle fill="#808284" cx="22.297" cy="23.037" r="22.5"/>
+<circle fill="none" stroke="#4C4D4F" stroke-width="2.25" cx="22.295" cy="23.129" r="21.572"/>
+<circle fill="none" stroke="#4C4D4F" cx="22.628" cy="22.89" r="17.085"/>
+<g>
+ <path fill="#FFFFFF" stroke="#FFFFFF" d="M18.717,15.967c1.383-0.625,2.621-0.938,3.715-0.938c1.477,0,2.617,0.332,3.422,0.996
+ s1.207,1.645,1.207,2.941c0,1.344-0.871,2.738-2.613,4.184c-0.758,0.625-1.221,1.041-1.389,1.248s-0.303,0.459-0.404,0.756
+ s-0.152,0.668-0.152,1.113v0.645h-1.605v-0.668c0-1.359,0.434-2.418,1.301-3.176l1.547-1.359c0.5-0.422,0.867-0.85,1.102-1.283
+ s0.352-0.896,0.352-1.389c0-0.812-0.258-1.436-0.773-1.869s-1.219-0.65-2.109-0.65c-0.805,0-1.82,0.289-3.047,0.867L18.717,15.967z
+ M21.905,29.198c0.414,0,0.762,0.141,1.043,0.422s0.422,0.629,0.422,1.043c0,0.422-0.143,0.783-0.428,1.084
+ s-0.631,0.451-1.037,0.451c-0.398,0-0.738-0.15-1.02-0.451s-0.422-0.662-0.422-1.084s0.139-0.771,0.416-1.049
+ S21.498,29.198,21.905,29.198z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/tempo1.svg b/TamTamJam.activity/icons/tempo1.svg
new file mode 100644
index 0000000..bb9aeec
--- /dev/null
+++ b/TamTamJam.activity/icons/tempo1.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M23.5,6.5c3,3,7,7,9,11c-7,5-4,6-3,26c-1,1-8,1-9,0c0,0,2,1,2-1
+ c0-3-2-7-2-11c0-2,1-4,1-6c0-3-2-1-2-3c0-3,3-8,3-11c0-2-1-1-2-2v-3H23.5z"/>
+</svg>
diff --git a/TamTamJam.activity/icons/tempo2.svg b/TamTamJam.activity/icons/tempo2.svg
new file mode 100644
index 0000000..4a98310
--- /dev/null
+++ b/TamTamJam.activity/icons/tempo2.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M27.5,44.5v-3C28.5,42.5,28.5,43.5,27.5,44.5z M26.5,10.5
+ c2,2,2,6,2,8c0,4-3,11-3,13s4,7,7,10c-2,2-4,3-5,5h-6c1-1,2-3,2-5c0-3-2-9-3-14c0,0,0-1-1,0v-6c0-3,3-8,3-11c0-1-2-2-2-6h3
+ C23.5,5.5,26.5,9.5,26.5,10.5z"/>
+</svg>
diff --git a/TamTamJam.activity/icons/tempo3.svg b/TamTamJam.activity/icons/tempo3.svg
new file mode 100644
index 0000000..bd893bd
--- /dev/null
+++ b/TamTamJam.activity/icons/tempo3.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M30.5,17.5c0,3-2,2-2,4c0,3,4,14,7,21c-1,0-3,1-5,1c1-1,2,0,2-3
+ c0-2-4-7-6-10c-3,3-5,8-7,13c-1,0-3-1-4-1c3-3,7-14,7-18s-1-3-4-4c3-2,4-8,4-14h3C23.5,9.5,30.5,14.5,30.5,17.5z"/>
+</svg>
diff --git a/TamTamJam.activity/icons/tempo4.svg b/TamTamJam.activity/icons/tempo4.svg
new file mode 100644
index 0000000..6fa5afa
--- /dev/null
+++ b/TamTamJam.activity/icons/tempo4.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M34.5,22.5c-1-1-2-4-5-6c-1,2,0,3,0,6c0,2-3,4-3,7c0,2,4,2,4,4
+ c0,3-1,4-2,5c0-1,0-3-1-4c-1,3-2,7-3,10c-4-3,0-6,0-9s-3-11-4-17l-4,4c1-5,8.25-11.12,7.25-16.12c0.68,0.68,3.029,0,2.87,2.12
+ C26.5,10.25,33.62,17.75,34.5,22.5z"/>
+</svg>
diff --git a/TamTamJam.activity/icons/tempo5.svg b/TamTamJam.activity/icons/tempo5.svg
new file mode 100644
index 0000000..9500e7e
--- /dev/null
+++ b/TamTamJam.activity/icons/tempo5.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M24.5,13.5c2,1,5,3,5,6c0,2-2,3-2,5c0,9,11,4,11,13c-1,0-3-2-4-3
+ c-3-1-9,1-10-3c-2,3-5,7-7,11c-3,0-3-1-4-1c0-2,3-3,4-6s4-8,4-10c0-3-1-3-2-5c-1,0-2,1-3,2c0-1,2-3,2-4c1-2,3-5,2-8c0,0,1-1,4-2
+ C25.5,9.5,25.5,11.5,24.5,13.5z"/>
+</svg>
diff --git a/TamTamJam.activity/icons/tempo6.svg b/TamTamJam.activity/icons/tempo6.svg
new file mode 100644
index 0000000..9844fd6
--- /dev/null
+++ b/TamTamJam.activity/icons/tempo6.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M22.5,10.5c3,2,7,5,7,7c0,3-4,8-4,10c0,3,1,3,1,5h5l2-2l2,2v4
+ c-1,0-3-2-5-2c-3,0-5,1-8,1c-1,3-2,7-2,10h-5c1-1,3-3,3-4c1-5,1-11,1-18l-1-1c-1,1-1.75,2.88-2.75,2.88c0,0-0.25-0.63-0.25-1.63
+ c4-4,2-8.25,2-13.25c0-1,0.25-2.5,0.38-5.38L22.5,5.5C23.12,6.5,22.5,8.5,22.5,10.5z"/>
+<polygon fill-rule="evenodd" clip-rule="evenodd" fill="#333333" stroke="#333333" stroke-linecap="round" stroke-linejoin="round" points="
+ 25,20 25.25,16.75 26.5,17.88 "/>
+</svg>
diff --git a/TamTamJam.activity/icons/tempo7.svg b/TamTamJam.activity/icons/tempo7.svg
new file mode 100644
index 0000000..54bed80
--- /dev/null
+++ b/TamTamJam.activity/icons/tempo7.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M20.5,7.5c1,1,1,3,1,4c10,4,8,6,8,14c0,2,6,9,10,13c-1,2-2,4-4,5
+ c1.62-8.88-8.75-13.88-12-15c-1,1-1,0-1,2c0,3,2,5,3,7c-1,1-3,2-6,2c0-1,2-1,2-4c0-2-4-4-4-6c0-3,3-4,5-6c-3-8-8-2-11-6h6
+ c0-1,1,0,1-3c0-2-1-1-2-2l1-5H20.5z"/>
+</svg>
diff --git a/TamTamJam.activity/icons/tempo8.svg b/TamTamJam.activity/icons/tempo8.svg
new file mode 100644
index 0000000..2c0154f
--- /dev/null
+++ b/TamTamJam.activity/icons/tempo8.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M20.5,12.5c0.67,0.4,0.4,1.9,1.75,2.25s1.05-0.38,1.5-0.37
+ c4.971,0,10.95-0.88,11.75,7.12c-1-2-3-4-5-5l-4,1c1,2,4,4,5,7c1,1,1,4,1,6c3,3,8-1,11,6c-2.88-0.82-4.25-2.62-12.75-2.75
+ c-1.561-0.02-2.34-1.561-3.75-1.87c-3.42-0.76-4.67-0.38-5.5-0.38c-3,0-8,7-11,7c-2,0-3-1-3-2c4,2,8-4,9-7c2-1,5-1,8-3c-2-4-6-5-8-3
+ l-6-6l2-2c1,1,1,2,1,4c1,0,4.12,0.38,6.12-0.62L16.5,17.5v-5H20.5z"/>
+</svg>
diff --git a/TamTamJam.activity/icons/volume0.svg b/TamTamJam.activity/icons/volume0.svg
new file mode 100644
index 0000000..963ebf0
--- /dev/null
+++ b/TamTamJam.activity/icons/volume0.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
+<g>
+ <path fill="#FFFFFF" d="M6.23,13.818c0,1.617-1.312,2.931-2.934,2.931c-1.618,0-2.931-1.313-2.931-2.931
+ c0-1.614,1.312-2.93,2.932-2.93C4.918,10.889,6.23,12.204,6.23,13.818z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/volume1.svg b/TamTamJam.activity/icons/volume1.svg
new file mode 100644
index 0000000..a23fb00
--- /dev/null
+++ b/TamTamJam.activity/icons/volume1.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M8.929,7.078c3.546,3.548,3.546,9.297,0,12.846"
+ />
+<g>
+ <path fill="#FFFFFF" d="M6.23,13.818c0,1.617-1.312,2.931-2.934,2.931c-1.618,0-2.931-1.313-2.931-2.931
+ c0-1.614,1.312-2.93,2.932-2.93C4.918,10.889,6.23,12.204,6.23,13.818z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/volume2.svg b/TamTamJam.activity/icons/volume2.svg
new file mode 100644
index 0000000..7b719e4
--- /dev/null
+++ b/TamTamJam.activity/icons/volume2.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M14.574,4.015c5.242,5.236,5.242,13.73,0,18.973"
+ />
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M8.929,7.078c3.546,3.548,3.546,9.297,0,12.846"
+ />
+<g>
+ <path fill="#FFFFFF" d="M6.23,13.818c0,1.617-1.312,2.931-2.934,2.931c-1.618,0-2.931-1.313-2.931-2.931
+ c0-1.614,1.312-2.93,2.932-2.93C4.918,10.889,6.23,12.204,6.23,13.818z"/>
+</g>
+</svg>
diff --git a/TamTamJam.activity/icons/volume3.svg b/TamTamJam.activity/icons/volume3.svg
new file mode 100644
index 0000000..db92a17
--- /dev/null
+++ b/TamTamJam.activity/icons/volume3.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M14.574,4.015c5.242,5.236,5.242,13.73,0,18.973"
+ />
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M8.929,7.078c3.546,3.548,3.546,9.297,0,12.846"
+ />
+<g>
+ <path fill="#FFFFFF" d="M6.23,13.818c0,1.617-1.312,2.931-2.934,2.931c-1.618,0-2.931-1.313-2.931-2.931
+ c0-1.614,1.312-2.93,2.932-2.93C4.918,10.889,6.23,12.204,6.23,13.818z"/>
+</g>
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M20.135,0.528
+ c7.166,7.164,7.166,18.777-0.002,25.943"/>
+</svg>
diff --git a/TamTamJam.activity/setup.py b/TamTamJam.activity/setup.py
new file mode 100644
index 0000000..5b21f11
--- /dev/null
+++ b/TamTamJam.activity/setup.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+# Copyright (C) 2006, Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+from sugar.activity import bundlebuilder
+
+bundlebuilder.start('TamTam')