From 82bce73030735492a017113f0486ebe8aae5fae6 Mon Sep 17 00:00:00 2001 From: amartin Date: Tue, 28 Aug 2007 11:58:27 +0000 Subject: Jam save/load --- (limited to 'Jam') 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 ) -- cgit v0.9.1