Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Jam/JamMain.py
diff options
context:
space:
mode:
authorNat <natcl@hotmail.com>2007-09-13 16:01:09 (GMT)
committer Nat <natcl@hotmail.com>2007-09-13 16:01:09 (GMT)
commit6d7e6f83bec91d4c3e49fe64d7793f12d1d3509b (patch)
tree013613f48b4f4a3f91d3ea9f1c0511153cd6f2b4 /Jam/JamMain.py
parente12dbff4dda5aafbaac98f75f0467ef00dc06c32 (diff)
split
Diffstat (limited to 'Jam/JamMain.py')
-rw-r--r--Jam/JamMain.py1112
1 files changed, 0 insertions, 1112 deletions
diff --git a/Jam/JamMain.py b/Jam/JamMain.py
deleted file mode 100644
index c339ae6..0000000
--- a/Jam/JamMain.py
+++ /dev/null
@@ -1,1112 +0,0 @@
-
-import pygtk
-pygtk.require( '2.0' )
-import gtk
-import pango
-
-from SubActivity import SubActivity
-
-import os, sys, shutil
-
-import Config
-from gettext import gettext as _
-import sugar.graphics.style as style
-
-from Jam.Desktop import Desktop
-import Jam.Picker as Picker
-import Jam.Block as Block
-from Jam.Toolbars import JamToolbar, DesktopToolbar
-
-
-from Util.CSoundNote import CSoundNote
-from Util.CSoundClient import new_csound_client
-import Util.InstrumentDB as InstrumentDB
-from Util import NoteDB
-
-from Fillin import Fillin
-from RythmGenerator import generator
-from Generation.GenerationConstants import GenerationConstants
-from Util.NoteDB import Note, Page
-
-from Util import ControlStream
-
-import xdrlib
-import time
-import gobject
-import Util.Network as Net
-from sugar.presence import presenceservice
-from sugar.graphics.xocolor import XoColor
-
-from math import sqrt
-
-class JamMain(SubActivity):
-
- def __init__(self, activity, set_mode):
- SubActivity.__init__(self, set_mode)
-
- self.activity = activity
-
- self.instrumentDB = InstrumentDB.getRef()
- self.noteDB = NoteDB.NoteDB()
-
- #-- initial settings ----------------------------------
- self.tempo = Config.PLAYER_TEMPO
- self.beatDuration = 60.0/self.tempo
- self.ticksPerSecond = Config.TICKS_PER_BEAT*self.tempo/60.0
- self.volume = 0.5
-
- self.csnd = new_csound_client()
- for i in range(0,9):
- self.csnd.setTrackVolume( 100, i )
- self.csnd.setMasterVolume( self.volume*100 ) # csnd expects a range 0-100 for now
- self.csnd.setTempo( self.tempo )
-
- self.paused = False
-
- presenceService = presenceservice.get_instance()
- self.xoOwner = presenceService.get_owner()
-
- #-- Drawing -------------------------------------------
- def darken( colormap, hex ):
- hexToDec = { "0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "A":10, "B":11, "C":12, "D":13, "E":14, "F":15, "a":10, "b":11, "c":12, "d":13, "e":14, "f":15 }
- r = int( 0.7*(16*hexToDec[hex[1]] + hexToDec[hex[2]]) )
- g = int( 0.7*(16*hexToDec[hex[3]] + hexToDec[hex[4]]) )
- b = int( 0.7*(16*hexToDec[hex[5]] + hexToDec[hex[6]]) )
- return colormap.alloc_color( r*256, g*256, b*256 )
- def lighten( colormap, hex ):
- hexToDec = { "0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "A":10, "B":11, "C":12, "D":13, "E":14, "F":15, "a":10, "b":11, "c":12, "d":13, "e":14, "f":15 }
- r = 255 - int( 0.7*(255-(16*hexToDec[hex[1]] + hexToDec[hex[2]])) )
- g = 255 - int( 0.7*(255-(16*hexToDec[hex[3]] + hexToDec[hex[4]])) )
- b = 255 - int( 0.7*(255-(16*hexToDec[hex[5]] + hexToDec[hex[6]])) )
- return colormap.alloc_color( r*256, g*256, b*256 )
-
- xoColorKey = self.xoOwner.props.color
- if not xoColorKey:
- xoColorKey = ( "#8D8D8D,#FFDDEA" )
- xoColor = XoColor( xoColorKey )
-
- win = gtk.gdk.get_default_root_window()
- self.gc = gtk.gdk.GC( win )
- colormap = gtk.gdk.colormap_get_system()
- self.colors = { "bg": colormap.alloc_color( Config.PANEL_BCK_COLOR ),
- "black": colormap.alloc_color( style.COLOR_BLACK.get_html() ),
- #"Picker_Bg": colormap.alloc_color( "#404040" ),
- #"Picker_Bg_Inactive": colormap.alloc_color( "#808080" ),
- "Picker_Bg": colormap.alloc_color( style.COLOR_TOOLBAR_GREY.get_html() ),
- "Picker_Bg_Inactive": colormap.alloc_color( style.COLOR_BUTTON_GREY.get_html() ),
- "Picker_Fg": colormap.alloc_color( style.COLOR_WHITE.get_html() ),
- "Border_Active": colormap.alloc_color( xoColor.get_stroke_color() ), #colormap.alloc_color( "#590000" ),
- "Border_Inactive": colormap.alloc_color( "#8D8D8D" ),
- "Border_Highlight": colormap.alloc_color( "#FFFFFF" ),
- "Bg_Active": colormap.alloc_color( xoColor.get_fill_color() ), #colormap.alloc_color( "#FFDDEA" ),
- "Bg_Inactive": colormap.alloc_color( "#DBDBDB" ),
- "Preview_Note_Fill": colormap.alloc_color( Config.BG_COLOR ),
- "Preview_Note_Border": colormap.alloc_color( Config.FG_COLOR ),
- "Preview_Note_Selected": colormap.alloc_color( style.COLOR_WHITE.get_html() ),
- "Note_Fill_Active": lighten( colormap, "#590000" ), # base "Border_Active"
- "Note_Fill_Inactive": lighten( colormap, "#8D8D8D" ), # base "Border_Inactive"
- "Beat_Line": colormap.alloc_color( "#959595" ) }
- self.colors[ "Note_Border_Active"] = self.colors["Border_Active"]
- self.colors[ "Note_Border_Inactive"] = self.colors["Border_Inactive"]
-
-
- if True: # load block clipmask
- pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'jam-blockMask.png')
- pixels = pix.get_pixels()
- stride = pix.get_rowstride()
- channels = pix.get_n_channels()
- bitmap = ""
- byte = 0
- shift = 0
- for j in range(pix.get_height()):
- offset = stride*j
- for i in range(pix.get_width()):
- r = pixels[i*channels+offset]
- if r != "\0": byte += 1 << shift
- shift += 1
- if shift > 7:
- bitmap += "%c" % byte
- byte = 0
- shift = 0
- if shift > 0:
- bitmap += "%c" % byte
- byte = 0
- shift = 0
- self.blockMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() )
-
- pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+"sampleBG.png" )
- self.sampleBg = gtk.gdk.Pixmap( win, pix.get_width(), pix.get_height() )
- self.sampleBg.draw_pixbuf( self.gc, pix, 0, 0, 0, 0, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
- self.sampleBg.endOffset = pix.get_width()-5
- self.sampleNoteHeight = 7
- if True: # load sample note clipmask
- pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'sampleNoteMask.png')
- pixels = pix.get_pixels()
- stride = pix.get_rowstride()
- channels = pix.get_n_channels()
- bitmap = ""
- byte = 0
- shift = 0
- for j in range(pix.get_height()):
- offset = stride*j
- for i in range(pix.get_width()):
- r = pixels[i*channels+offset]
- if r != "\0": byte += 1 << shift
- shift += 1
- if shift > 7:
- bitmap += "%c" % byte
- byte = 0
- shift = 0
- if shift > 0:
- bitmap += "%c" % byte
- byte = 0
- shift = 0
- self.sampleNoteMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() )
- self.sampleNoteMask.endOffset = pix.get_width()-3
-
- self.loopPitchOffset = 4
- self.loopTickOffset = 13
- self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES-1) / (Block.Loop.HEIGHT - 2*self.loopPitchOffset - self.sampleNoteHeight)
- self.pixelsPerPitch = float(Block.Loop.HEIGHT - 2*self.loopPitchOffset - self.sampleNoteHeight)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
- self.pixelsPerTick = Block.Loop.BEAT/float(Config.TICKS_PER_BEAT)
- self.ticksPerPixel = 1.0/self.pixelsPerTick
-
- #-- Instrument Images ---------------------------------
- self.instrumentImage = {}
- self.instrumentImageActive = {}
- for inst in self.instrumentDB.getSet( "All" ):
- self.prepareInstrumentImage( inst.id, inst.img )
-
- #-- Loop Images ---------------------------------------
- self.loopImage = {} # get filled in through updateLoopImage
- self.loopImageActive = {} #
-
- #-- Key Images ----------------------------------------
- self.keyImage = {}
- self.keyImageActive = {}
- # use hardware key codes to work on any keyboard layout (hopefully)
- self.valid_shortcuts = { 18:"9", 19:"0", 20:"-", 21:"=",
- 32:"O", 33:"P", 34:"[", 35:"]",
- 47:";", 48:"'", 51:"\\",
- 60:".", 61:"/",
- None:" " }
- for key in self.valid_shortcuts.keys():
- self.prepareKeyImage( key )
-
- #-- Toolbars ------------------------------------------
- self.activity.activity_toolbar.keep.show()
-
- self.jamToolbar = JamToolbar( self )
- self.activity.toolbox.add_toolbar( _("Jam"), self.jamToolbar )
-
- self.desktopToolbar = DesktopToolbar( self )
- self.activity.toolbox.add_toolbar( _("Desktop"), self.desktopToolbar )
-
- #-- GUI -----------------------------------------------
- if True: # GUI
- self.modify_bg( gtk.STATE_NORMAL, self.colors["bg"] ) # window bg
-
- self.GUI = {}
- self.GUI["mainVBox"] = gtk.VBox()
- self.add( self.GUI["mainVBox"] )
-
- #-- Desktop -------------------------------------------
- self.desktop = self.GUI["desktop"] = Desktop( self )
- self.GUI["mainVBox"].pack_start( self.GUI["desktop"] )
-
- #-- Bank ----------------------------------------------
- separator = gtk.Label( " " )
- separator.set_size_request( -1, style.TOOLBOX_SEPARATOR_HEIGHT )
- self.GUI["mainVBox"].pack_start( separator, False )
- self.GUI["notebook"] = gtk.Notebook()
- self.GUI["notebook"].set_scrollable( True )
- self.GUI["notebook"].modify_bg( gtk.STATE_NORMAL, self.colors["Picker_Bg"] ) # active tab
- self.GUI["notebook"].modify_bg( gtk.STATE_ACTIVE, self.colors["Picker_Bg_Inactive"] ) # inactive tab
- self.GUI["notebook"].props.tab_vborder = style.TOOLBOX_TAB_VBORDER
- self.GUI["notebook"].props.tab_hborder = style.TOOLBOX_TAB_HBORDER
- self.GUI["notebook"].set_size_request( -1, 160 )
- self.GUI["notebook"].connect( "switch-page", self.setPicker )
- self.GUI["mainVBox"].pack_start( self.GUI["notebook"], False, False )
- self.pickers = {}
- self.pickerScroll = {}
- for type in [ Picker.Instrument, Picker.Drum, Picker.Loop ]:
- self.pickers[type] = type( self )
-
- def prepareLabel( name ):
- label = gtk.Label( _(name) )
- label.set_alignment( 0.0, 0.5 )
- label.modify_fg( gtk.STATE_NORMAL, self.colors["Picker_Fg"] )
- label.modify_fg( gtk.STATE_ACTIVE, self.colors["Picker_Fg"] )
- return label
-
- self.GUI["notebook"].append_page( self.pickers[Picker.Drum], prepareLabel("Drum Kits") )
- self.GUI["notebook"].append_page( self.pickers[Picker.Loop], prepareLabel("Loops") )
-
- sets = self.instrumentDB.getLabels()[:]
- sets.sort()
- for set in sets:
- page = gtk.HBox()
- page.set = set
- self.GUI["notebook"].append_page( page, prepareLabel( set ) )
-
- self.show_all()
-
- self.GUI["notebook"].set_current_page( 0 )
-
- #-- Keyboard ------------------------------------------
- self.key_dict = {}
- self.nextTrack = 1
- self.keyboardListener = None
- self.recordingNote = None
-
- self.keyMap = {}
-
- # default instrument
- self._updateInstrument( Config.INSTRUMENTS["kalimba"].instrumentId, 0.5 )
- self.instrumentStack = []
-
- #-- Drums ---------------------------------------------
- self.drumLoopId = None
- # use dummy values for now
- self.drumFillin = Fillin( 2, 100, Config.INSTRUMENTS["drum1kit"].instrumentId, 0, 1 )
-
- #-- Desktops ------------------------------------------
- self.curDesktop = None
- # copy preset desktops
- path = Config.TAM_TAM_ROOT+"/Resources/Desktops/"
- filelist = os.listdir( path )
- for file in filelist:
- shutil.copyfile( path+file, Config.SCRATCH_DIR+file )
-
- #-- Network -------------------------------------------
- self.network = Net.Network()
- self.network.addWatcher( self.networkStatusWatcher )
- self.network.connectMessage( Net.HT_SYNC_REPLY, self.processHT_SYNC_REPLY )
- self.network.connectMessage( Net.HT_TEMPO_UPDATE, self.processHT_TEMPO_UPDATE )
- self.network.connectMessage( Net.PR_SYNC_QUERY, self.processPR_SYNC_QUERY )
- self.network.connectMessage( Net.PR_TEMPO_QUERY, self.processPR_TEMPO_QUERY )
- self.network.connectMessage( Net.PR_REQUEST_TEMPO_CHANGE, self.processPR_REQUEST_TEMPO_CHANGE )
-
- # sync
- self.syncQueryStart = {}
- self.syncTimeout = None
- self.heartbeatLoop = self.csnd.loopCreate()
- self.csnd.loopSetNumTicks( Config.TICKS_PER_BEAT, self.heartbeatLoop )
- self.heartbeatStart = time.time()
- self.csnd.loopStart( self.heartbeatLoop )
-
- # data packing classes
- self.packer = xdrlib.Packer()
- self.unpacker = xdrlib.Unpacker("")
-
- # handle forced networking
- if self.network.isHost():
- self.updateSync()
- self.syncTimeout = gobject.timeout_add( 1000, self.updateSync )
- elif self.network.isPeer():
- self.sendTempoQuery()
- self.syncTimeout = gobject.timeout_add( 1000, self.updateSync )
-
- #-- Final Set Up --------------------------------------
- self.setVolume( self.volume )
- self.setTempo( self.tempo )
- self.activity.toolbox.set_current_toolbar(1) # JamToolbar
- self.setDesktop( 0, True )
-
-
- #==========================================================
- # SubActivity Handlers
-
- def onActivate( self, arg ):
- SubActivity.onActivate( self, arg )
-
- def onDeactivate( self ):
- SubActivity.onDeactivate( self )
-
- def onDestroy( self ):
- SubActivity.onDestroy( self )
-
- # clear up scratch folder
- path = Config.SCRATCH_DIR
- filelist = os.listdir( path )
- for file in filelist:
- os.remove( path+file )
-
-
- #==========================================================
- # Playback
-
- def onKeyPress( self, widget, event ):
- key = event.hardware_keycode
-
- if key in self.keyMap.keys():
- activate = True
- for block in self.keyMap[key]:
- if block.isActive():
- activate = False
- break
- if activate:
- for block in self.keyMap[key]:
- if not block.isActive():
- if block.type == Block.Drum: self.desktop.activateDrum( block )
- elif block.type == Block.Loop: self.desktop.activateLoop( block )
- else:
- for block in self.keyMap[key]:
- if block.isActive():
- if block.type == Block.Drum: self.desktop.deactivateDrum( block )
- elif block.type == Block.Loop: self.desktop.deactivateLoop( block )
- return
-
- if self.key_dict.has_key( key ): # repeated press
- return
-
- if Config.KEY_MAP_PIANO.has_key( key ):
- pitch = Config.KEY_MAP_PIANO[key]
- inst = Config.INSTRUMENTSID[self.instrument["id"]]
-
- if inst.kit: # drum kit
- if pitch in GenerationConstants.DRUMPITCH:
- pitch = GenerationConstants.DRUMPITCH[pitch]
- csnote = self._playNote( key,
- 36,
- self.instrument["amplitude"]*0.5, # trackVol*noteVol
- self.instrument["pan"],
- 100,
- inst.kit[pitch].instrumentId,
- self.instrument["reverb"] )
- else:
- if event.state == gtk.gdk.MOD1_MASK:
- pitch += 5
-
- if inst.csoundInstrumentId == Config.INST_PERC: #Percussions resonance
- duration = 60
- else:
- duration = -1
-
- csnote = self._playNote( key,
- pitch,
- self.instrument["amplitude"]*0.5, # trackVol*noteVol
- self.instrument["pan"],
- duration,
- self.instrument["id"],
- self.instrument["reverb"] )
-
- if self.keyboardListener:
- self.keyboardListener.recordNote( csnote.pitch )
- self.recordingNote = True
-
- def onKeyRelease( self, widget, event ):
- key = event.hardware_keycode
-
- if self.key_dict.has_key( key ):
- self._stopNote( key )
-
- if self.recordingNote:
- if self.keyboardListener:
- self.keyboardListener.finishNote()
- self.recordingNote = False
-
- def _playNote( self, key, pitch, amplitude, pan, duration, instrumentId, reverb ):
- self.key_dict[key] = CSoundNote( 0, # onset
- pitch,
- amplitude,
- pan,
- duration,
- self.nextTrack,
- instrumentId,
- reverbSend = reverb,
- tied = True,
- mode = 'mini' )
- self.nextTrack += 1
- if self.nextTrack > 8:
- self.nextTrack = 1
- self.csnd.play(self.key_dict[key], 0.3)
-
- return self.key_dict[key]
-
- def _stopNote( self, key ):
- csnote = self.key_dict[key]
- if Config.INSTRUMENTSID[ csnote.instrumentId ].csoundInstrumentId == Config.INST_TIED:
- csnote.duration = .5
- csnote.decay = 0.7
- csnote.tied = False
- self.csnd.play(csnote, 0.3)
- del self.key_dict[key]
-
- def _updateInstrument( self, id, volume, pan = 0, reverb = 0 ):
- self.instrument = { "id": id,
- "amplitude": volume,
- "pan": pan,
- "reverb": reverb }
-
- def pushInstrument( self, instrument ):
- self.instrumentStack.append( self.instrument )
- self.instrument = instrument
-
- def popInstrument( self ):
- self.instrument = self.instrumentStack.pop()
-
- def _playDrum( self, id, pageId, volume, reverb, beats, regularity, loopId = None ):
-
- if loopId == None: # create new loop
- startTick = 0
- firstTime = True
- else: # update loop
- startTick = self.csnd.loopGetTick( loopId )
- self.csnd.loopDestroy( loopId )
- firstTime = False
-
- loopId = self.csnd.loopCreate()
-
- # TODO update track volume
-
- noteOnsets = []
- notePitchs = []
- for n in self.noteDB.getNotesByTrack( pageId, 0 ):
- n.pushState()
- noteOnsets.append( n.cs.onset )
- notePitchs.append( n.cs.pitch )
- n.cs.amplitude = volume * n.cs.amplitude # TODO remove me once track volume is working
- n.cs.reverbSend = reverb
- self.csnd.loopPlay( n, 1, loopId = loopId ) #add as active
- n.popState()
-
- ticks = self.noteDB.getPage( pageId ).ticks
-
- self.csnd.loopSetNumTicks( ticks, loopId )
-
- self.drumFillin.setLoopId( loopId )
- self.drumFillin.setProperties( self.tempo, Config.INSTRUMENTSID[id].name, volume, beats, reverb )
- self.drumFillin.unavailable( noteOnsets, notePitchs )
-
- self.drumFillin.play()
-
- # sync to heartbeat
- if False: # firstTime: # always force first note to play rather than snaping to nearest beat.. good idea?
- startTick = ticks - Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
- else:
- while startTick > ticks: # align with last beat
- startTick -= Config.TICKS_PER_BEAT
- beatTick = int(startTick) % Config.TICKS_PER_BEAT
- heartTick = self.csnd.loopGetTick( self.heartbeatLoop )
- if beatTick > heartTick:
- if beatTick - heartTick < heartTick + Config.TICKS_PER_BEAT - beatTick:
- startTick = (int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
- else:
- startTick = (1 + int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
- else:
- if heartTick - beatTick < beatTick + Config.TICKS_PER_BEAT - heartTick:
- startTick = (int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
- else:
- startTick = (-1 + int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
-
- if startTick >= ticks:
- startTick -= ticks
- elif startTick < 0:
- startTick += ticks
-
- self.csnd.loopSetTick( startTick, loopId )
-
- if not self.paused:
- self.csnd.loopStart( loopId )
-
- return loopId
-
- def _stopDrum( self, loopId ):
- self.drumFillin.stop()
- self.csnd.loopDestroy( loopId )
-
- def _playLoop( self, id, volume, reverb, tune, loopId = None, force = False ):
- if loopId == None: # create new loop
- startTick = 0
- firstTime = True
- else: # update loop
- startTick = self.csnd.loopGetTick( loopId )
- self.csnd.loopDestroy( loopId )
- firstTime = False
-
- loopId = self.csnd.loopCreate()
-
- # TODO update track volume
-
- inst = Config.INSTRUMENTSID[id]
-
- offset = 0
- for page in tune:
- for n in self.noteDB.getNotesByTrack( page, 0 ):
- n.pushState()
- n.cs.instrumentId = id
- n.cs.amplitude = volume * n.cs.amplitude # TODO remove me once track volume is working
- n.cs.reverbSend = reverb
- if inst.kit: # drum kit
- if n.cs.pitch in GenerationConstants.DRUMPITCH:
- n.cs.pitch = GenerationConstants.DRUMPITCH[n.cs.pitch]
- n.cs.onset += offset
- self.csnd.loopPlay( n, 1, loopId = loopId )
- n.popState()
- offset += self.noteDB.getPage(page).ticks
-
-
- self.csnd.loopSetNumTicks( offset, loopId )
-
- # sync to heartbeat
- if False: # firstTime: # always force first note to play rather than snaping to nearest beat.. good idea?
- startTick = offset - Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
- else:
- while startTick > offset: # align with last beat
- startTick -= Config.TICKS_PER_BEAT
- beatTick = int(startTick) % Config.TICKS_PER_BEAT
- heartTick = self.csnd.loopGetTick( self.heartbeatLoop )
- if beatTick > heartTick:
- if beatTick - heartTick < heartTick + Config.TICKS_PER_BEAT - beatTick:
- startTick = (int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
- else:
- startTick = (1 + int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
- else:
- if heartTick - beatTick < beatTick + Config.TICKS_PER_BEAT - heartTick:
- startTick = (int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
- else:
- startTick = (-1 + int(startTick)//Config.TICKS_PER_BEAT)*Config.TICKS_PER_BEAT + self.csnd.loopGetTick( self.heartbeatLoop )
-
- if startTick >= offset:
- startTick -= offset
- elif startTick < 0:
- startTick += offset
-
-
- self.csnd.loopSetTick( startTick, loopId )
-
- if not self.paused or force:
- self.csnd.loopStart( loopId )
-
- return loopId
-
- def _stopLoop( self, loopId ):
- self.csnd.loopDestroy( loopId )
-
- def setPaused( self, paused ):
- if self.paused == paused:
- return
-
- loops = self.desktop.getLoopIds()
-
- if self.paused: # unpause
- self.paused = False
- for loop in loops:
- self.csnd.loopStart( loop )
- else: # pause
- self.paused = True
- for loop in loops:
- self.csnd.loopPause( loop )
-
- #==========================================================
- # Generate
-
- def _generateDrumLoop( self, instrumentId, beats, regularity, reverb, pageId = -1 ):
- def flatten(ll):
- rval = []
- for l in ll:
- rval += l
- return rval
-
- notes = flatten( generator( Config.INSTRUMENTSID[instrumentId].name, beats, 0.8, regularity, reverb) )
-
- if pageId == -1:
- page = Page( beats )
- pageId = self.noteDB.addPage( -1, page )
- else:
- self.noteDB.deleteNotesByTrack( [ pageId ], [ 0 ] )
-
- if len(notes):
- self.noteDB.addNotes( [ pageId, 0, len(notes) ] + notes + [-1] )
-
- return pageId
-
- def _generateTrack( self, instrumentId, page, track, parameters, algorithm ):
- dict = { track: { page: self.noteDB.getCSNotesByTrack( page, track ) } }
- instruments = { page: [ Config.INSTRUMENTSID[instrumentId].name for i in range(Config.NUMBER_OF_TRACKS) ] }
- beatsOfPages = { page: self.noteDB.getPage(page).beats }
-
- algorithm( parameters,
- [ 0.5 for i in range(Config.NUMBER_OF_TRACKS) ],
- instruments,
- self.tempo,
- beatsOfPages,
- [ track ],
- [ page ],
- dict,
- 4)
-
- # filter & fix input ...WTF!?
- for track in dict:
- for page in dict[track]:
- for note in dict[track][page]:
- intdur = int(note.duration)
- note.duration = intdur
- note.pageId = page
- note.trackId = track
-
- # prepare the new notes
- newnotes = []
- for tid in dict:
- for pid in dict[tid]:
- newnotes += dict[tid][pid]
-
- # delete the notes and add the new
- self.noteDB.deleteNotesByTrack( [ page ], [ track ] )
-
- self.noteDB.addNotes(
- [ page, track, len(dict[track][page]) ]
- + dict[track][page]
- + [ -1 ] )
-
-
- #==========================================================
- # Get/Set
-
- def getVolume( self ):
- return self.volume
-
- def setVolume( self, volume ):
- self.jamToolbar.volumeSlider.set_value( volume )
-
- def _setVolume( self, volume ):
- self.volume = volume
- self.csnd.setMasterVolume( self.volume*100 ) # csnd expects a range 0-100 for now
-
- def getTempo( self ):
- return self.tempo
-
- def setTempo( self, tempo ):
- self.jamToolbar.tempoSlider.set_value( tempo )
-
- def _setTempo( self, tempo ):
- if self.network.isHost() or self.network.isOffline():
- t = time.time()
- percent = self.heartbeatElapsed() / self.beatDuration
-
- self.tempo = tempo
- self.beatDuration = 60.0/self.tempo
- self.ticksPerSecond = Config.TICKS_PER_BEAT*self.tempo/60.0
- self.csnd.setTempo( self.tempo )
-
- if self.network.isHost() or self.network.isOffline():
- self.heatbeatStart = t - percent*self.beatDuration
- self.updateSync()
- self.sendTempoUpdate()
-
- def getInstrument( self ):
- return self.instrument
-
- def getDesktop( self ):
- return self.desktop
-
- def _clearDesktop( self, save = True ):
- if self.curDesktop == None:
- return
-
- if save:
- self._saveDesktop()
-
- self.desktop._clearDesktop()
-
- self.curDesktop = None
-
- def setDesktop( self, desktop, force = False ):
- radiobtn = self.desktopToolbar.getDesktopButton( desktop )
- if force and radiobtn.get_active():
- self._setDesktop( desktop )
- else:
- radiobtn.set_active( True )
-
- def _setDesktop( self, desktop ):
- self._clearDesktop()
-
- self.curDesktop = desktop
-
- TTTable = ControlStream.TamTamTable( self.noteDB, jam = self )
-
- filename = self.getDesktopScratchFile( self.curDesktop )
- try:
- stream = open( filename, "r" )
- TTTable.parseFile( stream )
- stream.close()
- except IOError, (errno, strerror):
- if Config.DEBUG > 3: print "IOError:: _setDesktop:", errno, strerror
-
- def getInstrumentImage( self, id, active = False ):
- if active: return self.instrumentImageActive[id]
- else: return self.instrumentImage[id]
-
- def getKeyImage( self, key, active = False ):
- if active: return self.keyImageActive[key]
- else: return self.keyImage[key]
-
- def getLoopImage( self, id, active = False ):
- if active: return self.loopImageActive[id]
- else: return self.loopImage[id]
-
- def setPicker( self, widget, pagePointer, page_num ):
- page = self.GUI["notebook"].get_nth_page( page_num )
- if page == self.pickers[Picker.Drum]:
- pass
- elif page == self.pickers[Picker.Loop]:
- pass
- else:
- self.pickers[Picker.Instrument].setFilter( ( page.set ) )
- parent = self.pickers[Picker.Instrument].get_parent()
- if parent != page:
- if parent != None:
- parent.remove( self.pickers[Picker.Instrument] )
- page.add( self.pickers[Picker.Instrument] )
-
- def setKeyboardListener( self, listener ):
- self.keyboardListener = listener
-
- def mapKey( self, key, block, oldKey = None ):
- if oldKey != None and block in self.keyMap[oldKey]:
- self.keyMap[oldKey].remove( block )
-
- if key == None:
- return
-
- if key not in self.keyMap.keys():
- self.keyMap[key] = []
-
- if block not in self.keyMap[key]:
- self.keyMap[key].append( block )
-
- #==========================================================
- # Pixmaps
-
- def prepareInstrumentImage( self, id, img_path ):
- try:
- win = gtk.gdk.get_default_root_window()
- pix = gtk.gdk.pixbuf_new_from_file( img_path )
- x = (Block.Block.WIDTH-pix.get_width())//2
- y = (Block.Block.HEIGHT-pix.get_height())//2
- img = gtk.gdk.Pixmap( win, Block.Block.WIDTH, Block.Block.HEIGHT )
- self.gc.foreground = self.colors["Bg_Inactive"]
- img.draw_rectangle( self.gc, True, 0, 0, Block.Block.WIDTH, Block.Block.HEIGHT )
- img.draw_pixbuf( self.gc, pix, 0, 0, x, y, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
- self.instrumentImage[id] = img
- img = gtk.gdk.Pixmap( win, Block.Block.WIDTH, Block.Block.HEIGHT )
- self.gc.foreground = self.colors["Bg_Active"]
- img.draw_rectangle( self.gc, True, 0, 0, Block.Block.WIDTH, Block.Block.HEIGHT )
- img.draw_pixbuf( self.gc, pix, 0, 0, x, y, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
- self.instrumentImageActive[id] = img
- except:
- if Config.DEBUG >= 5: print "JamMain:: file does not exist: " + img_path
- img = gtk.gdk.Pixmap( win, Block.Block.WIDTH, Block.Block.HEIGHT )
- self.gc.foreground = self.colors["Bg_Inactive"]
- img.draw_rectangle( self.gc, True, 0, 0, Block.Block.WIDTH, Block.Block.HEIGHT )
- self.instrumentImage[id] = img
- img = gtk.gdk.Pixmap( win, Block.Block.WIDTH, Block.Block.HEIGHT )
- self.gc.foreground = self.colors["Bg_Active"]
- img.draw_rectangle( self.gc, True, 0, 0, Block.Block.WIDTH, Block.Block.HEIGHT )
- self.instrumentImageActive[id] = img
-
- def _drawNotes( self, pixmap, beats, notes, active ):
- self.gc.set_clip_mask( self.sampleNoteMask )
- for note in notes: # draw N notes
- x = self.ticksToPixels( note.cs.onset )
- endX = self.ticksToPixels( note.cs.onset + note.cs.duration ) - 3 # include end cap offset
- width = endX - x
- if width < 5:
- width = 5
- endX = x + width
- y = self.pitchToPixels( note.cs.pitch )
- # draw fill
- if active: self.gc.foreground = self.colors["Note_Fill_Active"]
- else: self.gc.foreground = self.colors["Note_Fill_Inactive"]
- self.gc.set_clip_origin( x, y-self.sampleNoteHeight )
- pixmap.draw_rectangle( self.gc, True, x+1, y+1, width+1, self.sampleNoteHeight-2 )
- # draw border
- if active: self.gc.foreground = self.colors["Note_Border_Active"]
- else: self.gc.foreground = self.colors["Note_Border_Inactive"]
- self.gc.set_clip_origin( x, y )
- pixmap.draw_rectangle( self.gc, True, x, y, width, self.sampleNoteHeight )
- self.gc.set_clip_origin( endX-self.sampleNoteMask.endOffset, y )
- pixmap.draw_rectangle( self.gc, True, endX, y, 3, self.sampleNoteHeight )
-
- def prepareKeyImage( self, key ):
- win = gtk.gdk.get_default_root_window()
- pangolayout = self.create_pango_layout( _(self.valid_shortcuts[key]) )
- fontDesc = pango.FontDescription( "bold" )
- pangolayout.set_font_description( fontDesc )
- extents = pangolayout.get_pixel_extents()
- x = ( Block.Block.KEYSIZE - extents[1][2] ) // 2
- y = ( Block.Block.KEYSIZE - extents[1][3] ) // 2
-
- pixmap = gtk.gdk.Pixmap( win, Block.Block.KEYSIZE, Block.Block.KEYSIZE )
- self.gc.foreground = self.colors["Border_Inactive"]
- pixmap.draw_rectangle( self.gc, True, 0, 0, Block.Block.KEYSIZE, Block.Block.KEYSIZE )
- self.gc.foreground = self.colors["Bg_Inactive"]
- pixmap.draw_layout( self.gc, x, y, pangolayout )
- self.keyImage[key] = pixmap
-
- pixmap = gtk.gdk.Pixmap( win, Block.Block.KEYSIZE, Block.Block.KEYSIZE )
- self.gc.foreground = self.colors["Border_Active"]
- pixmap.draw_rectangle( self.gc, True, 0, 0, Block.Block.KEYSIZE, Block.Block.KEYSIZE )
- self.gc.foreground = self.colors["Bg_Active"]
- pixmap.draw_layout( self.gc, x, y, pangolayout )
- self.keyImageActive[key] = pixmap
-
- def updateLoopImage( self, id ):
- page = self.noteDB.getPage( id )
-
- win = gtk.gdk.get_default_root_window()
- width = Block.Loop.WIDTH[page.beats]
- height = Block.Loop.HEIGHT
-
- self.gc.set_clip_rectangle( gtk.gdk.Rectangle( 0, 0, width, height ) )
-
- pixmap = gtk.gdk.Pixmap( win, width, height )
- self.gc.foreground = self.colors["Bg_Inactive"]
- pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
- self._drawNotes( pixmap, page.beats, self.noteDB.getNotesByTrack( id, 0 ), False )
- self.loopImage[id] = pixmap
-
- self.gc.set_clip_rectangle( gtk.gdk.Rectangle( 0, 0, width, height ) )
-
- pixmap = gtk.gdk.Pixmap( win, width, height )
- self.gc.foreground = self.colors["Bg_Active"]
- pixmap.draw_rectangle( self.gc, True, 0, 0, width, height )
- self._drawNotes( pixmap, page.beats, self.noteDB.getNotesByTrack( id, 0 ), True )
- self.loopImageActive[id] = pixmap
-
- def ticksToPixels( self, ticks ):
- return self.loopTickOffset + int(round( ticks * self.pixelsPerTick ))
- def pitchToPixels( self, pitch ):
- return self.loopPitchOffset + int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
-
- #==========================================================
- # Load/Save
-
- def _saveDesktop( self ):
- if self.curDesktop == None:
- return
-
- filename = self.getDesktopScratchFile( self.curDesktop )
- if os.path.isfile( filename ):
- os.remove( filename )
-
- try:
- scratch = open( filename, "w" )
- stream = ControlStream.TamTamOStream(scratch)
-
- self.noteDB.dumpToStream( stream, True )
- self.desktop.dumpToStream( stream )
-
- scratch.close()
- except IOError, (errno, strerror):
- if Config.DEBUG > 3: print "IOError:: _saveDesktop:", errno, strerror
-
- def getDesktopScratchFile( self, i ):
- return Config.SCRATCH_DIR+"desktop%d" % i
-
- def handleJournalLoad( self, filepath ):
-
- self._clearDesktop( False )
-
- TTTable = ControlStream.TamTamTable( self.noteDB, jam = self )
-
- try:
- stream = open( filepath, "r" )
- TTTable.parseFile( stream )
- stream.close()
-
- self.setVolume( TTTable.masterVolume )
- self.setTempo( TTTable.tempo )
-
- except IOError, (errno, strerror):
- if Config.DEBUG > 3: print "IOError:: handleJournalLoad:", errno, strerror
-
- def handleJournalSave( self, filepath ):
-
- self._saveDesktop()
-
- try:
- streamF = open( filepath, "w" )
- stream = ControlStream.TamTamOStream( streamF )
-
- for i in range(10):
- desktop_file = self.getDesktopScratchFile( i )
- stream.desktop_store( desktop_file, i )
-
- stream.desktop_set( self.curDesktop )
-
- stream.master_vol( self.volume )
- stream.tempo( self.tempo )
-
- streamF.close()
-
- except IOError, (errno, strerror):
- if Config.DEBUG > 3: print "IOError:: handleJournalSave:", errno, strerror
-
- #==========================================================
- # Network
-
- #-- Activity ----------------------------------------------
-
- def shared( self, activity ):
- if Config.DEBUG: print "miniTamTam:: successfully shared, start host mode"
- self.activity._shared_activity.connect( "buddy-joined", self.buddy_joined )
- self.activity._shared_activity.connect( "buddy-left", self.buddy_left )
- self.network.setMode( Net.MD_HOST )
- self.updateSync()
- self.syncTimeout = gobject.timeout_add( 1000, self.updateSync )
-
- def joined( self, activity ):
- if Config.DEBUG:
- print "miniTamTam:: joined activity!!"
- for buddy in self.activity._shared_activity.get_joined_buddies():
- print buddy.props.ip4_address
-
- def buddy_joined( self, activity, buddy ):
- if Config.DEBUG:
- print "buddy joined " + str(buddy)
- try:
- print buddy.props.ip4_address
- except:
- print "bad ip4_address"
- if self.network.isHost():
- if buddy == self.xoOwner:
- return
- if buddy.props.ip4_address:
- self.network.introducePeer( buddy.props.ip4_address )
- else:
- print "miniTamTam:: new buddy does not have an ip4_address!!"
-
- def buddy_left( self, activity, buddy):
- if Config.DEBUG: print "buddy left"
-
- #def joined( self, activity ):
- # if Config.DEBUG: print "miniTamTam:: successfully joined, wait for host"
- # self.net.waitForHost()
-
- #-- Senders -----------------------------------------------
-
- def sendSyncQuery( self ):
- self.packer.pack_float(random.random())
- hash = self.packer.get_buffer()
- self.packer.reset()
- self.syncQueryStart[hash] = time.time()
- self.network.send( Net.PR_SYNC_QUERY, hash)
-
- def sendTempoUpdate( self ):
- self.packer.pack_int(self.tempo)
- self.network.sendAll( Net.HT_TEMPO_UPDATE, self.packer.get_buffer() )
- self.packer.reset()
-
- def sendTempoQuery( self ):
- self.network.send( Net.PR_TEMPO_QUERY )
-
- def requestTempoChange( self, val ):
- self.packer.pack_int(val)
- self.network.send( Net.PR_REQUEST_TEMPO_CHANGE, self.packer.get_buffer() )
- self.packer.reset()
-
- #-- Handlers ----------------------------------------------
-
- def networkStatusWatcher( self, mode ):
- if mode == Net.MD_OFFLINE:
- if self.syncTimeout:
- gobject.source_remove( self.syncTimeout )
- self.syncTimeout = None
- if mode == Net.MD_PEER:
- self.updateSync()
- if not self.syncTimeout:
- self.syncTimeout = gobject.timeout_add( 1000, self.updateSync )
- self.sendTempoQuery()
-
- def processHT_SYNC_REPLY( self, sock, message, data ):
- t = time.time()
- hash = data[0:4]
- latency = t - self.syncQueryStart[hash]
- self.unpacker.reset(data[4:8])
- nextBeat = self.unpacker.unpack_float()
- #print "mini:: got sync: next beat in %f, latency %d" % (nextBeat, latency*1000)
- self.heartbeatStart = t + nextBeat - self.beatDuration - latency/2
- self.correctSync()
- self.syncQueryStart.pop(hash)
-
- def processHT_TEMPO_UPDATE( self, sock, message, data ):
- self.unpacker.reset(data)
- val = self.unpacker.unpack_int()
- if self.tempoSliderActive:
- self.delayedTempo = val
- return
- self.tempoAdjustment.handler_block( self.tempoAdjustmentHandler )
- self.tempoAdjustment.set_value( val )
- self._updateTempo( val )
- self.tempoAdjustment.handler_unblock( self.tempoAdjustmentHandler )
- self.sendSyncQuery()
-
- def processPR_SYNC_QUERY( self, sock, message, data ):
- self.packer.pack_float(self.nextHeartbeat())
- self.network.send( Net.HT_SYNC_REPLY, data + self.packer.get_buffer(), sock )
- self.packer.reset()
-
- def processPR_TEMPO_QUERY( self, sock, message, data ):
- self.packer.pack_int(self.tempo)
- self.network.send( Net.HT_TEMPO_UPDATE, self.packer.get_buffer(), to = sock )
- self.packer.reset()
-
- def processPR_REQUEST_TEMPO_CHANGE( self, sock, message, data ):
- if self.tempoSliderActive:
- return
- self.unpacker.reset(data)
- val = self.unpacker.unpack_int()
- self.tempoAdjustment.set_value( val )
-
- #==========================================================
- # Sync
-
- def nextHeartbeat( self ):
- delta = time.time() - self.heartbeatStart
- return self.beatDuration - (delta % self.beatDuration)
-
- def nextHeartbeatInTicks( self ):
- delta = time.time() - self.heartbeatStart
- next = self.beatDuration - (delta % self.beatDuration)
- return self.ticksPerSecond*next
-
- def heartbeatElapsed( self ):
- delta = time.time() - self.heartbeatStart
- return delta % self.beatDuration
-
- def heartbeatElapsedTicks( self ):
- delta = time.time() - self.heartbeatStart
- return self.ticksPerSecond*(delta % self.beatDuration)
-
- def updateSync( self ):
- if self.network.isOffline():
- return False
- elif self.network.isWaiting():
- return True
- elif self.network.isHost():
- self.correctSync()
- else:
- self.sendSyncQuery()
- return True
-
- def correctSync( self ):
- curTick = self.csnd.loopGetTick( self.heartbeatLoop )
- curTicksIn = curTick % Config.TICKS_PER_BEAT
- ticksIn = self.heartbeatElapsedTicks()
- err = curTicksIn - ticksIn
- if err > Config.TICKS_PER_BEAT_DIV2:
- err -= Config.TICKS_PER_BEAT
- elif err < -Config.TICKS_PER_BEAT_DIV2:
- err += Config.TICKS_PER_BEAT
- correct = curTick - err
- if correct > Config.TICKS_PER_BEAT:
- correct -= Config.TICKS_PER_BEAT
- elif correct < 0:
- correct += Config.TICKS_PER_BEAT
- #print "correct:: %f ticks, %f ticks in, %f expected, %f err, correct %f" % (curTick, curTicksIn, ticksIn, err, correct)
- if abs(err) > 0.25:
- self.csnd.adjustTick(-err)
-
-