Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Jam
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@activitycentral.org>2011-06-28 09:23:54 (GMT)
committer Aleksey Lim <alsroot@activitycentral.org>2011-06-28 09:24:48 (GMT)
commit11003147ac4ea947ec5017921019d668cb4953d1 (patch)
treeeaab12347c33cb62326f9eb53121eff594301638 /Jam
parent35278d261ace09d5ed20bdae0983730d88dd8037 (diff)
Switch to the singular sources tree and releasing scheme
Diffstat (limited to 'Jam')
-rw-r--r--Jam/Block.py898
-rw-r--r--Jam/Desktop.py467
-rw-r--r--Jam/Fillin.py116
-rw-r--r--Jam/GenRythm.py84
-rw-r--r--Jam/JamMain.py1206
-rw-r--r--Jam/Parasite.py349
-rw-r--r--Jam/Picker.py398
-rw-r--r--Jam/Popup.py1466
-rw-r--r--Jam/RythmGenerator.py80
-rw-r--r--Jam/Toolbars.py600
-rw-r--r--Jam/__init__.py0
11 files changed, 5664 insertions, 0 deletions
diff --git a/Jam/Block.py b/Jam/Block.py
new file mode 100644
index 0000000..e6a7bf4
--- /dev/null
+++ b/Jam/Block.py
@@ -0,0 +1,898 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import random
+
+import common.Util.InstrumentDB as InstrumentDB
+import common.Config as Config
+
+from common.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.instrumentDB = InstrumentDB.getRef()
+ 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"] = 4 #random.randint(2, 12)
+ if not "regularity" in self.data.keys():
+ self.data["regularity"] = 0.8 #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.setData( "name", block.data["name"] )
+ self.setData( "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( self )
+
+ def testSubstitute( self, block ):
+ ret = Block.testSubstitute( self, block )
+ if ret:
+ return ret
+
+ if block.type == Loop:
+ return False
+
+ if self.instrumentDB.instId[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"] = 0.8 #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.active:
+ self.owner.deactivateLoop( 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.data["beats"] = value
+ self.owner.noteDB.updatePage( self.data["id"], PARAMETER.PAGE_BEATS, value )
+ self._updateWidth()
+ self.updateLoop()
+
+ 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/Jam/Desktop.py b/Jam/Desktop.py
new file mode 100644
index 0000000..0698e88
--- /dev/null
+++ b/Jam/Desktop.py
@@ -0,0 +1,467 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import common.Config as Config
+
+from gettext import gettext as _
+
+import common.Util.InstrumentDB as InstrumentDB
+from Jam import Block
+from Jam import Popup
+
+class Desktop( gtk.EventBox ):
+
+ def __init__( self, owner ):
+ gtk.EventBox.__init__( self )
+
+ self.instrumentDB = InstrumentDB.getRef()
+ 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.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.isActive():
+ self.deactivateDrum( block )
+
+ elif block.type == Block.Loop:
+ if block.isActive():
+ self.deactivateLoop( block )
+
+ 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/Jam/Fillin.py b/Jam/Fillin.py
new file mode 100644
index 0000000..8588f6d
--- /dev/null
+++ b/Jam/Fillin.py
@@ -0,0 +1,116 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+import gobject
+
+from RythmGenerator import *
+from common.Util.CSoundClient import new_csound_client
+from common.Util.NoteDB import Note
+import common.Config as 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/Jam/GenRythm.py b/Jam/GenRythm.py
new file mode 100644
index 0000000..b9e6d48
--- /dev/null
+++ b/Jam/GenRythm.py
@@ -0,0 +1,84 @@
+import random
+import common.Config as Config
+import common.Util.InstrumentDB as InstrumentDB
+
+from common.Generation.GenerationConstants import GenerationConstants
+from common.Generation.Utils import *
+
+class GenRythm:
+ def __init__(self):
+ self.instrumentDB = InstrumentDB.getRef()
+
+ def drumRythmSequence(self, instrumentName, nbeats, density, regularity ):
+ rythmSequence = []
+ binSelection = []
+ downBeats = []
+ upBeats = []
+ beats = []
+ countDown = 0
+ onsetTime = None
+
+ if self.instrumentDB.instNamed[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 self.instrumentDB.instNamed[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 self.instrumentDB.instNamed[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 self.instrumentDB.instNamed[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/Jam/JamMain.py b/Jam/JamMain.py
new file mode 100644
index 0000000..2798d4c
--- /dev/null
+++ b/Jam/JamMain.py
@@ -0,0 +1,1206 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+import pango
+
+import os, sys, shutil, commands
+import random
+
+import common.Util.Instruments
+import common.Config as 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, PlaybackToolbar, DesktopToolbar, RecordToolbar
+
+
+from common.Util.CSoundNote import CSoundNote
+from common.Util.CSoundClient import new_csound_client
+import common.Util.InstrumentDB as InstrumentDB
+from common.Util import NoteDB
+
+from Fillin import Fillin
+from RythmGenerator import generator
+from common.Generation.GenerationConstants import GenerationConstants
+from common.Util.NoteDB import Note, Page
+
+from common.Util import ControlStream
+from common.Util import OS
+from common.Tooltips import Tooltips
+
+import xdrlib
+import time
+import gobject
+import common.Util.Network as Net
+from sugar.presence import presenceservice
+from sugar.graphics.xocolor import XoColor
+
+from math import sqrt
+
+HEARTBEAT_BUFFER = 100 # increase the length of heartbeat loop to remove problems with wrapping during sync correction
+
+class JamMain(gtk.EventBox):
+
+ def __init__(self, activity):
+ gtk.EventBox.__init__(self)
+
+ 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.muted = 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
+
+ #-- Instruments ---------------------------------------
+ self.instrumentImage = {}
+ self.instrumentImageActive = {}
+ for inst in self.instrumentDB.getSet( "All" ):
+ if not inst.kitStage:
+ self.prepareInstrumentImage( inst.instrumentId, inst.img )
+ self.csnd.load_instrument(inst.name)
+
+ #-- 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.jamToolbar = JamToolbar( self )
+ self.activity.toolbox.add_toolbar( _("Jam"), self.jamToolbar )
+ self.playbackToolbar = PlaybackToolbar( self )
+ self.activity.toolbox.add_toolbar( _("Playback"), self.playbackToolbar )
+ self.desktopToolbar = DesktopToolbar( self )
+ self.activity.toolbox.add_toolbar( _("Desktop"), self.desktopToolbar )
+ if Config.FEATURES_MIC or Config.FEATURES_NEWSOUNDS:
+ self.recordToolbar = RecordToolbar( self )
+ self.activity.toolbox.add_toolbar( _("Record"), self.recordToolbar )
+
+ #-- 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(Tooltips.categories.get(name) or 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 = 2
+ self.keyboardListener = None
+ self.recordingNote = None
+
+ self.keyMap = {}
+
+ # default instrument
+ self._updateInstrument( self.instrumentDB.instNamed["kalimba"].instrumentId, 0.5 )
+ self.instrumentStack = []
+
+ # metronome
+ page = NoteDB.Page( 1, local = False )
+ self.metronomePage = self.noteDB.addPage( -1, page )
+ self.metronome = False
+
+ #-- Drums ---------------------------------------------
+ self.drumLoopId = None
+ # use dummy values for now
+ self.drumFillin = Fillin( 2, 100, self.instrumentDB.instNamed["drum1kit"].instrumentId, 0, 1 )
+
+ #-- Desktops ------------------------------------------
+ self.curDesktop = None
+ # copy preset desktops
+ path = Config.FILES_DIR+"/Desktops/"
+ filelist = os.listdir( path )
+ for file in filelist:
+ shutil.copyfile( path+file, Config.TMP_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.syncBeats = 4
+ self.syncTicks = self.syncBeats*Config.TICKS_PER_BEAT
+ self.offsetTicks = 0 # offset from the true heartbeat
+ self.csnd.loopSetNumTicks( self.syncTicks*HEARTBEAT_BUFFER, self.heartbeatLoop )
+ self.heartbeatStart = time.time()
+ self.csnd.loopStart( self.heartbeatLoop )
+ self.curBeat = 0
+ self.beatWheelTimeout = gobject.timeout_add( 100, self.updateBeatWheel )
+
+ # 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 )
+
+ self.activity.connect( "shared", self.shared )
+
+ if self.activity._shared_activity: # PEER
+ self.activity._shared_activity.connect( "buddy-joined", self.buddy_joined )
+ self.activity._shared_activity.connect( "buddy-left", self.buddy_left )
+ self.activity.connect( "joined", self.joined )
+ self.network.setMode( Net.MD_WAIT )
+
+ #-- Final Set Up --------------------------------------
+ self.setVolume( self.volume )
+ self.setTempo( self.tempo )
+ self.activity.toolbox.set_current_toolbar(1) # JamToolbar
+ self.setDesktop( 0, True )
+
+
+ #==========================================================
+
+ def onActivate( self, arg ):
+ pass
+ def onDeactivate( self ):
+ pass
+
+ def onDestroy( self ):
+ self.network.shutdown()
+
+ #clear up scratch folder
+ path = Config.TMP_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 = self.instrumentDB.instId[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,
+ self.instrumentDB.instNamed[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 = 2
+ self.csnd.play(self.key_dict[key], 0.3)
+
+ return self.key_dict[key]
+
+ def _stopNote( self, key ):
+ csnote = self.key_dict[key]
+ if self.instrumentDB.instId[ 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, sync = True ):
+
+ oldId = loopId
+ loopId = self.csnd.loopCreate()
+
+ noteOnsets = []
+ notePitchs = []
+ for n in self.noteDB.getNotesByTrack( pageId, 0 ):
+ n.pushState()
+ noteOnsets.append( n.cs.onset )
+ notePitchs.append( n.cs.pitch )
+ n.cs.instrumentId = id
+ n.cs.amplitude = volume * n.cs.amplitude
+ 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, self.instrumentDB.instId[id].name, volume, beats, reverb )
+ self.drumFillin.unavailable( noteOnsets, notePitchs )
+
+ self.drumFillin.play()
+
+ if oldId == None:
+ if sync: startTick = self.csnd.loopGetTick( self.heartbeatLoop ) % self.syncTicks
+ else: startTick = 0
+ else:
+ if sync: startTick = self.csnd.loopGetTick( oldId ) # TODO is this really safe? could potentially add several milliseconds of delay everytime a loop is updated
+ else: startTick = 0
+
+ while startTick > ticks:
+ startTick -= ticks
+
+ self.csnd.loopSetTick( startTick, loopId )
+ self.csnd.loopStart( loopId )
+
+ if oldId != None:
+ self.csnd.loopDestroy( oldId )
+
+ return loopId
+
+ def _stopDrum( self, loopId ):
+ self.drumFillin.stop()
+ self.csnd.loopDestroy( loopId )
+
+ def _playLoop( self, id, volume, reverb, tune, loopId = None, force = False, sync = True ):
+
+ oldId = loopId
+ loopId = self.csnd.loopCreate()
+
+ inst = self.instrumentDB.instId[id]
+
+ ticks = 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
+ 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 += ticks
+ self.csnd.loopPlay( n, 1, loopId = loopId )
+ n.popState()
+ for n in self.noteDB.getNotesByTrack( page, 1 ): # metronome track
+ self.csnd.loopPlay( n, 1, loopId = loopId )
+ for n in self.noteDB.getNotesByTrack( page, 2 ): # record scratch track
+ self.csnd.loopPlay( n, 1, loopId = loopId )
+ ticks += self.noteDB.getPage(page).ticks
+
+ self.csnd.loopSetNumTicks( ticks, loopId )
+
+ if oldId == None:
+ if sync: startTick = self.csnd.loopGetTick( self.heartbeatLoop ) % self.syncTicks
+ else: startTick = 0
+ else:
+ if sync: startTick = self.csnd.loopGetTick( oldId ) # TODO is this really safe? could potentially add several milliseconds of delay everytime a loop is updated
+ else: startTick = 0
+
+ while startTick > ticks:
+ startTick -= ticks
+
+ self.csnd.loopSetTick( startTick, loopId )
+ self.csnd.loopStart( loopId )
+
+ if oldId != None:
+ self.csnd.loopDestroy( oldId )
+
+ return loopId
+
+ def _stopLoop( self, loopId ):
+ self.csnd.loopDestroy( loopId )
+
+ def addMetronome( self, page, period ):
+ self.noteDB.deleteNotesByTrack( [ page ], [ 1 ] )
+
+ baseCS = CSoundNote( 0, # onset
+ 36, # pitch
+ 0.2, # amplitude
+ 0.5, # pan
+ 100, # duration
+ 1, # track
+ self.instrumentDB.instNamed["drum1hatpedal"].instrumentId,
+ reverbSend = 0.5,
+ tied = True,
+ mode = 'mini' )
+
+ stream = []
+ offset = 0
+
+ for b in range( self.noteDB.getPage( page ).beats ):
+ cs = baseCS.clone()
+ cs.instrumentId = self.instrumentDB.instNamed["drum1hatshoulder"].instrumentId
+ cs.amplitude = 0.5
+ cs.onset += offset
+
+ stream.append( cs )
+
+ onset = period
+ while onset < Config.TICKS_PER_BEAT:
+ cs = baseCS.clone()
+ cs.onset = onset + offset
+ stream.append( cs )
+ onset += period
+
+ offset += Config.TICKS_PER_BEAT
+
+ self.noteDB.addNotes( [ page, 1, len(stream) ] + stream + [ -1 ] )
+
+ def removeMetronome( self, page ):
+ self.noteDB.deleteNotesByTrack( [ page ], [ 1 ] )
+
+ def setMuted( self, muted ):
+ self.playbackToolbar.setMuted( muted )
+
+ def _setMuted( self, muted ):
+ if self.muted == muted:
+ return False
+
+ if self.muted: # unmute
+ self.muted = False
+ self.csnd.setTrackVolume( 100, 0 )
+ else: # mute
+ self.muted = True
+ self.csnd.setTrackVolume( 0, 0 )
+
+ return True
+
+ def setStopped( self ):
+ for drum in list(self.desktop.drums):
+ self.desktop.deactivateDrum(drum)
+
+ for loop in list(self.desktop.loops): # we copy the list using the list() method
+ self.desktop.deactivateLoop(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( self.instrumentDB.instId[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: [ self.instrumentDB.instId[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 ] )
+
+
+ #==========================================================
+ # Mic recording
+ def micRec(self, widget, mic):
+ self.csnd.inputMessage("i5600 0 4")
+ OS.arecord(4, "crop.csd", mic)
+ self.csnd.load_mic_instrument(mic)
+
+
+ #==========================================================
+ # Loop Settings
+ def loopSettingsChannel(self, channel, value):
+ self.csnd.setChannel(channel, value)
+
+ def loopSettingsPlayStop(self, state, loop):
+ if not state:
+ if loop:
+ self.loopSettingsPlaying = True
+ self.csnd.inputMessage(Config.CSOUND_PLAY_LS_NOTE % 5022)
+ else:
+ self.csnd.inputMessage(Config.CSOUND_PLAY_LS_NOTE % 5023)
+ else:
+ if loop:
+ self.loopSettingsPlaying = False
+ self.csnd.inputMessage(Config.CSOUND_STOP_LS_NOTE)
+
+ def load_ls_instrument(self, soundName):
+ self.csnd.load_ls_instrument(soundName)
+
+ #==========================================================
+ # Get/Set
+
+ def getVolume( self ):
+ return self.volume
+
+ def setVolume( self, volume ):
+ self.jamToolbar.volumeSlider.set_value( volume )
+
+ def _setVolume( self, volume ):
+ if self.muted:
+ self.setMuted( False )
+ 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, quiet = False ):
+ self.jamToolbar.setTempo( tempo, quiet )
+
+ def _setTempo( self, tempo, propagate = True ):
+ if self.network.isHost() or self.network.isOffline():
+ t = time.time()
+ elapsedTicks = (t - self.heartbeatStart)*self.ticksPerSecond
+
+ 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 - elapsedTicks*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 ):
+ win = gtk.gdk.get_default_root_window()
+ try:
+ pix = gtk.gdk.pixbuf_new_from_file( img_path )
+ except:
+ if Config.DEBUG >= 5: print "JamMain:: file does not exist: " + img_path
+ pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT + "generic.png" )
+ 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
+
+ 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 )
+ stream.sync_beats( self.syncBeats )
+
+ scratch.close()
+ except IOError, (errno, strerror):
+ if Config.DEBUG > 3: print "IOError:: _saveDesktop:", errno, strerror
+
+ def getDesktopScratchFile( self, i ):
+ return Config.TMP_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 "TamTamJam:: 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 "TamTamJam:: 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 "TamTamJam:: 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])
+ elapsed = self.unpacker.unpack_float()
+ #print "mini:: got sync: next beat in %f, latency %d" % (nextBeat, latency*1000)
+ self.heartbeatStart = t - elapsed - 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()
+ self.setTempo( val, True )
+ self.sendSyncQuery()
+
+ def processPR_SYNC_QUERY( self, sock, message, data ):
+ self.packer.pack_float(time.time() - self.heartbeatStart)
+ 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.jamToolbar.tempoSliderActive:
+ return
+ self.unpacker.reset(data)
+ val = self.unpacker.unpack_int()
+ self.setTempo( val )
+
+ #==========================================================
+ # Sync
+
+ def setSyncBeats( self, beats ):
+ self.playbackToolbar.setSyncBeats( beats )
+
+ def _setSyncBeats( self, beats ):
+ if beats == self.syncBeats:
+ return
+
+ elapsedTicks = (time.time() - self.heartbeatStart)*self.ticksPerSecond + self.offsetTicks
+ elapsedBeats = int(elapsedTicks) // Config.TICKS_PER_BEAT
+
+ targBeat = (elapsedBeats % self.syncBeats) % beats
+ curBeat = elapsedBeats % beats
+ offset = (targBeat - curBeat) * Config.TICKS_PER_BEAT
+
+ self.syncBeats = beats
+ self.syncTicks = beats*Config.TICKS_PER_BEAT
+
+ self.offsetTicks = (offset + self.offsetTicks) % (self.syncTicks*HEARTBEAT_BUFFER)
+ elapsedTicks += offset
+
+ newTick = elapsedTicks % (self.syncTicks*HEARTBEAT_BUFFER)
+
+ self.csnd.loopSetTick( newTick, self.heartbeatLoop )
+ self.csnd.loopSetNumTicks( self.syncTicks*HEARTBEAT_BUFFER, self.heartbeatLoop )
+
+ self.updateSync()
+
+ def _setBeat( self, beat ):
+ curTick = self.csnd.loopGetTick( self.heartbeatLoop ) % self.syncTicks
+ curBeat = int(curTick) // Config.TICKS_PER_BEAT
+ offset = (beat - curBeat) * Config.TICKS_PER_BEAT
+ if offset > self.syncTicks//2:
+ offset -= self.syncTicks
+ elif offset < -self.syncTicks//2:
+ offset += self.syncTicks
+
+ self.offsetTicks = (offset + self.offsetTicks) % (self.syncTicks*HEARTBEAT_BUFFER)
+
+ for id in self.desktop.getLoopIds() + [ self.heartbeatLoop ]:
+ tick = self.csnd.loopGetTick( id )
+ maxTick = self.csnd.loopGetNumTicks( id )
+ newTick = (tick + offset) % maxTick
+ self.csnd.loopSetTick( newTick, id )
+
+ def updateBeatWheel( self ):
+ curTick = self.csnd.loopGetTick( self.heartbeatLoop ) % self.syncTicks
+ self.curBeat = int( curTick ) // Config.TICKS_PER_BEAT
+ self.playbackToolbar.updateBeatWheel( self.curBeat )
+ return True
+
+ def correctedHeartbeat( self ):
+ elapsedTicks = (time.time() - self.heartbeatStart)*self.ticksPerSecond
+ return (elapsedTicks + self.offsetTicks) % (self.syncTicks*HEARTBEAT_BUFFER)
+
+ def updateSync( self ):
+ if Config.DEBUG:
+ # help the log print out on time
+ sys.stdout.flush()
+
+ 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 )
+ corTick = self.correctedHeartbeat()
+ err = corTick - curTick
+ maxTick = self.syncTicks * HEARTBEAT_BUFFER
+ if err < -maxTick//2: # these should never happen becasue of HEARTBEAT_BUFFER, but hey
+ err += maxTick
+ elif err > maxTick//2:
+ err -= maxTick
+
+ #print "correctSync", curTick, corTick, err, maxTick, self.offsetTicks
+
+ if abs(err) > 4*Config.TICKS_PER_BEAT: # we're way off
+ for id in self.desktop.getLoopIds() + [ self.heartbeatLoop ]:
+ tick = self.csnd.loopGetTick( id )
+ maxTick = self.csnd.loopGetNumTicks( id )
+ newTick = (tick + err) % maxTick
+ self.csnd.loopSetTick( newTick, id )
+ elif abs(err) > 0.25: # soft correction
+ self.csnd.adjustTick( err/3 )
diff --git a/Jam/Parasite.py b/Jam/Parasite.py
new file mode 100644
index 0000000..74b9e0d
--- /dev/null
+++ b/Jam/Parasite.py
@@ -0,0 +1,349 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import common.Config as Config
+
+from common.Util.NoteDB import PARAMETER
+from common.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/Jam/Picker.py b/Jam/Picker.py
new file mode 100644
index 0000000..f1030bf
--- /dev/null
+++ b/Jam/Picker.py
@@ -0,0 +1,398 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import os
+
+import random #TEMP
+import sets
+
+from common.Util.CSoundClient import new_csound_client
+from common.port.scrolledbox import HScrolledBox
+import common.Config as Config
+from gettext import gettext as _
+
+from sugar.graphics.palette import Palette, WidgetInvoker
+
+from common.Util import ControlStream
+from common.Util import InstrumentDB
+
+from Jam import Block
+
+class Picker(HScrolledBox):
+
+ def __init__( self, owner, filter = None ):
+ HScrolledBox.__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.pickerBox = gtk.HBox()
+ self.set_viewport(self.pickerBox)
+ self.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 ):
+ # tooltip
+ invoker = WidgetInvoker(block)
+ invoker._position_hint = WidgetInvoker.AT_CURSOR
+ invoker.set_palette(Palette(name))
+
+ 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.get_adjustment().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.get_adjustment().set_value( self.scroll[filter] )
+ else:
+ self.get_adjustment().set_value( 0 )
+ self.scroll[filter] = 0
+
+ def _testAgainstFilter( self, block ):
+ return 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()
+
+ all = []
+ lab = []
+ mic = []
+
+ for i in self.instrumentDB.getSet( "All" ):
+ if i.name.startswith('lab'):
+ lab.append(i)
+ elif i.name.startswith('mic'):
+ mic.append(i)
+ elif not i.kitStage and not i.kit:
+ all.append(i)
+
+ all += sorted(lab, key=lambda i: i.name)
+ all += sorted(mic, key=lambda i: i.name)
+
+ for inst in all:
+ self.addBlock( inst.instrumentId )
+
+ def addBlock( self, id ):
+ # match data structure of Block.Instrument
+ data = { "name": self.instrumentDB.instId[id].nameTooltip,
+ "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
+
+ if self.instrumentDB.getInstrument( block.data["id"] ).category in self.filter:
+ return True
+
+ return False
+
+ def on_button_press( self, widget, event ):
+ walloc = widget.get_allocation()
+ valloc = self.get_viewport_allocation()
+ loc = ( valloc.x + walloc.x + event.x, -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( "percussions" ):
+ if self.instrumentDB.instNamed[inst.name].kit:
+ self.addBlock( inst.instrumentId )
+
+ def addBlock( self, id ):
+ # match data structure of Block.Drum
+ data = { "name": self.instrumentDB.instId[id].nameTooltip,
+ "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()
+ valloc = self.get_viewport_allocation()
+ loc = ( valloc.x + walloc.x + event.x, -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": _('Loop'),
+ "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()
+ valloc = self.get_viewport_allocation()
+ loc = ( valloc.x + walloc.x + event.x, -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/Jam/Popup.py b/Jam/Popup.py
new file mode 100644
index 0000000..8ff1888
--- /dev/null
+++ b/Jam/Popup.py
@@ -0,0 +1,1466 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import common.Config as Config
+
+from gettext import gettext as _
+from sugar.graphics import style
+from sugar.graphics.palette import Palette, Invoker
+import gobject
+
+from Jam import Block
+from common.Util.NoteDB import PARAMETER
+from common.Util.CSoundNote import CSoundNote
+from common.Util.CSoundClient import new_csound_client
+from Jam.Parasite import LoopParasite
+
+from common.Generation.Generator import generator1, GenerationParameters
+from common.Generation.GenerationConstants import GenerationConstants
+
+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.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:
+ self._palette_popup_sid = None
+
+ def popup( self, immediate = False ):
+ if hasattr(self, '_set_state'):
+ self._set_state(self.SECONDARY)
+ Palette.popup( self, immediate)
+ else:
+ Palette.popup( self, immediate, state = Palette.SECONDARY )
+
+ def popdown( self, immediate = False ):
+ self.block = None
+
+ Palette.popdown( self, immediate )
+
+ 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.01, 0.01, 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( True )
+ self.GUI["volumeSlider"].set_digits( 2 )
+ self.GUI["volumeSlider"].set_value_pos( gtk.POS_RIGHT )
+ 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.01, 0.01, 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( True )
+ self.GUI["panSlider"].set_digits( 2 )
+ self.GUI["panSlider"].set_value_pos( gtk.POS_RIGHT )
+ 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.01, 0.01, 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( True )
+ self.GUI["reverbSlider"].set_digits( 2 )
+ self.GUI["reverbSlider"].set_value_pos(gtk.POS_RIGHT)
+ 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.01, 0.01, 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( True )
+ self.GUI["volumeSlider"].set_digits( 2 )
+ self.GUI["volumeSlider"].set_value_pos(gtk.POS_RIGHT)
+ 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.01, 0.01, 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( True )
+ self.GUI["reverbSlider"].set_digits( 2 )
+ self.GUI["reverbSlider"].set_value_pos( gtk.POS_RIGHT )
+ 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( True )
+ self.GUI["beatsSlider"].set_value_pos( gtk.POS_RIGHT )
+ self.GUI["beatsSlider"].set_digits(0)
+ 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.8, 0.0, 1.0, 0.01, 0.01, 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( True )
+ self.GUI["regularitySlider"].set_digits( 2 )
+ self.GUI["regularitySlider"].set_value_pos( gtk.POS_RIGHT )
+ 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( True )
+ self.GUI["beatsSlider"].set_value_pos( gtk.POS_RIGHT )
+ self.GUI["beatsSlider"].set_digits( 0 )
+ 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.8, 0.0, 1.0, 0.01, 0.01, 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( True )
+ self.GUI["regularitySlider"].set_digits( 2 )
+ self.GUI["regularitySlider"].set_value_pos( gtk.POS_RIGHT )
+ 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.grid = Config.DEFAULT_GRID
+
+ self.activeTrack = 0 # which track notes are being edited/displayed on
+
+ 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 )
+
+ if self.block != None:
+ self.applyNoteSelection( SELECTNOTES.NONE, 0, [], self.curPage )
+
+ 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(), self.activeTrack, self ):
+ n.updateTransform( True )
+ self.invalidatePreview( 0, 0, self.previewDA.width, self.previewDA.height )
+
+ if self.recordLoop:
+ self.owner.removeMetronome( self.curPage )
+ self.owner.addMetronome( self.curPage, self.grid )
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], self.recordLoop, force = True, sync = False )
+
+ def handleRegularity( self, widget ):
+ if not self.settingBlock:
+ self.block.setData( "regularity", widget.get_value() )
+
+ def handleRegenerate( self, widget ):
+ parameters = GenerationParameters(
+ density = [0.9 for x in range(4)],
+ rythmRegularity = [self.block.getData( "regularity" ) for x in range(4)],
+ step = [0.5 for x in range(4)],
+ pitchRegularity = [0. for x in range(4)],
+ articule = [1. for x in range(4)],
+ silence = [0.1 for x in range(4)],
+ pattern = [3 for x in range(4)],
+ scale = GenerationConstants.NATURAL_MINOR)
+
+ self.owner._generateTrack( self.instrument["id"], self.curPage, self.activeTrack, 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, sync = False )
+
+ def handleClear( self, widget ):
+ if self.recording:
+ self.noteDB.deleteNotesByTrack( [ self.curPage ], [ 2 ] )
+ else:
+ 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, sync = False )
+
+ 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, self.activeTrack, 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,
+ self.activeTrack,
+ instrumentId = self.instrument["id"] )
+ cs.pageId = page
+ id = self.noteDB.addNote( -1, page, self.activeTrack, cs )
+ n = self.noteDB.getNote( page, self.activeTrack, id, self )
+ self.selectNotes( { self.activeTrack:[n] }, True )
+ n.playSampleNote( False )
+
+ noteS = self.noteDB.getNotesByTrack( page, self.activeTrack )
+ for note in noteS:
+ if note.cs.onset < onset and (note.cs.onset + note.cs.duration) > onset:
+ self.noteDB.updateNote(self.curPage, self.activeTrack, 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, self.activeTrack, [], 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, self.activeTrack, len( self.selectedNotes[0] ) ]
+ + [ n.note.id for n in self.selectedNotes[0] ]
+ + [ -1 ] )
+ 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, sync = False )
+
+ 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, self.activeTrack, 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.changedMute = self.owner._setMuted( True )
+ self.owner.pushInstrument( self.instrument )
+ self.owner.setKeyboardListener( self )
+
+ self.owner.addMetronome( self.curPage, self.grid )
+
+ # record to scratch track
+ self.owner.noteDB.tracksToClipboard( [ self.curPage ], [ 0 ] )
+ self.owner.noteDB.pasteClipboard( [ self.curPage ], 0, { 2:0 }, { 2:self.instrument["id"] } )
+ self.activeTrack = 2
+
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], force = True, sync = False )
+ self.updatePlayhead()
+ self.recordTimeout = gobject.timeout_add( 20, self._record_timeout )
+ self.recording = True
+
+ def stopRecording( self ):
+ if not self.recording:
+ return
+
+ gobject.source_remove( self.recordTimeout )
+ self.recording = False
+
+ if self.recordingNote:
+ self.finishNote()
+
+ self.owner.removeMetronome( self.curPage )
+
+ # copy scratch track back to default track
+ self.noteDB.deleteNotesByTrack( [ self.curPage ], [ 0 ] )
+ self.owner.noteDB.tracksToClipboard( [ self.curPage ], [ 2 ] )
+ self.owner.noteDB.pasteClipboard( [ self.curPage ], 0, { 0:2 } )
+ self.noteDB.deleteNotesByTrack( [ self.curPage ], [ 2 ] )
+ self.activeTrack = 0
+ self.block.updateLoop()
+
+ self.owner._stopLoop( self.recordLoop )
+ self.recordLoop = None
+ self.clearPlayhead()
+
+ self.owner.popInstrument()
+ self.owner.setKeyboardListener( None )
+ if self.changedMute:
+ self.owner._setMuted( False )
+ self.changedMute = False
+
+ def recordNote( self, pitch ):
+ page = self.block.getData("id")
+ ticks = self.owner.noteDB.getPage(page).ticks
+
+ onset = self.csnd.loopGetTick( self.recordLoop )
+ onset = self.grid * int( onset/self.grid + 0.5 )
+ if onset < 0: onset += ticks
+ elif onset >= ticks: onset -= ticks
+ self.recordingNotePassed = False
+
+ cs = CSoundNote( onset,
+ pitch,
+ 0.75,
+ 0.5,
+ self.grid,
+ 2,
+ instrumentId = self.instrument["id"] )
+ cs.pageId = self.curPage
+
+ for n in self.noteDB.getNotesByTrack( self.curPage, 2 )[:]:
+ if onset < n.cs.onset:
+ break
+ if onset >= n.cs.onset + n.cs.duration:
+ continue
+ if n.cs.onset < onset and n.cs.duration > self.grid:
+ self.noteDB.updateNote( n.page, n.track, n.id, PARAMETER.DURATION, onset - n.cs.onset )
+ else:
+ self.noteDB.deleteNote( n.page, n.track, n.id )
+
+ self.recordingNote = self.noteDB.addNote( -1, self.curPage, 2, cs )
+
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], self.recordLoop, force = True, sync = False )
+
+ def finishNote( self ):
+ self.recordingNote = None
+
+ 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, sync = False )
+
+ def _updateNote( self ):
+ page = self.block.getData("id")
+ ticks = self.owner.noteDB.getPage(page).ticks
+
+ tick = self.csnd.loopGetTick( self.recordLoop )
+ tick = self.grid * int( tick/self.grid + 0.5 )
+ if tick < 0: tick += ticks
+ elif tick >= ticks: tick -= ticks
+
+
+ note = self.noteDB.getNote( self.curPage, self.activeTrack, self.recordingNote )
+
+ if tick > note.cs.onset:
+ self.recordingNotePassed = True
+
+ if self.recordingNotePassed and 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, self.activeTrack ):
+ if n.cs.onset <= note.cs.onset:
+ continue
+ if 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, self.activeTrack ):
+ if n.cs.onset <= note.cs.onset:
+ continue
+ if n.cs.onset < note.cs.onset + note.cs.duration:
+ self.noteDB.deleteNote( n.page, n.track, n.id )
+ else:
+ break
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], self.recordLoop, force = True, sync = False )
+
+ 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()
+ if self.recordLoop:
+ self.recordLoop = self.owner._playLoop( self.instrument["id"], self.instrument["amplitude"], self.instrument["reverb"], [ self.curPage ], self.recordLoop, force = True, sync = False )
+
+ 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(), self.activeTrack, 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/Jam/RythmGenerator.py b/Jam/RythmGenerator.py
new file mode 100644
index 0000000..70e734c
--- /dev/null
+++ b/Jam/RythmGenerator.py
@@ -0,0 +1,80 @@
+import random
+
+import common.Util.InstrumentDB as InstrumentDB
+import common.Config as Config
+from common.Util.CSoundNote import CSoundNote
+from common.Generation.GenerationConstants import GenerationConstants
+from GenRythm import GenRythm
+
+instrumentDB = InstrumentDB.getRef()
+
+def generator( instrument, nbeats, density, regularity, reverbSend ):
+
+ makeRythm = GenRythm()
+
+ noteDuration = GenerationConstants.DOUBLE_TICK_DUR / 2
+ trackId = 0
+ 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 = instrumentDB.instNamed[ instrument ].kit[ drumPitch[0] ]
+
+ 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,
+ instrumentDB.instNamed[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/Jam/Toolbars.py b/Jam/Toolbars.py
new file mode 100644
index 0000000..9069060
--- /dev/null
+++ b/Jam/Toolbars.py
@@ -0,0 +1,600 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+import gobject
+
+import os, commands
+
+from gettext import gettext as _
+
+from sugar.graphics.palette import Palette, WidgetInvoker
+from sugar.graphics.radiotoolbutton import RadioToolButton
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.toggletoolbutton import ToggleToolButton
+from sugar.graphics.combobox import ComboBox
+from sugar.graphics.toolcombobox import ToolComboBox
+
+from common.Util.ThemeWidgets import *
+
+import common.Config as Config
+from common.Util import OS
+
+
+class JamToolbar( gtk.Toolbar ):
+
+ def __init__( self, owner ):
+ gtk.Toolbar.__init__( self )
+
+ self.owner = owner
+
+ self.toolItem = {}
+
+ self.volumeImg = gtk.Image()
+
+ self._insert_separator( True )
+
+ 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( 260, -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.delayedTempo = 0 # used to store tempo updates while the slider is active
+ self.tempoSliderActive = False
+
+ self.tempoAdjustment = gtk.Adjustment( Config.PLAYER_TEMPO_LOWER, Config.PLAYER_TEMPO_LOWER, Config.PLAYER_TEMPO_UPPER+1, 10, 10, 0 )
+ self.tempoAdjustmentHandler = self.tempoAdjustment.connect( 'value-changed', self.handleTempo )
+ self.tempoSlider = gtk.HScale( adjustment = self.tempoAdjustment )
+ self.tempoSlider.set_size_request( 260, -1 )
+ self.tempoSlider.set_draw_value( False )
+ self.tempoSlider.connect("button-press-event", self.handleTempoSliderPress)
+ self.tempoSlider.connect("button-release-event", self.handleTempoSliderRelease)
+ self._add_tooltip( self.tempoSlider, _("Tempo") )
+ self._insert_widget( self.tempoSlider, -1 )
+ self._insert_widget( self.tempoImg, -1 )
+
+ self._insert_separator( True )
+
+ 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 ):
+ if self.owner.network.isPeer():
+ self.owner.requestTempoChange(int(widget.get_value()))
+ else:
+ self._updateTempo( widget.get_value() )
+
+ def setTempo( self, tempo, quiet = False ):
+ if self.tempoSliderActive:
+ self.delayedTempo = tempo
+ elif quiet:
+ self.tempoAdjustment.handler_block( self.tempoAdjustmentHandler )
+ self.tempoAdjustment.set_value( tempo )
+ self._updateTempo( tempo )
+ self.tempoAdjustment.handler_unblock( self.tempoAdjustmentHandler )
+ else:
+ self.tempoAdjustment.set_value( tempo )
+
+ def _updateTempo( self, tempo ):
+ self.owner._setTempo( tempo )
+
+ img = self.mapRange( tempo, self.tempoAdjustment.lower, self.tempoAdjustment.upper, 1, 8 )
+ self.tempoImg.set_from_file(Config.TAM_TAM_ROOT + '/icons/tempo' + str(img) + '.svg')
+
+ def handleTempoSliderPress(self, widget, event):
+ self.tempoSliderActive = True
+
+ def handleTempoSliderRelease(self, widget, event):
+ self.tempoSliderActive = False
+ if self.owner.network.isPeer() and self.delayedTempo != 0:
+ if self.owner.getTempo() != self.delayedTempo:
+ self.setTempo( self.delayedTempo, True )
+ self.delayedTempo = 0
+ self.owner.sendSyncQuery()
+
+class PlaybackToolbar( gtk.Toolbar ):
+
+ def __init__( self, owner ):
+ gtk.Toolbar.__init__( self )
+
+ self.owner = owner
+
+ self.toolItem = {}
+
+ self.stopButton = ToolButton('media-playback-stop')
+ self.stopButton.connect('clicked',self.handleStopButton)
+ self.insert(self.stopButton, -1)
+ self.stopButton.show()
+ self.stopButton.set_tooltip(_('Stop Loops'))
+
+ self.muteButton = ToggleToolButton('mute')
+ self.muteButton.connect('clicked',self.handleMuteButton)
+ self.insert(self.muteButton, -1)
+ self.muteButton.show()
+ self.muteButton.set_tooltip(_('Mute Loops'))
+
+ self._insert_separator( True )
+
+ self.blockBeat = False
+ self.beatWheel = []
+
+ btn = RadioToolButton(group = None )
+ btn.set_named_icon('beats')
+ btn.connect( 'toggled', self.setBeat, 0 )
+ btn.set_tooltip( _('Jump To Beat') )
+ self.insert( btn, -1 )
+ self.beatWheel.append( btn )
+
+ for i in range(1,12):
+ btn = RadioToolButton(group = self.beatWheel[0] )
+ btn.set_named_icon('beats')
+ btn.connect( 'toggled', self.setBeat, i )
+ btn.set_tooltip( _('Jump To Beat') )
+ self.insert( btn, -1 )
+ self.beatWheel.append( btn )
+
+
+ label = gtk.Label( _("Synch to:") )
+ self.syncLabel = gtk.ToolItem()
+ self.syncLabel.add( label )
+ self.insert( self.syncLabel, -1 )
+
+ self.comboBox = ComboBox()
+ self.comboBox.append_item( 1, _("1 Beat") )
+ self.comboBox.append_item( 2, _("2 Beats") )
+ self.comboBox.append_item( 3, _("3 Beats") )
+ self.comboBox.append_item( 4, _("4 Beats") )
+ self.comboBox.append_item( 5, _("5 Beats") )
+ self.comboBox.append_item( 6, _("6 Beats") )
+ self.comboBox.append_item( 7, _("7 Beats") )
+ self.comboBox.append_item( 8, _("8 Beats") )
+ self.comboBox.append_item( 9, _("9 Beats") )
+ self.comboBox.append_item( 10, _("10 Beats") )
+ self.comboBox.append_item( 11, _("11 Beats") )
+ self.comboBox.append_item( 12, _("12 Beats") )
+ self.comboBox.set_active( 4 - 1 ) # default 4 beats
+ self.comboBox.connect( "changed", self.changeSync )
+ self.syncBox = ToolComboBox( self.comboBox )
+ self.insert( self.syncBox, -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 setBeat( self, widget, beat ):
+ if not self.blockBeat and widget.get_active():
+ self.owner._setBeat( beat )
+
+ def updateBeatWheel( self, beat ):
+ self.blockBeat = True
+ self.beatWheel[ beat ].set_active( True )
+ self.blockBeat = False
+
+ def setSyncBeats( self, beats ):
+ self.comboBox.set_active( beats - 1 )
+
+ def changeSync( self, widget ):
+ beats = widget.get_active() + 1
+ for i in range(beats):
+ self.beatWheel[i].show()
+ for i in range(beats,12):
+ self.beatWheel[i].hide()
+
+ self.owner._setSyncBeats( beats )
+
+ def handleStopButton( self, widget ):
+ self.owner.setStopped()
+
+ def setMuted( self, muted ):
+ if self.muteButton.get_active() == muted:
+ return
+
+ self.muteButton.set_active( muted )
+
+ def handleMuteButton( self, widget ):
+ if widget.get_active():
+ self.owner._setMuted( True )
+ else:
+ self.owner._setMuted( False )
+
+
+class DesktopToolbar( gtk.Toolbar ):
+
+ def __init__( self, owner ):
+ gtk.Toolbar.__init__( self )
+
+ self.owner = owner
+
+ self._insert_separator( True )
+
+ self.desktop = []
+
+ btn = RadioToolButton(group = None )
+ btn.set_named_icon('preset1')
+ 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( group = self.desktop[0] )
+ btn.set_named_icon('preset%d'%i)
+ 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 )
+
+class RecordToolbar(gtk.Toolbar):
+ def __init__(self, jam):
+ gtk.Toolbar.__init__(self)
+
+ def _insertSeparator(x = 1):
+ for i in range(x):
+ self.separator = gtk.SeparatorToolItem()
+ self.separator.set_draw(True)
+ self.insert(self.separator,-1)
+ self.separator.show()
+
+ #self.toolbox = toolbox
+ self.jam = jam
+
+ if Config.FEATURES_MIC:
+ self.micRec1Button = ToolButton('rec1')
+ self.micRec1Button.connect('clicked',self.jam.micRec,'mic1')
+ self.insert(self.micRec1Button, -1)
+ self.micRec1Button.show()
+ self.micRec1Button.set_tooltip(_('Record microphone into slot 1'))
+
+ self.micRec2Button = ToolButton('rec2')
+ self.micRec2Button.connect('clicked',self.jam.micRec,'mic2')
+ self.insert(self.micRec2Button, -1)
+ self.micRec2Button.show()
+ self.micRec2Button.set_tooltip(_('Record microphone into slot 2'))
+
+ self.micRec3Button = ToolButton('rec3')
+ self.micRec3Button.connect('clicked',self.jam.micRec,'mic3')
+ self.insert(self.micRec3Button, -1)
+ self.micRec3Button.show()
+ self.micRec3Button.set_tooltip(_('Record microphone into slot 3'))
+
+ self.micRec4Button = ToolButton('rec4')
+ self.micRec4Button.connect('clicked',self.jam.micRec,'mic4')
+ self.insert(self.micRec4Button, -1)
+ self.micRec4Button.show()
+ self.micRec4Button.set_tooltip(('Record microphone into slot 4'))
+
+ _insertSeparator()
+
+ if Config.FEATURES_NEWSOUNDS:
+ self._loopSettingsPalette = LoopSettingsPalette(_('Add new Sound'), self.jam)
+ self.loopSetButton = ToggleToolButton('loop')
+ self.loopSetButton.set_palette(self._loopSettingsPalette)
+ self.insert(self.loopSetButton, -1)
+ self.loopSetButton.show()
+
+ self.show_all()
+
+class LoopSettingsPalette( Palette ):
+ def __init__( self, label, jam ):
+ Palette.__init__( self, label )
+ self.connect('popup', self.handlePopup)
+ self.connect('popdown', self.handlePopdown)
+
+ self.jam = jam
+
+ self.tooltips = gtk.Tooltips()
+ self.loopedSound = False
+ self.soundLength = 1.00
+ self.start = 0
+ self.end = 1.00
+ self.dur = 0.01
+ self.volume = 1
+ self.register = 0
+ self.ok = True
+
+ self.mainBox = gtk.VBox()
+
+ self.controlsBox = gtk.HBox()
+
+ self.GUI = {}
+
+ self.soundBox = gtk.HBox()
+ self.soundLabel = gtk.Label(_('Sound: '))
+ self.soundMenuBox = BigComboBox()
+ self.sounds = [snd for snd in os.listdir(Config.DATA_DIR) if snd != 'snds_info']
+ for sound in self.sounds:
+ self.soundMenuBox.append_item(self.sounds.index(sound), sound)
+ self.soundMenuBox.connect('changed', self.handleSound)
+ self.soundBox.pack_start(self.soundLabel, False, False, padding=10)
+ self.soundBox.pack_start(self.soundMenuBox, False, False, padding=10)
+
+ self.mainBox.pack_start(self.soundBox, False, False, 10)
+
+ nameBox = gtk.VBox()
+ self.nameEntry = gtk.Entry()
+ entrycolor = gtk.gdk.Color()
+ self.nameEntry.modify_text(gtk.STATE_NORMAL, entrycolor)
+ self.nameEntry.set_text("name_of_the_sound")
+ nameBox.pack_start(self.nameEntry)
+ self.mainBox.pack_start(nameBox, False, False, 10)
+
+ registerBox = gtk.HBox()
+ self.registerBoxLabel = gtk.Label(_('Register: '))
+ self.registerMenuBox = BigComboBox()
+ self.registers = ['LOW', 'MID', 'HIGH', 'PUNCH']
+ for reg in self.registers:
+ self.registerMenuBox.append_item(self.registers.index(reg), reg)
+ self.registerMenuBox.connect('changed', self.handleRegister)
+ registerBox.pack_start(self.registerBoxLabel, False, False, padding=10)
+ registerBox.pack_end(self.registerMenuBox, False, False, padding=10)
+ self.mainBox.pack_start(registerBox, False, False, 10)
+
+ loopedBox = gtk.HBox()
+ loopedLabel = gtk.Label("Looped sound: ")
+ loopedToggle = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ loopedToggle.connect('button-press-event', self.handleLooped )
+ loopedBox.pack_start(loopedLabel, False, False, padding=10)
+ loopedBox.pack_end(loopedToggle, False, False, padding=10)
+ self.mainBox.pack_start(loopedBox, False, False, 10)
+
+ startBox = gtk.VBox()
+ self.startAdjust = gtk.Adjustment( 0.01, 0, 1., .001, .001, 0)
+ self.GUI['startSlider'] = gtk.VScale( adjustment = self.startAdjust )
+ self.startAdjust.connect("value-changed", self.handleStart)
+ self.GUI['startSlider'].set_inverted(True)
+ self.GUI['startSlider'].set_size_request(50, 200)
+ self.GUI['startSlider'].set_digits(3)
+ # tooltip closes loop settings palette!!!
+ #self._add_tooltip( self.GUI['startSlider'], _("Loop start position") )
+ self.handleStart( self.startAdjust )
+ startBox.pack_start(self.GUI['startSlider'], True, True, 5)
+ self.controlsBox.pack_start(startBox)
+
+ endBox = gtk.VBox()
+ self.endAdjust = gtk.Adjustment( 0.9, 0, 1, .001, .001, 0)
+ self.GUI['endSlider'] = gtk.VScale( adjustment = self.endAdjust )
+ self.endAdjust.connect("value-changed", self.handleEnd)
+ self.GUI['endSlider'].set_inverted(True)
+ self.GUI['endSlider'].set_size_request(50, 200)
+ self.GUI['endSlider'].set_digits(3)
+ self.handleEnd( self.endAdjust )
+ endBox.pack_start(self.GUI['endSlider'], True, True, 5)
+ self.controlsBox.pack_start(endBox)
+
+ durBox = gtk.VBox()
+ self.durAdjust = gtk.Adjustment( 0.01, 0, 0.2, .001, .001, 0)
+ self.GUI['durSlider'] = gtk.VScale( adjustment = self.durAdjust )
+ self.durAdjust.connect("value-changed", self.handleDur)
+ self.GUI['durSlider'].set_inverted(True)
+ self.GUI['durSlider'].set_size_request(50, 200)
+ self.GUI['durSlider'].set_digits(3)
+ self.handleDur( self.durAdjust )
+ durBox.pack_start(self.GUI['durSlider'], True, True, 5)
+ self.controlsBox.pack_start(durBox)
+
+ volBox = gtk.VBox()
+ self.volAdjust = gtk.Adjustment( 1, 0, 2, .01, .01, 0)
+ self.GUI['volSlider'] = gtk.VScale( adjustment = self.volAdjust )
+ self.volAdjust.connect("value-changed", self.handleVol)
+ self.GUI['volSlider'].set_inverted(True)
+ self.GUI['volSlider'].set_size_request(50, 200)
+ self.GUI['volSlider'].set_digits(3)
+ self.handleVol( self.volAdjust )
+ volBox.pack_start(self.GUI['volSlider'], True, True, 5)
+ self.controlsBox.pack_start(volBox)
+
+ self.mainBox.pack_start(self.controlsBox, False, False, 10)
+
+ previewBox = gtk.VBox()
+ self.playStopButton = ImageToggleButton(Config.IMAGE_ROOT + 'miniplay.png', Config.IMAGE_ROOT + 'stop.png')
+ self.playStopButton.connect('button-press-event' , self.handlePlayButton)
+ previewBox.pack_start(self.playStopButton)
+ self.mainBox.pack_start(previewBox, False, False, 10)
+
+ checkBox = gtk.VBox()
+ checkButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/accept.svg')
+ checkButton.connect('clicked' , self.handleCheck)
+ checkBox.pack_start(checkButton)
+ self.mainBox.pack_start(checkBox, False, False, 10)
+
+ self.mainBox.show_all()
+ self.set_content(self.mainBox)
+
+ 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 handlePopup(self, widget, data=None):
+ self.setButtonState()
+ self.soundMenuBox.remove_all()
+ self.sounds = [snd for snd in os.listdir(Config.DATA_DIR) if snd != 'snds_info']
+ for sound in self.sounds:
+ self.soundMenuBox.append_item(self.sounds.index(sound), sound)
+ self.nameEntry.set_text("name_of_the_sound")
+
+ def handlePopdown(self, widget, data=None):
+ if self.playStopButton.get_active() == True:
+ self.jam.loopSettingsPlayStop(True, self.loopedSound)
+
+ def handleSound(self, widget, data=None):
+ self.sndname = self.sounds[widget.props.value]
+ fullname = Config.DATA_DIR + '/' + self.sndname
+ results = OS.system("du -b %s" % fullname)
+ if results[0] == 0:
+ list = results[1].split()
+ soundLength = float(list[0]) / 2 / 16000.
+ self.nameEntry.set_text(self.sndname)
+ self.set_values(soundLength)
+ self.startAdjust.set_all( 0.01, 0, soundLength, .001, .001, 0)
+ self.endAdjust.set_all( soundLength-0.01, 0, soundLength, .001, .001, 0)
+ self.timeoutLoad = gobject.timeout_add(2000, self.loopSettingsDelay)
+
+ def loopSettingsDelay(self):
+ self.jam.load_ls_instrument(self.sndname)
+ gobject.source_remove( self.timeoutLoad )
+
+ def handleCheck(self, widget):
+ if self.nameEntry.get_text() != self.sndname:
+ oldName = self.sndname
+ self.sndname = self.nameEntry.get_text()
+ copy = True
+ else:
+ copy = False
+
+ ofile = open(Config.SNDS_INFO_DIR + '/' + self.sndname, 'w')
+ if self.loopedSound:
+ tied = str(Config.INST_TIED)
+ else:
+ tied = str(Config.INST_SIMP)
+ register = str(self.register)
+ category = 'mysounds'
+ start = str(self.start)
+ end = str(self.end)
+ dur = str(self.dur)
+ vol = str(self.volume)
+
+ ofile.write('TamTam idf v1\n')
+ ofile.write(self.sndname + '\n')
+ ofile.write(tied + '\n')
+ ofile.write(register + '\n')
+ ofile.write(start + '\n')
+ ofile.write(end + '\n')
+ ofile.write(dur + '\n')
+ ofile.write(vol + '\n')
+ ofile.write(self.sndname + '\n')
+ ofile.write(Config.IMAGE_ROOT+"/"+self.sndname+".png\n")
+ ofile.write(category)
+ ofile.close()
+ if copy:
+ OS.system('cp ' + Config.DATA_DIR + '/' + oldName + ' ' + Config.DATA_DIR + '/' + self.sndname)
+
+ def set_values(self, soundLength):
+ self.soundLength = soundLength
+ self.handleStart(self.GUI['startSlider'])
+ self.handleEnd(self.GUI['endSlider'])
+
+ def handleLooped(self, widget, data=None):
+ if widget.get_active() == True:
+ self.loopedSound = False
+ else:
+ self.loopedSound = True
+
+ def handleRegister(self, widget, data=None):
+ self.register = self.registers[widget.props.value]
+
+ def handleStart(self, widget, data=None):
+ self.start = self.startAdjust.value
+ if self.start > self.end:
+ self.start = self.end
+ self.jam.loopSettingsChannel('lstart', self.start)
+
+ def handleEnd(self, widget, data=None):
+ self.end = self.endAdjust.value
+ if self.end < self.start:
+ self.end = self.start
+ self.jam.loopSettingsChannel('lend', self.end)
+
+ def handleDur(self, widget, data=None):
+ self.dur = self.durAdjust.value
+ self.jam.loopSettingsChannel('ldur', self.dur)
+
+ def handleVol(self, widget, data=None):
+ self.volume = self.volAdjust.value
+ self.jam.loopSettingsChannel('lvol', self.volume)
+
+ def handlePlayButton(self, widget, data=None):
+ if self.ok:
+ self.jam.loopSettingsPlayStop(widget.get_active(), self.loopedSound)
+ if self.loopedSound == False and widget.get_active() == False:
+ self.timeoutStop = gobject.timeout_add(int(self.soundLength * 1000)+500, self.playButtonState)
+
+ def setButtonState(self):
+ self.ok = False
+ self.playStopButton.set_active(False)
+ self.ok = True
+
+ def playButtonState(self):
+ self.ok = False
+ self.playStopButton.set_active(False)
+ gobject.source_remove(self.timeoutStop)
+ self.ok = True
diff --git a/Jam/__init__.py b/Jam/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Jam/__init__.py