Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Jam
diff options
context:
space:
mode:
authoramartin <olpc@xo-05-28-21.localdomain>2007-08-28 11:58:27 (GMT)
committer amartin <olpc@xo-05-28-21.localdomain>2007-08-28 11:58:27 (GMT)
commit82bce73030735492a017113f0486ebe8aae5fae6 (patch)
tree4e606e9e8b1183b43f90fbb572a78f3be9ee2ac7 /Jam
parent66de29ff7807cbc2d0849ece71146fc9e72e1fe4 (diff)
Jam save/load
Diffstat (limited to 'Jam')
-rw-r--r--Jam/Block.py134
-rw-r--r--Jam/Desktop.py24
-rw-r--r--Jam/Fillin.py10
-rw-r--r--Jam/JamMain.py154
-rw-r--r--Jam/Picker.py10
-rw-r--r--Jam/Toolbars.py24
6 files changed, 298 insertions, 58 deletions
diff --git a/Jam/Block.py b/Jam/Block.py
index 5695987..424b257 100644
--- a/Jam/Block.py
+++ b/Jam/Block.py
@@ -18,9 +18,9 @@ class Block():
SNAP = 15
- def __init__( self, owner, graphics_context, data ):
+ def __init__( self, owner, data ):
self.owner = owner
- self.gc = graphics_context
+ self.gc = owner.gc
self.data = {}
for key in data.keys():
@@ -50,7 +50,7 @@ class Block():
self.active = False
def dumpToStream( self, ostream, child = False ):
- ostream.block_add( ClassToStr[ self.type ], self.x, self.y, child, self.data )
+ 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 )
@@ -76,7 +76,7 @@ class Block():
self.x = int(x)
self.y = int(y)
- self.endX = self.x + self.getWidth()
+ self.endX = self.x + self.width
self.endY = self.y + self.height
self.invalidate_rect( not self.dragging )
@@ -85,7 +85,12 @@ class Block():
self.child.snapToParentLoc( self.getChildAnchor() )
def resetLoc( self ):
- self.setLoc( self.oldLoc[0], self.oldLoc[1] )
+ 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 )
@@ -96,14 +101,12 @@ class Block():
def snapToParentLoc( self, loc ):
self.setLoc( loc[0] - self.parentOffset, loc[1] )
- def getWidth( self ):
- return self.width
-
def substitute( self, block ):
pass # override in subclasses
def testSubstitute( self, block ):
- return False
+ if self.child:
+ return self.child.testSubstitute( block )
def testChild( self, loc ):
@@ -118,10 +121,17 @@ class Block():
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
@@ -158,6 +168,7 @@ class Block():
else:
return False
+ self.oldParent = self.parent
self.oldLoc = ( self.x, self.y )
self.dragOffset = ( event.x - self.x, event.y - self.y )
@@ -191,7 +202,7 @@ class Block():
def _beginDrag( self ):
self.dragging = True
- self.dragOffset = ( self.getWidth()//2, self.height//2 )
+ self.dragOffset = ( self.width//2, self.height//2 )
def invalidateBranch( self, base = True ):
self.invalidate_rect( base )
@@ -199,7 +210,7 @@ class Block():
self.child.invalidateBranch( base )
def invalidate_rect( self, base = True ):
- self.owner.invalidate_rect( self.x, self.y, self.getWidth(), self.height, base )
+ 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:
@@ -235,8 +246,8 @@ class Instrument(Block):
#::: data format:
# { "name": name, "id": instrumentId [, "volume": 0-1 ] }
#:::
- def __init__( self, owner, graphics_context, data ):
- Block.__init__( self, owner, graphics_context, data )
+ def __init__( self, owner, data ):
+ Block.__init__( self, owner, data )
self.type = Instrument
@@ -255,8 +266,15 @@ class Instrument(Block):
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 ):
- if self.type == Loop:
+ 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:
@@ -303,8 +321,8 @@ class Drum(Block):
#::: data format:
# { "name": name, "id": instrumentId [, "volume": 0-1, "beats": 2-12, "regularity": 0-1, "seed": 0-1 ] }
#:::
- def __init__( self, owner, graphics_context, data ):
- Block.__init__( self, owner, graphics_context, data )
+ def __init__( self, owner, data ):
+ Block.__init__( self, owner, data )
self.type = Drum
@@ -336,7 +354,11 @@ class Drum(Block):
self.owner.updateDrum()
def testSubstitute( self, block ):
- if self.type == Loop:
+ ret = Block.testSubstitute( self, block )
+ if ret:
+ return ret
+
+ if block.type == Loop:
return False
if Config.INSTRUMENTSID[block.data["id"]].kit == None:
@@ -400,31 +422,85 @@ class Loop(Block):
#::: data format:
# { "name": name, "id": pageId }
#:::
- def __init__( self, owner, graphics_context, data ):
- Block.__init__( self, owner, graphics_context, data )
+ 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
+ beats = self.owner.noteDB.getPage(self.data["id"]).beats
+ self.width = Loop.WIDTH[beats]
+
+ self.img = [ self.owner.getLoopImage( self.data["id"], False ),
+ self.owner.getLoopImage( self.data["id"], True ) ]
+
+ def destroy( self ):
+ self.owner.noteDB.deletePages( [ self.data["id"] ] )
+ Block.destroy( self )
+
def substitute( self, block ):
- self.data["id"] = block.data["id"]
+ self.invalidateBranch( True )
- self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
- self.owner.getInstrumentImage( self.data["id"], True ) ]
+ oldWidth = self.width
- self.invalidate_rect( True )
+ newid = self.owner.noteDB.duplicatePages( [ block.data["id"] ] )[block.data["id"]]
+ self.owner.updateLoopImage( newid )
+ self.data["id"] = newid
- if self.active:
- self.owner.updateDrum()
+ self.img = [ self.owner.getLoopImage( self.data["id"], False ),
+ self.owner.getLoopImage( self.data["id"], True ) ]
- def getWidth( self ):
beats = self.owner.noteDB.getPage(self.data["id"]).beats
- return Loop.WIDTH[beats]
-
+ self.width = Loop.WIDTH[beats]
+ self.endX = self.x + self.width
+
+ 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.child:
+ self.child.snapToParentLoc( self.getChildAnchor() )
+
+ if oldWidth < self.width: # or block.child:
+ self.invalidateBranch( True )
+
+ 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 )
@@ -472,7 +548,7 @@ class Loop(Block):
endY = min( stopY, self.endY )
height = endY - y
- loop = self.owner.getLoopImage( self.data["id"], self.active )
+ 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"]
diff --git a/Jam/Desktop.py b/Jam/Desktop.py
index 40b5675..a28a917 100644
--- a/Jam/Desktop.py
+++ b/Jam/Desktop.py
@@ -51,8 +51,8 @@ class Desktop( gtk.EventBox ):
def dumpToStream( self, ostream ):
for b in self.blocks:
- block.dumpToStream( ostream )
-
+ b.dumpToStream( ostream )
+
def 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()
@@ -73,7 +73,7 @@ class Desktop( gtk.EventBox ):
def addBlock( self, blockClass, blockData, loc = (-1,-1), drag = False ):
- block = blockClass( self, self.gc, blockData )
+ block = blockClass( self, blockData )
if loc[0] != -1: x = loc[0]
else: x = self.alloc.width//2
@@ -93,12 +93,14 @@ class Desktop( gtk.EventBox ):
block.setLoc( x - block.width//2, y - block.height//2 )
if blockClass == Block.Instrument:
- self.activateInstrument( block )
+ 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:
@@ -121,12 +123,19 @@ class Desktop( gtk.EventBox ):
block.destroy()
+ def _clearDesktop( self ):
+ for i in range( len(self.blocks)-1, -1, -1 ):
+ self.deleteBlock( self.blocks[i] )
+
def getInstrumentImage( self, id, active = False ):
return self.owner.getInstrumentImage( id, active )
def getLoopImage( self, id, active = False ):
return self.owner.getLoopImage( id, active )
+ def updateLoopImage( self, id ):
+ self.owner.updateLoopImage( id )
+
#==========================================================
# State
@@ -226,12 +235,13 @@ class Desktop( gtk.EventBox ):
elif self.possibleSubstitute:
self.possibleSubstitute.substitute( self.clickedBlock )
if self.clickedBlock.isPlaced():
- self.clickedBlock.resetLoc()
- self.blocks.append( self.clickedBlock )
+ if self.clickedBlock.resetLoc():
+ self.blocks.append( self.clickedBlock )
else:
self.deleteBlock( self.clickedBlock )
self.clickedBlock = None
- self.activateInstrument( self.possibleSubstitute )
+ if self.possibleSubstitute.type == Block.Instrument:
+ self.activateInstrument( self.possibleSubstitute )
self.possibleSubstitute = None
else:
self.blocks.append( self.clickedBlock )
diff --git a/Jam/Fillin.py b/Jam/Fillin.py
index f17fe01..8f31489 100644
--- a/Jam/Fillin.py
+++ b/Jam/Fillin.py
@@ -21,12 +21,16 @@ class Fillin:
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 )
@@ -70,11 +74,11 @@ class Fillin:
def clear( self ):
if self.notesList:
for n in self.notesList:
- self.csnd.loopDelete(n)
+ self.csnd.loopDelete(n, self.loopId)
self.notesList = []
def handleClock( self ):
- tick = self.csnd.loopGetTick()
+ tick = self.csnd.loopGetTick( self.loopId )
if tick < ( Config.TICKS_PER_BEAT / 2 + 1 ):
if self.gate == 0:
self.gate = 1
@@ -108,5 +112,5 @@ class Fillin:
n = Note(0, x.trackId, i, x)
self.notesList.append(n)
i += 1
- self.csnd.loopPlay(n,1) #add as active
+ self.csnd.loopPlay(n,1, loopId = self.loopId ) #add as active
diff --git a/Jam/JamMain.py b/Jam/JamMain.py
index 5763ffa..69b9677 100644
--- a/Jam/JamMain.py
+++ b/Jam/JamMain.py
@@ -5,6 +5,8 @@ import gtk
from SubActivity import SubActivity
+import os, sys, shutil
+
import Config
from gettext import gettext as _
import sugar.graphics.style as style
@@ -25,6 +27,8 @@ from RythmGenerator import generator
from Generation.GenerationConstants import GenerationConstants
from Util.NoteDB import Note
+from Util import ControlStream
+
from math import sqrt
class JamMain(SubActivity):
@@ -212,20 +216,39 @@ class JamMain(SubActivity):
self._updateInstrument( Config.INSTRUMENTS["kalimba"].instrumentId, 0.5 )
#-- Drums ---------------------------------------------
+ self.drumLoopId = None
# use dummy values for now
self.drumFillin = Fillin( 2, 100, Config.INSTRUMENTS["drum1kit"].instrumentId, self.reverb, 1 )
+ #-- Desktops ------------------------------------------
+ self.curDesktop = None
+ # copy preset desktops
+ path = Config.TAM_TAM_ROOT+"/Resources/Desktops/"
+ filelist = os.listdir( path )
+ for file in filelist:
+ shutil.copyfile( path+file, Config.SCRATCH_DIR+file )
+
+ self._setDesktop( 0 )
+
+
#==========================================================
# SubActivity Handlers
def onActivate( self, arg ):
- pass
+ SubActivity.onActivate( self, arg )
def onDeactivate( self ):
- pass
+ SubActivity.onDeactivate( self )
def onDestroy( self ):
- pass
+ SubActivity.onDestroy( self )
+
+ # clear up scratch folder
+ path = Config.SCRATCH_DIR
+ filelist = os.listdir( path )
+ for file in filelist:
+ os.remove( path+file )
+
#==========================================================
# Playback
@@ -313,31 +336,35 @@ class JamMain(SubActivity):
rval += l
return rval
+ if self.drumLoopId != None:
+ self._stopDrum()
+
+ self.drumLoopId = self.csnd.loopCreate()
+
noteOnsets = []
notePitchs = []
i = 0
- self.noteList= []
- self.csnd.loopClear()
for x in flatten( generator( Config.INSTRUMENTSID[id].name, beats, 0.8, regularity, self.reverb) ):
x.amplitude = x.amplitude * volume
noteOnsets.append(x.onset)
notePitchs.append(x.pitch)
n = Note(0, x.trackId, i, x)
- self.noteList.append( (x.onset, n) )
i = i + 1
- self.csnd.loopPlay(n,1) #add as active
- self.csnd.loopSetNumTicks( beats * Config.TICKS_PER_BEAT )
+ self.csnd.loopPlay( n, 1, loopId = self.drumLoopId ) #add as active
+ self.csnd.loopSetNumTicks( beats * Config.TICKS_PER_BEAT, self.drumLoopId )
+ self.drumFillin.setLoopId( self.drumLoopId )
self.drumFillin.setProperties( self.tempo, Config.INSTRUMENTSID[id].name, volume, beats, self.reverb )
self.drumFillin.unavailable( noteOnsets, notePitchs )
self.drumFillin.play()
- self.csnd.loopSetTick(0)
- self.csnd.loopStart()
+ #self.csnd.loopSetTick( 0 )
+ self.csnd.loopStart( self.drumLoopId )
def _stopDrum( self ):
self.drumFillin.stop()
- self.csnd.loopPause()
+ self.csnd.loopDestroy( self.drumLoopId )
+ self.drumLoopId = None
def _playLoop( self, id, volume, tune, loopId = None ):
if loopId == None: # create new loop
@@ -348,25 +375,26 @@ class JamMain(SubActivity):
self.csnd.loopDestroy( loopId )
loopId = self.csnd.loopCreate()
+ inst = Config.INSTRUMENTSID[id]
+
offset = 0
- print "------------", loopId, tune
- temp = []
for page in tune:
for n in self.noteDB.getNotesByTrack( page, 0 ):
- temp.append( n )
n.pushState()
n.cs.instrumentId = id
+ if inst.kit: # drum kit
+ if n.cs.pitch in GenerationConstants.DRUMPITCH:
+ n.cs.pitch = GenerationConstants.DRUMPITCH[n.cs.pitch]
n.cs.onset += offset
self.csnd.loopPlay( n, 1, loopId = loopId )
n.popState()
offset += self.noteDB.getPage(page).ticks
- print temp
self.csnd.loopSetNumTicks( offset, loopId )
while startTick > offset: # align with last beat
- startTick -= Config.TICK_PER_BEAT
+ startTick -= Config.TICKS_PER_BEAT
self.csnd.loopSetTick( startTick, loopId )
@@ -489,3 +517,97 @@ class JamMain(SubActivity):
return self.loopPitchOffset + int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+ #==========================================================
+ # 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 ):
+ self.desktopToolbar.desktop[desktop].set_active( True )
+
+ def _setDesktop( self, desktop ):
+ if self.curDesktop == desktop:
+ return
+
+ 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:
+ if Config.DEBUG > 3: print "ERROR:: setDesktop: unable to open file: " + filename
+
+ #==========================================================
+ # Load/Save
+
+ def _saveDesktop( self ):
+ if self.curDesktop == None:
+ return
+
+ filename = self.getDesktopScratchFile( self.curDesktop )
+ if os.path.isfile( filename ):
+ os.remove( filename )
+
+ try:
+ scratch = open( filename, "w" )
+ stream = ControlStream.TamTamOStream(scratch)
+
+ self.noteDB.dumpToStream( stream, True )
+ self.desktop.dumpToStream( stream )
+
+ scratch.close()
+ except:
+ print "ERROR:: _clearDesktop: unable to open file: " + filename
+
+ def getDesktopScratchFile( self, i ):
+ return Config.SCRATCH_DIR+"desktop%d" % i
+
+ def handleJournalLoad( self, filepath ):
+
+ self._clearDesktop( False )
+
+ TTTable = ControlStream.TamTamTable( self.noteDB, jam = self )
+
+ try:
+ stream = open( filepath, "r" )
+ TTTable.parseFile( stream )
+ stream.close()
+ except:
+ if Config.DEBUG > 3: print "ERROR:: handleJournalLoad:", sys.exc_info()[0]
+
+ def handleJournalSave( self, filepath ):
+
+ self._saveDesktop()
+
+ try:
+ streamF = open( filepath, "w" )
+ stream = ControlStream.TamTamOStream( streamF )
+
+ for i in range(10):
+ desktop_file = Config.TAM_TAM_ROOT+"/.scratch/desktop%d" % i
+ stream.desktop_store( desktop_file, i )
+
+ stream.desktop_set( self.curDesktop )
+
+ streamF.close()
+
+ self.curDesktop = None
+ except:
+ if Config.DEBUG > 3: print "ERROR:: handleJournalSave:", sys.exc_info()[0]
+
+
diff --git a/Jam/Picker.py b/Jam/Picker.py
index cfff3e6..f41f1b2 100644
--- a/Jam/Picker.py
+++ b/Jam/Picker.py
@@ -214,7 +214,9 @@ class Instrument( Picker ):
walloc = widget.get_allocation()
salloc = self.scrolledWindow.get_allocation()
loc = ( walloc.x + salloc.x + event.x - self.hadjustment.get_value(), -1 )
- self.desktop.addBlock( Block.Instrument, widget.data, loc, True )
+
+ block = self.desktop.addBlock( Block.Instrument, widget.data, loc, True )
+ self.desktop.activateInstrument( block )
class Drum( Picker ):
@@ -302,6 +304,8 @@ class Loop( Picker ):
return -1
id = newPages.pop() # new pageId
+
+ self.owner.noteDB.getPage( id ).local = False # flag as a global page
self.addBlock( id, filename[:-4] )
@@ -412,9 +416,9 @@ class Loop( Picker ):
data[key] = widget.data[key]
newid = self.owner.noteDB.duplicatePages( [ data["id"] ] )[data["id"]]
+ self.owner.updateLoopImage( newid )
data["id"] = newid
- self.owner.updateLoopImage( data["id"] )
- self.desktop.addBlock( Block.Loop, data, loc, True )
+ block = self.desktop.addBlock( Block.Loop, data, loc, True )
diff --git a/Jam/Toolbars.py b/Jam/Toolbars.py
index ecb710b..892a139 100644
--- a/Jam/Toolbars.py
+++ b/Jam/Toolbars.py
@@ -3,6 +3,10 @@ import pygtk
pygtk.require( '2.0' )
import gtk
+from gettext import gettext as _
+
+from sugar.graphics.radiotoolbutton import RadioToolButton
+
class DesktopToolbar( gtk.Toolbar ):
def __init__( self, owner ):
@@ -10,4 +14,24 @@ class DesktopToolbar( gtk.Toolbar ):
self.owner = owner
+ self.desktop = []
+
+ btn = RadioToolButton( 'preset1', group = None )
+ btn.connect( 'toggled', self.setDesktop, 0 )
+ btn.set_tooltip( _('Desktop 1') )
+ self.insert( btn, -1 )
+ self.desktop.append( btn )
+
+ for i in range(2,11):
+ btn = RadioToolButton( 'preset%d'%i, group = self.desktop[0] )
+ btn.connect( 'toggled', self.setDesktop, i-1 )
+ btn.set_tooltip( _('Desktop %d'%i) )
+ self.insert( btn, -1 )
+ self.desktop.append( btn )
+
+ self.show_all()
+
+ def setDesktop( self, widget, which ):
+ if widget.get_active():
+ self.owner._setDesktop( which )