Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Edit
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 /Edit
parent35278d261ace09d5ed20bdae0983730d88dd8037 (diff)
Switch to the singular sources tree and releasing scheme
Diffstat (limited to 'Edit')
-rw-r--r--Edit/EditToolbars.py1495
-rw-r--r--Edit/HitInterface.py195
-rw-r--r--Edit/MainWindow.py2281
-rw-r--r--Edit/NoteInterface.py345
-rw-r--r--Edit/Properties.py808
-rw-r--r--Edit/TrackInterface.py1374
-rw-r--r--Edit/TuneInterface.py646
-rw-r--r--Edit/__init__.py0
-rw-r--r--Edit/rm/BackgroundView.py501
-rw-r--r--Edit/rm/KeyboardInput.py97
-rw-r--r--Edit/rm/NoteView.py253
-rw-r--r--Edit/rm/PageBankView.py85
-rw-r--r--Edit/rm/PageView.py65
-rw-r--r--Edit/rm/PositionIndicator.py47
-rw-r--r--Edit/rm/TrackView.py263
-rw-r--r--Edit/rm/TunePageView.py17
-rw-r--r--Edit/rm/TuneView.py123
17 files changed, 8595 insertions, 0 deletions
diff --git a/Edit/EditToolbars.py b/Edit/EditToolbars.py
new file mode 100644
index 0000000..beb9556
--- /dev/null
+++ b/Edit/EditToolbars.py
@@ -0,0 +1,1495 @@
+import gtk
+
+import common.Config as Config
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.toggletoolbutton import ToggleToolButton
+from sugar.graphics.radiotoolbutton import RadioToolButton
+from sugar.graphics.palette import Palette
+from sugar.graphics.icon import Icon
+from common.Util.ThemeWidgets import *
+from gettext import gettext as _
+
+#Generation palette
+import gobject
+from common.Generation.Generator import GenerationParameters
+#Generation palette and Properties palette
+from common.Generation.GenerationConstants import GenerationConstants
+from common.Generation.GenerationRythm import GenerationRythm
+from common.Generation.GenerationPitch import GenerationPitch
+
+#Properties palette
+from common.Util.NoteDB import PARAMETER
+from common.Generation.Drunk import *
+import common.Generation.Utils as Utils
+from types import *
+from math import sqrt
+from random import *
+
+class mainToolbar(gtk.Toolbar):
+ def __init__(self,toolbox, edit):
+ gtk.Toolbar.__init__(self)
+
+ def _insertSeparator(x = 1):
+ for i in range(x):
+ self.separator = gtk.SeparatorToolItem()
+ self.separator.set_expand(True)
+ self.separator.set_draw(True)
+ self.insert(self.separator,-1)
+ self.separator.show()
+
+ self.toolbox = toolbox
+ self.edit = edit
+
+ self.tooltips = gtk.Tooltips()
+
+ #Play button
+ self.playButton = ToggleToolButton('media-playback-start')
+ self.playButtonHandler = self.playButton.connect('toggled', self.handlePlayPause)
+ self.insert(self.playButton, -1)
+ self.playButton.show()
+ self.playButton.set_tooltip(_('Play / Pause'))
+
+ #Stop button
+ self.stopButton = ToolButton('media-playback-stop')
+ self.stopButton.connect('clicked', self.handleStop)
+ self.insert(self.stopButton, -1)
+ self.stopButton.show()
+ self.stopButton.set_tooltip(_('Stop'))
+
+ #Play button Image
+ self.playButtonImg = gtk.Image()
+ self.playButtonImg.set_from_icon_name('media-playback-start', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.playButtonImg.show()
+
+ #Pause button Image
+ self.pauseButtonImg = gtk.Image()
+ self.pauseButtonImg.set_from_icon_name('media-playback-pause', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.pauseButtonImg.show()
+
+ #Record button
+ self.recordButton = ToggleToolButton('recordK')
+ self.recordButton.connect('clicked', self.edit.handleKeyboardRecordButton)
+ self.insert(self.recordButton, -1)
+ self.recordButton.show()
+ self.recordButton.set_tooltip(_('Record keyboard'))
+
+ if Config.FEATURES_OGG:
+ #RecordOgg button
+ self.recordOggButton = ToggleToolButton('recordO')
+ self.recordOggButton.connect('clicked', self.handleRecord)
+ self.insert(self.recordOggButton, -1)
+ self.recordOggButton.show()
+ self.recordOggButton.set_tooltip(_('Record to ogg'))
+
+ _insertSeparator(1)
+
+ #Pointer button
+ self._pointerPalette = pointerPalette(_('Select tool'), self.edit)
+ self.pointerButton = RadioToolButton(group = None)
+ self.pointerButton.set_named_icon('edit-pointer')
+ self.pointerButton.set_palette(self._pointerPalette)
+ self.pointerButton.connect('toggled', self.edit.handleToolClick, 'default')
+ self.insert(self.pointerButton, -1)
+ self.pointerButton.show()
+
+ #Draw button
+ self._drawPalette = drawPalette(_('Draw Tool'), self.edit)
+ self.drawButton = RadioToolButton(group = self.pointerButton)
+ self.drawButton.set_named_icon('edit-pencil')
+ self.drawButton.set_palette(self._drawPalette)
+ self.drawButton.connect('toggled', self.edit.handleToolClick, 'draw')
+ self.insert(self.drawButton, -1)
+ self.drawButton.show()
+
+ #Paint button
+ self._paintPalette = paintPalette(_('Paint Tool'), self.edit)
+ self.paintButton = RadioToolButton(group = self.pointerButton)
+ self.paintButton.set_named_icon('edit-brush')
+ self.paintButton.set_palette(self._paintPalette)
+ self.paintButton.connect('toggled', self.edit.handleToolClick, 'paint')
+ self.insert(self.paintButton, -1)
+ self.paintButton.show()
+
+ _insertSeparator(1)
+
+ #Duplicate button
+ self.duplicateButton = ToggleToolButton('duplicate')
+ self.duplicateButton.connect('toggled', self.handleDuplicate)
+ self.insert(self.duplicateButton, -1)
+ self.duplicateButton.show()
+ self.duplicateButton.set_tooltip(_('Duplicate'))
+
+ #Volume / Tempo button
+ self._volumeTempoPalette = volumeTempoPalette(_('Volume / Tempo'), self.edit)
+ self.volumeTempoButton = ToggleToolButton('voltemp')
+ self.volumeTempoButton.set_palette(self._volumeTempoPalette)
+ self.insert(self.volumeTempoButton, -1)
+ self.volumeTempoButton.show()
+
+ def handleRecord(self, widget, data = None):
+ if widget.get_active():
+ self.playButton.set_active(False)
+ self.edit.handleAudioRecord(widget, data)
+ if widget.get_active():
+ gobject.timeout_add( 500, self._startAudioRecord )
+
+ def _startAudioRecord( self ):
+ self.playButton.set_active(True)
+ return False
+
+ def handlePlayPause(self, widget, data = None):
+ if widget.get_active():
+ self.edit.handlePlay(widget)
+ self.edit._generateToolbar.handler_block(self.edit._generateToolbar.playButtonHandler)
+ self.edit._generateToolbar.playButton.set_active(True)
+ self.edit._generateToolbar.handler_unblock(self.edit._generateToolbar.playButtonHandler)
+ widget.set_icon_widget(self.pauseButtonImg)
+ self.edit._generateToolbar.playButton.set_icon_widget(self.edit._generateToolbar.pauseButtonImg)
+ else:
+ self.edit.handleStop(widget, False)
+ self.edit._generateToolbar.handler_block(self.edit._generateToolbar.playButtonHandler)
+ self.edit._generateToolbar.playButton.set_active(False)
+ self.edit._generateToolbar.handler_unblock(self.edit._generateToolbar.playButtonHandler)
+ widget.set_icon_widget(self.playButtonImg)
+ self.edit._generateToolbar.playButton.set_icon_widget(self.edit._generateToolbar.playButtonImg)
+
+ def handleStop(self, widget, data = None):
+ self.playButton.set_active(False)
+ self.edit.handleStop(widget, True)
+ if self.recordButton.get_active():
+ self.recordButton.set_active(False)
+
+ def handleDuplicate(self, widget):
+ if widget.get_active():
+ if self.edit.getContext() == 0: #Page
+ self.edit.pageDuplicate()
+ elif self.edit.getContext() == 1: #Track
+ self.edit.trackDuplicateWidget(widget)
+ elif self.edit.getContext() == 2: #Note
+ self.edit.noteDuplicateWidget(widget)
+ widget.set_active(False)
+
+class generateToolbar(gtk.Toolbar):
+ def __init__(self,toolbox, edit):
+ gtk.Toolbar.__init__(self)
+
+ def _insertSeparator(x = 1):
+ for i in range(x):
+ self.separator = gtk.SeparatorToolItem()
+ self.separator.set_expand(True)
+ self.separator.set_draw(False)
+ self.insert(self.separator,-1)
+ self.separator.show()
+
+ self.toolbox = toolbox
+ self.edit = edit
+
+ self.tooltips = gtk.Tooltips()
+
+ #Play button
+ self.playButton = ToggleToolButton('media-playback-start')
+ self.playButtonHandler = self.playButton.connect('toggled', self.handlePlayPause)
+ self.insert(self.playButton, -1)
+ self.playButton.show()
+ self.playButton.set_tooltip(_('Play / Pause'))
+
+ #Stop button
+ self.stopButton = ToolButton('media-playback-stop')
+ self.stopButton.connect('clicked', self.handleStop)
+ self.insert(self.stopButton, -1)
+ self.stopButton.show()
+ self.stopButton.set_tooltip(_('Stop'))
+
+ #Play button Image
+ self.playButtonImg = gtk.Image()
+ self.playButtonImg.set_from_icon_name('media-playback-start', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.playButtonImg.show()
+
+ #Pause button Image
+ self.pauseButtonImg = gtk.Image()
+ self.pauseButtonImg.set_from_icon_name('media-playback-pause', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.pauseButtonImg.show()
+
+ _insertSeparator(1)
+
+ #BigGeneration button
+ self.bigGenerationButton = ToolButton('diceB')
+ self.bigGenerationButton.connect('clicked', self.edit.createNewTune)
+ self.insert(self.bigGenerationButton, -1)
+ self.bigGenerationButton.show()
+ self.bigGenerationButton.set_tooltip(_('Generate Tune'))
+
+ #Generation button
+ self._generationPalette = generationPalette(_('Generation'), self.edit)
+ self.generationButton = ToggleToolButton('dice')
+ #self.generationButton.connect(None)
+ self.generationButton.set_palette(self._generationPalette)
+ self.insert(self.generationButton, -1)
+ self.generationButton.show()
+
+ #Properties button
+ self._propertiesPalette = propertiesPalette(_('Properties'), self.edit)
+ self.propsButton = ToggleToolButton('props')
+ self.propsButton.set_palette(self._propertiesPalette)
+ self.insert(self.propsButton, -1)
+ self.propsButton.show()
+
+ def handlePlayPause(self, widget, data = None):
+ if widget.get_active():
+ self.edit.handlePlay(widget)
+ self.edit._mainToolbar.handler_block(self.edit._mainToolbar.playButtonHandler)
+ self.edit._mainToolbar.playButton.set_active(True)
+ self.edit._mainToolbar.handler_unblock(self.edit._mainToolbar.playButtonHandler)
+ widget.set_icon_widget(self.pauseButtonImg)
+ self.edit._mainToolbar.playButton.set_icon_widget(self.edit._mainToolbar.pauseButtonImg)
+ else:
+ self.edit.handleStop(widget, False)
+ self.edit._mainToolbar.handler_block(self.edit._mainToolbar.playButtonHandler)
+ self.edit._mainToolbar.playButton.set_active(False)
+ self.edit._mainToolbar.handler_unblock(self.edit._mainToolbar.playButtonHandler)
+ widget.set_icon_widget(self.playButtonImg)
+ self.edit._mainToolbar.playButton.set_icon_widget(self.edit._mainToolbar.playButtonImg)
+
+ def handleStop(self, widget, data = None):
+ self.edit.handleStop(widget, True)
+ self.playButton.set_active(False)
+ if self.edit._mainToolbar.recordButton.get_active():
+ self.edit._mainToolbar.recordButton.set_active(False)
+
+class pointerPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.pointerBox = gtk.VBox()
+
+ self.snapGridHBox = gtk.HBox()
+ self.snapGridImage = gtk.Image()
+ self.snapGridImage.set_from_file(Config.TAM_TAM_ROOT + '/icons/grid.svg')
+ self.snapGridBox = BigComboBox()
+ self.snapGridBox.connect('changed', self.handleSnapGrid)
+ self.gridDurs = [1, 2, 3, 4, 6, 12, 24]
+ durs = [_('1/12'), _('1/6'), _('1/4'), _('1/3'), _('1/2'), _('1'), _('2') ]
+ for dur in durs:
+ self.snapGridBox.append_item(durs.index(dur),dur)
+ self.snapGridBox.set_active(0)
+ self.snapGridHBox.pack_start(self.snapGridImage, False, False, padding = 5)
+ self.snapGridHBox.pack_start(self.snapGridBox, False, False, padding = 5)
+
+ self.pointerBox.pack_start(self.snapGridHBox, False, False, padding = 5)
+ self.pointerBox.show_all()
+
+ self.set_content(self.pointerBox)
+
+ pass
+ #self.noteDur = widget.props.value
+
+ def handleSnapGrid(self, widget):
+ data = widget.props.value
+ grid = int(self.gridDurs[data])
+ self.edit.trackInterface.setPointerGrid(grid)
+
+class drawPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.drawBox = gtk.VBox()
+
+ self.snapGridHBox = gtk.HBox()
+ self.snapGridImage = gtk.Image()
+ self.snapGridImage.set_from_file(Config.TAM_TAM_ROOT + '/icons/grid.svg')
+ self.snapGridBox = BigComboBox()
+ self.snapGridBox.connect('changed', self.handleSnapGrid)
+ self.gridDurs = [1, 2, 3, 4, 6, 12, 24]
+ durs = [_('1/12'), _('1/6'), _('1/4'), _('1/3'), _('1/2'), _('1'), _('2') ]
+ for dur in durs:
+ self.snapGridBox.append_item(durs.index(dur),dur)
+ self.snapGridBox.set_active(0)
+ self.snapGridHBox.pack_start(self.snapGridImage, False, False, padding = 5)
+ self.snapGridHBox.pack_start(self.snapGridBox, False, False, padding = 5)
+
+ self.drawBox.pack_start(self.snapGridHBox, False, False, padding = 5)
+ self.drawBox.show_all()
+
+ self.set_content(self.drawBox)
+
+ pass
+ #self.noteDur = widget.props.value
+
+ def handleSnapGrid(self, widget):
+ data = widget.props.value
+ grid = int(self.gridDurs[data])
+ self.edit.trackInterface.setDrawGrid(grid)
+
+class paintPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.paintBox = gtk.VBox()
+
+ self.noteDurHBox = gtk.HBox()
+ self.noteDurImage = gtk.Image()
+ self.noteDurImage.set_from_file(Config.TAM_TAM_ROOT + '/icons/notedur.svg')
+ self.noteDurBox = BigComboBox()
+ self.noteDurBox.connect('changed', self.handleNoteDur)
+ self.noteDurs = [1, 2, 3, 4, 6, 12, 24]
+ self.durs = [_('1/12'), _('1/6'), _('1/4'), _('1/3'), _('1/2'), _('1'), _('2') ]
+ for dur in self.durs:
+ self.noteDurBox.append_item(self.durs.index(dur),dur)
+ self.noteDurBox.set_active(2)
+ self.noteDurHBox.pack_start(self.noteDurImage, False, False, padding = 5)
+ self.noteDurHBox.pack_start(self.noteDurBox, False, False, padding = 5)
+
+ self.snapGridHBox = gtk.HBox()
+ self.snapGridImage = gtk.Image()
+ self.snapGridImage.set_from_file(Config.TAM_TAM_ROOT + '/icons/grid.svg')
+ self.snapGridBox = BigComboBox()
+ self.snapGridBox.connect('changed', self.handleSnapGrid)
+ self.gridDurs = [1, 2, 3, 4, 6, 12, 24]
+ durs = [_('1/12'), _('1/6'), _('1/4'), _('1/3'), _('1/2'), _('1'), _('2')]
+ for dur in durs:
+ self.snapGridBox.append_item(durs.index(dur),dur)
+ self.snapGridBox.set_active(2)
+ self.snapGridHBox.pack_start(self.snapGridImage, False, False, padding = 5)
+ self.snapGridHBox.pack_start(self.snapGridBox, False, False, padding = 5)
+
+ self.paintBox.pack_start(self.noteDurHBox, False, False, padding = 5)
+ self.paintBox.pack_start(self.snapGridHBox, False, False, padding = 5)
+ self.paintBox.show_all()
+
+ self.set_content(self.paintBox)
+
+ def resizeNoteDur(self):
+ oldActive = self.noteDurBox.get_active()
+ len = self.snapGridBox.get_active()
+ self.noteDurBox.remove_all()
+ for dur in self.durs[0:len+1]:
+ self.noteDurBox.append_item(self.durs.index(dur), dur)
+ if oldActive <= len:
+ self.noteDurBox.set_active(oldActive)
+ else:
+ self.noteDurBox.set_active(len)
+
+ def handleNoteDur(self, widget):
+ data = widget.props.value
+ noteDur = int(self.noteDurs[data])
+ self.edit.trackInterface.setPaintNoteDur(noteDur)
+
+ def handleSnapGrid(self, widget):
+ data = widget.props.value
+ grid = int(self.gridDurs[data])
+ self.edit.trackInterface.setPaintGrid(grid)
+ self.resizeNoteDur()
+
+class volumeTempoPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.volumeTempoBox = gtk.VBox()
+
+ self.volumeSliderBox = gtk.HBox()
+ self.volumeSliderLabel = gtk.Label(_('Volume'))
+ self.volumeSliderAdj = gtk.Adjustment(Config.DEFAULT_VOLUME, 0, 100, 1, 1, 0)
+ self.volumeSliderAdj.connect('value-changed', self.edit.handleVolume)
+ self.volumeSlider = gtk.HScale(adjustment = self.volumeSliderAdj)
+ self.volumeSlider.set_size_request(250,-1)
+ self.volumeSlider.set_inverted(False)
+ self.volumeSlider.set_draw_value(False)
+ self.volumeSliderBox.pack_start(self.volumeSliderLabel, False, False, padding = 5)
+ self.volumeSliderBox.pack_end(self.volumeSlider, False, False, padding = 5)
+
+ self.tempoSliderBox = gtk.HBox()
+ self.tempoSliderLabel = gtk.Label(_('Tempo'))
+ self.tempoSliderAdj = gtk.Adjustment(Config.PLAYER_TEMPO, 40, 240, 1, 1, 0)
+ self.tempoSliderAdj.connect('value-changed', self.edit.handleTempo)
+ self.tempoSlider = gtk.HScale(adjustment = self.tempoSliderAdj)
+ self.tempoSlider.set_size_request(250,-1)
+ self.tempoSlider.set_inverted(False)
+ self.tempoSlider.set_draw_value(False)
+ self.tempoSliderBox.pack_start(self.tempoSliderLabel, False, False, padding = 5)
+ self.tempoSliderBox.pack_end(self.tempoSlider, False, False, padding = 5)
+
+ self.volumeTempoBox.pack_start(self.volumeSliderBox, padding = 5)
+ self.volumeTempoBox.pack_start(self.tempoSliderBox, padding = 5)
+ self.volumeTempoBox.show_all()
+
+ self.set_content(self.volumeTempoBox)
+
+class generationPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.rythmDensity = GenerationConstants.DEFAULT_DENSITY
+ self.rythmRegularity = GenerationConstants.DEFAULT_RYTHM_REGULARITY
+ self.pitchRegularity = GenerationConstants.DEFAULT_PITCH_REGULARITY
+ self.pitchStep = GenerationConstants.DEFAULT_STEP
+ self.duration = GenerationConstants.DEFAULT_DURATION
+ self.silence = GenerationConstants.DEFAULT_SILENCE
+ self.rythmMethod = GenerationConstants.DEFAULT_RYTHM_METHOD
+ self.pitchMethod = GenerationConstants.DEFAULT_PITCH_METHOD
+ self.pattern = GenerationConstants.DEFAULT_PATTERN
+ self.scale = GenerationConstants.DEFAULT_SCALE
+
+ self.mainBox = gtk.VBox()
+ self.slidersBox = gtk.HBox()
+ self.scaleModeBox = gtk.VBox()
+ self.decisionBox = gtk.HBox()
+
+ self.XYSlider1MainBox = gtk.VBox()
+ self.XYSlider1TopLabel = gtk.Label(_('Rhythm'))
+ self.XSlider1BottomLabelBox = gtk.HBox()
+ self.XSlider1Img = gtk.Image()
+ self.XSlider1Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/sideR.svg')
+ self.XSlider1BottomLabel = gtk.Label(_('Density'))
+ self.YSlider1BottomLabelBox = gtk.HBox()
+ self.YSlider1Img = gtk.Image()
+ self.YSlider1Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/updownR.svg')
+ self.YSlider1BottomLabel = gtk.Label(_('Regularity'))
+ self.XYSliderBox1 = RoundFixed(fillcolor = '#CCCCCC', bordercolor = '#000000')
+ self.XYSliderBox1.set_size_request(200,200)
+ self.XYButton1 = ImageToggleButton( Config.TAM_TAM_ROOT + '/icons/XYBut.svg', Config.TAM_TAM_ROOT + '/icons/XYButDown.svg')
+ self.XAdjustment1 = gtk.Adjustment(self.rythmDensity[0] * 100, 0, 100, 1, 1, 1)
+ self.XAdjustment1.connect("value-changed", self.handleXAdjustment1)
+ self.YAdjustment1 = gtk.Adjustment(self.rythmRegularity[0] * 100, 0, 100, 1, 1, 1)
+ self.YAdjustment1.connect("value-changed", self.handleYAdjustment1)
+ self.XYSlider1 = XYSlider( self.XYSliderBox1, self.XYButton1, self.XAdjustment1, self.YAdjustment1, False, True )
+ self.XSlider1BottomLabelBox.pack_start(self.XSlider1Img, False, False, padding = 5)
+ self.XSlider1BottomLabelBox.pack_start(self.XSlider1BottomLabel, False, False, padding = 5)
+ self.YSlider1BottomLabelBox.pack_start(self.YSlider1Img, False, False, padding = 5)
+ self.YSlider1BottomLabelBox.pack_start(self.YSlider1BottomLabel, False, False, padding = 5)
+ self.XYSlider1MainBox.pack_start(self.XYSlider1TopLabel, False, False, padding = 5)
+ self.XYSlider1MainBox.pack_start(self.XYSlider1, False, False, padding = 2)
+ self.XYSlider1MainBox.pack_start(self.XSlider1BottomLabelBox, False, False, padding = 2)
+ self.XYSlider1MainBox.pack_start(self.YSlider1BottomLabelBox, False, False, padding = 2)
+
+ self.XYSlider2MainBox = gtk.VBox()
+ self.XYSlider2TopLabel = gtk.Label(_('Pitch'))
+ self.XSlider2BottomLabelBox = gtk.HBox()
+ self.XSlider2Img = gtk.Image()
+ self.XSlider2Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/sideR.svg')
+ self.XSlider2BottomLabel = gtk.Label(_('Regularity'))
+ self.YSlider2BottomLabelBox = gtk.HBox()
+ self.YSlider2Img = gtk.Image()
+ self.YSlider2Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/updownR.svg')
+ self.YSlider2BottomLabel = gtk.Label(_('Maximum step'))
+ self.XYSliderBox2 = RoundFixed(fillcolor = '#CCCCCC', bordercolor = '#000000')
+ self.XYSliderBox2.set_size_request(200,200)
+ self.XYButton2 = ImageToggleButton( Config.TAM_TAM_ROOT + '/icons/XYBut.svg', Config.TAM_TAM_ROOT + '/icons/XYButDown.svg')
+ self.XAdjustment2 = gtk.Adjustment(self.pitchRegularity[0] * 100, 0, 100, 1, 1, 1)
+ self.XAdjustment2.connect("value-changed", self.handleXAdjustment2)
+ self.YAdjustment2 = gtk.Adjustment(self.pitchStep[0] * 100, 0, 100, 1, 1, 1)
+ self.YAdjustment2.connect("value-changed", self.handleYAdjustment2)
+ self.XYSlider2 = XYSlider( self.XYSliderBox2, self.XYButton2, self.XAdjustment2, self.YAdjustment2, False, True )
+ self.XSlider2BottomLabelBox.pack_start(self.XSlider2Img, False, False, padding = 5)
+ self.XSlider2BottomLabelBox.pack_start(self.XSlider2BottomLabel, False, False, padding = 5)
+ self.YSlider2BottomLabelBox.pack_start(self.YSlider2Img, False, False, padding = 5)
+ self.YSlider2BottomLabelBox.pack_start(self.YSlider2BottomLabel, False, False, padding = 5)
+ self.XYSlider2MainBox.pack_start(self.XYSlider2TopLabel, False, False, padding = 5)
+ self.XYSlider2MainBox.pack_start(self.XYSlider2, False, False, padding = 2)
+ self.XYSlider2MainBox.pack_start(self.XSlider2BottomLabelBox, False, False, padding = 2)
+ self.XYSlider2MainBox.pack_start(self.YSlider2BottomLabelBox, False, False, padding = 2)
+
+ self.XYSlider3MainBox = gtk.VBox()
+ self.XYSlider3TopLabel = gtk.Label(_('Duration'))
+ self.XSlider3BottomLabelBox = gtk.HBox()
+ self.XSlider3Img = gtk.Image()
+ self.XSlider3Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/sideR.svg')
+ self.XSlider3BottomLabel = gtk.Label(_('Note duration'))
+ self.YSlider3BottomLabelBox = gtk.HBox()
+ self.YSlider3Img = gtk.Image()
+ self.YSlider3Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/updownR.svg')
+ self.YSlider3BottomLabel = gtk.Label(_('Silence density'))
+ self.XYSliderBox3 = RoundFixed(fillcolor = '#CCCCCC', bordercolor = '#000000')
+ self.XYSliderBox3.set_size_request(200,200)
+ self.XYButton3 = ImageToggleButton( Config.TAM_TAM_ROOT + '/icons/XYBut.svg', Config.TAM_TAM_ROOT + '/icons/XYButDown.svg')
+ self.XAdjustment3 = gtk.Adjustment(self.duration[0] * 100, 0, 100, 1, 1, 1)
+ self.XAdjustment3.connect("value-changed", self.handleXAdjustment3)
+ self.YAdjustment3 = gtk.Adjustment(self.silence[0] * 100, 0, 100, 1, 1, 1)
+ self.YAdjustment3.connect("value-changed", self.handleYAdjustment3)
+ self.XYSlider3 = XYSlider( self.XYSliderBox3, self.XYButton3, self.XAdjustment3, self.YAdjustment3, False, True )
+ self.XSlider3BottomLabelBox.pack_start(self.XSlider3Img, False, False, padding = 5)
+ self.XSlider3BottomLabelBox.pack_start(self.XSlider3BottomLabel, False, False, padding = 5)
+ self.YSlider3BottomLabelBox.pack_start(self.YSlider3Img, False, False, padding = 5)
+ self.YSlider3BottomLabelBox.pack_start(self.YSlider3BottomLabel, False, False, padding = 5)
+ self.XYSlider3MainBox.pack_start(self.XYSlider3TopLabel, False, False, padding = 5)
+ self.XYSlider3MainBox.pack_start(self.XYSlider3, False, False, padding = 2)
+ self.XYSlider3MainBox.pack_start(self.XSlider3BottomLabelBox, False, False, padding = 2)
+ self.XYSlider3MainBox.pack_start(self.YSlider3BottomLabelBox, False, False, padding = 2)
+
+ self.slidersBox.pack_start(self.XYSlider1MainBox, False, False, padding = 5)
+ self.slidersBox.pack_start(self.XYSlider2MainBox, False, False, padding = 5)
+ self.slidersBox.pack_start(self.XYSlider3MainBox, False, False, padding = 5)
+
+ self.previewBox = gtk.HBox()
+ self.previewDA = gtk.DrawingArea()
+ self.previewDA.set_size_request( -1, 100 )
+ self.previewDA.connect( "size-allocate", self.handlePreviewAlloc )
+ self.previewDA.connect( "expose-event", self.handlePreviewExpose )
+ self.previewBox.pack_start( self.previewDA, True, True, padding = 5 )
+
+ self.scaleBoxHBox = gtk.HBox()
+ self.scaleBoxLabel = gtk.Label(_('Scale: '))
+ self.scaleBox = BigComboBox()
+ scales = [_('Major scale'), _('Harmonic minor scale'), _('Natural minor scale'), _('Phrygian scale'), _('Dorian scale'), _('Lydian scale'), _('Mixolydian scale')]
+ for scale in scales:
+ self.scaleBox.append_item(scales.index(scale), scale)
+ self.scaleBox.connect('changed', self.handleScale)
+
+ self.modeBoxHBox = gtk.HBox()
+ self.modeBoxLabel = gtk.Label(_('Mode: '))
+ self.modeBox = BigComboBox()
+ modes = [_('Drunk'), _('Drone and Jump'), _('Repeater'), _('Loop segments')]
+ for mode in modes:
+ self.modeBox.append_item(modes.index(mode), mode)
+ self.modeBox.connect('changed', self.handleMode)
+
+ self.scaleBoxHBox.pack_start(self.scaleBoxLabel, False, False, padding = 10)
+ self.scaleBoxHBox.pack_start(self.scaleBox, False, False, padding = 10)
+ self.modeBoxHBox.pack_start(self.modeBoxLabel, False, False, padding = 10)
+ self.modeBoxHBox.pack_start(self.modeBox, False, False, padding = 10)
+ self.scaleModeBox.pack_start(self.scaleBoxHBox, False, False, padding = 5)
+ self.scaleModeBox.pack_start(self.modeBoxHBox, False, False, padding = 5)
+
+ self.acceptButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/accept.svg')
+ self.acceptButton.connect('clicked',self.generate)
+ self.cancelButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/cancel.svg')
+ self.cancelButton.connect('clicked',self.cancel)
+ self.decisionBox.pack_start(self.cancelButton, False, False, padding = 5)
+ self.decisionBox.pack_start(self.acceptButton, False, False, padding = 5)
+
+ self.mainBox.pack_start(self.slidersBox, False, False, padding = 5)
+ self.mainBox.pack_start( self.previewBox, False, False, padding = 5 )
+ self.mainBox.pack_start(self.scaleModeBox, False, False, padding = 5)
+ self.mainBox.pack_start(self.decisionBox, False, False, padding = 5)
+ self.mainBox.show_all()
+
+ self.set_content(self.mainBox)
+
+ #-- preview drawing -----------------------------------
+ win = gtk.gdk.get_default_root_window()
+ self.gc = gtk.gdk.GC( win )
+ self.parametersDirty = False
+ self.drawingPreview = False
+ self.predrawTarget = 0
+ self.predrawIdleAbort = False
+ self.predrawBuffer = False
+ # self.predrawBuffer is initialized in handlePreviewAlloc
+ 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 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
+
+ colormap = self.previewDA.get_colormap()
+ self.colors = { "Beat_Line": colormap.alloc_color( "#959595", True, True ),
+ "Note_Border": colormap.alloc_color( Config.BG_COLOR, True, True ),
+ "Note_Fill": colormap.alloc_color( Config.FG_COLOR, True, True ) }
+
+ self.scaleBox.set_active(0)
+ self.modeBox.set_active(0)
+
+
+ def handleXAdjustment1( self, data ):
+ self.rythmDensity = [self.XAdjustment1.value * .01 for x in range(4)]
+
+ self.parametersChanged()
+
+ def handleYAdjustment1( self, data ):
+ self.rythmRegularity = [self.YAdjustment1.value * .01 for x in range(4)]
+ self.parametersChanged()
+
+ def handleXAdjustment2( self, data ):
+ self.pitchRegularity = [self.XAdjustment2.value * .01 for x in range(4)]
+ self.parametersChanged()
+
+ def handleYAdjustment2( self, data ):
+ self.pitchStep = [self.YAdjustment2.value * .01 for x in range(4)]
+ self.parametersChanged()
+
+ def handleXAdjustment3( self, data ):
+ self.duration = [self.XAdjustment3.value * .01 for x in range(4)]
+ self.parametersChanged()
+
+ def handleYAdjustment3( self, data ):
+ self.silence = [self.YAdjustment3.value * .01 for x in range(4)]
+ self.parametersChanged()
+
+ def handleScale(self, widget, data = None):
+ self.scale = widget.props.value
+ self.edit.scale = self.scale
+ self.parametersChanged()
+
+ def handleMode( self, widget, data = None ):
+ self.pattern = [widget.props.value for x in range(4)]
+ self.parametersChanged()
+
+ def getGenerationParameters( self ):
+ return GenerationParameters( self.rythmDensity,
+ self.rythmRegularity,
+ self.pitchStep,
+ self.pitchRegularity,
+ self.duration,
+ self.silence,
+ self.rythmMethod,
+ self.pitchMethod,
+ self.pattern,
+ self.scale )
+
+ def cancel(self, widget, data = None):
+ self.popdown(True)
+
+ def generate(self, widget, data=None):
+ context = self.edit.getContext()
+ if context == 0: # Page
+ mode = 'page'
+ elif context == 1: # Track
+ mode = 'track'
+ elif context == 2: # Note
+ self.popdown(True)
+ return
+ self.edit.setPageGenerateMode(mode)
+ self.edit.generate(self.getGenerationParameters())
+ print self.rythmDensity, self.rythmRegularity,self.pitchRegularity,self.pitchStep, self.duration, self.silence,self.pattern
+ self.popdown(True)
+
+ ############ generate a preview melody ##############s
+ def previewGenerator(self, parameters):
+ makeRythm = GenerationRythm()
+ makePitch = GenerationPitch()
+ table_duration = Utils.scale(parameters.articule[0], GenerationConstants.ARTICULATION_SCALE_MIN_MAPPING, GenerationConstants.ARTICULATION_SCALE_MAX_MAPPING, GenerationConstants.ARTICULATION_SCALE_STEPS)
+ table_pitch = GenerationConstants.SCALES[parameters.scale]
+ beat = self.edit.noteDB.pages[self.edit.tuneInterface.getSelectedIds()[0]].beats
+ barLength = Config.TICKS_PER_BEAT * beat
+ trackNotes = []
+
+ rythmSequence = makeRythm.celluleRythmSequence(parameters, barLength, 0)
+ pitchSequence = makePitch.drunkPitchSequence(len(rythmSequence),parameters, table_pitch, 0)
+ gainSequence = self.makeGainSequence(rythmSequence)
+ durationSequence = self.makeDurationSequence(rythmSequence, parameters, table_duration, barLength)
+
+ for i in range(len(rythmSequence)):
+ if random() > parameters.silence[0]:
+ trackNotes.append([rythmSequence[i], pitchSequence[i], gainSequence[i], durationSequence[i]])
+ #print "-------------------------------------------------------",trackNotes
+ return ( trackNotes, beat )
+
+ def makeGainSequence( self, onsetList ):
+ gainSequence = []
+ max = GenerationConstants.GAIN_MAX_BOUNDARY
+ midMax = GenerationConstants.GAIN_MID_MAX_BOUNDARY
+ midMin = GenerationConstants.GAIN_MID_MIN_BOUNDARY
+ min = GenerationConstants.GAIN_MIN_BOUNDARY
+ for onset in onsetList:
+ if onset == 0:
+ gainSequence.append(uniform(midMax, max))
+ elif ( onset % Config.TICKS_PER_BEAT) == 0:
+ gainSequence.append(uniform(midMin, midMax))
+ else:
+ gainSequence.append(uniform(min, midMin))
+ return gainSequence
+
+ def makeDurationSequence( self, onsetList, parameters, table_duration, barLength ):
+ durationSequence = []
+ if len( onsetList ) > 1:
+ for i in range(len(onsetList) - 1):
+ durationSequence.append((onsetList[i+1] - onsetList[i]) * Utils.prob2( table_duration ))
+ durationSequence.append(( barLength - onsetList[-1]) * Utils.prob2( table_duration ))
+ elif len( onsetList ) == 1:
+ durationSequence.append( ( barLength - onsetList[0] ) * Utils.prob2( table_duration ))
+ return durationSequence
+
+ def parametersChanged( self ):
+ if not self.drawingPreview:
+ self.drawPreview()
+ else:
+ self.parametersDirty = True
+
+ def drawPreview( self, force = False ):
+ if not self.predrawBuffer:
+ return # not alloc'ed yet
+
+ if self.drawingPreview and not force:
+ return # should never happen
+
+ notes, beats = self.previewGenerator( self.getGenerationParameters() )
+ self.parametersDirty = False
+
+ if force:
+ if self.drawingPreview:
+ self.predrawIdleAbort = True
+ self._idleDraw( notes, beats, True, True )
+ else:
+ self.drawingPreview = True
+ gobject.idle_add( self._idleDraw, notes, beats, True, False )
+
+ def _idleDraw( self, notes, beats, fresh, force ):
+ if self.predrawIdleAbort and not force:
+ self.predrawIdleAbort = False
+ return False
+
+ pixmap = self.predrawBuffer[self.predrawTarget]
+
+ if fresh:
+ # draw bg
+ pixmap.draw_drawable( self.gc, self.sampleBg, 0, 0, 0, 0, self.previewDA.width-5, self.previewDA.height )
+ pixmap.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]
+ pixmap.draw_line( self.gc, x, 1, x, self.previewDA.height-1 )
+
+ if not force:
+ gobject.idle_add( self._idleDraw, notes, beats, False, False )
+ return False
+
+ if force: N = len(notes)
+ else: N = min( 3, len( notes ) ) # adjust this value to get a reasonable response
+
+ self.gc.set_clip_mask( self.sampleNoteMask )
+ for i in range( N ): # draw N notes
+ note = notes.pop()
+ x = self.ticksToPixels( beats, note[0] )
+ endX = self.ticksToPixels( beats, note[0] + note[3] ) - 3 # include end cap offset
+ width = endX - x
+ y = self.pitchToPixels( note[1] )
+ # draw fill
+ self.gc.foreground = self.colors["Note_Fill"]
+ 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
+ self.gc.foreground = self.colors["Note_Border"]
+ 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 )
+ self.gc.set_clip_rectangle( self.clearClipMask )
+
+ if not len(notes):
+ self.predrawTarget = not self.predrawTarget
+ self.previewDA.queue_draw()
+
+ self.drawingPreview = False
+
+ if self.parametersDirty:
+ self.drawPreview()
+
+ return False
+
+ return True
+
+ def handlePreviewAlloc( self, widget, allocation ):
+ win = gtk.gdk.get_default_root_window()
+ self.previewDA.width = allocation.width
+ self.previewDA.height = allocation.height
+ self.predrawBuffer = [ gtk.gdk.Pixmap( win, allocation.width, allocation.height ),
+ gtk.gdk.Pixmap( win, allocation.width, allocation.height ) ]
+ self.clearClipMask = gtk.gdk.Rectangle( 0, 0, allocation.width, allocation.height )
+
+ self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.previewDA.height - self.sampleNoteHeight)
+ self.pixelsPerPitch = float(self.previewDA.height - self.sampleNoteHeight)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ 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) ] )
+
+ self.drawPreview( True )
+
+ def handlePreviewExpose( self, widget, event ):
+ widget.window.draw_drawable( self.gc, self.predrawBuffer[not self.predrawTarget], event.area.x, event.area.y, event.area.x, event.area.y, event.area.width, event.area.height )
+
+ def ticksToPixels( self, beats, ticks ):
+ return int(round( ticks * self.pixelsPerTick[beats] ))
+ def pitchToPixels( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+
+
+class propertiesPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+ self.connect('popup', self.handlePopup)
+ self.connect('popdown', self.handlePopdown)
+
+ self.edit = edit
+
+ self.filterTypes = [_('None'), _('Lowpass'), _('Bandpass'), _('Highpass')]
+ self.geneTypes = [_('Line'),_('Drunk'),_('Drone and Jump'),_('Repeater'),_('Loop segments')]
+ self.colors = [_('Purple'), _('Green'), _('Blue'), _('Yellow')]
+ self.currentFilterType = self.filterTypes[0]
+
+ self.line = Line(0, 100)
+ self.drunk = Drunk(0, 100)
+ self.droneAndJump = DroneAndJump(0, 100)
+ self.repeter = Repeter(0, 100)
+ self.loopseg = Loopseg(0, 100)
+ self.algoTypes = [self.line, self.drunk, self.droneAndJump, self.repeter, self.loopseg]
+ self.algorithm = self.algoTypes[0]
+ self.geneMinimum = 0
+ self.geneMaximum = 100
+ self.geneRandom = 20
+
+ self.setup = False
+ self.hidden = False
+ self.geneCheckButtonDic = {}
+
+ self.pageIds = []
+ self.context = "page"
+
+ self.mainBox = gtk.VBox()
+
+ self.gridDivisionBox = gtk.HBox()
+ self.gridDivisionLabel = gtk.Label(_('Grid division: '))
+ self.gridDivisionSliderAdj = gtk.Adjustment(4, 2, 12, 1, 1, 0)
+ self.gridDivisionSlider = gtk.HScale(adjustment = self.gridDivisionSliderAdj)
+ self.gridDivisionSlider.set_digits(0)
+ self.gridDivisionSlider.connect('button-release-event', self.handleBeat)
+ self.gridDivisionSlider.set_size_request(200,-1)
+ self.gridDivisionSlider.set_value_pos(gtk.POS_RIGHT)
+ self.gridDivisionBox.pack_start(self.gridDivisionLabel, False, False, padding = 5)
+ self.gridDivisionBox.pack_end(self.gridDivisionSlider, False, False, padding = 52)
+
+ self.pageColorBox = gtk.HBox()
+ self.pageColorLabel = gtk.Label(_('Page color: '))
+ self.pageColorComboBox = BigComboBox()
+ for color in (0,1,2,3):
+ self.pageColorComboBox.append_item(color, text = None, icon_name = Config.IMAGE_ROOT + 'pageThumbnailBG' + str(color) + '.png', size = (30,40))
+ self.pageColorComboBox.set_active(0)
+ self.pageColorComboBox.connect('changed', self.handleColor)
+ self.pageColorBox.pack_start(self.pageColorLabel, False, False, padding = 5)
+ self.pageColorBox.pack_end(self.pageColorComboBox, False, False, padding = 55)
+
+ self.pageSeparator = gtk.HSeparator()
+ self.pageSeparator.set_size_request(20, -1)
+
+ self.transposeBox = gtk.HBox()
+ self.transposeLabel = gtk.Label(_('Transposition: '))
+ self.transposeDownButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/arrow-down.svg')
+ self.transposeDownButton.connect('clicked', self.stepPitch, -1)
+ self.transposeUpButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/arrow-up.svg')
+ self.transposeUpButton.connect('clicked', self.stepPitch, 1)
+ self.transposeCheckButton = gtk.CheckButton()
+ self.transposeCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['transpose'] = self.transposeCheckButton
+ self.transposeBox.pack_start(self.transposeLabel, False, False, padding = 5)
+ self.transposeBox.pack_end(self.transposeCheckButton, False, False, padding = 5)
+ self.transposeBox.pack_end(self.transposeUpButton, False, False, padding = 50)
+ self.transposeBox.pack_end(self.transposeDownButton, False, False, padding = 5)
+
+ self.volumeBox = gtk.HBox()
+ self.volumeLabel = gtk.Label(_('Volume') + ': ')
+ self.volumeDownButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/arrow-down.svg')
+ self.volumeDownButton.connect('clicked', self.stepVolume, -0.1)
+ self.volumeUpButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/arrow-up.svg')
+ self.volumeUpButton.connect('clicked', self.stepVolume, 0.1)
+ self.volumeCheckButton = gtk.CheckButton()
+ self.volumeCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['volume'] = self.volumeCheckButton
+ self.volumeBox.pack_start(self.volumeLabel, False, False, padding = 5)
+ self.volumeBox.pack_end(self.volumeCheckButton, False, False, padding = 5)
+ self.volumeBox.pack_end(self.volumeUpButton, False, False, padding = 50)
+ self.volumeBox.pack_end(self.volumeDownButton, False, False, padding = 5)
+
+ self.panBox = gtk.HBox()
+ self.panLabel = gtk.Label(_('Pan: '))
+ self.panSliderAdj = gtk.Adjustment(0.5, 0, 1, .1, .1, 0)
+ self.panSliderAdj.connect('value-changed', self.handlePan)
+ self.panSlider = gtk.HScale(adjustment = self.panSliderAdj)
+ self.panSlider.set_size_request(200,-1)
+ self.panSlider.set_value_pos(gtk.POS_RIGHT)
+ self.panSlider.set_digits(2)
+ self.panSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.panCheckButton = gtk.CheckButton()
+ self.panCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['pan'] = self.panCheckButton
+ self.panBox.pack_start(self.panLabel, False, False, padding = 5)
+ self.panBox.pack_end(self.panCheckButton, False, False, padding = 5)
+ self.panBox.pack_end(self.panSlider, False, False, padding = 5)
+
+ self.reverbBox = gtk.HBox()
+ self.reverbLabel = gtk.Label(_('Reverb') + ': ')
+ self.reverbSliderAdj = gtk.Adjustment(0.1, 0, 1, 0.1, 0.1, 0)
+ self.reverbSliderAdj.connect("value-changed", self.handleReverb)
+ self.reverbSlider = gtk.HScale(adjustment = self.reverbSliderAdj)
+ self.reverbSlider.set_size_request(200,-1)
+ self.reverbSlider.set_value_pos(gtk.POS_RIGHT)
+ self.reverbSlider.set_digits(2)
+ self.reverbSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.reverbCheckButton = gtk.CheckButton()
+ self.reverbCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['reverb'] = self.reverbCheckButton
+ self.reverbBox.pack_start(self.reverbLabel, False, False, padding = 5)
+ self.reverbBox.pack_end(self.reverbCheckButton, False, False, padding = 5)
+ self.reverbBox.pack_end(self.reverbSlider, False, False, padding = 5)
+
+ self.attackDurBox = gtk.HBox()
+ self.attackDurLabel = gtk.Label(_('Attack duration') + ': ')
+ self.attackDurSliderAdj = gtk.Adjustment(0.04, 0.03, 1, .01, .01, 0)
+ self.attackDurSliderAdj.connect('value-changed', self.handleAttack)
+ self.attackDurSlider = gtk.HScale(adjustment = self.attackDurSliderAdj)
+ self.attackDurSlider.set_size_request(200,-1)
+ self.attackDurSlider.set_value_pos(gtk.POS_RIGHT)
+ self.attackDurSlider.set_digits(2)
+ self.attackDurSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.attackDurCheckButton = gtk.CheckButton()
+ self.attackDurCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['attack'] = self.attackDurCheckButton
+ self.attackDurBox.pack_start(self.attackDurLabel, False, False, padding = 5)
+ self.attackDurBox.pack_end(self.attackDurCheckButton, False, False, padding = 5)
+ self.attackDurBox.pack_end(self.attackDurSlider, False, False, padding = 5)
+
+ self.decayDurBox = gtk.HBox()
+ self.decayDurLabel = gtk.Label(_('Decay duration') + ': ')
+ self.decayDurSliderAdj = gtk.Adjustment(0.31, 0.03, 1, .01, .01, 0)
+ self.decayDurSliderAdj.connect('value-changed', self.handleDecay)
+ self.decayDurSlider = gtk.HScale(adjustment = self.decayDurSliderAdj)
+ self.decayDurSlider.set_size_request(200,-1)
+ self.decayDurSlider.set_value_pos(gtk.POS_RIGHT)
+ self.decayDurSlider.set_digits(2)
+ self.decayDurSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.decayDurCheckButton = gtk.CheckButton()
+ self.decayDurCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['decay'] = self.decayDurCheckButton
+ self.decayDurBox.pack_start(self.decayDurLabel, False, False, padding = 5)
+ self.decayDurBox.pack_end(self.decayDurCheckButton, False, False, padding = 5)
+ self.decayDurBox.pack_end(self.decayDurSlider, False, False, padding = 5)
+
+ self.filterTypeBox = gtk.HBox()
+ self.filterTypeLabel = gtk.Label(_('Filter Type: '))
+ self.filterTypeComboBox = BigComboBox()
+ for filtertype in self.filterTypes:
+ self.filterTypeComboBox.append_item(self.filterTypes.index(filtertype), filtertype)
+ self.filterTypeComboBox.connect('changed', self.handleFilterTypes)
+ self.filterTypeBox.pack_start(self.filterTypeLabel, False, False, padding = 5)
+ self.filterTypeBox.pack_end(self.filterTypeComboBox, False, False, padding = 55)
+
+ self.filterCutoffBox = gtk.HBox()
+ self.filterCutoffLabel = gtk.Label(_('Filter cutoff') + ': ')
+ self.filterCutoffSliderAdj = gtk.Adjustment(1000, 100, 7000, 100, 100, 0)
+ self.filterCutoffSliderAdj.connect('value-changed', self.handleFilter)
+ self.filterCutoffSlider = gtk.HScale(adjustment = self.filterCutoffSliderAdj)
+ self.filterCutoffSlider.set_size_request(200,-1)
+ self.filterCutoffSlider.set_value_pos(gtk.POS_RIGHT)
+ self.filterCutoffSlider.set_digits(0)
+ self.filterCutoffSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.filterCutoffCheckButton = gtk.CheckButton()
+ self.filterCutoffCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['filter'] = self.filterCutoffCheckButton
+ self.filterCutoffBox.pack_start(self.filterCutoffLabel, False, False, padding = 5)
+ self.filterCutoffBox.pack_end(self.filterCutoffCheckButton, False, False, padding = 5)
+ self.filterCutoffBox.pack_end(self.filterCutoffSlider, False, False, padding = 5)
+
+ self.generationMainBox = gtk.VBox()
+ self.generationSeparator = gtk.HSeparator()
+ self.generationLabel = gtk.Label(_('Generation'))
+
+ self.generationTypeBox = gtk.HBox()
+ self.generationTypeLabel = gtk.Label(_('Type') + ': ')
+ self.generationTypeComboBox = BigComboBox()
+ for genetype in self.geneTypes:
+ self.generationTypeComboBox.append_item(self.geneTypes.index(genetype), genetype)
+ self.generationTypeComboBox.connect('changed', self.handleGeneTypes)
+ self.generationTypeComboBox.set_active(0)
+ self.generationTypeBox.pack_start(self.generationTypeLabel, False, False, padding = 5)
+ self.generationTypeBox.pack_end(self.generationTypeComboBox, False, False, padding = 55)
+
+ self.minimumBox = gtk.HBox()
+ self.minimumLabel = gtk.Label(_('Minimum') + ': ')
+ self.minimumSliderAdj = gtk.Adjustment(0, 0, 100, 1, 1, 0)
+ self.minimumSliderAdj.connect('value-changed', self.handleMinimum)
+ self.minimumSlider = gtk.HScale(adjustment = self.minimumSliderAdj)
+ self.minimumSlider.set_size_request(200,-1)
+ self.minimumSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.minimumSlider.set_value_pos(gtk.POS_RIGHT)
+ self.minimumSlider.set_digits(0)
+ self.minimumBox.pack_start(self.minimumLabel, False, False, padding = 5)
+ self.minimumBox.pack_end(self.minimumSlider, False, False, padding = 52)
+
+ self.maximumBox = gtk.HBox()
+ self.maximumLabel = gtk.Label(_('Maximum') + ': ')
+ self.maximumSliderAdj = gtk.Adjustment(100, 0, 100, 1, 1, 0)
+ self.maximumSliderAdj.connect('value-changed', self.handleMaximum)
+ self.maximumSlider = gtk.HScale(adjustment = self.maximumSliderAdj)
+ self.maximumSlider.set_size_request(200,-1)
+ self.maximumSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.maximumSlider.set_value_pos(gtk.POS_RIGHT)
+ self.maximumSlider.set_digits(0)
+ self.maximumBox.pack_start(self.maximumLabel, False, False, padding = 5)
+ self.maximumBox.pack_end(self.maximumSlider, False, False, padding = 52)
+
+ self.randomBox = gtk.HBox()
+ self.randomLabel = gtk.Label(_('Random') + ': ')
+ self.randomSliderAdj = gtk.Adjustment(20, 0, 100, 1, 1, 0)
+ self.randomSliderAdj.connect('value-changed', self.handleRandom)
+ self.randomSlider = gtk.HScale(adjustment = self.randomSliderAdj)
+ self.randomSlider.set_size_request(200,-1)
+ self.randomSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.randomSlider.set_value_pos(gtk.POS_RIGHT)
+ self.randomSlider.set_digits(0)
+ self.randomBox.pack_start(self.randomLabel, False, False, padding = 5)
+ self.randomBox.pack_end(self.randomSlider, False, False, padding = 52)
+
+ self.decisionBox = gtk.HBox()
+ self.acceptButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/accept.svg')
+ self.acceptButton.connect('clicked', self.acceptGeneration)
+ self.cancelButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/cancel.svg')
+ self.cancelButton.connect('clicked', self.resetGeneCheckButton)
+ self.decisionBox.pack_start(self.cancelButton, False, False, padding = 5)
+ self.decisionBox.pack_start(self.acceptButton, False, False, padding = 5)
+
+ self.mainBox.pack_start(self.gridDivisionBox, padding = 3)
+ self.mainBox.pack_start(self.pageColorBox, padding = 3)
+ self.mainBox.pack_start(self.pageSeparator, padding = 10)
+ self.mainBox.pack_start(self.transposeBox, padding = 3)
+ self.mainBox.pack_start(self.volumeBox, padding = 3)
+ self.mainBox.pack_start(self.panBox, padding = 3)
+ self.mainBox.pack_start(self.reverbBox, padding = 3)
+ self.mainBox.pack_start(self.attackDurBox, padding = 3)
+ self.mainBox.pack_start(self.decayDurBox, padding = 3)
+ self.mainBox.pack_start(self.filterTypeBox, padding = 3)
+ self.mainBox.pack_start(self.filterCutoffBox, padding = 3)
+ self.generationMainBox.pack_start(self.generationSeparator, padding = 5)
+ self.generationMainBox.pack_start(self.generationLabel, padding = 10)
+ self.generationMainBox.pack_start(self.generationTypeBox, padding = 3)
+ self.generationMainBox.pack_start(self.minimumBox, padding = 3)
+ self.generationMainBox.pack_start(self.maximumBox, padding = 3)
+ self.generationMainBox.pack_start(self.randomBox, padding = 3)
+ self.generationMainBox.pack_start(self.decisionBox, padding = 3)
+ self.mainBox.pack_start(self.generationMainBox, padding = 3)
+ self.mainBox.show_all()
+
+ self.generationMainBox.hide()
+
+ self.set_content(self.mainBox)
+
+ def handlePopup(self, widget, data = None):
+ if self.edit.getContext() == 0: #Page
+ self.setContext('page', self.edit._generateToolbar._generationPalette.scale, self.edit.tuneInterface.getSelectedIds())
+ elif self.edit.getContext() == 1: #Track
+ self.setContext('track', self.edit._generateToolbar._generationPalette.scale, self.edit.tuneInterface.getSelectedIds(), [ i for i in range(Config.NUMBER_OF_TRACKS) if self.edit.trackSelected[i] ])
+ elif self.edit.getContext() == 2: #Note
+ ids = self.edit.trackInterface.getSelectedNotes()
+ notes = { self.edit.displayedPage: {} }
+ for t in range(Config.NUMBER_OF_TRACKS):
+ if len(ids[t]):
+ notes[self.edit.displayedPage][t] = [ self.edit.noteDB.getNote( self.edit.displayedPage, t, id ) for id in ids[t] ]
+ self.setContext('note', self.edit._generateToolbar._generationPalette.scale, notes = notes)
+
+ def handlePopdown(self, widget, data = None):
+ self.resetGeneCheckButton(self.cancelButton)
+
+ def setContext( self, context, scale, pageIds = None, trackIds = None, notes = {} ):
+ self.context = context
+ self.scale = GenerationConstants.SCALES[scale]
+ self.notes = {}
+ self.pageIds = pageIds
+ self.trackIds = trackIds
+
+ if context == "page":
+ self.trackIds = [0,1,2,3,4]
+ for p in pageIds:
+ self.notes[p] = {}
+ for t in range(Config.NUMBER_OF_TRACKS):
+ self.notes[p][t] = self.edit.noteDB.getNotesByTrack( p, t )
+ page = self.edit.noteDB.getPage(pageIds[0])
+ self.gridDivisionSliderAdj.set_value(page.beats)
+ elif context == "track":
+ for p in pageIds:
+ self.notes[p] = {}
+ for t in trackIds:
+ self.notes[p][t] = self.edit.noteDB.getNotesByTrack( p, t )
+ else:
+ self.notes = notes
+ self.pageIds = self.notes.keys()
+ self.trackIds = self.notes[self.pageIds[0]].keys()
+
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ # initialize values from first note
+ self.setup = True
+ n = self.notes[p][t][0]
+ self.panSliderAdj.set_value( n.cs.pan )
+ self.reverbSliderAdj.set_value( n.cs.reverbSend )
+ self.attackDurSliderAdj.set_value( n.cs.attack )
+ self.decayDurSliderAdj.set_value( n.cs.decay )
+ self.filterTypeComboBox.set_active(n.cs.filterType)
+ self.currentFilterType = n.cs.filterType
+ self.filterCutoffSliderAdj.set_value( n.cs.filterCutoff )
+ self.setup = False
+
+ def acceptGeneration( self, widget ):
+ valList = [self.geneMinimum, self.geneMaximum, self.geneRandom]
+ if self.geneCheckButtonDic['transpose'].get_active(): self.algoPitch(valList, self.algorithm)
+ if self.geneCheckButtonDic['volume'].get_active(): self.algoVolume(valList, self.algorithm)
+ if self.geneCheckButtonDic['pan'].get_active(): self.algoPan(valList, self.algorithm)
+ if self.geneCheckButtonDic['reverb'].get_active(): self.algoReverb(valList, self.algorithm)
+ if self.geneCheckButtonDic['attack'].get_active(): self.algoAttack(valList, self.algorithm)
+ if self.geneCheckButtonDic['decay'].get_active(): self.algoDecay(valList, self.algorithm)
+ if self.geneCheckButtonDic['filter'].get_active(): self.algoCutoff(valList, self.algorithm)
+
+ def resetGeneCheckButton(self, widget):
+ if self.hidden:
+ self.generationMainBox.hide()
+
+ for key in self.geneCheckButtonDic:
+ self.geneCheckButtonDic[key].set_active(False)
+
+ def handleGeneCheckButton(self, widget, data = None):
+ self.hidden = True
+ if widget.get_active():
+ self.generationMainBox.show()
+ else:
+ for key in self.geneCheckButtonDic:
+ if self.geneCheckButtonDic[key].get_active():
+ self.hidden = False
+ if self.hidden:
+ self.generationMainBox.hide()
+
+
+ def handleBeat(self, widget, signal_id):
+ beats = int(widget.get_adjustment().value)
+ stream = []
+ for page in self.pageIds:
+ stream += [ page, beats ]
+ if len(stream):
+ self.edit.noteDB.updatePages( [ PARAMETER.PAGE_BEATS, len(stream)//2 ] + stream )
+
+ def handleColor(self, widget):
+ index = widget.props.value
+ stream = []
+ for page in self.pageIds:
+ stream += [ page, index ]
+ if len(stream):
+ self.edit.noteDB.updatePages( [ PARAMETER.PAGE_COLOR, len(stream)//2 ] + stream )
+
+ def stepPitch(self, widget, step):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ substream = []
+ if step > 0:
+ if t != Config.NUMBER_OF_TRACKS-1: # regular note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MAXIMUM_PITCH:
+ substream += [ n.id, min( Config.MAXIMUM_PITCH, n.cs.pitch + step ) ]
+ else: # drum note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MAXIMUM_PITCH_DRUM:
+ substream += [ n.id, min( Config.MAXIMUM_PITCH_DRUM, n.cs.pitch + step*Config.PITCH_STEP_DRUM ) ]
+ else:
+ if t != Config.NUMBER_OF_TRACKS-1: # regular note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MINIMUM_PITCH:
+ substream += [ n.id, max( Config.MINIMUM_PITCH, n.cs.pitch + step ) ]
+ else: # drum note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MINIMUM_PITCH_DRUM:
+ substream += [ n.id, max( Config.MINIMUM_PITCH_DRUM, n.cs.pitch + step*Config.PITCH_STEP_DRUM ) ]
+ if len(substream):
+ stream += [ p, t, PARAMETER.PITCH, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoPitch( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ scaleLength = len(self.scale)-1
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ if self.trackIds[t] != Config.NUMBER_OF_TRACKS-1:
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, self.scale[int(val*0.01*scaleLength)]+36 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PITCH, len(substream)//2 ] + substream
+ else:
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ val = int((val*0.12)*2+24)
+ if val in GenerationConstants.DRUMPITCH.keys():
+ val = GenerationConstants.DRUMPITCH[val]
+ substream += [ n.id, val ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PITCH, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def stepVolume(self, widget, step):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ substream = []
+ if step > 0:
+ for n in self.notes[p][t]:
+ if n.cs.amplitude != Config.MAXIMUM_AMPLITUDE:
+ substream += [ n.id, min( Config.MAXIMUM_AMPLITUDE, n.cs.amplitude + step ) ]
+ else:
+ for n in self.notes[p][t]:
+ if n.cs.amplitude != Config.MINIMUM_AMPLITUDE:
+ substream += [ n.id, max( Config.MINIMUM_AMPLITUDE, n.cs.amplitude + step ) ]
+ if len(substream):
+ stream += [ p, t, PARAMETER.AMPLITUDE, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+
+ def algoVolume( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, min( Config.MAXIMUM_AMPLITUDE, val*0.01 ) ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.AMPLITUDE, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handlePan(self, adjust):
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.PAN, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoPan( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PAN, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleReverb(self, adjust):
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.REVERB, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoReverb( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.02 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.REVERB, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleAttack(self, adjust):
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.ATTACK, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoAttack( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.ATTACK, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleDecay(self, adjust):
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.DECAY, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+
+ def algoDecay( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.DECAY, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleFilterTypes(self, widget):
+ self.currentFilterType = widget.props.value
+
+ if not self.currentFilterType:
+ self.filterCutoffSlider.set_sensitive(False)
+ else:
+ self.filterCutoffSlider.set_sensitive(True)
+
+ if not self.setup:
+ if self.currentFilterType:
+ typestream = []
+ cutoffstream = []
+ cutoff = self.filterCutoffSliderAdj.value
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ substream = []
+ typestream += [ p, t, PARAMETER.FILTERTYPE, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ typestream += [ n.id, self.currentFilterType ]
+ if n.cs.filterCutoff != cutoff:
+ substream += [ n.id, cutoff ]
+ if len(substream):
+ cutoffstream += [ p, t, PARAMETER.FILTERCUTOFF, len(substream)//2 ] + substream
+ if len(typestream):
+ self.edit.noteDB.updateNotes( typestream + [-1] )
+ if len(cutoffstream):
+ self.edit.noteDB.updateNotes( cutoffstream + [-1] )
+ else:
+ self.currentFilterType = 0
+ typestream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ typestream += [ p, t, PARAMETER.FILTERTYPE, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ typestream += [ n.id, 0 ]
+ if len(typestream):
+ self.edit.noteDB.updateNotes( typestream + [-1] )
+
+ def handleFilter(self, adjust):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.FILTERCUTOFF, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoCutoff( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*70+100 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.FILTERCUTOFF, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleGeneTypes(self, widget):
+ self.algorithm = self.algoTypes[widget.props.value]
+
+ def handleMinimum(self, adjust):
+ self.geneMinimum = int(adjust.value)
+
+ def handleMaximum(self, adjust):
+ self.geneMaximum = int(adjust.value)
+
+ def handleRandom(self, adjust):
+ self.geneRandom = int(adjust.value)
diff --git a/Edit/HitInterface.py b/Edit/HitInterface.py
new file mode 100644
index 0000000..049c2b0
--- /dev/null
+++ b/Edit/HitInterface.py
@@ -0,0 +1,195 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from common.Util.NoteDB import PARAMETER
+from Edit.NoteInterface import NoteInterface
+import common.Config as Config
+
+class HitInterface( NoteInterface ):
+
+ def __init__( self, noteDB, owner, note ):
+ NoteInterface.__init__( self, noteDB, owner, note )
+
+ self.width = self.height = Config.HIT_HEIGHT
+ self.imgWidth = self.imgHeight = Config.HIT_HEIGHT + Config.HIT_IMAGE_PADDING_MUL2
+
+ self.firstTransform = True
+ self.updateTransform()
+
+ def updateTransform( self ):
+ if self.note.page in self.owner.getActivePages():
+ if not self.firstTransform:
+ oldX = self.imgX
+ oldY = self.imgY
+ oldEndX = self.imgX + self.imgWidth
+ dirty = True
+ else:
+ dirty = False
+
+ beats = self.noteDB.getPage( self.note.page ).beats
+ if self.note.cs.onset != self.oldOnset or beats != self.oldBeats:
+ self.x = self.owner.ticksToPixels( beats, self.note.cs.onset )
+ self.x += self.origin[0]
+ self.imgX = self.x - Config.NOTE_IMAGE_PADDING
+ self.oldOnset = self.note.cs.onset
+ self.oldBeats = beats
+ if self.note.cs.pitch != self.oldPitch:
+ self.y = self.owner.pitchToPixelsDrum( self.note.cs.pitch ) + self.origin[1]
+ self.imgY = self.y - Config.NOTE_IMAGE_PADDING
+ self.oldPitch = self.note.cs.pitch
+
+ if dirty:
+ if self.firstTransform:
+ self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page )
+ self.firstTransform = False
+ else:
+ x = min( self.imgX, oldX )
+ y = min( self.imgY, oldY )
+ endx = max( self.imgX + self.imgWidth, oldEndX )
+ endy = max( self.imgY, oldY ) + self.imgHeight
+ self.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page )
+
+ self.firstTransform = False
+
+ def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound, maxRightBound ):
+ left = 0 - self.note.cs.onset
+ right = maxRightBound - self.note.cs.duration - self.note.cs.onset
+ up = Config.MAXIMUM_PITCH_DRUM - self.note.cs.pitch
+ down = Config.MINIMUM_PITCH_DRUM - self.note.cs.pitch
+
+ 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
+
+ # store the current loc as a reference point
+ self.baseOnset = self.note.cs.onset
+ self.basePitch = self.note.cs.pitch
+
+ #=======================================================
+ # Events
+
+ # handleButtonPress returns:
+ # -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 0 # not a hit
+
+ 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 += 1
+ 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.getSelected(): # 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.5: emitter.setCurrentAction( "note-drag-onset", self )
+ else:
+ emitter.setCurrentAction( "note-drag-pitch-drum", self )
+ if playSample: self.playSampleNote()
+
+ return 1
+
+ def noteDragPitch( self, dp, stream ):
+ self.potentialDeselect = False
+ if dp != self.lastDragP and not dp%2:
+ self.lastDragP = dp
+ stream += [ self.note.id, self.basePitch + dp ]
+
+ def noteDragDuration( self, dd, stream ):
+ return
+
+ 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 leftBound
+
+ 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 rightBound
+
+ def noteDecPitch( self, step, stream ):
+ if self.note.cs.pitch > Config.MINIMUM_PITCH_DRUM:
+ stream += [ self.note.id, max( self.note.cs.pitch+2*step, Config.MINIMUM_PITCH_DRUM ) ]
+
+ def noteIncPitch( self, step, stream ):
+ if self.note.cs.pitch < Config.MAXIMUM_PITCH_DRUM:
+ stream += [ self.note.id, min( self.note.cs.pitch+2*step, Config.MAXIMUM_PITCH_DRUM ) ]
+
+ def noteDecDuration( self, step, stream ):
+ return
+
+ def noteIncDuration( self, step, rightBound, stream ):
+ return
+
+ # updateTooltip returns:
+ # -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 0 # not a hit
+
+ percent = eX/self.width
+ if percent < 0.5: emitter.setCursor("drag-onset")
+ else: emitter.setCursor("drag-pitch")
+
+ return 1 # we handled it
+
+ #=======================================================
+ # Draw
+
+ def draw( self, win, gc, startX, stopX ):
+ if stopX < self.imgX: return False # we don't need to draw and no one after us will draw
+ if startX > self.imgX + self.imgWidth: return True # we don't need to draw, but maybe a later note does
+
+ gc.foreground = self.color
+ win.draw_rectangle( gc, True, self.x+2, self.y+2, self.width-4, self.height-4 )
+
+ if self.selected: img = self.imageSelected
+ else: img = self.image
+ win.draw_pixbuf( gc, img, 0, 0, self.imgX, self.imgY, self.imgWidth, self.imgHeight, gtk.gdk.RGB_DITHER_NONE )
+
+ return True # we drew something
+
diff --git a/Edit/MainWindow.py b/Edit/MainWindow.py
new file mode 100644
index 0000000..0a2c44f
--- /dev/null
+++ b/Edit/MainWindow.py
@@ -0,0 +1,2281 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import gobject
+
+import common.Util.Instruments
+import common.Util.InstrumentDB as InstrumentDB
+from common.Util.ThemeWidgets import *
+from common.Util.Profiler import TP
+from common.Util import NoteDB
+from common.Util.NoteDB import PARAMETER
+from common.Util import ControlStream
+from common.Util.CSoundClient import new_csound_client
+from common.Util.CSoundNote import CSoundNote
+from EditToolbars import mainToolbar
+from EditToolbars import generateToolbar
+from gettext import gettext as _
+from subprocess import Popen
+from sugar.graphics.palette import Palette, Invoker
+from sugar.datastore import datastore
+import time
+import os
+import commands
+import random
+from common.Util import OS
+from common.port.scrolledbox import HScrolledBox
+from sugar.graphics import style
+
+class CONTEXT:
+ PAGE = 0
+ TRACK = 1
+ NOTE = 2
+
+import common.Config as Config
+
+from common.Generation.GenerationConstants import GenerationConstants
+from Edit.Properties import Properties
+from Edit.TrackInterface import TrackInterface, TrackInterfaceParasite
+from Edit.TuneInterface import TuneInterface, TuneInterfaceParasite
+
+from common.Generation.Generator import generator1, GenerationParameters
+
+Tooltips = Config.Tooltips()
+KEY_MAP_PIANO = Config.KEY_MAP_PIANO
+
+#-----------------------------------
+# The main TamTam window
+#-----------------------------------
+class MainWindow( gtk.EventBox ):
+
+ def __init__( self, activity ):
+ gtk.EventBox.__init__(self)
+ self.instrumentDB = InstrumentDB.getRef()
+ self.csnd = new_csound_client()
+ self.tooltips = gtk.Tooltips()
+ self.activity = activity
+
+ for i in [6,7,8,9,10]:
+ self.csnd.setTrackVolume(100, i)
+ self.trackCount = 6
+
+ self.scale = GenerationConstants.DEFAULT_SCALE
+
+ # META ALGO: [section, variation or not, nPages] A B A C
+ self.tuneForm = [[0, False, 2], [1, False, 4], [0, True, 2], [2, False, 2]]
+
+ def init_data( ):
+ TP.ProfileBegin("init_data")
+ self._data = {}
+
+ #[ volume, ... ]
+ self._data['track_volume'] = [ Config.DEFAULT_VOLUME ] * Config.NUMBER_OF_TRACKS
+ self._data['track_mute'] = [ 1.0 ] * Config.NUMBER_OF_TRACKS
+
+ #[ instrument index, ... ]
+ self.trackInstrumentDefault = [
+ self.instrumentDB.instNamed["kalimba"],
+ self.instrumentDB.instNamed["kalimba"],
+ self.instrumentDB.instNamed["kalimba"],
+ self.instrumentDB.instNamed["kalimba"],
+ self.instrumentDB.instNamed["drum2kit"] ]
+ self.trackInstrument = self.trackInstrumentDefault[:]
+
+ for i in self.trackInstrument:
+ if i.kit == None:
+ self.csnd.load_instrument(i.name)
+ else:
+ self.csnd.load_drumkit(i.name)
+
+ if len(self.trackInstrument) != Config.NUMBER_OF_TRACKS: raise 'error'
+ self.drumIndex = Config.NUMBER_OF_TRACKS - 1
+
+ self.last_clicked_instTrackID = 0
+ self.last_clicked_instPrimary = 'kalimba'
+
+ #second instrument for melodic tracks
+ self.trackInstrument2Default = [ None, None, None, None]
+ self.trackInstrument2 = self.trackInstrument2Default[:]
+
+ self._data['volume'] = Config.DEFAULT_VOLUME
+ self._data['tempo'] = Config.PLAYER_TEMPO
+
+ self.playScope = "Selection"
+ self.displayedPage = -1
+ self.trackSelected = [ 0 for i in range(Config.NUMBER_OF_TRACKS) ]
+ self.trackActive = [ 1 for i in range(Config.NUMBER_OF_TRACKS) ]
+
+ self.pages_playing = []
+ self.journalCalled = False
+
+ self.noteDB = NoteDB.NoteDB()
+ TP.ProfileEnd("init_data")
+
+ def formatRoundBox( box, fillcolor ):
+ box.set_radius( 7 )
+ box.set_border_width( 1 )
+ box.set_fill_color( fillcolor )
+ box.set_border_color( Config.PANEL_BCK_COLOR )
+ return box
+
+ def init_GUI():
+
+ self.GUI = {}
+ self.GUI["2main"] = gtk.VBox()
+ self.GUI["2instrumentPalette"] = instrumentPalette(_('Track 1 Volume'), self)
+
+ def draw_inst_icons():
+ instruments = [ k for k in self.instrumentDB.inst if not k.kitStage ]
+ self.GUI["2instrumentIcons"] = {}
+ for i in instruments:
+ try:
+ self.GUI["2instrumentIcons"][i.name] = gtk.gdk.pixbuf_new_from_file(i.img)
+ except:
+ self.GUI["2instrumentIcons"][i.name] = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT + 'generic.png')
+ TP.ProfileBegin("init_GUI::instrument icons")
+ draw_inst_icons()
+ TP.ProfileEnd("init_GUI::instrument icons")
+
+
+ #------------------------------------------------------------------------
+ # page
+ self.GUI["2page"] = gtk.HBox()
+ self.scrollWin = gtk.ScrolledWindow()
+ self.scrollWin.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC)
+ self.scrollWin.add_with_viewport(self.GUI["2page"])
+ self.GUI["2main"].pack_start( self.scrollWin, True )
+
+ if 1: # + instrument panel
+ self.GUI["2instrumentPanel"] = gtk.VBox()
+ self.GUI["2page"].pack_start( self.GUI["2instrumentPanel"], True )
+ # + + instrument 1 box
+ self.GUI["2instrument1Box"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2instrument1Box"].set_size_request( -1, 132 )
+ self.GUI["2instrument1volBox"] = gtk.VBox()
+ #self.GUI["2instrument1volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][1], 0, 100, 1, 1, 0 )
+ #self.GUI["2instrument1volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 0 )
+ #self.GUI["2instrument1volumeSlider"] = gtk.VScale(self.GUI["2instrument1volumeAdjustment"])
+ #self.GUI["2instrument1volumeSlider"].set_draw_value(False)
+ #self.GUI["2instrument1volumeSlider"].set_inverted(True)
+ #self.GUI["2instrument1volumeSlider"].set_size_request( 30, -1 )
+ #self.GUI["2instrument1volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 0 )
+ self.GUI["2instrument1muteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2instrument1muteButton"].connect("toggled",self.handlemuteButton,0)
+ self.GUI["2instrument1muteButton"].connect("button-press-event",self.handlemuteButtonRightClick,0)
+ self.GUI["2instrument1muteButton"].set_active(True)
+ #self.GUI["2instrument1volBox"].pack_start( self.GUI["2instrument1volumeSlider"], True, True, 0 )
+ #self.GUI["2instrument1volBox"].pack_start( self.GUI["2instrument1muteButton"], False, False, 5 )
+ self.GUI["2instrument1Box"].pack_start( self.GUI["2instrument1volBox"], False, False, 0 )
+ self.GUI["2instrument1Button"] = InstrumentButton( self, 0, Config.BG_COLOR )
+ self.GUI["2instrument1Button"].connect('button-release-event',self.GUI["2instrumentPalette"].setBlock, 0)
+ self.GUI["2instrument1Button"].setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[0].name] )
+ self.GUI["2instrument1Box"].pack_start( self.GUI["2instrument1Button"], padding = 3 )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument1Box"] )
+ # + + instrument 2 box
+ self.GUI["2instrument2Box"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2instrument2Box"].set_size_request( -1, 132 )
+ self.GUI["2instrument2volBox"] = gtk.VBox()
+ #self.GUI["2instrument2volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][1], 0, 100, 1, 1, 0 )
+ #self.GUI["2instrument2volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 1 )
+ #self.GUI["2instrument2volumeSlider"] = gtk.VScale(self.GUI["2instrument2volumeAdjustment"])
+ #self.GUI["2instrument2volumeSlider"].set_draw_value(False)
+ #self.GUI["2instrument2volumeSlider"].set_inverted(True)
+ #self.GUI["2instrument2volumeSlider"].set_size_request( 30, -1 )
+ #self.GUI["2instrument2volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 1 )
+ self.GUI["2instrument2muteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2instrument2muteButton"].connect("toggled",self.handlemuteButton,1)
+ self.GUI["2instrument2muteButton"].connect("button-press-event",self.handlemuteButtonRightClick,1)
+ self.GUI["2instrument2muteButton"].set_active(True)
+ #self.GUI["2instrument2volBox"].pack_start( self.GUI["2instrument2volumeSlider"], True, True, 0 )
+ #self.GUI["2instrument2volBox"].pack_start( self.GUI["2instrument2muteButton"], False, False, 5 )
+ self.GUI["2instrument2Box"].pack_start( self.GUI["2instrument2volBox"], False, False, 0 )
+ self.GUI["2instrument2Button"] = InstrumentButton( self, 1, Config.BG_COLOR )
+ self.GUI["2instrument2Button"].connect('button-release-event',self.GUI["2instrumentPalette"].setBlock, 1)
+ self.GUI["2instrument2Button"].setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[1].name] )
+ self.GUI["2instrument2Box"].pack_start( self.GUI["2instrument2Button"], padding = 3 )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument2Box"] )
+ # + + instrument 3 box
+ self.GUI["2instrument3Box"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2instrument3Box"].set_size_request( -1, 132 )
+ self.GUI["2instrument3volBox"] = gtk.VBox()
+ #self.GUI["2instrument3volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][2], 0, 100, 1, 1, 0 )
+ #self.GUI["2instrument3volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 2 )
+ #self.GUI["2instrument3volumeSlider"] = gtk.VScale(self.GUI["2instrument3volumeAdjustment"])
+ #self.GUI["2instrument3volumeSlider"].set_draw_value(False)
+ #self.GUI["2instrument3volumeSlider"].set_inverted(True)
+ #elf.GUI["2instrument3volumeSlider"].set_size_request( 30, -1 )
+ #self.GUI["2instrument3volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 2 )
+ self.GUI["2instrument3muteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2instrument3muteButton"].connect("toggled",self.handlemuteButton,2)
+ self.GUI["2instrument3muteButton"].connect("button-press-event",self.handlemuteButtonRightClick,2)
+ self.GUI["2instrument3muteButton"].set_active(True)
+ #self.GUI["2instrument3volBox"].pack_start( self.GUI["2instrument3volumeSlider"], True, True, 0 )
+ #self.GUI["2instrument3volBox"].pack_start( self.GUI["2instrument3muteButton"], False, False, 5 )
+ self.GUI["2instrument3Box"].pack_start( self.GUI["2instrument3volBox"], False, False, 0 )
+ self.GUI["2instrument3Button"] = InstrumentButton( self, 2, Config.BG_COLOR )
+ self.GUI["2instrument3Button"].connect('button-release-event',self.GUI["2instrumentPalette"].setBlock, 2)
+ self.GUI["2instrument3Button"].setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[2].name] )
+ self.GUI["2instrument3Box"].pack_start( self.GUI["2instrument3Button"], padding = 3 )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument3Box"] )
+ # + + instrument 4 box
+ self.GUI["2instrument4Box"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2instrument4Box"].set_size_request( -1, 132 )
+ self.GUI["2instrument4volBox"] = gtk.VBox()
+ #self.GUI["2instrument4volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][3], 0, 100, 1, 1, 0 )
+ #self.GUI["2instrument4volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 3 )
+ #self.GUI["2instrument4volumeSlider"] = gtk.VScale(self.GUI["2instrument4volumeAdjustment"])
+ #self.GUI["2instrument4volumeSlider"].set_draw_value(False)
+ #self.GUI["2instrument4volumeSlider"].set_inverted(True)
+ #self.GUI["2instrument4volumeSlider"].set_size_request( 30, -1 )
+ #self.GUI["2instrument4volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 3 )
+ self.GUI["2instrument4muteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2instrument4muteButton"].connect("toggled",self.handlemuteButton,3)
+ self.GUI["2instrument4muteButton"].connect("button-press-event",self.handlemuteButtonRightClick,3)
+ self.GUI["2instrument4muteButton"].set_active(True)
+ #self.GUI["2instrument4volBox"].pack_start( self.GUI["2instrument4volumeSlider"], True, True, 0 )
+ #self.GUI["2instrument4volBox"].pack_start( self.GUI["2instrument4muteButton"], False, False, 5 )
+ self.GUI["2instrument4Box"].pack_start( self.GUI["2instrument4volBox"], False, False, 0 )
+ self.GUI["2instrument4Button"] = InstrumentButton( self, 3, Config.BG_COLOR )
+ self.GUI["2instrument4Button"].connect('button-release-event',self.GUI["2instrumentPalette"].setBlock, 3)
+ self.GUI["2instrument4Button"].setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[3].name] )
+ self.GUI["2instrument4Box"].pack_start( self.GUI["2instrument4Button"], padding = 3 )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument4Box"] )
+ # + + drum box
+ self.GUI["2drumBox"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2drumBox"].set_size_request( -1, 165 )
+ self.GUI["2drumVolBox"] = gtk.VBox()
+ self.GUI["2drumvolumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][4], 0, 100, 1, 1, 0 )
+ self.GUI["2drumvolumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 4 )
+ #self.GUI["2drumvolumeSlider"] = gtk.VScale(self.GUI["2drumvolumeAdjustment"])
+ #self.GUI["2drumvolumeSlider"].set_draw_value(False)
+ #self.GUI["2drumvolumeSlider"].set_inverted(True)
+ #self.GUI["2drumvolumeSlider"].set_size_request( 30, -1 )
+ self.GUI["2drumvolumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 4 )
+ self.GUI["2drumMuteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2drumMuteButton"].connect("toggled",self.handlemuteButton,4)
+ self.GUI["2drumMuteButton"].connect("button-press-event",self.handlemuteButtonRightClick,4)
+ self.GUI["2drumMuteButton"].set_active(True)
+ #self.GUI["2drumVolBox"].pack_start( self.GUI["2drumvolumeSlider"], True, True, 0 )
+ #self.GUI["2drumVolBox"].pack_start( self.GUI["2drumMuteButton"], False, False, 5 )
+ self.GUI["2drumBox"].pack_start( self.GUI["2drumVolBox"], False, False, 0 )
+ self.GUI["2drumButton"] = ImageToggleButton(Config.IMAGE_ROOT + self.trackInstrument[4].name + '.png', Config.IMAGE_ROOT + self.trackInstrument[4].name + '.png')
+ self.GUI["2drumPalette"] = drumPalette(_('Track 5 Properties'), self, 4)
+ self.GUI["2drumButton"].connect("toggled", self.pickDrum)
+ self.GUI["2drumButton"].connect('button-release-event',self.GUI["2drumPalette"].setBlock)
+ self.GUI["2drumBox"].pack_start( self.GUI["2drumButton"] )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2drumBox"] )
+ self.GUI["2page"].pack_start( self.GUI["2instrumentPanel"], True )
+ # + track interface
+ tracks_width = gtk.gdk.screen_width() - 140
+ self.trackInterface = TrackInterface( self.noteDB, self, self.getScale, tracks_width )
+ self.noteDB.addListener( self.trackInterface, TrackInterfaceParasite, True )
+ self.trackInterface.set_size_request( tracks_width, -1 )
+ self.GUI["2page"].pack_start( self.trackInterface, False )
+
+ #------------------------------------------------------------------------
+ # tune interface
+ if 1: # + tune interface
+ self.GUI["2tuneScrolledWindow"] = HScrolledBox()
+ self.tuneInterface = TuneInterface( self.noteDB, self, self.GUI["2tuneScrolledWindow"].get_adjustment() )
+ self.noteDB.addListener( self.tuneInterface, TuneInterfaceParasite, True )
+ self.GUI["2tuneScrolledWindow"].set_viewport( self.tuneInterface )
+ self.tuneInterface.get_parent().set_shadow_type( gtk.SHADOW_NONE )
+ self.GUI["2tuneScrolledWindow"].set_size_request(-1, 100)
+ self.GUI["2tuneScrolledWindow"].modify_bg(gtk.STATE_NORMAL,
+ style.Color(Config.TOOLBAR_BCK_COLOR).get_gdk_color())
+ self.GUI["2main"].pack_start( self.GUI["2tuneScrolledWindow"], False, True )
+
+ # set tooltips
+ for key in self.GUI:
+ if Tooltips.Edit.has_key(key):
+ self.tooltips.set_tip(self.GUI[key],Tooltips.Edit[key])
+
+ self.add( self.GUI["2main"] )
+
+ self.skipCleanup = "" # used when jumping between duplicate note/track
+
+
+ # Popups
+ TP.ProfileBegin("init_GUI::popups")
+ # + generation window
+ #TP.ProfileBegin("init_GUI::generationPanel")
+ #self.generationPanel = GenerationParametersWindow( self.generate, self.doneGenerationPopup )
+ #TP.ProfileEnd("init_GUI::generationPanel")
+ #self.GUI["9generationPopup"] = gtk.Window(gtk.WINDOW_POPUP)
+ #self.GUI["9generationPopup"].set_modal(True)
+ #self.GUI["9generationPopup"].add_events( gtk.gdk.BUTTON_PRESS_MASK )
+ #self.GUI["9generationPopup"].connect("button-release-event", lambda w,e:self.doneGenerationPopup() )
+ #self.GUI["9generationPopup"].add( self.generationPanel )
+ # + properties window
+ #self.GUI["9propertiesPopup"] = gtk.Window(gtk.WINDOW_POPUP)
+ #self.GUI["9propertiesPopup"].set_modal(True)
+ #self.GUI["9propertiesPopup"].add_events( gtk.gdk.BUTTON_PRESS_MASK )
+ #self.GUI["9propertiesPopup"].connect("button-release-event", lambda w,e:self.donePropertiesPopup() )
+ #TP.ProfileBegin("init_GUI::propertiesPanel")
+ #self.propertiesPanel = Properties( self.noteDB, self.donePropertiesPopup, self.GUI["9propertiesPopup"] )
+ #TP.ProfileEnd("init_GUI::propertiesPanel")
+ #self.GUI["9propertiesPopup"].add( self.propertiesPanel )
+ # + playback scope
+ self.GUI["9loopPopup"] = gtk.Window(gtk.WINDOW_POPUP)
+ self.GUI["9loopPopup"].move( 100, 100 )
+ self.GUI["9loopPopup"].resize( 300, 100 )
+ self.GUI["9loopPopup"].set_modal(True)
+ self.GUI["9loopPopup"].add_events( gtk.gdk.BUTTON_PRESS_MASK )
+ self.GUI["9loopPopup"].connect("button-release-event", lambda w,e:self.GUI["2loopButton"].set_active(False) )
+ self.GUI["9loopBox"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["9loopAllOnce"] = gtk.Button("AO")
+ self.GUI["9loopBox"].pack_start( self.GUI["9loopAllOnce"] )
+ self.GUI["9loopAllRepeat"] = gtk.Button("AR")
+ self.GUI["9loopBox"].pack_start( self.GUI["9loopAllRepeat"] )
+ self.GUI["9loopSelectedOnce"] = gtk.Button("SO")
+ self.GUI["9loopBox"].pack_start( self.GUI["9loopSelectedOnce"] )
+ self.GUI["9loopSelectedRepeat"] = gtk.Button("SR")
+ self.GUI["9loopBox"].pack_start( self.GUI["9loopSelectedRepeat"] )
+ self.GUI["9loopPopup"].add(self.GUI["9loopBox"])
+ TP.ProfileEnd("init_GUI::popups")
+
+ #===================================================
+ # begin initialization
+
+ # keyboard variables
+ self.kb_record = False
+ self.kb_keydict = {}
+
+ # playback params
+ self.playing = False
+ self.playSource = 'Page'
+ self.currentpageId = 0
+ self.playingTuneIdx = 0
+
+ # timers
+ self.playbackTimeout = False
+
+ # FPS stuff
+ self.fpsTotalTime = 0
+ self.fpsFrameCount = 0
+ self.fpsN = 100 # how many frames to average FPS over
+ self.fpsLastTime = time.time() # fps will be borked for the first few frames but who cares?
+
+ self.context = -1 # invalidate
+ self.contextTrackActive = False
+ self.contextNoteActive = False
+
+ init_data() #above
+ init_GUI() #above
+
+ # register for notification AFTER track and tune interfaces
+ self.noteDB.addListener( self, page=True, note=True )
+
+ self.csnd.setMasterVolume( self.getVolume() )
+ self.initTrackVolume()
+
+ for tid in range(Config.NUMBER_OF_TRACKS):
+ self.handleInstrumentChanged( ( tid, self.trackInstrument[tid] ) )
+
+ instrumentsIds = []
+ for inst in self.trackInstrument:
+ instrumentsIds.append(inst.instrumentId)
+
+ first = self.noteDB.addPage( -1, NoteDB.Page(4, instruments = instrumentsIds) )
+ self.displayPage( first )
+
+ if not self.journalCalled:
+ self.createNewTune( None )
+
+ # Toolbar
+ self._mainToolbar = mainToolbar(self.activity.toolbox, self)
+ self._generateToolbar = generateToolbar(self.activity.toolbox, self)
+ self.activity.toolbox.add_toolbar(_('Compose'), self._mainToolbar)
+ self.activity.toolbox.add_toolbar(_('Generate'), self._generateToolbar)
+ self.activity.toolbox.set_current_toolbar(1)
+ self._mainToolbar.show()
+ self._generateToolbar.show()
+
+ self.show_all() #gtk command
+
+ self.setContext( CONTEXT.PAGE )
+
+ self.audioRecordWidget = None
+
+ def createNewTune( self, widget, data=None ):
+ self.createNewTune3()
+
+ def createNewTune3( self ):
+
+ if self.playing == True:
+ self.handleStop()
+
+ self.tuneInterface.selectPages( self.noteDB.getTune() )
+
+ beats = random.randint(3,6)
+ stream = []
+ for page in self.noteDB.getTune():
+ stream += [ page, beats ]
+ if len(stream):
+ self.noteDB.updatePages( [ PARAMETER.PAGE_BEATS, len(stream)//2 ] + stream )
+
+ orch = self.newOrchestra()
+
+ for i in orch:
+ if i.kit == None:
+ self.csnd.load_instrument(i.name)
+ else:
+ self.csnd.load_drumkit(i.name)
+
+ instrumentsIds = []
+ for inst in orch:
+ instrumentsIds.append(inst.instrumentId)
+
+ self.pageDelete( -1, instruments = instrumentsIds )
+
+ initTempo = random.randint(60, 132)
+ self._data['tempo'] = initTempo
+
+ formsUsed = []
+ for section in self.tuneForm:
+ if section[0] not in formsUsed:
+ param = self.chooseGenParams()
+ self.tuneInterface.selectPages( self.noteDB.getTune() )
+ if not formsUsed:
+ for i in range(section[2]-1):
+ self.pageAdd(instruments = instrumentsIds)
+ else:
+ for i in range(section[2]):
+ self.pageAdd(instruments = instrumentsIds)
+ formsUsed.append(section[0])
+
+ self.tuneInterface.selectPages( self.noteDB.getTune()[-section[2]:] )
+ self.generateMode = 'page'
+ self.generate( GenerationParameters( density = param[0], rythmRegularity = param[1], step = param[2], pitchRegularity = param[3], articule = param[4], silence = param[5], pattern = param[6], scale = param[7]), section[2] )
+ else:
+ pageOffset = 0
+ pageIds = []
+ firstPos = [i[0] for i in self.tuneForm].index(section[0])
+ if firstPos == 0:
+ pageOffset = 0
+ else:
+ for i in range(firstPos):
+ pageOffset += self.tuneForm[i][2]
+ for i in range(section[2]):
+ pageIds.append(self.noteDB.getTune()[pageOffset + i])
+ after = self.noteDB.getTune()[-1]
+ self.displayPage( self.noteDB.getTune()[pageOffset] )
+ self.tuneInterface.selectPages(self.noteDB.getTune())
+ self.pageDuplicate(-1, pageIds)
+
+ self.tuneInterface.selectPages( self.noteDB.getTune() )
+ self.displayPage( self.noteDB.getTune()[0] )
+
+
+ def newOrchestra(self):
+ stringsPickup = []
+ windsPickup = []
+ keyboardPickup = []
+ fxPickup = []
+ drumsPickup = ["drum1kit", "drum2kit", "drum3kit", "drum4kit", "drum5kit"]
+ for name in self.instrumentDB.instNamed.keys():
+ if self.instrumentDB.instNamed[name].category == 'strings' and self.instrumentDB.instNamed[name].name != 'violin':
+ stringsPickup.append(name)
+ elif self.instrumentDB.instNamed[name].category == 'winds' and self.instrumentDB.instNamed[name].name != 'didjeridu':
+ windsPickup.append(name)
+ elif self.instrumentDB.instNamed[name].category == 'keyboard' or self.instrumentDB.instNamed[name].category == 'percussions' and not name.startswith('drum'):
+ if self.instrumentDB.instNamed[name].name != 'zap' and self.instrumentDB.instNamed[name].name != 'cling':
+ keyboardPickup.append(name)
+ return [
+ self.instrumentDB.instNamed[random.choice(stringsPickup)],
+ self.instrumentDB.instNamed[random.choice(stringsPickup)],
+ self.instrumentDB.instNamed[random.choice(windsPickup)],
+ self.instrumentDB.instNamed[random.choice(keyboardPickup)],
+ self.instrumentDB.instNamed[random.choice(drumsPickup)] ]
+
+ def chooseGenParams(self):
+ choose = [random.randint(0,16) for x in range(4)]
+ density = [GenerationConstants.RYTHM_DENSITY_BANK[i] for i in choose]
+ rytReg = [GenerationConstants.RYTHM_REGU_BANK[i] for i in choose]
+ step = [GenerationConstants.PITCH_STEP_BANK[i] for i in choose]
+ pitReg = [GenerationConstants.PITCH_REGU_BANK[i] for i in choose]
+ dur = [GenerationConstants.DURATION_BANK[i] for i in choose]
+ silence = [GenerationConstants.SILENCE_BANK[i] for i in choose]
+ pattern = [GenerationConstants.PATTERN_BANK[i] for i in choose]
+ scale = random.randint(0,6)
+ return [density, rytReg, step, pitReg, dur, silence, pattern, scale]
+
+ def onActivate( self, arg ):
+ # whatever needs to be done on initialization
+ self.csnd.loopPause()
+ self.csnd.loopClear()
+ for n in self.noteDB.getNotes( ):
+ self.csnd.loopPlay(n, 0) #adds all notes to c client in inactive state
+
+
+ def onDeactivate( self ):
+ # clean up things like popups etc
+ self.csnd.loopPause()
+ self.csnd.loopClear()
+
+
+ def updateFPS( self ):
+ t = time.time()
+ dt = t - self.fpsLastTime
+ self.fpsLastTime = t
+ self.fpsTotalTime += dt
+ self.fpsFrameCount += 1
+ if self.fpsFrameCount == self.fpsN:
+ fps = self.fpsN/self.fpsTotalTime
+ avgMS = 1000/fps
+ fps = "FPS %d ms %.2f" % (fps, avgMS)
+ #self.fpsText.set_text(fps )
+ if (Config.DEBUG > 2): print fps
+ self.fpsTotalTime = 0
+ self.fpsFrameCount = 0
+
+ #=========================================================
+ # Popup Windows
+
+ def doneGenerationPopup( self ):
+ if self.GUI["2pageGenerateButton"].get_active():
+ self.GUI["2pageGenerateButton"].set_active( False )
+ if self.GUI["2trackGenerateButton"].get_active():
+ self.GUI["2trackGenerateButton"].set_active( False )
+
+ def donePropertiesPopup( self ):
+ if self.GUI["2pagePropertiesButton"].get_active():
+ self.GUI["2pagePropertiesButton"].set_active( False )
+ if self.GUI["2trackPropertiesButton"].get_active():
+ self.GUI["2trackPropertiesButton"].set_active( False )
+ if self.GUI["2notePropertiesButton"].get_active():
+ self.GUI["2notePropertiesButton"].set_active( False )
+
+ def cancelPopup( self, w, event, popup ):
+ popup.hide()
+
+
+ def handleLoopButton( self, w ):
+ if w.get_active(): self.GUI["9loopPopup"].show_all()
+ else: self.GUI["9loopPopup"].hide()
+
+ #-----------------------------------
+ # playback functions
+ #-----------------------------------
+
+ def updatePageSelection( self, selectedIds ):
+ if not self.playing:
+ return
+
+ if self.playScope == "All":
+ return
+
+ if self.displayedPage in selectedIds:
+ startPage = self.displayedPage
+ else:
+ startPage = selectedIds[0]
+
+ self._playPages( selectedIds, startPage, self.trackInterface.getPlayhead() )
+
+ def updatePagesPlaying( self ):
+ if not self.playing:
+ return
+
+ curTick = self.csnd.loopGetTick()
+
+ pageTick = self.page_onset[self.displayedPage]
+ if curTick < pageTick:
+ pageTick = 0
+ startPage = self.pages_playing[0]
+ else:
+ startPage = self.displayedPage
+
+ localTick = curTick - pageTick
+
+ self._playPages( self.tuneInterface.getSelectedIds(), startPage, localTick )
+
+ def handleAudioRecord( self, widget, data=None ):
+ if widget.get_active() == True:
+ self.audioRecordWidget = widget
+ self.audioRecordTick = -1
+ else:
+ self.audioRecordWidget = None
+
+ def handlePlay( self, widget = None ):
+ if widget:
+ widget.event( gtk.gdk.Event( gtk.gdk.LEAVE_NOTIFY ) ) # fake the leave event
+
+ if self.audioRecordWidget:
+ filename = Config.TMP_DIR + "/perf.wav"
+ self.csnd.inputMessage( Config.CSOUND_RECORD_PERF % filename)
+ time.sleep( 0.01 )
+
+ if self.playScope == "All":
+ toPlay = self.noteDB.getTune()
+ else:
+ toPlay = self.tuneInterface.getSelectedIds()
+
+ self._playPages( toPlay, self.displayedPage, self.trackInterface.getPlayhead() )
+
+ self.playing = True
+
+ def _playPages( self, pages, startPage, startTick ):
+
+ self.pages_playing = pages[:]
+
+ trackset = set( [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackActive[i] ] )
+
+ numticks = 0
+ self.page_onset = {}
+ for pid in self.pages_playing:
+ self.page_onset[pid] = numticks
+ numticks += self.noteDB.getPage(pid).ticks
+
+ # check for a second instrument on melodic tracks
+ stream = []
+ for page in self.pages_playing:
+ for track in trackset:
+ if track != self.drumIndex:
+ if self.trackInstrument2[track] != None:
+ if len(self.noteDB.getNotesByTrack(page, track)):
+ stream += [ page, track, NoteDB.PARAMETER.INSTRUMENT2, len(self.noteDB.getNotesByTrack(page, track)) ]
+ for n in self.noteDB.getNotesByTrack(page, track):
+ stream += [ n.id, self.trackInstrument2[track].instrumentId ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ self.csnd.loopClear()
+ for page in self.pages_playing:
+ for track in trackset:
+ for n in self.noteDB.getNotesByTrack( page, track ):
+ self.csnd.loopPlay(n, 1)
+ self.csnd.loopUpdate(n, NoteDB.PARAMETER.ONSET, n.cs.onset + self.page_onset[n.page] , 1)
+
+ self.csnd.loopSetNumTicks( numticks )
+
+ self.csnd.loopSetTick( self.page_onset[startPage] + startTick )
+ self.csnd.setTempo(self._data['tempo'])
+ if (Config.DEBUG > 3): print "starting from tick", startTick, 'at tempo', self._data['tempo']
+ self.csnd.loopStart()
+
+ if not self.playbackTimeout:
+ self.playbackTimeout = gobject.timeout_add( 50, self.onTimeout )
+
+
+
+ def handleStop( self, widget = None, rewind = True ):
+
+ if widget:
+ widget.event( gtk.gdk.Event( gtk.gdk.LEAVE_NOTIFY ) ) # fake the leave event
+
+ if self.audioRecordWidget:
+ filename = Config.TMP_DIR + "/perf.wav"
+ self.csnd.inputMessage( Config.CSOUND_STOP_RECORD_PERF % filename)
+ time.sleep( 0.01 )
+
+ if self.playbackTimeout:
+ gobject.source_remove( self.playbackTimeout )
+ self.playbackTimeout = False
+
+ self.csnd.loopPause()
+ self.csnd.loopDeactivate()
+
+ if self.audioRecordWidget:
+ time.sleep(4)
+ self.csnd.__del__()
+ time.sleep(0.5)
+ tmp_ogg = os.path.join(Config.TMP_DIR, "perf.ogg")
+
+ command = "gst-launch-0.10 filesrc location=" + Config.TMP_DIR + "/perf.wav ! wavparse ! audioconvert ! vorbisenc ! oggmux ! filesink location=" + tmp_ogg
+ command2 = "rm " + Config.TMP_DIR + "/perf.wav"
+ OS.system(command)
+ OS.system(command2)
+
+ from datetime import datetime
+ title = '%s %s.ogg' % (self.activity.get_title(),
+ datetime.now().isoformat(' '))
+
+ jobject = datastore.create()
+ jobject.metadata['title'] = title
+ jobject.metadata['keep'] = '1'
+ jobject.metadata['mime_type'] = 'audio/ogg'
+ jobject.file_path = tmp_ogg
+ datastore.write(jobject)
+
+ os.remove(tmp_ogg)
+ self.audioRecordWidget.set_active(False)
+ self.audioRecordWidget = None
+
+ self.csnd.__init__()
+ time.sleep(0.1)
+ self.csnd.connect(True)
+ time.sleep(0.1)
+ self.waitToSet()
+ self.csnd.load_instruments()
+ self.GUI["2recordButton"].set_active(False)
+ self.playing = False
+
+ if rewind: self.handleRewind()
+
+ def handleRewind( self, widget = None ):
+ if self.playScope == "All": id = self.noteDB.getPageByIndex(0)
+ else: id = self.tuneInterface.getFirstSelected()
+ self.trackInterface.setPlayhead( 0 )
+ self.displayPage( id )
+
+ def handleClose(self,widget):
+ self.activity.close()
+
+ def onTimeout(self):
+ self.updateFPS()
+
+ curTick = self.csnd.loopGetTick()
+
+ pageTick = self.page_onset[self.displayedPage]
+ if curTick < pageTick:
+ pageTick = 0
+ ind = 0
+ else:
+ ind = self.pages_playing.index(self.displayedPage)
+
+ localTick = curTick - pageTick
+ pageLength = self.noteDB.getPage(self.pages_playing[ind]).ticks
+ max = len(self.pages_playing)
+ while localTick > pageLength:
+ ind += 1
+ if ind == max: ind = 0
+ localTick -= pageLength
+ pageLength = self.noteDB.getPage(self.pages_playing[ind]).ticks
+
+ self.trackInterface.setPlayhead( localTick )
+
+ if self.pages_playing[ind] != self.displayedPage:
+ if ind + 1 < max: predraw = self.pages_playing[ind+1]
+ else: predraw = self.pages_playing[0]
+ self._displayPage( self.pages_playing[ind], predraw )
+ else:
+ self.trackInterface.predrawPage()
+
+ if self.audioRecordWidget:
+ if self.audioRecordTick > curTick: # we've looped around
+ self.handleStop()
+ else:
+ self.audioRecordTick = curTick
+
+
+ return True
+
+ def onMuteTrack( self, widget, trackId ):
+ self._data['track_mute'][trackId] = not self._data['track_mute'][trackId]
+ #if self._data['track_mute'][trackId]:
+ #self.noteLooper.setMute( trackId, 0.0 )
+ #else:
+ #self.noteLooper.setMute( trackId, 1.0 )
+
+ def onTrackVolumeChanged( self, widget, trackId ):
+ v = widget.get_value() / 100.0
+ self._data['track_volume'][trackId] = v
+ #self.noteLooper.setVolume( trackId, v )
+
+ def clearInstrument( self, id, primary = True ):
+ btn = self.GUI["2instrument%dButton" % (id+1)]
+ if primary:
+ if self.trackInstrument2[id] == None:
+ return
+ self.handleInstrumentChanged( ( id, self.trackInstrument2[id] ), True )
+ self.handleInstrumentChanged( ( id, None ), False )
+ btn.setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[id].name] )
+ btn.setSecondary( None )
+ else:
+ self.handleInstrumentChanged( ( id, None ), False )
+ btn.setSecondary( None )
+ pages = self.tuneInterface.getSelectedIds()
+ self.noteDB.setInstrument2( pages, id, -1 )
+
+ # data is tuple ( trackId, instrumentName )
+ def handleInstrumentChanged( self, data, primary = True ):
+ (id, instrument) = data
+ if primary:
+ self.trackInstrument[id] = instrument
+ else:
+ self.trackInstrument2[id] = instrument
+
+ if instrument:
+ if instrument.kit == None:
+ self.csnd.load_instrument(instrument.name)
+ else:
+ self.csnd.load_drumkit(instrument.name)
+
+ if primary: # TODO handle secondary instruments properly
+ if (Config.DEBUG > 3): print "handleInstrumentChanged", id, instrument.name, primary
+
+ pages = self.tuneInterface.getSelectedIds()
+ self.noteDB.setInstrument( pages, id, instrument.instrumentId )
+
+ def getScale(self):
+ return self.scale
+
+ def handleVolume( self, widget ):
+ self._data["volume"] = round( widget.get_value() )
+ self.csnd.setMasterVolume(self._data["volume"])
+ img = min(3,int(4*self._data["volume"]/100)) # volume 0-3
+ #self.GUI["2volumeImage"].set_from_file( Config.IMAGE_ROOT+"volume"+str(img)+".png" )
+
+ def initTrackVolume( self ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ self.csnd.setTrackVolume(self._data["track_volume"][i], i)
+
+ def handleTrackVolume( self, widget = None, track = None ):
+ self._data["track_volume"][track] = round( widget.get_value() )
+ self.csnd.setTrackVolume(self._data["track_volume"][track], track)
+
+ def getTrackInstrument( self, track ):
+ return self.trackInstrument[track]
+
+ def getTrackVolume( self, track ):
+ return self._data["track_volume"][track]
+
+ def handleTempo( self, widget ):
+ self._data['tempo'] = round( widget.get_value() )
+ img = min(7,int(8*(self._data["tempo"]-widget.lower)/(widget.upper-widget.lower)))+1# tempo 1-8
+ #self.GUI["2tempoImage"].set_from_file( Config.IMAGE_ROOT+"tempo"+str(img)+".png" )
+ if self.playing:
+ self.csnd.setTempo(self._data['tempo'])
+
+ def handleToolClick( self, widget, mode ):
+ if widget.get_active(): self.trackInterface.setInterfaceMode( mode )
+
+ def getTool( self ):
+ if self.GUI["2toolPointerButton"].get_active(): return "default"
+ else: return "draw"
+
+ def handleKeyboardRecordButton( self, widget, data=None ):
+ self.kb_record = widget.get_active()
+
+ def pickInstrument( self, widget, num, primary = True ):
+ self.last_clicked_instTrackID = num
+ self.last_clicked_instPrimary = primary
+
+ if primary or self.trackInstrument2[num] == None:
+ instrument = self.trackInstrument[num]
+ else:
+ instrument = self.trackInstrument2[num]
+
+ self.GUI["2instrumentPalette"].setInstrument(instrument)
+
+ def cancelInstrumentSelection( self ):
+ self.GUI["9instrumentPopup"].hide()
+
+ def donePickInstrument( self, instrumentName ):
+ self.handleInstrumentChanged( (self.last_clicked_instTrackID, self.instrumentDB.instNamed[instrumentName]), self.last_clicked_instPrimary )
+ btn = self.GUI["2instrument%dButton" % (self.last_clicked_instTrackID+1)]
+ if self.last_clicked_instPrimary:
+ btn.setPrimary( self.GUI["2instrumentIcons"][instrumentName] )
+ else:
+ btn.setSecondary( self.GUI["2instrumentIcons"][instrumentName] )
+ #self.GUI["9instrumentPopup"].hide()
+
+
+ def pickDrum( self, widget , data = None ):
+ if widget.get_active():
+ self.GUI['2drumPalette'].setDrum(self.trackInstrument[Config.NUMBER_OF_TRACKS-1].name)
+
+ def cancelDrumSelection( self ):
+ self.GUI["2drumButton"].set_active( False )
+
+ def donePickDrum( self, drumName ):
+ self.handleInstrumentChanged( ( self.drumIndex, self.instrumentDB.instNamed[drumName] ) )
+ self.GUI["2drumButton"].setImage( "main", self.GUI["2instrumentIcons"][drumName] )
+ self.GUI["2drumButton"].setImage( "alt", self.GUI["2instrumentIcons"][drumName] )
+ self.GUI["2drumButton"].set_active( False )
+
+ def playInstrumentNote( self, instrumentName, secs_per_tick = 0.025):
+ self.csnd.play(
+ CSoundNote( onset = 0,
+ pitch = 36,
+ amplitude = 1,
+ pan = 0.5,
+ duration = 20,
+ trackId = 1,
+ instrumentId = self.instrumentDB.instNamed[instrumentName].instrumentId,
+ reverbSend = 0),
+ secs_per_tick)
+
+ def handlemuteButton(self,widget,track):
+ if widget.get_active():
+ self.trackActive[track] = True
+ else:
+ self.trackActive[track] = False
+ self.updatePagesPlaying()
+
+ def handlemuteButtonRightClick(self,widget,event,track):
+ if event.button == 3:
+ widget.set_active(True)
+ #if the other tracks are inactive
+ if self.trackActive.count(False) == Config.NUMBER_OF_TRACKS - 1:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == 4:
+ #self.GUI["2drumMuteButton"].set_active(True)
+ self.GUI["2drumPalette"].muteButton.set_active(True)
+ else:
+ #self.GUI["2instrument" + str(i+1) + "muteButton"].set_active(True)
+ self.GUI["2instrument" + str(i+1) + "Palette"].muteButton.set_active(True)
+ else:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i != track:
+ if i == 4:
+ #self.GUI["2drumMuteButton"].set_active(False)
+ self.GUI["2drumPalette"].muteButton.set_active(False)
+ else:
+ #self.GUI["2instrument" + str(i+1) + "muteButton"].set_active(False)
+ self.GUI["2instrument" + str(i+1) + "Palette"].muteButton.set_active(False)
+ self.updatePagesPlaying()
+
+ #-----------------------------------
+ # generation functions
+ #-----------------------------------
+
+ def recompose( self, algo, params, nPagesCycle = 4):
+ if self.generateMode == "track":
+ if self.trackSelected == [ 0 for i in range(Config.NUMBER_OF_TRACKS) ]:
+ newtracks = set(range(Config.NUMBER_OF_TRACKS))
+ else:
+ newtracks = set( [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] )
+ newpages = self.tuneInterface.getSelectedIds()
+ else: # page mode
+ newtracks = set(range(Config.NUMBER_OF_TRACKS))
+ newpages = self.tuneInterface.getSelectedIds()
+
+ dict = {}
+ for t in newtracks:
+ dict[t] = {}
+ for p in newpages:
+ dict[t][p] = self.noteDB.getCSNotesByTrack( p, t )
+
+ beatsOfPages = {}
+ for pageId in newpages:
+ beatsOfPages[pageId] = self.noteDB.pages[pageId].beats
+
+ instruments = self.noteDB.getInstruments(newpages)
+
+ #[ i.name for i in self.trackInstrument ],
+ algo(
+ params,
+ self._data['track_volume'][:],
+ instruments,
+ self._data['tempo'],
+ beatsOfPages,
+ newtracks,
+ newpages,
+ dict, nPagesCycle)
+
+ # 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( newpages, newtracks )
+
+ stream = []
+ for page in newpages:
+ for track in newtracks:
+ stream += [ page, track, len(dict[track][page]) ]
+ stream += dict[track][page]
+ stream += [-1]
+ self.noteDB.addNotes( stream )
+
+ def generate( self, params, nPagesCycle = 4 ):
+ self.recompose( generator1, params, nPagesCycle)
+
+ #=======================================================
+ # Clipboard Functions
+
+ def getClipboardArea( self, page = -1 ):
+ if page == -1: page = self.displayedPage
+ ids = self.tuneInterface.getSelectedIds()
+ return self.noteDB.getClipboardArea( ids.index(page) )
+
+ def pasteClipboard( self, offset, trackMap ):
+ pages = self.tuneInterface.getSelectedIds()
+ instrumentMap = {}
+ for t in trackMap:
+ if t != trackMap[t]: instrumentMap[t] = self.trackInstrument[t].instrumentId
+ return self.noteDB.pasteClipboard( pages, offset, trackMap, instrumentMap )
+
+ def cleanupClipboard( self ):
+ self.trackInterface.donePaste()
+
+
+ #=======================================================
+ # Note Functions
+
+ def noteProperties( self, widget ):
+ if widget.get_active():
+ ids = self.trackInterface.getSelectedNotes()
+ notes = { self.displayedPage: {} }
+ for t in range(Config.NUMBER_OF_TRACKS):
+ if len(ids[t]):
+ notes[self.displayedPage][t] = [ self.noteDB.getNote( self.displayedPage, t, id ) for id in ids[t] ]
+
+ self.propertiesPanel.setContext("note", self.generationPanel.scale, notes = notes )
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ if walloc.height != 1: # hack to deal with showing the window before first allocation T_T
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 30, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].move(0, 2048) # off the screen
+ self.GUI["9propertiesPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 30, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].hide()
+
+ def noteDelete( self ):
+ ids = self.trackInterface.getSelectedNotes()
+ stream = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ N = len(ids[t])
+ if not N: continue
+ stream += [ self.displayedPage, t, N ] + ids[t]
+ if len(stream):
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ def noteDuplicate( self ):
+ ids = self.trackInterface.getSelectedNotes()
+ stream = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ N = len(ids[t])
+ if not N: continue
+ stream += [ self.displayedPage, t, N ] + ids[t]
+ if len(stream):
+ self.skipCleanup = "note"
+ self.skipCleanup = ""
+ self.noteDB.notesToClipboard( stream + [-1] )
+ self.trackInterface.setInterfaceMode("paste_notes")
+ return True
+ return False
+
+ def noteDuplicateWidget( self, widget ):
+ if widget.get_active():
+ if self.noteDuplicate(): # duplicate succeeded
+ return
+ # cancel duplicate
+ widget.set_active(False)
+ self.trackInterface.setInterfaceMode("tool")
+ else:
+ self.trackInterface.setInterfaceMode("tool")
+
+ def noteOnset( self, step ):
+ self.trackInterface.noteStepOnset( step )
+
+ def notePitch( self, step ):
+ # TODO
+ return
+
+ def noteDuration( self, step ):
+ # TODO
+ return
+
+ def noteVolume( self, step ):
+ # TODO
+ return
+
+ #=======================================================
+ # Track Functions
+
+ def toggleTrack( self, trackN, exclusive ):
+ if exclusive:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackSelected[i]:
+ self.trackSelected[i] = False
+ self.trackInterface.trackToggled( i )
+ self.tuneInterface.trackToggled( i )
+ self.trackSelected[trackN] = True
+ self.trackInterface.trackToggled( trackN )
+ self.tuneInterface.trackToggled( trackN )
+ self.setContextState( CONTEXT.TRACK, True )
+ self.setContext( CONTEXT.TRACK )
+ else:
+ self.trackSelected[trackN] = not self.trackSelected[trackN]
+ self.trackInterface.trackToggled( trackN )
+ self.tuneInterface.trackToggled( trackN )
+ trackSelected = False
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackSelected[i]:
+ self.setContextState( CONTEXT.TRACK, True )
+ self.setContext( CONTEXT.TRACK )
+ trackSelected = True
+ break
+ if not trackSelected:
+ self.setContextState( CONTEXT.TRACK, False )
+
+ def setTrack( self, trackN, state ):
+ if self.trackSelected[trackN] != state:
+ self.trackSelected[trackN] = state
+ self.trackInterface.trackToggled( trackN )
+
+ def clearTracks( self ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackSelected[i]:
+ self.trackSelected[i]= False
+ self.trackInterface.trackToggled( i )
+ self.tuneInterface.trackToggled( i )
+
+ self.setContextState( CONTEXT.TRACK, False )
+
+ def getTrackSelected( self, trackN ):
+ return self.trackSelected[trackN]
+
+ def trackGenerate( self, widget ):
+ if widget.get_active():
+ self.generateMode = "track"
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9generationPopup"].get_allocation()
+ if walloc.height != 1: # hack to make deal with showing the window before first allocation T_T
+ self.GUI["9generationPopup"].move( balloc.x + winLoc[0], balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9generationPopup"].move(0, 2048) # off the screen
+ self.GUI["9generationPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9generationPopup"].get_allocation()
+ self.GUI["9generationPopup"].move( balloc.x + winLoc[0], balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9generationPopup"].hide()
+
+
+ def trackProperties( self, widget ):
+ if widget.get_active():
+ self.propertiesPanel.setContext( "track", self.generationPanel.scale, self.tuneInterface.getSelectedIds(), [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] )
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ if walloc.height != 1: # hack to make deal with showing the window before first allocation T_T
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 30, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].move(0, 2048) # off the screen
+ self.GUI["9propertiesPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 30, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].hide()
+
+ def trackDelete( self, pageIds = -1, trackIds = -1 ):
+
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+ if trackIds == -1: trackIds = [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ]
+
+ self.noteDB.deleteNotesByTrack( pageIds, trackIds )
+
+ def trackDuplicate( self, pageIds = -1, trackIds = -1 ):
+
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+ if trackIds == -1: trackIds = [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ]
+
+ if len(trackIds):
+ self.skipCleanup = "track"
+ self.skipCleanup = ""
+ self.noteDB.tracksToClipboard( pageIds, trackIds )
+ self.trackInterface.setInterfaceMode("paste_tracks")
+ return True
+ return False
+
+ def trackDuplicateWidget( self, widget ):
+ if widget.get_active():
+ if self.trackDuplicate(): # duplicate succeeded
+ return
+ # cancel duplicate
+ widget.set_active(False)
+ self.trackInterface.setInterfaceMode("tool")
+ else:
+ self.trackInterface.setInterfaceMode("tool")
+
+ #-----------------------------------
+ # tune/page functions
+ #-----------------------------------
+
+ def displayPage( self, pageId, nextId = -1 ):
+ if self.playing:
+ if self.displayedPage != pageId and pageId in self.pages_playing:
+ self.csnd.loopSetTick( self.page_onset[pageId] )
+
+ self._displayPage( pageId, nextId )
+
+
+ # only called locally!
+ def _displayPage( self, pageId, nextId = -1 ):
+
+ self.displayedPage = pageId
+
+ page = self.noteDB.getPage(pageId)
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackInstrument[i].instrumentId != page.instruments[i]:
+ self.trackInstrument[i] = self.instrumentDB.instId[page.instruments[i]]
+ if i == Config.NUMBER_OF_TRACKS-1:
+ btn = self.GUI["2drumButton"]
+ btn.setImage( "main", self.GUI["2instrumentIcons"][self.trackInstrument[i].name] )
+ btn.setImage( "alt", self.GUI["2instrumentIcons"][self.trackInstrument[i].name] )
+ else:
+ btn = self.GUI["2instrument%dButton"%(i+1)]
+ btn.setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[i].name] )
+ if self.trackInstrument2[i] != None:
+ btn.setSecondary( self.GUI["2instrumentIcons"][self.trackInstrument2[i].name] )
+ else:
+ btn.setSecondary( None )
+ self.tuneInterface.displayPage( pageId )
+ self.trackInterface.displayPage( pageId, nextId )
+
+ def predrawPage( self, pageId ):
+ if self.playbackTimeout: return # we're playing, predrawing is already handled
+ if self.trackInterface.setPredrawPage( pageId ): # page needs to be drawn
+ self.trackInterface.predrawPage()
+
+ def abortPredrawPage( self ):
+ self.trackInterface.abortPredrawPage()
+
+ def pageGenerate( self, widget ):
+ if widget.get_active():
+ self.generateMode = "page"
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9generationPopup"].get_allocation()
+ if walloc.height != 1: # hack to make deal with showing the window before first allocation T_T
+ self.GUI["9generationPopup"].move( balloc.x + winLoc[0], balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9generationPopup"].move(0, 2048) # off the screen
+ self.GUI["9generationPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9generationPopup"].get_allocation()
+ self.GUI["9generationPopup"].move( balloc.x + winLoc[0], balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9generationPopup"].hide()
+
+ def setPageGenerateMode(self, mode):
+ self.generateMode = mode
+
+ def pageProperties( self, widget ):
+ if widget.get_active():
+ self.propertiesPanel.setContext( "page", self.generationPanel.scale, self.tuneInterface.getSelectedIds() )
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ if walloc.height != 1: # hack to make deal with showing the window before first allocation T_T
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 100, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].move(0, 2048) # off the screen
+ self.GUI["9propertiesPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 100, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].hide()
+
+ def pageDelete( self, pageIds = -1, instruments = False ):
+
+ if pageIds == -1:
+ pageIds = self.tuneInterface.getSelectedIds()
+
+ if instruments == False:
+ instruments = []
+ for inst in self.trackInstrument:
+ instruments.append(inst.instrumentId)
+
+ self.noteDB.deletePages( pageIds[:], instruments )
+
+ def pageDuplicate( self, after = -1, pageIds = False ):
+
+ if after == -1: after = self.tuneInterface.getLastSelected()
+ if not pageIds: pageIds = self.tuneInterface.getSelectedIds()
+
+ new = self.noteDB.duplicatePages( pageIds[:], after )
+ self.displayPage( new[self.displayedPage] )
+ self.tuneInterface.selectPages( new.values() )
+
+ def pageAdd( self, after = -1, beats = False, color = False, instruments = False ):
+
+ if after == -1: after = self.tuneInterface.getLastSelected()
+ page = self.noteDB.getPage( self.displayedPage )
+ if not beats: beats = page.beats
+ if not color: color = page.color
+ if not instruments: instruments = page.instruments
+
+ # TODO think about network mode here...
+ self.displayPage( self.noteDB.addPage( -1, NoteDB.Page(beats,color,instruments), after ) )
+
+ def pageBeats( self, pageIds = -1 ):
+
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+
+ # TODO change the beats
+
+ #=======================================================
+ # NoteDB notifications
+
+ def notifyPageAdd( self, id, at ):
+ return
+
+ def notifyPageDelete( self, which, safe ):
+ if self.displayedPage in which:
+ self.displayPage( safe )
+
+ def notifyPageDuplicate( self, new, at ):
+ return
+
+ def notifyPageMove( self, which, low, high ):
+ return
+
+ def notifyPageUpdate( self, page, parameter, value ):
+ pass
+
+ def notifyNoteAdd( self, page, track, id ):
+ if (Config.DEBUG > 3) : print 'INFO: adding note to loop', page, track, id
+ n = self.noteDB.getNote(page, track, id)
+ self.csnd.loopPlay(n,0)
+ if self.playing and (n.page in self.page_onset ):
+ onset = n.cs.onset + self.page_onset[n.page]
+ self.csnd.loopUpdate(n, NoteDB.PARAMETER.ONSET, onset, 1) #set onset + activate
+
+ def notifyNoteDelete( self, page, track, id ):
+ if (Config.DEBUG > 3) : print 'INFO: deleting note from loop', page, track, id
+ self.csnd.loopDelete1(page,id)
+ def notifyNoteUpdate( self, page, track, id, parameter, value ):
+ if (Config.DEBUG > 3) : print 'INFO: updating note ', page, id, parameter, value
+ note = self.noteDB.getNote(page, track, id)
+ self.csnd.loopUpdate(note, parameter, value, -1)
+
+ #-----------------------------------
+ # load and save functions
+ #-----------------------------------
+
+ def waitToSet(self):
+ self.csnd.setMasterVolume(self._data['volume'])
+ self.csnd.setTempo(self._data['tempo'])
+ self.initTrackVolume()
+
+ def handleSave(self, widget = None):
+
+ chooser = gtk.FileChooserDialog(
+ title='Save Tune',
+ action=gtk.FILE_CHOOSER_ACTION_SAVE,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
+ filter = gtk.FileFilter()
+ filter.add_pattern('*.tam')
+ chooser.set_filter(filter)
+ chooser.set_current_folder(Config.DATA_DIR)
+
+ for f in chooser.list_shortcut_folder_uris():
+ chooser.remove_shortcut_folder_uri(f)
+
+ if chooser.run() == gtk.RESPONSE_OK:
+ ofilename = chooser.get_filename()
+ if ofilename[-4:] != '.tam':
+ ofilename += '.tam'
+ try:
+ ofile = open(ofilename, 'w')
+ ofilestream = ControlStream.TamTamOStream (ofile)
+ self.noteDB.dumpToStream(ofilestream)
+ ofilestream.track_vol(self._data['track_volume'])
+ ofilestream.master_vol(self._data['volume'])
+ ofilestream.tempo(self._data['tempo'])
+ ofile.close()
+ except OSError,e:
+ print 'ERROR: failed to open file %s for writing\n' % ofilename
+ chooser.destroy()
+
+ def handleLoopSave(self):
+ date = str(time.localtime()[3]) + '-' + str(time.localtime()[4]) + '-' + str(time.localtime()[5])
+ ofilename = Config.DATA_DIR + '/' + date + '.ttl'
+ ofile = open(ofilename, 'w')
+ ofilestream = ControlStream.TamTamOStream (ofile)
+ self.noteDB.dumpToStream(ofilestream)
+ ofilestream.track_vol(self._data['track_volume'])
+ ofilestream.master_vol(self._data['volume'])
+ ofilestream.tempo(self._data['tempo'])
+ ofile.close()
+
+ def handleJournalSave(self, file_path):
+ ofile = open(file_path, 'w')
+ ofilestream = ControlStream.TamTamOStream (ofile)
+ self.noteDB.dumpToStream(ofilestream)
+ ofilestream.track_vol(self._data['track_volume'])
+ ofilestream.master_vol(self._data['volume'])
+ ofilestream.tempo(self._data['tempo'])
+ ofile.close()
+
+ def _loadFile( self, path ):
+ try:
+ oldPages = self.noteDB.getTune()[:]
+
+ ifile = open(path, 'r')
+ ttt = ControlStream.TamTamTable ( self.noteDB )
+ ttt.parseFile(ifile)
+ self.trackInstrument = self.trackInstrumentDefault[:] # these will get set correctly in displayPage
+ self._data['track_volume'] = ttt.tracks_volume
+ self._data['volume'] = float(ttt.masterVolume)
+ self._data['tempo'] = float(ttt.tempo)
+ #self.GUI["2volumeAdjustment"].set_value(self._data['volume'])
+ #self.GUI["2tempoAdjustment"].set_value(self._data['tempo'])
+ ifile.close()
+
+ self.noteDB.deletePages( oldPages )
+
+ self.tuneInterface.selectPages( self.noteDB.getTune() )
+ except OSError,e:
+ print 'ERROR: failed to open file %s for reading\n' % ofilename
+
+ def handleLoad(self, widget):
+ chooser = gtk.FileChooserDialog(
+ title='Load Tune',
+ action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
+
+ filter = gtk.FileFilter()
+ filter.add_pattern('*.tam')
+ chooser.set_filter(filter)
+ chooser.set_current_folder(Config.DATA_DIR)
+
+ for f in chooser.list_shortcut_folder_uris():
+ chooser.remove_shortcut_folder_uri(f)
+
+ if chooser.run() == gtk.RESPONSE_OK:
+ print 'DEBUG: loading file: ', chooser.get_filename()
+ self._loadFile( chooser.get_filename() )
+
+ chooser.destroy()
+ self.delay = gobject.timeout_add(1000, self.waitToSet)
+
+ def handleJournalLoad(self,file_path):
+ self.journalCalled = True
+ self._loadFile( file_path )
+
+ #-----------------------------------
+ # Record functions
+ #-----------------------------------
+ def handleMicRecord( self, widget, data ):
+ self.csnd.micRecording( data )
+ def handleCloseMicRecordWindow( self, widget = None, data = None ):
+ self.micRecordWindow.destroy()
+ self.micRecordButton.set_active( False )
+
+ #-----------------------------------
+ # callback functions
+ #-----------------------------------
+ def handleKeyboardShortcuts(self,event):
+ keyval = event.keyval
+
+ if self.activity.activity_toolbar.title.is_focus():
+ return
+
+ # backspace and del keys
+ if keyval == gtk.keysyms.Delete or keyval == gtk.keysyms.BackSpace:
+ if self.context == CONTEXT.PAGE: self.pageDelete()
+ if self.context == CONTEXT.TRACK: self.trackDelete()
+ if self.context == CONTEXT.NOTE: self.noteDelete()
+ # plus key
+ if keyval == gtk.keysyms.equal:
+ self.pageAdd()
+ # duplicate ctrl-c
+ if event.state == gtk.gdk.CONTROL_MASK and keyval == gtk.keysyms.c:
+ if self.context == CONTEXT.PAGE: self.pageDuplicate()
+ if self.context == CONTEXT.TRACK: self.trackDuplicate()
+ if self.context == CONTEXT.NOTE: self.noteDuplicate()
+ #Arrows
+ if event.state == gtk.gdk.SHIFT_MASK:
+ # up/down arrows volume
+ if keyval == gtk.keysyms.Up: self.trackInterface.noteStepVolume(0.1)
+ if keyval == gtk.keysyms.Down: self.trackInterface.noteStepVolume(-0.1)
+ # left/right arrows onset
+ if keyval == gtk.keysyms.Left: self.trackInterface.noteStepDuration(-1)
+ if keyval == gtk.keysyms.Right: self.trackInterface.noteStepDuration(1)
+ else:
+ # up/down arrows pitch
+ if keyval == gtk.keysyms.Up: self.trackInterface.noteStepPitch(1)
+ if keyval == gtk.keysyms.Down: self.trackInterface.noteStepPitch(-1)
+ # left/right arrows duration
+ if keyval == gtk.keysyms.Left: self.trackInterface.noteStepOnset(-1)
+ if keyval == gtk.keysyms.Right: self.trackInterface.noteStepOnset(1)
+ #Save Loop
+ if event.state == gtk.gdk.CONTROL_MASK and keyval == gtk.keysyms.s:
+ self.handleLoopSave()
+
+
+ def onKeyPress(self,widget,event):
+
+ self.handleKeyboardShortcuts(event)
+ Config.ModKeys.keyPress( event.hardware_keycode )
+ key = event.hardware_keycode
+
+ # If the key is already in the dictionnary, exit function (to avoir key repeats)
+ if self.kb_keydict.has_key(key):
+ return
+
+ # Assign on which track the note will be created according to the number of keys pressed
+ if self.trackCount >= 9:
+ self.trackCount = 6
+ fakeTrack = self.trackCount
+ self.trackCount += 1
+
+ # If the pressed key is in the keymap
+ if KEY_MAP_PIANO.has_key(key):
+ pitch = KEY_MAP_PIANO[key]
+ duration = -1
+
+ # get instrument from top selected track if a track is selected
+ if True in self.trackSelected:
+ index = self.trackSelected.index(True)
+ instrument = self.getTrackInstrument(index).name
+ else:
+ return
+
+ tid = index
+
+ # pitch, inst and duration for drum recording
+ if tid == Config.NUMBER_OF_TRACKS-1:
+ if GenerationConstants.DRUMPITCH.has_key( pitch ):
+ pitch = GenerationConstants.DRUMPITCH[pitch]
+ if self.instrumentDB.instNamed[instrument].kit != None:
+ instrument = self.instrumentDB.instNamed[instrument].kit[pitch].name
+ duration = 100
+
+ # Create and play the note
+ self.kb_keydict[key] = CSoundNote(onset = 0,
+ pitch = pitch,
+ amplitude = 1,
+ pan = 0.5,
+ duration = duration,
+ trackId = fakeTrack,
+ instrumentId = self.instrumentDB.instNamed[instrument].instrumentId,
+ tied = False,
+ mode = 'edit')
+ self.csnd.play(self.kb_keydict[key], 0.3)
+
+ # doesn't keep track of keys for drum recording
+ if tid == Config.NUMBER_OF_TRACKS-1:
+ del self.kb_keydict[key]
+
+ # remove previosly holded key from dictionary
+ if len(self.kb_keydict) > 1:
+ for k in self.kb_keydict.keys():
+ if k != key:
+ gobject.source_remove( self.durUpdate )
+ self.durUpdate = False
+ self.kb_keydict[k].duration = 0.5
+ self.kb_keydict[k].amplitude = 0
+ self.kb_keydict[k].decay = 0.7
+ self.kb_keydict[k].tied = False
+ self.csnd.play(self.kb_keydict[k], 0.3)
+ if not self.kb_record:
+ del self.kb_keydict[k]
+ return
+ self.removeRecNote(self.csId)
+
+ if not self.kb_record:
+ return
+
+ #record the note on track
+ pageList = self.tuneInterface.getSelectedIds()
+ pid = self.displayedPage
+ minOnset = self.page_onset[pid]
+ onsetQuantized = Config.DEFAULT_GRID * int((self.csnd.loopGetTick() - minOnset) / Config.DEFAULT_GRID + 0.5)
+
+ maxOnset = self.noteDB.getPage(pid).ticks
+ if onsetQuantized >= maxOnset:
+ if pid == pageList[-1]:
+ pid = pageList[0]
+ else:
+ if len(pageList) > 1:
+ pidPos = pageList.index(pid)
+ pid = pageList[pidPos+1]
+ onsetQuantized = 0
+
+ if tid < Config.NUMBER_OF_TRACKS-1:
+ for n in self.noteDB.getNotesByTrack( pid, tid ):
+ if onsetQuantized < n.cs.onset:
+ break
+ if onsetQuantized >= n.cs.onset + n.cs.duration:
+ continue
+ if onsetQuantized < n.cs.onset + n.cs.duration - 2:
+ self.noteDB.deleteNote(n.page, n.track, n.id)
+ elif onsetQuantized - n.cs.onset < 1:
+ self.noteDB.deleteNote(n.page, n.track, n.id)
+ else:
+ self.noteDB.updateNote( n.page, n.track, n.id, PARAMETER.DURATION, onsetQuantized - n.cs.onset )
+ break
+ else:
+ for n in self.noteDB.getNotesByTrack( pid, tid ):
+ if onsetQuantized < n.cs.onset:
+ break
+ if onsetQuantized == n.cs.onset:
+ if pitch < n.cs.pitch:
+ break
+ if pitch == n.cs.pitch:
+ return # don't bother with a new note
+
+ csnote = CSoundNote(onset = 0,
+ pitch = pitch,
+ amplitude = 1,
+ pan = 0.5,
+ duration = duration,
+ trackId = index,
+ instrumentId = self.instrumentDB.instNamed[instrument].instrumentId,
+ tied = False,
+ mode = 'edit')
+
+ csnote.onset = onsetQuantized
+ csnote.duration = 1
+ csnote.pageId = pid
+ id = self.noteDB.addNote(-1, pid, tid, csnote)
+ # csId: PageId, TrackId, Onset, Key, DurationSetOnce
+ self.csId = [pid, tid, id, csnote.onset, key, False ]
+ if tid < Config.NUMBER_OF_TRACKS-1:
+ self.durUpdate = gobject.timeout_add( 25, self.durationUpdate )
+
+ def onKeyRelease(self,widget,event):
+
+ Config.ModKeys.keyRelease( event.hardware_keycode )
+ key = event.hardware_keycode
+
+ if True in self.trackSelected:
+ index = self.trackSelected.index(True)
+ if index == Config.NUMBER_OF_TRACKS-1:
+ return
+ else:
+ return
+
+ if KEY_MAP_PIANO.has_key(key) and self.kb_keydict.has_key(key):
+ if self.kb_record and self.durUpdate:
+ gobject.source_remove( self.durUpdate )
+ self.durUpdate = False
+
+ if self.instrumentDB.instId[ self.kb_keydict[key].instrumentId ].csoundInstrumentId == Config.INST_TIED:
+ self.kb_keydict[key].duration = 0.5
+ self.kb_keydict[key].amplitude = 0
+ self.kb_keydict[key].decay = 0.5
+ self.kb_keydict[key].tied = False
+ self.csnd.play(self.kb_keydict[key], 0.3)
+ if not self.kb_record:
+ del self.kb_keydict[key]
+ return
+
+ self.removeRecNote(self.csId)
+
+ def removeRecNote(self, csId):
+ newDuration = (int(self.csnd.loopGetTick()) - self.page_onset[csId[0]]) - csId[3]
+ maxTick = self.noteDB.getPage(csId[0]).ticks
+
+ if not csId[5]: # handle notes that were created right at the end of a page
+ if newDuration > maxTick//2:
+ newDuration = 1
+ else:
+ csId[5] = True
+
+ if newDuration < -Config.DEFAULT_GRID_DIV2: # we looped around
+ newDuration = maxTick - self.csId[3]
+ elif newDuration < 1:
+ newDuration = 1
+
+ if (csId[3] + newDuration) > maxTick:
+ newDuration = maxTick - csId[3]
+
+ for n in self.noteDB.getNotesByTrack( csId[0], csId[1] ):
+ if n.id == csId[2]:
+ continue
+ if csId[3] + newDuration <= n.cs.onset:
+ break
+ if csId[3] >= n.cs.onset + n.cs.duration:
+ continue
+ self.noteDB.deleteNote(n.page, n.track, n.id)
+ break
+
+ self.noteDB.updateNote( csId[0], csId[1], csId[2], PARAMETER.DURATION, newDuration)
+
+ del self.kb_keydict[csId[4]]
+
+ def durationUpdate(self):
+ newDuration = (int(self.csnd.loopGetTick()) - self.page_onset[self.csId[0]]) - self.csId[3]
+
+ maxTick = self.noteDB.getPage(self.csId[0]).ticks
+ stop = False
+
+ if not self.csId[5]: # handle notes that were created right at the end of a page
+ if newDuration > maxTick//2:
+ newDuration = 1
+ else:
+ self.csId[5] = True
+
+ if newDuration < -Config.DEFAULT_GRID_DIV2: # we looped around
+ newDuration = maxTick - self.csId[3]
+ stop = True
+ elif newDuration < 1:
+ newDuration = 1
+
+ if (self.csId[3] + newDuration) > maxTick:
+ stop = True
+ newDuration = maxTick - self.csId[3]
+
+ for n in self.noteDB.getNotesByTrack( self.csId[0], self.csId[1] ):
+ if n.id == self.csId[2]:
+ continue
+ if self.csId[3] + newDuration <= n.cs.onset:
+ break
+ if self.csId[3] >= n.cs.onset + n.cs.duration:
+ continue
+ self.noteDB.deleteNote(n.page, n.track, n.id)
+ break
+
+ self.noteDB.updateNote( self.csId[0], self.csId[1], self.csId[2], PARAMETER.DURATION, newDuration)
+
+ if stop:
+ key = self.csId[4]
+ if self.instrumentDB.instId[ self.kb_keydict[key].instrumentId ].csoundInstrumentId == Config.INST_TIED:
+ self.kb_keydict[key].duration = 0.5
+ self.kb_keydict[key].amplitude = 0
+ self.kb_keydict[key].decay = 0.5
+ self.kb_keydict[key].tied = False
+ self.csnd.play(self.kb_keydict[key], 0.3)
+
+ del self.kb_keydict[key]
+ return False
+ return True
+
+ def delete_event( self, widget, event, data = None ):
+ return False
+
+ def onDestroy( self ):
+
+ if (Config.DEBUG > 1): print TP.PrintAll()
+
+ def setContextState( self, context, state ):
+ if context == CONTEXT.TRACK:
+ self.contextTrackActive = state
+ if not state:
+ if self.context == CONTEXT.TRACK:
+ if self.contextNoteActive:
+ self.setContext( CONTEXT.NOTE )
+ else:
+ self.setContext( CONTEXT.PAGE )
+ else:
+ self.contextNoteActive = state
+ if not state:
+ if self.context == CONTEXT.NOTE:
+ self.prevContext()
+
+ def setContext( self, context, force = False ):
+
+ if self.context == context and not force: return
+
+ self.context = context
+
+ if self.context == CONTEXT.NOTE:
+ self._generateToolbar.generationButton.set_sensitive(False)
+ else:
+ self._generateToolbar.generationButton.set_sensitive(True)
+
+ def getContext(self):
+ return self.context
+
+ def prevContext( self ):
+ if self.context == CONTEXT.TRACK:
+ self.setContext( CONTEXT.PAGE )
+ elif self.contextTrackActive:
+ self.setContext( CONTEXT.TRACK )
+ else:
+ self.setContext( CONTEXT.PAGE )
+
+ def nextContext( self ):
+ if self.context == CONTEXT.TRACK:
+ self.setContext( CONTEXT.NOTE )
+ elif self.contextTrackActive:
+ self.setContext( CONTEXT.TRACK )
+ else:
+ self.setContext( CONTEXT.NOTE )
+
+ #-----------------------------------
+ # access functions (not sure if this is the best way to go about doing this)
+ #-----------------------------------
+ def getVolume( self ):
+ return self._data["volume"]
+
+ def getTempo( self ):
+ return self._data["tempo"]
+ #return round( self.tempoAdjustment.value, 0 )
+
+ def getBeatsPerPage( self ):
+ return int(round( self.beatsPerPageAdjustment.value, 0 ))
+
+ def getWindowTitle( self ):
+ return "Tam-Tam [Volume %i, Tempo %i, Beats/Page %i]" % ( self.getVolume(), self.getTempo(), self.getBeatsPerPage() )
+
+
+class InstrumentButton( gtk.DrawingArea ):
+
+ def __init__( self, owner, index, backgroundFill ):
+ gtk.DrawingArea.__init__( self )
+
+ self.index = index
+ self.owner = owner
+
+ self.win = gtk.gdk.get_default_root_window()
+ self.gc = gtk.gdk.GC( self.win )
+
+ colormap = self.get_colormap()
+ self.color = { "background": colormap.alloc_color( backgroundFill, True, True ),
+ "divider": colormap.alloc_color( "#000", True, True ),
+ "+/-": colormap.alloc_color( Config.FG_COLOR, True, True ),
+ "+/-Highlight": colormap.alloc_color( "#FFF", True, True ) }
+
+ self.pixmap = None
+ self.primary = None
+ self.primaryWidth = self.primaryHeight = 1
+ self.secondary = None
+ self.secondaryWidth = self.secondaryHeight = 1
+
+ self.clicked = None
+ self.hover = None
+
+ self.add_events( gtk.gdk.BUTTON_PRESS_MASK
+ | gtk.gdk.BUTTON_RELEASE_MASK
+ | gtk.gdk.POINTER_MOTION_MASK
+ | gtk.gdk.POINTER_MOTION_HINT_MASK
+ | gtk.gdk.LEAVE_NOTIFY_MASK
+ | gtk.gdk.ENTER_NOTIFY_MASK )
+ self.connect( "size-allocate", self.size_allocate )
+ self.connect( "button-press-event", self.button_press )
+ self.connect( "button-release-event", self.button_release )
+ self.connect( "motion-notify-event", self.motion_notify )
+ self.connect( "leave-notify-event", self.leave_notify )
+ self.connect( "expose-event", self.expose )
+
+ def size_allocate( self, widget, allocation ):
+ self.alloc = allocation
+ self.pixmap = gtk.gdk.Pixmap( self.win, allocation.width, allocation.height )
+ self.primaryX = (self.alloc.width - self.primaryWidth) // 2
+ self.primaryY = (self.alloc.height - self.primaryHeight) // 2
+ self.secondaryX = (self.alloc.width - self.secondaryWidth) // 2
+ self.secondaryY = self.alloc.height//2
+
+ self.hotspots = [ [ self.alloc.width-24, self.alloc.height-29, self.alloc.width-8, self.alloc.height-13 ],
+ [ self.alloc.width-24, self.alloc.height//2-23, self.alloc.width-8, self.alloc.height//2-7 ] ]
+
+ self.hotspots[0] += [ (self.hotspots[0][0]+self.hotspots[0][2])//2, (self.hotspots[0][1]+self.hotspots[0][3])//2 ]
+ self.hotspots[1] += [ (self.hotspots[1][0]+self.hotspots[1][2])//2, (self.hotspots[1][1]+self.hotspots[1][3])//2 ]
+
+ self._updatePixmap()
+
+ def button_press( self, widget, event ):
+
+ self.clicked = "PRIMARY"
+ self.hover = None
+
+ if event.x >= self.hotspots[0][0] and event.x <= self.hotspots[0][2] \
+ and event.y >= self.hotspots[0][1] and event.y <= self.hotspots[0][3]:
+ self.clicked = "HOTSPOT_0"
+
+ elif self.secondary != None:
+
+ if event.x >= self.hotspots[1][0] and event.x <= self.hotspots[1][2] \
+ and event.y >= self.hotspots[1][1] and event.y <= self.hotspots[1][3]:
+ self.clicked = "HOTSPOT_1"
+
+ elif event.y > self.alloc.height//2:
+ self.clicked = "SECONDARY"
+
+ def button_release( self, widget, event ):
+ if self.clicked == "PRIMARY":
+ self.owner.pickInstrument( self, self.index, True )
+ elif self.clicked == "SECONDARY":
+ self.owner.pickInstrument( self, self.index, False )
+ elif self.clicked == "HOTSPOT_0":
+ if self.secondary != None: # remove secondary
+ self.owner.clearInstrument( self.index, False )
+ else: # add secondary
+ self.owner.pickInstrument( self, self.index, False )
+ else: # HOTSPOT_1, remove primary
+ self.owner.clearInstrument( self.index, True )
+
+ self.clicked = None
+
+ def motion_notify( self, widget, event ):
+
+ if self.clicked != None:
+ return
+
+ if event.is_hint:
+ x, y, state = widget.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ if event.x >= self.hotspots[0][0] and event.x <= self.hotspots[0][2] \
+ and event.y >= self.hotspots[0][1] and event.y <= self.hotspots[0][3]:
+ if self.hover != "HOTSPOT_0":
+ self.hover = "HOTSPOT_0"
+ self.queue_draw()
+
+
+ elif self.secondary != None \
+ and event.x >= self.hotspots[1][0] and event.x <= self.hotspots[1][2] \
+ and event.y >= self.hotspots[1][1] and event.y <= self.hotspots[1][3]:
+ if self.hover != "HOTSPOT_1":
+ self.hover = "HOTSPOT_1"
+ self.queue_draw()
+ else:
+ if self.hover != None:
+ self.hover = None
+ self.queue_draw()
+
+ def leave_notify( self, widget, event ):
+ if event.mode != gtk.gdk.CROSSING_NORMAL:
+ return
+ if self.hover != None:
+ self.hover = None
+ if self.clicked == None:
+ self.queue_draw()
+
+
+ def setPrimary( self, img ):
+ self.primary = img
+ self.primaryWidth = img.get_width()
+ self.primaryHeight = img.get_height()
+ if self.pixmap:
+ self.primaryX = (self.alloc.width - self.primaryWidth) // 2
+ self.primaryY = (self.alloc.height - self.primaryHeight) // 2
+ self._updatePixmap()
+
+ def setSecondary( self, img ):
+ self.secondary = img
+ if img != None:
+ self.secondaryWidth = img.get_width()
+ self.secondaryHeight = img.get_height()
+ self.secondaryOffset = self.secondaryHeight//2
+ if self.pixmap:
+ self.secondaryX = (self.alloc.width - self.secondaryWidth) // 2
+ self.secondaryY = self.alloc.height//2
+ if self.pixmap:
+ self._updatePixmap()
+
+ def _updatePixmap( self ):
+ self.gc.foreground = self.color["background"]
+ self.pixmap.draw_rectangle( self.gc, True, 0, 0, self.alloc.width, self.alloc.height )
+ if self.secondary != None:
+ self.pixmap.draw_pixbuf( self.gc, self.primary, 0, 0, self.primaryX, self.primaryY, self.primaryWidth, self.primaryHeight//2, gtk.gdk.RGB_DITHER_NONE )
+ self.pixmap.draw_pixbuf( self.gc, self.secondary, 0, self.secondaryOffset, self.secondaryX, self.secondaryY, self.secondaryWidth, self.secondaryHeight//2, gtk.gdk.RGB_DITHER_NONE )
+ self.gc.foreground = self.color["divider"]
+ self.gc.set_line_attributes( 2, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.pixmap.draw_line( self.gc, 2, self.alloc.height//2, self.alloc.width-4, self.alloc.height//2 )
+ else:
+ self.pixmap.draw_pixbuf( self.gc, self.primary, 0, 0, self.primaryX, self.primaryY, self.primaryWidth, self.primaryHeight, gtk.gdk.RGB_DITHER_NONE )
+ self.queue_draw()
+
+ def expose( self, widget, event ):
+ self.window.draw_drawable( self.gc, self.pixmap, 0, 0, 0, 0, self.alloc.width, self.alloc.height )
+ self.gc.set_line_attributes( 4, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER )
+ if self.secondary != None:
+ if self.clicked == "HOTSPOT_0" or (self.clicked == None and self.hover == "HOTSPOT_0" ):
+ self.gc.foreground = self.color["+/-Highlight"]
+ else:
+ self.gc.foreground = self.color["+/-"]
+ self.window.draw_line( self.gc, self.hotspots[0][0], self.hotspots[0][5], self.hotspots[0][2], self.hotspots[0][5] )
+ if self.clicked == "HOTSPOT_1" or (self.clicked == None and self.hover == "HOTSPOT_1" ):
+ self.gc.foreground = self.color["+/-Highlight"]
+ else:
+ self.gc.foreground = self.color["+/-"]
+ self.window.draw_line( self.gc, self.hotspots[1][0], self.hotspots[1][5], self.hotspots[1][2], self.hotspots[1][5] )
+ else:
+ if self.clicked == "HOTSPOT_0" or self.hover == "HOTSPOT_0":
+ self.gc.foreground = self.color["+/-Highlight"]
+ else:
+ self.gc.foreground = self.color["+/-"]
+ self.window.draw_line( self.gc, self.hotspots[0][0], self.hotspots[0][5], self.hotspots[0][2], self.hotspots[0][5] )
+ self.window.draw_line( self.gc, self.hotspots[0][4], self.hotspots[0][1], self.hotspots[0][4], self.hotspots[0][3] )
+
+ def set_palette(self, palette):
+ pass
+
+
+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 )
+
+class instrumentPalette( Popup ):
+ ICON_SIZE = (70,70)
+ def __init__(self, label, edit):
+ Popup.__init__(self, label, edit)
+
+ self.instrumentDB = InstrumentDB.getRef()
+ self.edit = edit
+
+ self.skip = False
+ self.skipVolAdj = False
+ self.lastClickedTrack = None
+
+ self.tooltips = gtk.Tooltips()
+
+ self.mainBox = gtk.VBox()
+ self.volumeBox = gtk.HBox()
+ self.instrumentMainBox = gtk.HBox()
+
+
+ self.muteButtonLabel = gtk.Label(_('M'))
+ self.muteButton = gtk.CheckButton()
+ self.muteButton.connect("toggled",self.handlemuteButton)
+ self.muteButton.set_active(True)
+ self.tooltips.set_tip(self.muteButton, _('Mute track'))
+
+ self.soloButtonLabel = gtk.Label(_('S'))
+ self.soloButton = gtk.CheckButton()
+ self.soloButton.connect("toggled",self.handlesoloButton)
+ self.soloButton.set_active(True)
+ self.tooltips.set_tip(self.soloButton, _('Solo track'))
+
+ self.volumeSliderAdj = gtk.Adjustment( self.edit._data["track_volume"][0], 0, 100, 1, 1, 0 )
+ self.volumeSliderAdj.connect( "value-changed", self.handleTrackVolume)
+ self.volumeSlider = gtk.HScale(adjustment = self.volumeSliderAdj)
+ self.volumeSlider.set_size_request(250, -1)
+ self.volumeSlider.set_inverted(False)
+ self.volumeSlider.set_draw_value(False)
+
+ self.categories = Config.CATEGORIES
+ self.categoryBox = BigComboBox()
+ for category in self.categories:
+ image = Config.IMAGE_ROOT + category.lower() + '.png'
+ if not os.path.isfile(image):
+ image = Config.IMAGE_ROOT + 'generic.png'
+ self.categoryBox.append_item(category, category.capitalize(),
+ icon_name = image, size = instrumentPalette.ICON_SIZE)
+ self.categoryBox.connect('changed', self.handleCategoryChange)
+
+ self.icons = []
+
+ for i in self.instrumentDB.inst:
+ if not i.kit and not i.kitStage:
+ self.icons.append([i, gtk.gdk.pixbuf_new_from_file_at_size(
+ i.img, instrumentPalette.ICON_SIZE[0],
+ instrumentPalette.ICON_SIZE[1])])
+
+ self.instruments = []
+ self.instrumentBox1 = BigComboBox()
+ self.instrumentBox1.connect('changed', self.handleInstrumentChange)
+
+ self.volumeBox.pack_start(self.muteButtonLabel, padding = 5)
+ self.volumeBox.pack_start(self.muteButton, padding = 5)
+ #self.volumeBox.pack_start(self.soloButtonLabel, padding = 5)
+ #self.volumeBox.pack_start(self.soloButton, padding = 5)
+ self.volumeBox.pack_start(self.volumeSlider, padding = 5)
+ self.mainBox.pack_start(self.volumeBox, padding = 5)
+ self.instrumentMainBox.pack_start(self.categoryBox, padding = 5)
+ self.instrumentMainBox.pack_start(self.instrumentBox1, padding = 5)
+ self.mainBox.pack_start(self.instrumentMainBox, padding = 5)
+ self.mainBox.show_all()
+
+ self.set_content(self.mainBox)
+
+ def handleTrackVolume(self, widget):
+ if not self.skipVolAdj:
+ if self.lastClickedTrack != None:
+ self.edit.handleTrackVolume(widget = widget, track = self.lastClickedTrack)
+
+ def handlemuteButton(self, widget):
+ if not self.skipVolAdj:
+ if self.lastClickedTrack != None:
+ self.edit.handlemuteButton(widget, self.lastClickedTrack)
+
+ def handlesoloButton(self, widget, event = None):
+ pass
+
+ def handleInstrumentChange(self, widget):
+ if not self.skip and self.instrumentBox1.get_active() != -1:
+ instrument = widget.props.value
+ self.edit.donePickInstrument(instrument)
+ time.sleep(0.05)
+ self.edit.playInstrumentNote(instrument)
+ self.popdown(True)
+
+ def handleCategoryChange(self, widget):
+ category = widget.props.value.lower()
+
+ self.instrumentBox1.set_active(-1)
+ self.instrumentBox1.remove_all()
+ self.instruments = []
+
+ for i in self.icons:
+ if category == 'all' or i[0].category == category:
+ self.instrumentBox1.append_item(i[0].name, None, pixbuf = i[1])
+ self.instruments.append(i[0].name)
+
+ if not self.skip:
+ self.instrumentBox1.popup()
+
+ def setInstrument(self, instrument):
+ self.skip = True
+ self.categoryBox.set_active(self.categories.index(instrument.category))
+ self.instrumentBox1.set_active(self.instruments.index(instrument.name))
+ self.skip = False
+
+ def setBlock( self, widget = None, event = None, block = None ):
+ if self.is_up():
+ self.popdown(True)
+ else:
+ self.set_primary_text(_('Track %s Properties' % str(block+1)))
+ self.skipVolAdj = True
+ self.volumeSliderAdj.set_value(self.edit._data["track_volume"][block])
+ if self.edit.trackActive[block]:
+ self.muteButton.set_active(True)
+ else:
+ self.muteButton.set_active(False)
+ self.skipVolAdj = False
+ self.lastClickedTrack = block
+ self.popup( True )
+
+class drumPalette( Popup ):
+ ICON_SIZE = (70,70)
+ def __init__(self, label, edit, trackID):
+ Popup.__init__(self, label, edit)
+
+ self.instrumentDB = InstrumentDB.getRef()
+ self.trackID = trackID
+ self.edit = edit
+
+ self.skip = False
+
+ self.tooltips = gtk.Tooltips()
+
+ self.mainBox = gtk.VBox()
+ self.volumeBox = gtk.HBox()
+ self.instrumentMainBox = gtk.HBox()
+
+
+ self.muteButton = gtk.CheckButton()
+ self.muteButton.connect("toggled",self.edit.handlemuteButton, self.trackID)
+ self.muteButton.connect("button-press-event",self.edit.handlemuteButtonRightClick, self.trackID)
+ self.muteButton.set_active(True)
+ self.tooltips.set_tip(self.muteButton, _('Left click to mute, right click to solo'))
+
+ if self.trackID < 4:
+ exec "self.volumeSliderAdj = self.edit.GUI['2instrument%svolumeAdjustment']" % str(self.trackID+1)
+ else:
+ self.volumeSliderAdj = self.edit.GUI["2drumvolumeAdjustment"]
+ self.volumeSliderAdj.connect( "value-changed", self.edit.handleTrackVolume, self.trackID)
+ self.volumeSlider = gtk.HScale(adjustment = self.volumeSliderAdj)
+ self.volumeSlider.set_size_request(250, -1)
+ self.volumeSlider.set_inverted(False)
+ self.volumeSlider.set_draw_value(False)
+
+ self.drums = self.getDrums()
+
+ self.drumBox = BigComboBox()
+ self.loadDrumMenu(self.getDrums())
+ self.drumBox.connect('changed', self.handleInstrumentChange)
+
+ self.volumeBox.pack_start(self.muteButton, padding = 5)
+ self.volumeBox.pack_start(self.volumeSlider, padding = 5)
+ self.mainBox.pack_start(self.volumeBox, padding = 5)
+ self.instrumentMainBox.pack_start(self.drumBox, False, False, padding = 5)
+ self.mainBox.pack_start(self.instrumentMainBox, padding = 5)
+ self.mainBox.show_all()
+
+ self.set_content(self.mainBox)
+
+ def handleInstrumentChange(self, widget):
+ if not self.skip:
+ drum = widget.props.value
+ self.edit.donePickDrum(drum)
+ time.sleep(0.05)
+ self.edit.playInstrumentNote(drum)
+ self.popdown(True)
+
+
+ def setDrum(self, Drum):
+ self.skip = True
+ self.drumBox.set_active(self.drums.index(Drum))
+ self.skip = False
+
+ def loadDrumMenu(self, instruments):
+ self.drumBox.remove_all()
+ for instrument in instruments:
+ image = Config.IMAGE_ROOT + instrument + '.png'
+ if not os.path.isfile(image):
+ image = Config.IMAGE_ROOT + 'generic.png'
+ self.drumBox.append_item(instrument, text = None, icon_name = image, size = instrumentPalette.ICON_SIZE)
+
+ def getDrums(self):
+ return sorted([instrument for instrument in self.instrumentDB.instNamed.keys() if self.instrumentDB.instNamed[instrument].kit])
+
+ def setBlock( self, widget = None, event = None, block = None ):
+ if self.is_up():
+ self.popdown(True)
+ else:
+ self.popup( True )
diff --git a/Edit/NoteInterface.py b/Edit/NoteInterface.py
new file mode 100644
index 0000000..9dc4247
--- /dev/null
+++ b/Edit/NoteInterface.py
@@ -0,0 +1,345 @@
+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 NoteInterface:
+
+ def __init__( self, noteDB, owner, note ):
+ self.noteDB = noteDB
+ self.owner = owner
+ self.note = note
+
+ self.origin = self.owner.getTrackOrigin( note.track )
+ self.firstTransform = True
+ self.x = 0
+ self.y = 0
+ self.width = 1
+ self.height = Config.NOTE_HEIGHT
+ self.imgX = 0
+ self.imgY = 0
+ self.imgWidth = 1
+ self.imgHeight = self.height + Config.NOTE_IMAGE_PADDING_MUL2
+
+ 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.image, self.imageSelected, self.colormap, self.baseColors = self.owner.getDrawingPackage( note.track )
+
+ self.updateParameter( None, None )
+
+ 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.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True )
+
+ def updateParameter( self, parameter, value ):
+ self.end = self.note.cs.onset + self.note.cs.duration
+
+ if self.oldAmplitude != self.note.cs.amplitude:
+ r = self.baseColors[0][0] + int(self.baseColors[1][0]*self.note.cs.amplitude)
+ g = self.baseColors[0][1] + int(self.baseColors[1][1]*self.note.cs.amplitude)
+ b = self.baseColors[0][2] + int(self.baseColors[1][2]*self.note.cs.amplitude)
+ self.color = self.colormap.alloc_color( r, g, b, True, True ) # TODO potential memory leak?
+ self.oldAmplitude = self.note.cs.amplitude
+
+ 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 ):
+ if self.note.page in self.owner.getActivePages():
+ if not self.firstTransform:
+ oldX = self.imgX
+ oldY = self.imgY
+ oldEndX = self.imgX + self.imgWidth
+ dirty = True
+ else:
+ dirty = False
+
+ beats = self.noteDB.getPage( self.note.page ).beats
+ if self.note.cs.onset != self.oldOnset or beats != self.oldBeats:
+ self.x = self.owner.ticksToPixels( beats, self.note.cs.onset )
+ self.x += self.origin[0]
+ self.imgX = self.x - Config.NOTE_IMAGE_PADDING
+ self.oldOnset = self.note.cs.onset
+ if 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.origin[0]
+ self.imgWidth = self.width + Config.NOTE_IMAGE_PADDING_MUL2
+ self.oldEnd = self.end
+ if self.note.cs.pitch != self.oldPitch:
+ self.y = self.owner.pitchToPixels( self.note.cs.pitch ) + self.origin[1]
+ self.imgY = self.y - Config.NOTE_IMAGE_PADDING
+ self.oldPitch = self.note.cs.pitch
+ self.oldBeats = beats
+
+ if dirty:
+ if self.firstTransform:
+ self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True )
+ else:
+ x = min( self.imgX, oldX )
+ y = min( self.imgY, oldY )
+ endx = max( self.imgX + self.imgWidth, oldEndX )
+ endy = max( self.imgY, oldY ) + self.imgHeight
+ self.owner.invalidate_rect( 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
+ self.note.cs.onset = 0
+ csnd.play( self.note.cs, 0.024)
+ self.note.cs.onset = onset
+ else:
+ (onset,duration) = ( self.note.cs.onset, self.note.cs.duration)
+ ( self.note.cs.onset, self.note.cs.duration) = (0, 10)
+ csnd.play( self.note.cs, 0.024)
+ ( self.note.cs.onset, self.note.cs.duration) = (onset,duration)
+
+ #=======================================================
+ # 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.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, 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.imgX: return False # we don't need to draw and no one after us will draw
+ if startX > self.imgX + self.imgWidth: return True # we don't need to draw, but maybe a later note does
+
+ gc.foreground = self.color
+ win.draw_rectangle( gc, True, self.x+1, self.y+1, self.width-2, self.height-2 )
+
+ if self.selected: img = self.imageSelected
+ else: img = self.image
+ win.draw_pixbuf( gc, img, 0, 0, self.imgX, self.imgY, self.imgWidth-Config.NOTE_IMAGE_ENDLENGTH, self.imgHeight, gtk.gdk.RGB_DITHER_NONE )
+ win.draw_pixbuf( gc, img, Config.NOTE_IMAGE_TAIL, 0, self.imgX+self.imgWidth-Config.NOTE_IMAGE_ENDLENGTH, self.imgY, Config.NOTE_IMAGE_ENDLENGTH, self.imgHeight, gtk.gdk.RGB_DITHER_NONE )
+
+ return True # we drew something
+
diff --git a/Edit/Properties.py b/Edit/Properties.py
new file mode 100644
index 0000000..2dc6d84
--- /dev/null
+++ b/Edit/Properties.py
@@ -0,0 +1,808 @@
+import pygtk
+pygtk.require('2.0')
+import gtk
+from types import *
+from math import sqrt
+from random import *
+from common.Generation.Drunk import *
+from common.Generation.GenerationConstants import GenerationConstants
+from common.Util.ThemeWidgets import *
+from common.Util.NoteDB import PARAMETER
+import common.Config as Config
+Tooltips = Config.Tooltips()
+
+class Properties( gtk.VBox ):
+ def __init__( self, noteDB, doneHandler, popup ):
+ gtk.VBox.__init__( self )
+ self.tooltips = gtk.Tooltips()
+ self.noteDB = noteDB
+ #self.doneHandler = doneHandler
+ self.popup = popup
+ self.popup.resize( 545, 378 )
+
+ self.context = "page"
+ self.notes = {} # notes indexed by page and track
+ self.setup = False # flag to block note updates durning setup
+
+ self.line = Line(0, 100)
+ self.drunk = Drunk(0, 100)
+ self.droneAndJump = DroneAndJump(0, 100)
+ self.repeter = Repeter(0, 100)
+ self.loopseg = Loopseg(0, 100)
+ self.algoTypes = [self.line, self.drunk, self.droneAndJump, self.repeter, self.loopseg]
+ self.algorithm = self.algoTypes[0]
+
+ #self.set_size_request( 300, 200 )
+
+ self.filterType = 0
+ self.minValue = 0.
+ self.maxValue = 100.
+ self.paraValue = 20.
+
+ self.activeWidget = None
+
+ self.pageIds = []
+
+ self.GUI = {}
+ self.parametersBox = RoundHBox(fillcolor=Config.INST_BCK_COLOR, bordercolor=Config.PANEL_BCK_COLOR)
+ #self.parametersBox.set_border_width(1)
+ self.parametersBox.set_radius(10)
+ self.pack_start(self.parametersBox)
+ self.fixed = gtk.Fixed()
+ self.parametersBox.pack_start( self.fixed )
+
+ self.controlsBox = gtk.HBox()
+
+ #-- Page Properties ------------------------------------------------
+ self.pageBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ self.pageBox.set_size_request( 125, -1 )
+ self.pageBox.set_border_width(3)
+ self.pageBox.set_radius(10)
+ beatBox = gtk.VBox()
+ self.beatAdjust = gtk.Adjustment( 4, 2, 12, 1, 1, 0)
+ self.GUI['beatSlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.beatAdjust, 7 )
+ self.GUI['beatSlider'].connect("button-release-event", self.handleBeat)
+ self.GUI['beatSlider'].set_snap( 1 )
+ self.GUI['beatSlider'].set_inverted(True)
+ self.GUI['beatSlider'].set_size_request(50, 200)
+ beatBox.pack_start( self.GUI['beatSlider'] )
+ self.beatLabel = gtk.Image()
+ self.beatLabel.set_from_file(Config.IMAGE_ROOT + 'volume3.png')
+ self.beatAdjust.connect("value-changed", self.updateBeatLabel)
+ self.updateBeatLabel( self.beatAdjust )
+ beatBox.pack_start( self.beatLabel )
+ self.pageBox.pack_start( beatBox )
+ colorBox = gtk.VBox()
+ self.GUI["color0Button"] = ImageRadioButton( None, Config.IMAGE_ROOT+"pageThumbnailBut0.png", Config.IMAGE_ROOT+"pageThumbnailBut0Down.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI["color0Button"].set_size_request( 80, -1 )
+ self.GUI["color0Button"].connect( "clicked", self.handleColor, 0 )
+ colorBox.pack_start( self.GUI["color0Button"] )
+ self.GUI["color1Button"] = ImageRadioButton( self.GUI["color0Button"], Config.IMAGE_ROOT+"pageThumbnailBut1.png", Config.IMAGE_ROOT+"pageThumbnailBut1Down.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI["color1Button"].connect( "clicked", self.handleColor, 1 )
+ colorBox.pack_start( self.GUI["color1Button"] )
+ self.GUI["color2Button"] = ImageRadioButton( self.GUI["color0Button"], Config.IMAGE_ROOT+"pageThumbnailBut2.png", Config.IMAGE_ROOT+"pageThumbnailBut2Down.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI["color2Button"].connect( "clicked", self.handleColor, 2 )
+ colorBox.pack_start( self.GUI["color2Button"] )
+ self.GUI["color3Button"] = ImageRadioButton( self.GUI["color0Button"], Config.IMAGE_ROOT+"pageThumbnailBut3.png", Config.IMAGE_ROOT+"pageThumbnailBut3Down.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI["color3Button"].connect( "clicked", self.handleColor, 3 )
+ colorBox.pack_start( self.GUI["color3Button"] )
+ self.pageBox.pack_start( colorBox )
+ self.pageBox.show_all()
+ #self.controlsBox.pack_start(self.pageBox)
+
+ #-- Note Properties ------------------------------------------------
+ pitchBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ pitchBox.set_border_width(3)
+ pitchBox.set_radius(10)
+ self.GUI['pitchUp'] = ImageButton( Config.IMAGE_ROOT+"arrowEditUp.png", Config.IMAGE_ROOT+"arrowEditUpDown.png", Config.IMAGE_ROOT+"arrowEditUpOver.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['pitchUp'].connect( "clicked", lambda w:self.stepPitch( 1 ) )
+ self.GUI['pitchGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['pitchGen'].connect( "clicked", self.openAlgoBox, 'pitch' )
+ pitchBox.pack_start( self.GUI['pitchGen'], False, False, 5 )
+ pitchBox.pack_start( self.GUI['pitchUp'] )
+ self.pitchIcon = gtk.Image()
+ self.pitchIcon.set_from_file(Config.IMAGE_ROOT + 'propPitch2.png')
+ pitchBox.pack_start(self.pitchIcon)
+ self.GUI['pitchDown'] = ImageButton( Config.IMAGE_ROOT+"arrowEditDown.png", Config.IMAGE_ROOT+"arrowEditDownDown.png", Config.IMAGE_ROOT+"arrowEditDownOver.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['pitchDown'].connect( "clicked", lambda w:self.stepPitch( -1 ) )
+ pitchBox.pack_start( self.GUI['pitchDown'] )
+ self.controlsBox.pack_start(pitchBox)
+
+ volumeBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ volumeBox.set_border_width(3)
+ volumeBox.set_radius(10)
+ self.GUI['volumeUp'] = ImageButton( Config.IMAGE_ROOT+"arrowEditUp.png", Config.IMAGE_ROOT+"arrowEditUpDown.png", Config.IMAGE_ROOT+"arrowEditUpOver.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['volumeUp'].connect( "clicked", lambda w:self.stepVolume( 0.1 ) )
+ self.GUI['volumeGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['volumeGen'].connect( "clicked", self.openAlgoBox, 'volume' )
+ volumeBox.pack_start( self.GUI['volumeGen'], False, False, 5 )
+ volumeBox.pack_start( self.GUI['volumeUp'] )
+ self.volumeIcon = gtk.Image()
+ self.volumeIcon.set_from_file(Config.IMAGE_ROOT + 'volume3.png')
+ volumeBox.pack_start(self.volumeIcon)
+ self.GUI['volumeDown'] = ImageButton( Config.IMAGE_ROOT+"arrowEditDown.png", Config.IMAGE_ROOT+"arrowEditDownDown.png", Config.IMAGE_ROOT+"arrowEditDownOver.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['volumeDown'].connect( "clicked", lambda w:self.stepVolume( -0.1 ) )
+ volumeBox.pack_start( self.GUI['volumeDown'] )
+ self.controlsBox.pack_start(volumeBox)
+
+ panBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ panBox.set_border_width(3)
+ panBox.set_radius(10)
+ self.panAdjust = gtk.Adjustment( 0.5, 0, 1, .1, .1, 0)
+ self.GUI['panSlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.panAdjust, 7 )
+ self.panAdjust.connect("value-changed", self.handlePan)
+ self.GUI['panSlider'].set_snap( 0.1 )
+ self.GUI['panSlider'].set_inverted(True)
+ self.GUI['panSlider'].set_size_request(50, 200)
+ self.panLabel = gtk.Image()
+ self.handlePan( self.panAdjust )
+ self.GUI['panGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['panGen'].connect( "clicked", self.openAlgoBox, 'pan' )
+ panBox.pack_start(self.GUI['panGen'], True, True, 5)
+ panBox.pack_start(self.GUI['panSlider'], True, True, 5)
+ panBox.pack_start(self.panLabel, False, padding=10)
+ self.controlsBox.pack_start(panBox)
+
+ reverbBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ reverbBox.set_border_width(3)
+ reverbBox.set_radius(10)
+ self.reverbAdjust = gtk.Adjustment(0.1, 0, 1, 0.1, 0.1, 0)
+ self.GUI['reverbSlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.reverbAdjust, 7 )
+ self.reverbAdjust.connect("value-changed", self.handleReverb)
+ self.GUI['reverbSlider'].set_snap( 0.1 )
+ self.GUI['reverbSlider'].set_inverted(True)
+ self.GUI['reverbSlider'].set_size_request(50, 200)
+ self.reverbLabel = gtk.Image()
+ self.handleReverb( self.reverbAdjust )
+ self.GUI['reverbGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['reverbGen'].connect( "clicked", self.openAlgoBox, 'reverb' )
+ reverbBox.pack_start(self.GUI['reverbGen'], True, True, 5)
+ reverbBox.pack_start(self.GUI['reverbSlider'], True, True, 5)
+ reverbBox.pack_start(self.reverbLabel, False, padding=10)
+ self.controlsBox.pack_start(reverbBox)
+
+ attackBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ attackBox.set_border_width(3)
+ attackBox.set_radius(10)
+ self.attackAdjust = gtk.Adjustment(0.04, 0.03, 1, .01, .01, 0)
+ self.GUI['attackSlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.attackAdjust, 7 )
+ self.attackAdjust.connect("value-changed", self.handleAttack)
+ self.GUI['attackSlider'].set_snap( 0.01 )
+ self.GUI['attackSlider'].set_inverted(True)
+ self.GUI['attackSlider'].set_size_request(50, 200)
+ self.attackLabel = gtk.Image()
+ self.handleAttack( self.attackAdjust )
+ self.GUI['attackGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['attackGen'].connect( "clicked", self.openAlgoBox, 'attack' )
+ attackBox.pack_start(self.GUI['attackGen'], True, True, 5)
+ attackBox.pack_start(self.GUI['attackSlider'], True, True, 5)
+ attackBox.pack_start(self.attackLabel, False, padding=10)
+ self.controlsBox.pack_start(attackBox)
+
+ decayBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ decayBox.set_border_width(3)
+ decayBox.set_radius(10)
+ self.decayAdjust = gtk.Adjustment(0.31, 0.03, 1, .01, .01, 0)
+ self.GUI['decaySlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.decayAdjust, 7 )
+ self.decayAdjust.connect("value-changed", self.handleDecay)
+ self.GUI['decaySlider'].set_snap( 0.01 )
+ self.GUI['decaySlider'].set_inverted(True)
+ self.GUI['decaySlider'].set_size_request(50, 200)
+ self.decayLabel = gtk.Image()
+ self.handleDecay( self.decayAdjust )
+ self.GUI['decayGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['decayGen'].connect( "clicked", self.openAlgoBox, 'decay' )
+ decayBox.pack_start(self.GUI['decayGen'], True, True, 5)
+ decayBox.pack_start(self.GUI['decaySlider'], True, True, 5)
+ decayBox.pack_start(self.decayLabel, False, padding=10)
+ self.controlsBox.pack_start(decayBox)
+
+ filterBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ filterBox.set_border_width(3)
+ filterBox.set_radius(10)
+
+ filterTypeBox = gtk.VBox()
+ self.GUI['filterTypeLowButton'] = ImageToggleButton(Config.IMAGE_ROOT + 'propLow3.png', Config.IMAGE_ROOT + 'propLow3Sel.png', Config.IMAGE_ROOT + 'propLow3Over.png')
+ self.GUI['filterTypeLowButton'].connect( "toggled", self.handleFilterType, 1 )
+ filterTypeBox.pack_start( self.GUI['filterTypeLowButton'] )
+ self.GUI['filterTypeHighButton'] = ImageToggleButton(Config.IMAGE_ROOT + 'propHi3.png', Config.IMAGE_ROOT + 'propHi3Sel.png', Config.IMAGE_ROOT + 'propHi3Over.png')
+ self.GUI['filterTypeHighButton'].connect( "toggled", self.handleFilterType, 2 )
+ filterTypeBox.pack_start( self.GUI['filterTypeHighButton'] )
+ self.GUI['filterTypeBandButton'] = gtk.ToggleButton( "B" )
+ self.GUI['filterTypeBandButton'] = ImageToggleButton(Config.IMAGE_ROOT + 'propBand3.png', Config.IMAGE_ROOT + 'propBand3Sel.png', Config.IMAGE_ROOT + 'propBand3Over.png')
+ self.GUI['filterTypeBandButton'].connect( "toggled", self.handleFilterType, 3 )
+ filterTypeBox.pack_start( self.GUI['filterTypeBandButton'] )
+ filterBox.pack_start( filterTypeBox )
+
+ self.filterSliderBox = gtk.VBox()
+ self.filterSliderBox.set_size_request(50, -1)
+ self.cutoffAdjust = gtk.Adjustment(1000, 100, 7000, 100, 100, 0)
+ self.GUI['cutoffSlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.cutoffAdjust, 7 )
+ self.GUI['cutoffSlider'].set_snap(100)
+ self.cutoffAdjust.connect("value-changed", self.handleFilter)
+ self.GUI['cutoffSlider'].set_inverted(True)
+ self.GUI['cutoffSlider'].set_size_request(50, 200)
+ self.GUI['cutoffGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['cutoffGen'].connect( "clicked", self.openAlgoBox, 'cutoff' )
+ self.filterSliderBox.pack_start(self.GUI['cutoffGen'], True, True, 5)
+ self.filterSliderBox.pack_start(self.GUI['cutoffSlider'], True, True, 5)
+ self.filterLabel = gtk.Image()
+ self.filterLabel.set_from_file(Config.IMAGE_ROOT + 'propFilter1.png')
+ self.filterSliderBox.pack_start(self.filterLabel, False, padding=10)
+
+ filterBox.pack_start(self.filterSliderBox)
+
+ self.controlsBox.pack_start(filterBox)
+
+ self.algoBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ self.algoBox.set_size_request( -1, 378 )
+ self.algoBox.set_border_width(3)
+ self.algoBox.set_radius(10)
+ #self.algoBox = gtk.VBox()
+
+ algoUpperBox = gtk.HBox()
+
+ algoRadioButtonBox = gtk.VBox()
+ algoRadioButtonBox.set_size_request(100, 150)
+ #algoRadioButtonBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ #algoRadioButtonBox.set_border_width(3)
+ #algoRadioButtonBox.set_radius(10)
+
+ self.GUI['line'] = ImageRadioButton( None, Config.IMAGE_ROOT + 'propLine.png', Config.IMAGE_ROOT + 'propLineDown.png', Config.IMAGE_ROOT + 'propLineOver.png' )
+ self.GUI['line'].connect( "toggled", self.handleAlgo, 0 )
+ algoRadioButtonBox.pack_start( self.GUI['line'], False, False, 1 )
+ self.GUI['drunk'] = ImageRadioButton( self.GUI['line'], Config.IMAGE_ROOT + 'propDrunk.png', Config.IMAGE_ROOT + 'propDrunkDown.png', Config.IMAGE_ROOT + 'propDrunkOver.png' )
+ self.GUI['drunk'].connect( "toggled", self.handleAlgo, 1 )
+ algoRadioButtonBox.pack_start( self.GUI['drunk'], False, False, 1 )
+ self.GUI['droneJump'] = ImageRadioButton( self.GUI['line'], Config.IMAGE_ROOT + 'propDroneJump.png', Config.IMAGE_ROOT + 'propDroneJumpDown.png', Config.IMAGE_ROOT + 'propDroneJumpOver.png' )
+ self.GUI['droneJump'].connect( "toggled", self.handleAlgo, 2 )
+ algoRadioButtonBox.pack_start( self.GUI['droneJump'], False, False, 1 )
+ self.GUI['repeater'] = ImageRadioButton( self.GUI['line'], Config.IMAGE_ROOT + 'propRepeater.png', Config.IMAGE_ROOT + 'propRepeaterDown.png', Config.IMAGE_ROOT + 'propRepeaterOver.png' )
+ self.GUI['repeater'].connect( "toggled", self.handleAlgo, 3 )
+ algoRadioButtonBox.pack_start( self.GUI['repeater'], False, False, 1 )
+ self.GUI['loopseg'] = ImageRadioButton( self.GUI['line'], Config.IMAGE_ROOT + 'propLoopseg.png', Config.IMAGE_ROOT + 'propLoopsegDown.png', Config.IMAGE_ROOT + 'propLoopsegOver.png' )
+ self.GUI['loopseg'].connect( "toggled", self.handleAlgo, 4 )
+ algoRadioButtonBox.pack_start( self.GUI['loopseg'], False, False, 1 )
+
+ algoUpperBox.pack_start(algoRadioButtonBox)
+
+ algoSlidersBox = gtk.HBox()
+ algoSlidersBox.set_size_request(150, 320)
+ #algoSlidersBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ #algoSlidersBox.set_border_width(3)
+ #algoSlidersBox.set_radius(10)
+ minBox = gtk.VBox()
+ self.minAdjust = gtk.Adjustment(0, 0, 100, 1, 1, 0)
+ self.GUI['minSlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.minAdjust, 7 )
+ self.GUI['minSlider'].set_snap(1)
+ self.minAdjust.connect("value-changed", self.handleMin)
+ self.GUI['minSlider'].set_inverted(True)
+ self.GUI['minSlider'].set_size_request(50, 200)
+ minBox.pack_start(self.GUI['minSlider'], True, True, 5)
+ algoSlidersBox.pack_start(minBox)
+
+ maxBox = gtk.VBox()
+ self.maxAdjust = gtk.Adjustment(100, 0, 100, 1, 1, 0)
+ self.GUI['maxSlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.maxAdjust, 7 )
+ self.GUI['maxSlider'].set_snap(1)
+ self.maxAdjust.connect("value-changed", self.handleMax)
+ self.GUI['maxSlider'].set_inverted(True)
+ self.GUI['maxSlider'].set_size_request(50, 200)
+ maxBox.pack_start(self.GUI['maxSlider'], True, True, 5)
+ algoSlidersBox.pack_start(maxBox)
+
+ paraBox = gtk.VBox()
+ self.paraAdjust = gtk.Adjustment(20, 0, 100, 1, 1, 0)
+ self.GUI['paraSlider'] = ImageVScale( Config.IMAGE_ROOT + "/sliderEditVolume.png", self.paraAdjust, 7 )
+ self.GUI['paraSlider'].set_snap(1)
+ self.paraAdjust.connect("value-changed", self.handlePara)
+ self.GUI['paraSlider'].set_inverted(True)
+ self.GUI['paraSlider'].set_size_request(50, 200)
+ paraBox.pack_start(self.GUI['paraSlider'], True, True, 5)
+ algoSlidersBox.pack_start(paraBox)
+
+ algoUpperBox.pack_start(algoSlidersBox)
+
+ self.algoBox.pack_start(algoUpperBox)
+
+ #transButtonBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ #transButtonBox.set_border_width(3)
+ #transButtonBox.set_radius(10)
+ transButtonBox = gtk.HBox()
+ transButtonBox.set_size_request(150, 50)
+
+ # create cancel/check button
+ self.GUI["checkButton"] = ImageButton(Config.IMAGE_ROOT + 'check.png', backgroundFill=Config.PANEL_COLOR )
+ self.GUI["checkButton"].connect("clicked", self.apply)
+
+ self.GUI["cancelButton"] = ImageButton(Config.IMAGE_ROOT + 'closeA.png', backgroundFill=Config.PANEL_COLOR )
+ self.GUI["cancelButton"].connect("clicked", self.cancel)
+
+ transButtonBox.pack_end(self.GUI["checkButton"], False, False, 10)
+ transButtonBox.pack_end(self.GUI["cancelButton"], False, False)
+ self.algoBox.pack_start(transButtonBox)
+
+ self.fixed.put( self.controlsBox, 0, 0 )
+ self.algoBox.show_all()
+
+ # set tooltips
+ for key in self.GUI:
+ if Tooltips.PROP.has_key(key):
+ self.tooltips.set_tip(self.GUI[key],Tooltips.PROP[key])
+ self.tooltips.set_tip(self.GUI['paraSlider'], 'Random')
+
+
+ self.show_all()
+
+ def openAlgoBox( self, widget, data=None ):
+ if widget.get_active() == True:
+ self.property = data
+ if self.activeWidget:
+ self.activeWidget.set_active(False)
+ self.activeWidget = widget
+ if self.context == "page":
+ if self.algoBox.parent == None: self.fixed.put( self.algoBox, 671, 0 )
+ else: self.fixed.move( self.algoBox, 671, 0 )
+ self.popup.resize( 927, 378 )
+ else:
+ self.popup.resize( 801, 378 )
+ if self.algoBox.parent == None: self.fixed.put( self.algoBox, 545, 0 )
+ else: self.fixed.move( self.algoBox, 545, 0 )
+ else:
+ self.property = None
+ self.activeWidget = None
+ if self.algoBox.parent != None:
+ if self.context == "page": self.popup.resize( 671, 378 )
+ else: self.popup.resize( 545, 378 )
+ self.fixed.remove( self.algoBox )
+
+ def setContext( self, context, scale, pageIds = None, trackIds = None, notes = {} ):
+ self.context = context
+ self.scale = GenerationConstants.SCALES[scale]
+ self.notes = {}
+ self.pageIds = pageIds
+ self.trackIds = trackIds
+
+ try:
+ self.activeWidget.set_active(False)
+ self.activeWidget = None
+ except:
+ self.activeWidget = None
+
+ if context == "page":
+ if self.pageBox.parent == None:
+ self.controlsBox.pack_start( self.pageBox )
+ self.controlsBox.reorder_child( self.pageBox, 0 )
+ self.controlsBox.set_size_request( 671, 378 )
+ self.popup.resize( 671, 378 )
+ self.trackIds = [0,1,2,3,4]
+ for p in pageIds:
+ self.notes[p] = {}
+ for t in range(Config.NUMBER_OF_TRACKS):
+ self.notes[p][t] = self.noteDB.getNotesByTrack( p, t )
+ page = self.noteDB.getPage(pageIds[0])
+ self.beatAdjust.set_value(page.beats)
+ btn = "color%dButton" % page.color
+ self.GUI[btn].set_active(True)
+ elif context == "track":
+ if self.pageBox.parent != None:
+ self.controlsBox.remove( self.pageBox )
+ self.controlsBox.set_size_request( 545, 378 )
+ self.popup.resize( 545, 378 )
+ for p in pageIds:
+ self.notes[p] = {}
+ for t in trackIds:
+ self.notes[p][t] = self.noteDB.getNotesByTrack( p, t )
+ else:
+ if self.pageBox.parent != None:
+ self.controlsBox.remove( self.pageBox )
+ self.controlsBox.set_size_request( 545, 378 )
+ self.popup.resize( 545, 378 )
+ self.notes = notes
+ self.pageIds = self.notes.keys()
+ self.trackIds = self.notes[self.pageIds[0]].keys()
+
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ # initialize values from first note
+ self.setup = True
+ n = self.notes[p][t][0]
+ self.panAdjust.set_value( n.cs.pan )
+ self.reverbAdjust.set_value( n.cs.reverbSend )
+ self.attackAdjust.set_value( n.cs.attack )
+ self.decayAdjust.set_value( n.cs.decay )
+ if n.cs.filterType == 0:
+ self.GUI['filterTypeLowButton'].set_active(False)
+ self.GUI['filterTypeHighButton'].set_active(False)
+ self.GUI['filterTypeBandButton'].set_active(False)
+ self.filterLabel.hide()
+ self.GUI['cutoffSlider'].hide()
+ self.GUI['cutoffGen'].hide()
+ else:
+ if n.cs.filterType == 1:
+ self.GUI['filterTypeLowButton'].set_active(True)
+ if n.cs.filterType == 2:
+ self.GUI['filterTypeHighButton'].set_active(True)
+ if n.cs.filterType == 3:
+ self.GUI['filterTypeBandButton'].set_active(True)
+ self.filterLabel.show()
+ self.GUI['cutoffSlider'].show()
+ self.GUI['cutoffGen'].show()
+ self.filterType = n.cs.filterType
+ self.cutoffAdjust.set_value( n.cs.filterCutoff )
+ self.setup = False
+ return
+
+ def handleColor( self, widget, index ):
+ stream = []
+ for page in self.pageIds:
+ stream += [ page, index ]
+ if len(stream):
+ self.noteDB.updatePages( [ PARAMETER.PAGE_COLOR, len(stream)//2 ] + stream )
+
+ def updateBeatLabel( self, adjust ):
+ beats = int(adjust.value)
+ self.beatLabel.set_from_file(Config.IMAGE_ROOT + 'propBeats' + str(beats) + '.png')
+
+ def handleBeat( self, widget, signal_id ):
+ beats = int(widget.get_adjustment().value)
+ stream = []
+ for page in self.pageIds:
+ stream += [ page, beats ]
+ if len(stream):
+ self.noteDB.updatePages( [ PARAMETER.PAGE_BEATS, len(stream)//2 ] + stream )
+
+
+ def stepPitch( self, step ):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ substream = []
+ if step > 0:
+ if t != Config.NUMBER_OF_TRACKS-1: # regular note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MAXIMUM_PITCH:
+ substream += [ n.id, min( Config.MAXIMUM_PITCH, n.cs.pitch + step ) ]
+ else: # drum note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MAXIMUM_PITCH_DRUM:
+ substream += [ n.id, min( Config.MAXIMUM_PITCH_DRUM, n.cs.pitch + step*Config.PITCH_STEP_DRUM ) ]
+ else:
+ if t != Config.NUMBER_OF_TRACKS-1: # regular note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MINIMUM_PITCH:
+ substream += [ n.id, max( Config.MINIMUM_PITCH, n.cs.pitch + step ) ]
+ else: # drum note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MINIMUM_PITCH_DRUM:
+ substream += [ n.id, max( Config.MINIMUM_PITCH_DRUM, n.cs.pitch + step*Config.PITCH_STEP_DRUM ) ]
+ if len(substream):
+ stream += [ p, t, PARAMETER.PITCH, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoPitch( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ scaleLength = len(self.scale)-1
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ if self.trackIds[t] != Config.NUMBER_OF_TRACKS-1:
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, self.scale[int(val*0.01*scaleLength)]+36 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PITCH, len(substream)//2 ] + substream
+ else:
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ val = int((val*0.12)*2+24)
+ if val in GenerationConstants.DRUMPITCH.keys():
+ val = GenerationConstants.DRUMPITCH[val]
+ substream += [ n.id, val ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PITCH, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def stepVolume( self, step ):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ substream = []
+ if step > 0:
+ for n in self.notes[p][t]:
+ if n.cs.amplitude != Config.MAXIMUM_AMPLITUDE:
+ substream += [ n.id, min( Config.MAXIMUM_AMPLITUDE, n.cs.amplitude + step ) ]
+ else:
+ for n in self.notes[p][t]:
+ if n.cs.amplitude != Config.MINIMUM_AMPLITUDE:
+ substream += [ n.id, max( Config.MINIMUM_AMPLITUDE, n.cs.amplitude + step ) ]
+ if len(substream):
+ stream += [ p, t, PARAMETER.AMPLITUDE, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoVolume( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, min( Config.MAXIMUM_AMPLITUDE, val*0.01 ) ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.AMPLITUDE, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handlePan( self, adjust ):
+ img = min( 4, int(adjust.value * 5) )
+ self.panLabel.set_from_file(Config.IMAGE_ROOT + 'propPan' + str(img) + '.png')
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.PAN, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoPan( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PAN, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handleReverb( self, adjust ):
+ img = min( 5, int(adjust.value * 6) )
+ self.reverbLabel.set_from_file(Config.IMAGE_ROOT + 'propReverb' + str(img) + '.png')
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.REVERB, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoReverb( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.02 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.REVERB, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handleAttack( self, adjust ):
+ val = adjust.value #*adjust.value
+ img = min( 4, int(val * 4) )
+ self.attackLabel.set_from_file(Config.IMAGE_ROOT + 'propAtt' + str(img) + '.png')
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.ATTACK, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoAttack( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.ATTACK, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handleDecay( self, adjust ):
+ val = adjust.value #*adjust.value
+ img = min( 4, int(val * 4) )
+ self.decayLabel.set_from_file(Config.IMAGE_ROOT + 'propDec' + str(img) + '.png')
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.DECAY, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoDecay( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.DECAY, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handleFilterType( self, widget, type ):
+
+ if widget.get_active():
+ if self.filterType == 0:
+ self.filterLabel.show()
+ self.GUI['cutoffSlider'].show()
+ self.GUI['cutoffGen'].show()
+
+ self.filterType = type
+ self.updateFilterLabel()
+
+ if widget != self.GUI['filterTypeLowButton'] and self.GUI['filterTypeLowButton'].get_active():
+ self.GUI['filterTypeLowButton'].set_active( False )
+ if widget != self.GUI['filterTypeBandButton'] and self.GUI['filterTypeBandButton'].get_active():
+ self.GUI['filterTypeBandButton'].set_active( False )
+ if widget != self.GUI['filterTypeHighButton'] and self.GUI['filterTypeHighButton'].get_active():
+ self.GUI['filterTypeHighButton'].set_active( False )
+ if not self.setup:
+ typestream = []
+ cutoffstream = []
+ cutoff = self.cutoffAdjust.value
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ substream = []
+ typestream += [ p, t, PARAMETER.FILTERTYPE, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ typestream += [ n.id, type ]
+ if n.cs.filterCutoff != cutoff:
+ substream += [ n.id, cutoff ]
+ if len(substream):
+ cutoffstream += [ p, t, PARAMETER.FILTERCUTOFF, len(substream)//2 ] + substream
+ if len(typestream):
+ self.noteDB.updateNotes( typestream + [-1] )
+ if len(cutoffstream):
+ self.noteDB.updateNotes( cutoffstream + [-1] )
+
+ elif type == self.filterType:
+ self.filterType = 0
+ self.filterLabel.hide()
+ self.GUI['cutoffSlider'].hide()
+ self.GUI['cutoffGen'].hide()
+ if not self.setup:
+ typestream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ typestream += [ p, t, PARAMETER.FILTERTYPE, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ typestream += [ n.id, 0 ]
+ if len(typestream):
+ self.noteDB.updateNotes( typestream + [-1] )
+
+ def handleFilter( self, adjust ):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.FILTERCUTOFF, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoCutoff( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*70+100 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.FILTERCUTOFF, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ self.updateFilterLabel()
+
+ def handleAlgo( self, widget, data ):
+ self.algorithm = self.algoTypes[data]
+ paraTooltips = ['Random', 'Maximum step', 'Maximum step', 'Maximum step', 'Maximum step']
+ self.tooltips.set_tip(self.GUI['paraSlider'], paraTooltips[data])
+
+ def handleMin( self, adjust ):
+ self.minValue = adjust.value
+
+ def handleMax( self, adjust ):
+ self.maxValue = adjust.value
+
+ def handlePara( self, adjust ):
+ self.paraValue = adjust.value
+
+ def apply( self, widget, data=None ):
+ valList = [self.minValue, self.maxValue, self.paraValue]
+ if self.property == 'pitch':
+ self.algoPitch(valList, self.algorithm)
+ elif self.property == 'volume':
+ self.algoVolume(valList, self.algorithm)
+ elif self.property == 'pan':
+ self.algoPan(valList, self.algorithm)
+ elif self.property == 'reverb':
+ self.algoReverb(valList, self.algorithm)
+ elif self.property == 'attack':
+ self.algoAttack(valList, self.algorithm)
+ elif self.property == 'decay':
+ self.algoDecay(valList, self.algorithm)
+ elif self.property == 'cutoff':
+ self.algoCutoff(valList, self.algorithm)
+ self.cancel(self.activeWidget)
+
+ def cancel( self, widget, data=None ):
+ self.activeWidget.set_active(False)
+
+ def updateFilterLabel( self ):
+ val = (self.cutoffAdjust.value-self.cutoffAdjust.lower)/(self.cutoffAdjust.upper-self.cutoffAdjust.lower)
+ img = min( 5, int(val * 6) )
+ self.filterLabel.set_from_file(Config.IMAGE_ROOT + 'propFilter%d.%d' % (self.filterType, img) + '.png')
+
+
+
+
+
+
diff --git a/Edit/TrackInterface.py b/Edit/TrackInterface.py
new file mode 100644
index 0000000..ad740ee
--- /dev/null
+++ b/Edit/TrackInterface.py
@@ -0,0 +1,1374 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import gobject
+
+from math import floor
+import time
+
+import common.Config as Config
+from Edit.NoteInterface import NoteInterface
+from Edit.HitInterface import HitInterface
+from Edit.MainWindow import CONTEXT
+
+from common.Util.NoteDB import PARAMETER
+from common.Util.CSoundNote import CSoundNote
+from common.Generation.GenerationConstants import GenerationConstants
+from common.Util.Profiler import TP
+
+class SELECTNOTES:
+ ALL = -1
+ NONE = 0
+ ADD = 1
+ REMOVE = 2
+ FLIP = 3
+ EXCLUSIVE = 4
+
+class INTERFACEMODE:
+ DEFAULT = 0
+ DRAW = 1
+ PASTE_NOTES = 2
+ PASTE_TRACKS = 3
+ PAINT = 4
+
+class TrackInterfaceParasite:
+ def __init__( self, noteDB, owner, note ):
+ if note.track == Config.NUMBER_OF_TRACKS-1: # drum track
+ self.parasite = HitInterface( noteDB, owner, note )
+ else:
+ self.parasite = NoteInterface( noteDB, owner, note )
+
+ def attach( self ):
+ return self.parasite
+
+class TrackInterface( gtk.EventBox ):
+
+ def __init__( self, noteDB, owner, getScaleFunction, width ):
+ gtk.EventBox.__init__( self )
+
+ self.noteDB = noteDB
+ self.owner = owner
+ self.getScale = getScaleFunction
+
+ self.drawingArea = gtk.DrawingArea()
+ self.drawingAreaDirty = False # are we waiting to draw?
+ self.add( self.drawingArea )
+ self.dirtyRectToAdd = gtk.gdk.Rectangle() # used by the invalidate_rect function
+
+ self.fullWidth = 1 # store the maximum allowed width
+ self.width = 1
+ self.height = 1
+
+ self.interfaceMode = INTERFACEMODE.DEFAULT
+
+ self.curPage = -1 # this isn't a real page at all!
+ self.curBeats = 4
+ self.painting = False
+ self.pointerGrid = 1
+ self.drawGrid = Config.DEFAULT_GRID
+ self.paintGrid = Config.DEFAULT_GRID
+ self.paintNoteDur = Config.DEFAULT_GRID
+
+ 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.pasteTick = -1
+ self.pasteTrack = -1
+ self.pasteRect = False
+
+ self.playheadT = 0
+ self.playheadX = Config.TRACK_SPACING_DIV2
+
+ 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.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK)
+
+ self.connect( "size-allocate", self.size_allocate )
+
+ self.drawingArea.connect( "expose-event", self.expose )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "button-release-event", self.handleButtonRelease )
+ self.connect( "motion-notify-event", self.handleMotion )
+
+ # prepare drawing stuff
+ hexToInt = { "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 }
+ self.trackColors = []
+ for i in Config.TRACK_COLORS:
+ low = ( 256*(hexToInt[i[0][1]]*16+hexToInt[i[0][2]]), 256*(hexToInt[i[0][3]]*16+hexToInt[i[0][4]]), 256*(hexToInt[i[0][5]]*16+hexToInt[i[0][6]]) )
+ high = ( 256*(hexToInt[i[1][1]]*16+hexToInt[i[1][2]]), 256*(hexToInt[i[1][3]]*16+hexToInt[i[1][4]]), 256*(hexToInt[i[1][5]]*16+hexToInt[i[1][6]]) )
+ delta = ( high[0]-low[0], high[1]-low[1], high[2]-low[2] )
+ self.trackColors.append( (low, delta) )
+
+ colormap = self.drawingArea.get_colormap()
+ self.beatColor = colormap.alloc_color( Config.BEAT_COLOR, True, True )
+ self.playheadColor = colormap.alloc_color( Config.PLAYHEAD_COLOR, True, True )
+ self.marqueeColor = colormap.alloc_color( Config.MARQUEE_COLOR, True, True )
+
+ self.image = {}
+ win = gtk.gdk.get_default_root_window()
+ self.gc = gtk.gdk.GC( win )
+
+ def prepareDrawable( name, width = -1 ):
+ pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+name+".png" )
+ if width != -1:
+ pix = pix.scale_simple(width, pix.get_height(), gtk.gdk.INTERP_BILINEAR)
+ self.image[name] = gtk.gdk.Pixmap( win, pix.get_width(), pix.get_height() )
+ self.image[name].draw_pixbuf( self.gc, pix, 0, 0, 0, 0, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
+ def preparePixbuf( name ):
+ self.image[name] = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+name+".png" )
+
+ prepareDrawable( "trackBG", width )
+ prepareDrawable( "trackBGSelected", width )
+ prepareDrawable( "trackBGDrum", width )
+ prepareDrawable( "trackBGDrumSelected", width )
+ preparePixbuf( "note" )
+ preparePixbuf( "noteSelected" )
+ preparePixbuf( "hit" )
+ preparePixbuf( "hitSelected" )
+
+ # define dimensions
+ self.width = self.trackFullWidth = width
+ self.trackWidth = self.width - Config.TRACK_SPACING
+ self.trackFullHeight = self.image["trackBG"].get_size()[1]
+ self.trackHeight = self.trackFullHeight - Config.TRACK_SPACING
+ self.trackFullHeightDrum = self.image["trackBGDrum"].get_size()[1]
+ self.trackHeightDrum = self.trackFullHeightDrum - Config.TRACK_SPACING
+ self.height = self.trackHeight*(Config.NUMBER_OF_TRACKS-1) + self.trackHeightDrum + Config.TRACK_SPACING*Config.NUMBER_OF_TRACKS
+ self.trackLimits = []
+ self.trackRect = []
+ self.drumIndex = Config.NUMBER_OF_TRACKS-1
+ for i in range(self.drumIndex):
+ start = i*(self.trackFullHeight)
+ self.trackLimits.append( (start,start+self.trackFullHeight) )
+ self.trackRect.append( gtk.gdk.Rectangle(Config.TRACK_SPACING_DIV2,start+Config.TRACK_SPACING_DIV2, self.trackWidth, self.trackHeight ) )
+ self.trackLimits.append( ( self.height - self.trackFullHeightDrum, self.height ) )
+ self.trackRect.append( gtk.gdk.Rectangle( Config.TRACK_SPACING_DIV2, self.height - self.trackFullHeightDrum + Config.TRACK_SPACING_DIV2, self.trackWidth, self.trackHeightDrum ) )
+
+ self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.trackHeight - Config.NOTE_HEIGHT)
+ self.pixelsPerPitch = float(self.trackHeight-Config.NOTE_HEIGHT)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ self.pitchPerPixelDrum = float(Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM / (self.trackHeightDrum - Config.HIT_HEIGHT)
+ self.pixelsPerPitchDrum = float(self.trackHeightDrum-Config.HIT_HEIGHT)/(Config.MAXIMUM_PITCH_DRUM - Config.MINIMUM_PITCH_DRUM )
+
+ self.pixelsPerTick = [0] + [ self.trackWidth/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) ] )
+
+ # screen buffers
+ self.screenBuf = [ gtk.gdk.Pixmap( win, self.width, self.height ), \
+ gtk.gdk.Pixmap( win, self.width, self.height ) ]
+ self.screenBufPage = [ -1, -1 ]
+ self.screenBufBeats = [ -1, -1 ]
+ self.screenBufDirtyRect = [ gtk.gdk.Rectangle(), gtk.gdk.Rectangle() ]
+ self.screenBufDirty = [ False, False ]
+ self.screenBufResume = [ [0,0], [0,0] ] # allows for stopping and restarting in the middle of a draw
+ self.curScreen = 0
+ self.preScreen = 1
+ self.predrawTimeout = False
+
+ #-- private --------------------------------------------
+
+ def _updateClipboardArea( self ):
+ self.clipboardArea = self.owner.getClipboardArea( self.curPage )
+ self.clipboardTrackTop = 0
+ for t in range(self.drumIndex):
+ if self.clipboardArea["tracks"][t]: break
+ self.clipboardTrackTop += 1
+ self.clipboardDrumTrack = self.clipboardArea["tracks"][self.drumIndex]
+
+ #=======================================================
+ # NoteDB notifications
+
+ def notifyPageAdd( self, id, at ):
+ return
+
+ def notifyPageDelete( self, which, safe ):
+ if self.screenBufPage[self.preScreen] in which:
+ self.screenBufPage[self.preScreen] = -1
+
+ def notifyPageDuplicate( self, new, at ):
+ return
+
+ def notifyPageMove( self, which, low, high ):
+ return
+
+ def notifyPageUpdate( self, page, parameter, value ):
+ if parameter == PARAMETER.PAGE_BEATS:
+ notes = self.noteDB.getNotesByPage( page, self )
+ for note in notes:
+ note.updateTransform()
+
+ if page == self.screenBufPage[self.curScreen]:
+ self.screenBufBeats[self.curScreen] = value
+ self.curBeats = value
+ if self.playheadT >= value*Config.TICKS_PER_BEAT:
+ self.playheadT = value*Config.TICKS_PER_BEAT - 1
+ self.playheadX = self.ticksToPixels( self.curBeats, self.playheadT ) + Config.TRACK_SPACING_DIV2
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ if page == self.screenBufPage[self.preScreen]:
+ self.screenBufBeats[self.preScreen] = value
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ self.predrawPage()
+
+
+ #=======================================================
+ # Module Interface
+
+ def getDrawingPackage( self, track ):
+ if track == self.drumIndex:
+ return ( self.image["hit"], self.image["hitSelected"], self.drawingArea.get_colormap(), self.trackColors[track] )
+ else:
+ return ( self.image["note"], self.image["noteSelected"], self.drawingArea.get_colormap(), self.trackColors[track] )
+
+ def getActivePages( self ):
+ return self.screenBufPage
+
+ def setPredrawPage( self, page ):
+ if self.screenBufPage[self.preScreen] != page:
+ self.screenBufPage[self.preScreen] = page
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(page).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ return True
+ return False
+
+ def predrawPage( self ):
+ if self.screenBufPage[self.preScreen] == -1: return True # no page to predraw
+ if not self.predrawTimeout:
+ self.predrawTimeout = gobject.timeout_add( 50, self._predrawTimeout )
+
+ def abortPredrawPage( self ):
+ if self.predrawTimeout:
+ gobject.source_remove( self.predrawTimeout )
+ self.predrawTimeout = False
+
+ def _predrawTimeout( self ):
+ if self.preScreen == -1: return False # no page to predraw
+ if self.draw( self.preScreen, False, time.time() + 0.020 ): # 20 ms time limit
+ self.predrawTimeout = False
+ return False
+ return True
+
+
+
+ def displayPage( self, page, predraw = -1 ):
+ if page == self.curPage:
+ if predraw >= 0 and self.screenBufPage[self.preScreen] != predraw:
+ self.screenBufPage[self.preScreen] = predraw
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, predraw )
+ return
+
+ if self.curPage >= 0 and self.curPage != page: clearNotes = True
+ else: clearNotes = False
+
+ oldPage = self.curPage
+ self.curPage = page
+ self.curBeats = self.noteDB.getPage(page).beats
+
+ if self.screenBufPage[self.preScreen] == self.curPage: # we predrew this page, so smart!
+ t = self.preScreen
+ self.preScreen = self.curScreen
+ self.curScreen = t
+ self.invalidate_rect( 0, 0, self.width, self.height, self.curPage, False )
+ else: # we need to draw this page from scratch
+ self.screenBufPage[self.curScreen] = self.curPage
+ self.screenBufBeats[self.curScreen] = self.curBeats
+ self.invalidate_rect( 0, 0, self.width, self.height, self.curPage )
+
+ if predraw >= 0 and self.screenBufPage[self.preScreen] != predraw:
+ self.screenBufPage[self.preScreen] = predraw
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, predraw )
+
+ if clearNotes: # clear the notes now that we've sorted out the screen buffers
+ self.clearSelectedNotes( oldPage )
+
+ if self.curAction == "paste":
+ self._updateClipboardArea()
+
+ def getPlayhead( self ):
+ return self.playheadT
+
+ def setPlayhead( self, ticks ):
+ if self.playheadT != ticks:
+ self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False )
+ self.playheadX = self.ticksToPixels( self.curBeats, ticks ) + Config.TRACK_SPACING_DIV2
+ self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False )
+ self.playheadT = ticks
+
+ def setInterfaceMode( self, mode ):
+ self.doneCurrentAction()
+
+ if mode == "tool":
+ mode = self.owner.getTool()
+
+ if mode == "draw":
+ self.interfaceMode = INTERFACEMODE.DRAW
+ elif mode == "paint":
+ self.interfaceMode = INTERFACEMODE.PAINT
+ elif mode == "paste_notes":
+ self.interfaceMode = INTERFACEMODE.PASTE_NOTES
+ self.setCurrentAction("paste", self)
+ elif mode == "paste_tracks":
+ self.interfaceMode = INTERFACEMODE.PASTE_TRACKS
+ self.setCurrentAction("paste", self )
+ else:
+ self.interfaceMode = INTERFACEMODE.DEFAULT
+
+ def getSelectedNotes( self ):
+ ids = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ ids.append( [ n.note.id for n in self.selectedNotes[t] ] )
+ return ids
+
+ #=======================================================
+ # Event Callbacks
+
+ def size_allocate( self, widget, allocation ):
+ self.alloc = allocation
+ width = allocation.width
+ height = allocation.height
+
+ self.drawingArea.set_size_request( width, height )
+
+ if self.window != None:
+ self.invalidate_rect( 0, 0, width, height, self.curPage, False )
+
+ def setPointerGrid(self, value):
+ self.pointerGrid = value
+
+ def setDrawGrid(self, value):
+ self.drawGrid = value
+
+ def setPaintGrid(self, value):
+ self.paintGrid = value
+
+ def setPaintNoteDur(self, value):
+ self.paintNoteDur = value
+
+ def handleButtonPress( self, widget, event ):
+
+ TP.ProfileBegin( "TI::handleButtonPress" )
+
+ 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) ]
+
+
+ if self.curAction == "paste":
+ self.doPaste()
+ self.setCurrentAction("block-track-select")
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+
+
+ # check if we clicked on the playhead
+ if event.x >= self.playheadX and event.x <= self.playheadX + Config.PLAYHEAD_SIZE:
+ self.setCurrentAction( "playhead-drag", self )
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+
+ if event.x < Config.TRACK_SPACING_DIV2 or event.x > self.trackWidth + Config.TRACK_SPACING_DIV2:
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+
+ handled = 0
+ notes = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ last = len(notes)-1
+ for n in range(last+1):
+ if i == self.drumIndex and n < last: # check to see if the next hit overlaps this one
+ if notes[n].getStartTick() == notes[n+1].getStartTick() and notes[n].getPitch() == notes[n+1].getPitch():
+ continue
+ 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
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+ else: # all other options mean we can stop looking
+ break
+
+ if self.interfaceMode == INTERFACEMODE.DRAW:
+ if not handled or handled == -1: # event didn't overlap any notes, so we can draw
+ if i == self.drumIndex: pitch = min( self.pixelsToPitchDrumFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ else: pitch = min( self.pixelsToPitchFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ onset = self.pixelsToTicksFloor( self.curBeats, self.clickLoc[0] - self.trackRect[i].x)
+ snapOnset = self.drawGrid * int(onset / float(self.drawGrid) + 0.5)
+ cs = CSoundNote( snapOnset,
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ for note in noteS:
+ if note.cs.onset < snapOnset and (note.cs.onset + note.cs.duration) > snapOnset:
+ self.noteDB.updateNote(self.curPage, i, note.id, PARAMETER.DURATION, snapOnset - note.cs.onset)
+
+ if i != self.drumIndex: # switch to drag duration
+ self.updateDragLimits()
+ self.clickLoc[0] += self.ticksToPixels( self.curBeats, 1 )
+ self.setCurrentAction( "note-drag-duration", n )
+ self.setCursor("drag-duration")
+ else:
+ self.curAction = True # we handled this, but there's no real action
+
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+ elif self.interfaceMode == INTERFACEMODE.PAINT:
+ self.scale = self.getScale()
+ self.painting = True
+ self.paintTrack = i
+ if i == self.drumIndex:
+ pitch = min( self.pixelsToPitchDrumFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ else:
+ pitch = min( self.pixelsToPitchFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+
+ minDiff = 100
+ for pit in GenerationConstants.SCALES[self.scale]:
+ diff = abs(pitch-(pit+36))
+ if diff < minDiff:
+ minDiff = diff
+ nearestPit = pit
+ pitch = nearestPit+36
+
+ onset = self.pixelsToTicksFloor( self.curBeats, self.clickLoc[0] - self.trackRect[i].x )
+ onset = self.paintGrid * int(onset / self.paintGrid + 0.5)
+ self.pLastPos = onset
+ if i != self.drumIndex:
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ ids = []
+ stream = []
+ for n in noteS:
+ if n.cs.onset >= onset and n.cs.onset < (onset + self.paintNoteDur):
+ ids.append(n.id)
+ if onset > n.cs.onset and onset < (n.cs.onset + n.cs.duration):
+ ids.append(n.id)
+ if len(ids):
+ stream += [self.curPage, i, len(ids)] + ids
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ cs = CSoundNote( int(onset),
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ self.noteDB.updateNote(self.curPage, i, id, PARAMETER.DURATION, self.paintNoteDur)
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+ self.curAction = True
+
+ TP.ProfileEnd( "TI::handleButtonPress" )
+
+
+ def handleButtonRelease( 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
+ self.painting = False
+
+ TP.ProfileBegin( "TI::handleButtonRelease" )
+
+ if event.button != 1:
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ return
+
+ if not self.curAction: #do track selection stuff here so that we can also handle marquee selection
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+ if event.button == 1:
+ if self.buttonPressCount == 1: self.owner.toggleTrack( i, False )
+ elif self.buttonPressCount == 2: self.owner.toggleTrack( i, True )
+ else: self.owner.clearTracks()
+ break
+
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ return
+
+ if not self.curActionObject: # there was no real action to carry out
+ self.curAction = False
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ 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 )
+ elif self.curAction == "playhead-drag": self.donePlayhead( event )
+ self.updateTooltip( event )
+
+
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ return
+
+ def handleMotion( self, widget, event ):
+ TP.ProfileBegin( "TI::handleMotion::Common" )
+
+ if event.is_hint:
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ if self.painting:
+ i = self.paintTrack
+ curPos = self.pixelsToTicksFloor(self.curBeats, event.x - self.trackRect[i].x)
+ gridPos = self.paintGrid * int(curPos / self.paintGrid + 0.5)
+ if gridPos >= self.curBeats * Config.TICKS_PER_BEAT:
+ return
+ if gridPos != self.pLastPos:
+ self.pLastPos = gridPos
+ if i == self.drumIndex:
+ pitch = min( self.pixelsToPitchDrumFloor( int(event.y) - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ else:
+ pitch = min( self.pixelsToPitchFloor( int(event.y) - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ minDiff = 100
+ for pit in GenerationConstants.SCALES[self.scale]:
+ diff = abs(pitch-(pit+36))
+ if diff < minDiff:
+ minDiff = diff
+ nearestPit = pit
+ pitch = nearestPit+36
+
+ onset = gridPos
+ if i != self.drumIndex:
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ ids = []
+ stream = []
+ for n in noteS:
+ if n.cs.onset >= onset and n.cs.onset < (onset + self.paintNoteDur):
+ ids.append(n.id)
+ if onset > n.cs.onset and onset < (n.cs.onset + n.cs.duration):
+ ids.append(n.id)
+ if len(ids):
+ stream += [self.curPage, i, len(ids)] + ids
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ cs = CSoundNote( int(onset),
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ self.noteDB.updateNote(self.curPage, i, id, PARAMETER.DURATION, self.paintNoteDur)
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+ self.curAction = True
+
+
+ TP.ProfileEnd( "TI::handleMotion::Common" )
+
+ if not self.clickButton and self.curAction != "paste": # we recieved this event but were never clicked! (probably a popup window was open)
+ TP.ProfileBegin( "TI::handleMotion::Hover" )
+ self.updateTooltip( event )
+ TP.ProfileEnd( "TI::handleMotion::Hover" )
+ return
+
+ if self.curAction == "paste":
+ TP.ProfileBegin( "TI::handleMotion::Paste" )
+ top = Config.NUMBER_OF_TRACKS
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+ top = i
+ break
+ self.updatePaste( self.pixelsToTicksFloor( self.curBeats, event.x ), top )
+ TP.ProfileEnd( "TI::handleMotion::Paste" )
+ elif event.state & gtk.gdk.BUTTON1_MASK:
+ TP.ProfileBegin( "TI::handleMotion::Drag" )
+
+ 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 )
+
+ elif self.curAction == "playhead-drag":
+ self.updatePlayhead( event )
+
+ TP.ProfileEnd( "TI::handleMotion::Drag" )
+ else:
+ TP.ProfileBegin( "TI::handleMotion::Hover" )
+ self.updateTooltip( event )
+ TP.ProfileEnd( "TI::handleMotion::Hover" )
+
+ return
+
+ #=======================================================
+ # 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()
+ elif action == "paste":
+ self._updateClipboardArea()
+ self.setCursor("paste")
+
+ 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 )
+ elif action == "paste":
+ self.owner.cleanupClipboard()
+
+ def trackToggled( self, trackN = -1 ):
+ if trackN == -1: self.invalidate_rect( 0, 0, self.width, self.height )
+ else: self.invalidate_rect( 0, self.trackLimits[trackN][0], self.width, self.trackLimits[trackN][1]-self.trackLimits[trackN][0] )
+
+ 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()
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if len(self.selectedNotes[i]):
+ self.owner.setContextState( CONTEXT.NOTE, True )
+ self.owner.setContext( CONTEXT.NOTE )
+ return
+ self.owner.setContextState( CONTEXT.NOTE, False )
+
+ 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 ) )
+ do = self.pointerGrid * int(do / self.pointerGrid)
+
+ 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] )
+
+ if self.curActionObject.note.track != self.drumIndex:
+ self.curActionObject.playSampleNote( True )
+ elif dp != self.lastDrumDP and not dp%2: # only play of "full" drum pitches
+ self.lastDrumDP = dp
+ self.curActionObject.playSampleNote( False )
+
+ 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 )
+
+ 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.width: self.marqueeLoc[0] = self.width
+ if self.marqueeLoc[1] < 0: self.marqueeLoc[1] = 0
+ elif self.marqueeLoc[1] > self.height: self.marqueeLoc[1] = self.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.invalidate_rect( 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 = {}
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ intersectionY = [ max(self.marqueeRect[0][1],self.trackLimits[i][0]), min(stop[1],self.trackLimits[i][1]) ]
+ if intersectionY[0] > intersectionY[1]:
+ continue
+
+ notes = []
+ track = self.noteDB.getNotesByTrack( self.curPage, i, 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[i] = notes
+
+ self.selectNotes( select )
+
+ self.marqueeLoc = False
+ self.doneCurrentAction()
+
+ self.invalidate_rect( self.marqueeRect[0][0]-1, self.marqueeRect[0][1]-1, self.marqueeRect[1][0]+2, self.marqueeRect[1][1]+2, self.curPage, False )
+
+ def updatePlayhead( self, event ):
+ x = min( self.trackWidth - self.pixelsPerTick[self.curBeats], max( Config.TRACK_SPACING_DIV2, event.x ) )
+ self.setPlayhead( self.pixelsToTicks( self.curBeats, x ) )
+
+ def donePlayhead( self, event ):
+ x = min( self.trackWidth - self.pixelsPerTick[self.curBeats], max( Config.TRACK_SPACING_DIV2, event.x ) )
+ ticks = self.pixelsToTicks( self.curBeats, x )
+ print "set playhead to %d ticks" % (ticks)
+ self.doneCurrentAction()
+
+ def updatePaste( self, tick, track ):
+ if self.interfaceMode == INTERFACEMODE.PASTE_TRACKS: tick = 0
+ if self.pasteTick == tick and self.pasteTrack == track: return
+ if self.noteDB.getPage(self.curPage).ticks < tick < 0 \
+ or track > self.drumIndex \
+ or ( track == self.drumIndex and not self.clipboardDrumTrack ):
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ self.pasteTick = self.pasteTrack = -1
+ self.pasteRect = False
+ return
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ if self.clipboardDrumTrack:
+ bottom = self.drumIndex
+ else:
+ bottom = self.drumIndex - 1
+ for t in range(self.drumIndex-1,self.clipboardTrackTop-1,-1):
+ if self.clipboardArea["tracks"][t]: break
+ bottom -= 1
+ end = -tick + min( self.noteDB.getPage(self.curPage).ticks, tick + self.clipboardArea["limit"][1]-self.clipboardArea["limit"][0] )
+ self.pasteTick = tick
+ self.pasteTrack = track
+ self.pasteRect = [ [ self.ticksToPixels( self.curBeats, tick ), \
+ self.trackLimits[track][0] ], \
+ [ self.ticksToPixels( self.curBeats, end), \
+ self.trackLimits[bottom][1] ] ]
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+
+ def doPaste( self ):
+ if self.pasteTrack == -1:
+ self.doneCurrentAction()
+ return
+
+ trackMap = {}
+ for t in range(self.pasteTrack,self.drumIndex):
+ ind = t+self.clipboardTrackTop-self.pasteTrack
+ if ind >= self.drumIndex: break
+ if not self.clipboardArea["tracks"][ind]:
+ continue
+ trackMap[t] = ind
+ if self.clipboardDrumTrack:
+ trackMap[self.drumIndex] = self.drumIndex
+ new = self.owner.pasteClipboard( self.pasteTick - self.clipboardArea["limit"][0], trackMap )
+ if self.interfaceMode == INTERFACEMODE.PASTE_NOTES and self.curPage in new:
+ noteDic = {}
+ for t in range(Config.NUMBER_OF_TRACKS):
+ if len(new[self.curPage][t]):
+ noteDic[t] = [ self.noteDB.getNote( self.curPage, t, n, self ) for n in new[self.curPage][t] ]
+ self.selectNotes(noteDic)
+ elif self.interfaceMode == INTERFACEMODE.PASTE_TRACKS:
+ for t in range(self.drumIndex):
+ ind = t + self.clipboardTrackTop - self.pasteTrack
+ if ind >= self.drumIndex or ind < 0: self.owner.setTrack( t, False )
+ else: self.owner.setTrack( t, self.clipboardArea["tracks"][ind] )
+ self.owner.setTrack( self.drumIndex, self.clipboardDrumTrack )
+
+ self.doneCurrentAction()
+
+ def donePaste( self ):
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ self.pasteTick = self.pasteTrack = -1
+ self.pasteRect = False
+ self.setInterfaceMode("tool")
+ # make a fake event for updateTooltip
+ event = gtk.gdk.Event(gtk.gdk.MOTION_NOTIFY)
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+ self.updateTooltip( event )
+
+ def updateTooltip( self, event ):
+
+ # check clicked the playhead
+ if event.x >= self.playheadX and event.x <= self.playheadX + Config.PLAYHEAD_SIZE:
+ self.setCursor("drag-playhead")
+ return
+
+ if event.x < Config.TRACK_SPACING_DIV2 or event.x > self.trackWidth + Config.TRACK_SPACING_DIV2:
+ self.setCursor("default")
+ return
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+
+ notes = self.noteDB.getNotesByTrack( self.curPage, i, 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
+
+ # note wasn't handled, could potentially draw a note
+ if self.interfaceMode == INTERFACEMODE.DRAW:
+ if handled == -2: # event X overlapped with a note
+ self.setCursor("default")
+ return
+
+ self.setCursor("pencil")
+ return
+
+ break
+
+ self.setCursor("default")
+
+ def setCursor( self, cursor ):
+ self.window.set_cursor(self.cursor[cursor])
+
+ #=======================================================
+ # Drawing
+
+ def draw( self, buf, noescape = True, timeout = 0 ):
+ if not self.screenBufDirty[buf]: return True
+
+ TP.ProfileBegin( "TrackInterface::draw" )
+
+ startX = self.screenBufDirtyRect[buf].x
+ startY = self.screenBufDirtyRect[buf].y
+ stopX = self.screenBufDirtyRect[buf].x + self.screenBufDirtyRect[buf].width
+ stopY = self.screenBufDirtyRect[buf].y + self.screenBufDirtyRect[buf].height
+
+ beatStart = Config.TRACK_SPACING_DIV2
+ beats = self.screenBufBeats[buf]
+
+ pixmap = self.screenBuf[buf]
+
+ resume = self.screenBufResume[buf]
+
+ self.gc.set_clip_rectangle( self.screenBufDirtyRect[buf] )
+
+ self.gc.set_line_attributes( Config.BEAT_LINE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ # regular tracks
+ for i in range( resume[0], self.drumIndex ):
+ if resume[1] == 0:
+ if startY > self.trackLimits[i][1]: continue
+ if stopY < self.trackLimits[i][0]: break
+
+ # draw background
+ if self.owner.getTrackSelected( i ):
+ pixmap.draw_drawable( self.gc, self.image["trackBGSelected"], 0, 0, 0, self.trackLimits[i][0], self.trackFullWidth, self.trackFullHeight )
+ else:
+ pixmap.draw_drawable( self.gc, self.image["trackBG"], 0, 0, 0, self.trackLimits[i][0], self.trackFullWidth, self.trackFullHeight )
+
+ # draw beat lines
+ self.gc.foreground = self.beatColor
+ for j in range(1,self.screenBufBeats[buf]):
+ x = beatStart + self.beatSpacing[beats][j]
+ pixmap.draw_line( self.gc, x, self.trackRect[i].y, x, self.trackRect[i].y+self.trackRect[i].height )
+
+ resume[1] = 1 # background drawn
+
+ # draw notes
+ TP.ProfileBegin("TI::draw notes")
+ notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], i, self )
+ for n in range( resume[2], len(notes) ):
+ # check escape
+ if not noescape and time.time() > timeout:
+ resume[0] = i
+ resume[2] = n
+ TP.ProfilePause( "TrackInterface::draw" )
+ return False
+
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+ TP.ProfileEnd("TI::draw notes")
+
+ # finished a track, reset the resume values for the next one
+ resume[1] = 0
+ resume[2] = 0
+
+ # drum track
+ if stopY > self.trackLimits[self.drumIndex][0]:
+
+ if resume[1] == 0:
+ # draw background
+ if self.owner.getTrackSelected( self.drumIndex ):
+ pixmap.draw_drawable( self.gc, self.image["trackBGDrumSelected"], 0, 0, 0, self.trackLimits[self.drumIndex][0], self.trackFullWidth, self.trackFullHeightDrum )
+ else:
+ pixmap.draw_drawable( self.gc, self.image["trackBGDrum"], 0, 0, 0, self.trackLimits[self.drumIndex][0], self.trackFullWidth, self.trackFullHeightDrum )
+
+ # draw beat lines
+ self.gc.foreground = self.beatColor
+ for j in range(1,self.screenBufBeats[buf]):
+ x = beatStart + self.beatSpacing[beats][j]
+ pixmap.draw_line( self.gc, x, self.trackRect[self.drumIndex].y, x, self.trackRect[self.drumIndex].y+self.trackRect[self.drumIndex].height )
+
+ resume[1] = 1 # background drawn
+
+ # draw notes
+ notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], self.drumIndex, self )
+ for n in range( resume[2], len(notes) ):
+ # check escape
+ if not noescape and time.time() > timeout:
+ resume[0] = i
+ resume[2] = n
+ TP.ProfilePause( "TrackInterface::draw" )
+ return False
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+
+ self.screenBufDirty[buf] = False
+
+ TP.ProfileEnd( "TrackInterface::draw" )
+
+ return True
+
+ def expose( self, DA, event ):
+
+ if self.screenBufDirty[self.curScreen]:
+ self.draw( self.curScreen )
+
+ TP.ProfileBegin( "TrackInterface::expose" )
+
+ startX = event.area.x
+ startY = event.area.y
+ stopX = event.area.x + event.area.width
+ stopY = event.area.y + event.area.height
+
+ #print "%d %d %d %d" % (startX,startY,stopX,stopY)
+
+ self.gc.set_clip_rectangle( event.area )
+
+ # draw base
+ DA.window.draw_drawable( self.gc, self.screenBuf[self.curScreen], startX, startY, startX, startY, event.area.width, event.area.height )
+
+ # 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.playheadColor
+ DA.window.draw_line( self.gc, self.playheadX, startY, self.playheadX, stopY )
+
+ 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.marqueeColor
+ DA.window.draw_rectangle( self.gc, False, self.marqueeRect[0][0], self.marqueeRect[0][1], self.marqueeRect[1][0], self.marqueeRect[1][1] )
+
+ if self.pasteRect: # draw the paste highlight
+ self.gc.set_function( gtk.gdk.INVERT )
+ for t in range(self.pasteTrack,self.drumIndex):
+ ind = t+self.clipboardTrackTop-self.pasteTrack
+ if ind >= self.drumIndex: break
+ if not self.clipboardArea["tracks"][ind]:
+ continue
+ DA.window.draw_rectangle( self.gc, True, self.pasteRect[0][0], self.trackLimits[t][0] + Config.TRACK_SPACING_DIV2, self.pasteRect[1][0], self.trackHeight )
+ if self.clipboardDrumTrack:
+ DA.window.draw_rectangle( self.gc, True, self.pasteRect[0][0], self.trackLimits[self.drumIndex][0] + Config.TRACK_SPACING_DIV2, self.pasteRect[1][0], self.trackHeightDrum )
+ self.gc.set_function( gtk.gdk.COPY )
+
+ self.drawingAreaDirty = False
+
+ TP.ProfileEnd( "TrackInterface::expose" )
+
+ def invalidate_rect( self, x, y, width, height, page = -1, base = True ):
+ #print "%d %d %d %d Page %d CurPage %d" % (x,y,width,height,page,self.curPage)
+ 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 page == self.curPage or page == -1:
+ if base: # the base image has been dirtied
+ if not self.screenBufDirty[self.curScreen]:
+ self.screenBufDirtyRect[self.curScreen].x = x
+ self.screenBufDirtyRect[self.curScreen].y = y
+ self.screenBufDirtyRect[self.curScreen].width = width
+ self.screenBufDirtyRect[self.curScreen].height = height
+ else:
+ self.screenBufDirtyRect[self.curScreen] = self.screenBufDirtyRect[self.curScreen].union( self.dirtyRectToAdd )
+ self.screenBufResume[self.curScreen] = [0,0,0]
+ self.screenBufDirty[self.curScreen] = True
+ if self.drawingArea.window != None:
+ self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True )
+ self.drawingAreaDirty = True
+
+ if page == self.screenBufPage[self.preScreen] or page == -1:
+ if not self.screenBufDirty[self.preScreen]:
+ self.screenBufDirtyRect[self.preScreen].x = x
+ self.screenBufDirtyRect[self.preScreen].y = y
+ self.screenBufDirtyRect[self.preScreen].width = width
+ self.screenBufDirtyRect[self.preScreen].height = height
+ else:
+ self.screenBufDirtyRect[self.preScreen] = self.screenBufDirtyRect[self.preScreen].union( self.dirtyRectToAdd )
+ self.screenBufResume[self.preScreen] = [0,0,0]
+ self.screenBufDirty[self.preScreen] = True
+
+ #self.queue_draw()
+
+ def getTrackOrigin( self, track ):
+ return ( self.trackRect[track].x, self.trackRect[track].y )
+
+ 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 ticksToPixelsFloor( self, beats, ticks ):
+ return int( ticks * self.pixelsPerTick[beats] )
+ def pixelsToTicksFloor( self, beats, pixels ):
+ return int( pixels * self.ticksPerPixel[beats] )
+ def pitchToPixels( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+ 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)
+ def pitchToPixelsDrum( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum ))
+ def pixelsToPitchDrum( self, pixels ):
+ return int(round(-pixels*self.pitchPerPixelDrum))
+ def pitchToPixelsDrumFloor( self, pitch ):
+ return int( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum )
+ def pixelsToPitchDrumFloor( self, pixels ):
+ return int(-pixels*self.pitchPerPixelDrum)
diff --git a/Edit/TuneInterface.py b/Edit/TuneInterface.py
new file mode 100644
index 0000000..f8bc4e7
--- /dev/null
+++ b/Edit/TuneInterface.py
@@ -0,0 +1,646 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import common.Config as Config
+
+from common.Util.Profiler import TP
+from Edit.MainWindow import CONTEXT
+
+from common.Util.NoteDB import PARAMETER
+
+class TuneInterfaceParasite:
+
+ def __init__( self, noteDB, owner, note ):
+ self.noteDB = noteDB
+ self.owner = owner
+ self.note = note
+
+ self.x = self.y = self.width = -1
+
+ def attach( self ):
+ self.updateParameter( None, None )
+ return self
+
+ def destroy( self ):
+ self.owner.invalidate_thumbnail( self.note.page, self.x, self.y, self.width, 1 )
+
+ def updateParameter( self, parameter, value ):
+ if parameter == PARAMETER.AMPLITUDE: return
+ x = 2 + Config.THUMBNAIL_TRACK_RECT[self.note.track][0] + self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.onset )
+ if self.note.track == Config.NUMBER_OF_TRACKS-1: # drum track
+ y = Config.THUMBNAIL_TRACK_RECT[self.note.track][1] + self.owner.pitchToPixelsDrum( self.note.cs.pitch )
+ if x != self.x or y != self.y:
+ if parameter != None: # not the first update
+ xx = min( self.x, x )
+ yy = min( self.y, y )
+ endxx = max( self.endx, x + 1 )
+ endyy = max( self.y, y ) + 1
+ self.x = x
+ self.endx = x + 1
+ self.y = y
+ self.owner.invalidate_thumbnail( self.note.page, xx, yy, endxx-xx, endyy-yy )
+ else:
+ self.x = x
+ self.endx = x + 1
+ self.y = y
+ self.owner.invalidate_thumbnail( self.note.page, x, y, 1, 1 )
+ else:
+ y = Config.THUMBNAIL_TRACK_RECT[self.note.track][1] + self.owner.pitchToPixels( self.note.cs.pitch )
+ width = max( 1, self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.duration ) )
+ if x != self.x or y != self.y or width != self.width:
+ if parameter != None: # not the first update
+ xx = min( self.x, x )
+ yy = min( self.y, y )
+ endxx = max( self.endx, x + width )
+ endyy = max( self.y, y ) + 1
+ self.x = x
+ self.endx = x + width
+ self.y = y
+ self.width = width
+ self.owner.invalidate_thumbnail( self.note.page, xx, yy, endxx-xx, endyy-yy )
+ else:
+ self.x = x
+ self.endx = x + width
+ self.y = y
+ self.width = width
+ self.owner.invalidate_thumbnail( self.note.page, x, y, width, 1 )
+
+ 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.endx: return True # we don't need to draw, but maybe a later note does
+
+ win.draw_line( gc, self.x, self.y, self.endx, self.y )
+
+ return True # we drew something
+
+
+class TuneInterface( gtk.EventBox ):
+
+ DRAG_BLOCK = -1 # block other drag events
+ DRAG_SELECT = 1
+ DRAG_DESELECT = 2
+ DRAG_MOVE = 3
+
+ def __init__( self, noteDB, owner, adjustment ):
+ gtk.EventBox.__init__( self )
+
+ self.noteDB = noteDB
+ self.owner = owner
+ self.adjustment = adjustment
+ #adjustment.connect( "changed", self.adjustmentChanged )
+ adjustment.connect( "value-changed", self.adjustmentValue )
+
+ self.drawingArea = gtk.DrawingArea()
+ self.drawingAreaDirty = False # is the drawingArea waiting to draw?
+ self.add( self.drawingArea )
+ self.dirtyRectToAdd = gtk.gdk.Rectangle() # used by the invalidate_rect function
+
+ self.selectedIds = []
+ self.displayedPage = -1
+
+ self.drumIndex = Config.NUMBER_OF_TRACKS-1
+
+ self.trackRect = Config.THUMBNAIL_TRACK_RECT
+ self.thumbnail = {}
+ self.thumbnailDirty = {}
+ self.thumbnailDirtyRect = {}
+ self.defaultwin = gtk.gdk.get_default_root_window() # used when creating pixmaps
+ self.gc = gtk.gdk.GC( self.defaultwin )
+ colormap = self.drawingArea.get_colormap()
+ self.bgColor = colormap.alloc_color( Config.TOOLBAR_BCK_COLOR, True, True )
+ self.lineColor = colormap.alloc_color( Config.THUMBNAIL_DRAG_COLOR, True, True )
+ self.displayedColor = colormap.alloc_color( Config.THUMBNAIL_DISPLAYED_COLOR, True, True )
+ self.selectedColor = colormap.alloc_color( Config.THUMBNAIL_SELECTED_COLOR, True, True )
+
+ # prepare thumbnail
+ self.thumbnailBG = []
+ self.gc.foreground = self.bgColor
+ for i in range(4):
+ pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+"pageThumbnailBG%d.png"%i )
+ self.thumbnailBG.append( gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) )
+ self.thumbnailBG[i].draw_rectangle( self.gc, True, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailBG[i].draw_pixbuf( self.gc, pix, 0, 0, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT, gtk.gdk.RGB_DITHER_NONE )
+
+ # load clipmask
+ pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'pageThumbnailMask.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()):
+ if pixels[i*channels+offset] != "\0":
+ byte += 1 << shift
+ shift += 1
+ if shift > 7:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ if shift:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ self.clipMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() )
+ self.clearMask = gtk.gdk.Rectangle( 0, 0, 1200, 800 )
+
+ self.pageOffset = 5 # offset the first page by this
+ self.dropWidth = 5 # line thickness of the drop head
+ self.dropWidthDIV2 = self.dropWidth//2
+
+ self.pixelsPerPitch = float(self.trackRect[0][3]-1)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ self.pixelsPerPitchDrum = float(self.trackRect[self.drumIndex][3]-1)/(Config.MAXIMUM_PITCH_DRUM - Config.MINIMUM_PITCH_DRUM )
+ self.pixelsPerTick = [0] + [ float(self.trackRect[0][2]-4)/(i*Config.TICKS_PER_BEAT) for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.alloced = False
+ self.width = self.baseWidth = self.height = -1
+ self.waitingForAlloc = True
+ self.scrollTo = None
+ self.clickX = -1
+
+ self.set_size_request( self.width, self.height )
+
+ self.button1Down = False
+ self.dragMode = None
+ self.dropAt = -1
+ self.dropAtX = 0
+
+ self.visibleX = 0
+ self.visibleEndX = 0
+
+ self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK)
+
+ self.connect( "size-allocate", self.size_allocated )
+ self.drawingArea.connect( "expose-event", self.draw )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "button-release-event", self.handleButtonRelease )
+ self.connect( "motion-notify-event", self.handleMotion )
+
+ def size_allocated( self, widget, allocation ):
+ if not self.alloced:
+ self.baseWidth = allocation.width
+ self.visibleEndX = self.baseWidth
+ self.baseHeight = allocation.height
+ self.alloced = True
+ self.updateSize()
+ self.width = allocation.width
+ self.height = allocation.height
+ self.drawingArea.set_size_request( self.width, self.height )
+ self.clearMask.height = self.height
+ self.clearMask.width = self.width
+
+ self.pageY = 2 + (self.height-Config.PAGE_THUMBNAIL_HEIGHT)//2
+
+ if self.scrollTo != None:
+ if self.scrollTo >= 0: self.adjustment.set_value( self.scrollTo )
+ else: self.adjustment.set_value( self.width - self.baseWidth )
+ self.scrollTo = None
+
+ self.waitingForAlloc = False
+
+ def adjustmentValue( self, adj ):
+ self.visibleX = int(adj.value)
+ self.visibleEndX = self.visibleX + self.baseWidth
+
+ def updateSize( self ):
+ width = self.noteDB.getPageCount()*Config.PAGE_THUMBNAIL_WIDTH + 5 # add extra 5 for the first page
+ self.waitingForAlloc = True
+ if width < self.baseWidth:
+ self.pageOffset = ( self.baseWidth - width ) // 2 + 5
+ else:
+ self.pageOffset = 5
+
+ if self.alloced:
+ self.set_size_request( max( self.baseWidth, width), -1 )
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ def handleButtonPress( self, widget, event ):
+ if event.button != 1:
+ # bring up properties or something
+ return
+
+ self.button1Down = True
+
+ self.owner.abortPredrawPage()
+
+ ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ if ind >= self.noteDB.getPageCount():
+ if self.dragMode != self.DRAG_MOVE:
+ self.dragMode = self.DRAG_BLOCK
+ return
+ if ind < 0: ind = 0
+
+ self.clickX = event.x
+
+ id = self.noteDB.getPageByIndex( ind )
+
+ if event.type == gtk.gdk._3BUTTON_PRESS: # triple click -> select all
+ self.owner.displayPage( id )
+ self.selectAll()
+ elif event.type == gtk.gdk._2BUTTON_PRESS: # double click -> exclusive select
+ self.owner.displayPage( id )
+ self.selectPage( id )
+ else:
+ if Config.ModKeys.ctrlDown:
+ if id in self.selectedIds: # ctrl click, selected page -> remove page from selection
+ if self.deselectPage( id ):
+ self.dragMode = self.DRAG_DESELECT
+ self.dragLastInd = ind
+ else:
+ self.dragMode = self.DRAG_SELECT # special case, they clicked on the last selected page and it wasn't deselected
+ self.dragLastInd = ind
+ else: # ctrl click, unselected page -> add page to selection (but don't display it)
+ self.selectPage( id, False )
+ self.dragMode = self.DRAG_SELECT
+ self.dragLastInd = ind
+ elif id in self.selectedIds: # click, selected page -> display this page but don't change the selection
+ self.owner.displayPage( id )
+ else: # click, unselected page -> exclusive select
+ self.owner.displayPage( id )
+ self.selectPage( id )
+
+
+ self.owner.setContext( CONTEXT.PAGE )
+
+ def handleButtonRelease( self, widget, event ):
+ if event.button != 1:
+ return
+
+ self.button1Down = False
+
+ if self.dragMode == self.DRAG_MOVE:
+ self.invalidate_rect( self.dropAtX - self.dropWidthDIV2, 0, self.dropWidth, self.height ) # drop head
+
+ if self.dropAt > 0: after = self.noteDB.getPageByIndex( self.dropAt-1 )
+ else: after = False
+
+ self.noteDB.movePages( self.selectedIds, after )
+
+ self.dropAt = -1
+
+ self.dragMode = None
+
+ def handleMotion( self, widget, event ):
+
+ if event.is_hint:
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ if self.button1Down: # clicking
+ if Config.ModKeys.ctrlDown and (self.dragMode == None or self.dragMode == self.DRAG_MOVE):
+ self.dropAt = -1
+ self.dragMode = self.DRAG_SELECT
+ if event.x >= self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ else: ind = 0
+ self.dragLastInd = ind
+
+ if self.dragMode == self.DRAG_SELECT: # select on drag
+ if event.x > self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ else: ind = 0
+ pageCount = self.noteDB.getPageCount()
+ if ind >= pageCount: ind = pageCount-1
+ for i in range( min(ind,self.dragLastInd), max(ind,self.dragLastInd)+1):
+ self.selectPage( self.noteDB.getPageByIndex(i), False )
+ self.dragLastInd = ind
+ elif self.dragMode == self.DRAG_DESELECT: # deselect on drag
+ if event.x > self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ else: ind = 0
+ pageCount = self.noteDB.getPageCount()
+ if ind >= pageCount: ind = pageCount-1
+ for i in range( min(ind,self.dragLastInd), max(ind,self.dragLastInd)+1):
+ self.deselectPage( self.noteDB.getPageByIndex(i) )
+ self.dragLastInd = ind
+ elif self.dragMode == None and abs(self.clickX-event.x) > 20: # drag and drop
+ self.dragMode = self.DRAG_MOVE
+
+ if self.dragMode == self.DRAG_MOVE:
+ if self.dropAt >= 0: lastX = self.dropAtX
+ else: lastX = -1
+ if event.x > self.pageOffset: self.dropAt = int(event.x-self.pageOffset+Config.PAGE_THUMBNAIL_WIDTH_DIV2)//Config.PAGE_THUMBNAIL_WIDTH
+ else: self.dropAt = 0
+ c = self.noteDB.getPageCount()
+ if self.dropAt > c: self.dropAt = c
+ self.dropAtX = self.pageOffset + self.dropAt*Config.PAGE_THUMBNAIL_WIDTH - self.dropWidthDIV2 - 1
+ if lastX >= 0 and lastX != self.dropAtX:
+ if lastX < self.dropAtX:
+ x = lastX - self.dropWidthDIV2
+ w = self.dropAtX - lastX + self.dropWidth
+ else:
+ x = self.dropAtX - self.dropWidthDIV2
+ w = lastX - self.dropAtX + self.dropWidth
+ self.invalidate_rect( x, 0, w, self.height )
+ elif lastX == -1:
+ self.invalidate_rect( self.dropAtX-self.dropWidthDIV2, 0, self.dropWidth, self.height )
+
+ else: # hovering
+ ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ if ind != self.lastPredrawInd and 0 <= ind < self.noteDB.getPageCount():
+ id = self.noteDB.getPageByIndex(ind)
+ if id != self.displayedPage:
+ self.owner.predrawPage( id )
+ self.lastPredrawInd = ind
+
+
+ def trackToggled( self, i ):
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ def displayPage( self, id ):
+ if self.displayedPage == id: return -1
+
+ self.lastPredrawInd = -1
+
+ if self.displayedPage != -1:
+ ind = self.noteDB.getPageIndex( self.displayedPage )
+ self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ if not self.thumbnail.has_key( id ):
+ # premptive add
+ self.thumbnail[id] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirtyRect[id] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirty[id] = True
+ self.selectPage( id )
+ self.updateSize()
+
+ self.displayedPage = id
+
+ if id not in self.selectedIds:
+ self.selectPage( id )
+
+ ind = self.noteDB.getPageIndex( id )
+
+ startX = self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH
+ stopX = startX + Config.PAGE_THUMBNAIL_WIDTH
+
+ if self.adjustment.value > startX:
+ scroll = startX + Config.PAGE_THUMBNAIL_WIDTH + Config.PAGE_THUMBNAIL_WIDTH_DIV2 - self.baseWidth
+ if scroll < 0: scroll = 0
+ self.adjustment.set_value( scroll )
+ elif self.adjustment.value + self.baseWidth < stopX:
+ scroll = startX - Config.PAGE_THUMBNAIL_WIDTH_DIV2
+ if scroll + self.baseWidth > self.width:
+ if self.waitingForAlloc:
+ self.scrollTo = -1
+ else:
+ self.adjustment.set_value( self.width - self.baseWidth )
+ else:
+ if self.waitingForAlloc:
+ self.scrollTo = scroll
+ else:
+ self.adjustment.set_value( scroll )
+
+ self.invalidate_rect( startX, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ def selectPage( self, id, exclusive = True ):
+ if exclusive:
+ self._clearSelection()
+
+ if id in self.selectedIds: return False # no change
+
+ ind = self.noteDB.getPageIndex( id )
+ l = len(self.selectedIds)
+ i = 0 # in case len(self.selectedIds) == 0
+ while i < l:
+ if self.noteDB.getPageIndex( self.selectedIds[i] ) > ind: break
+ i += 1
+
+ self.selectedIds.insert( i, id )
+
+ self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ self.owner.updatePageSelection( self.selectedIds )
+
+ return True # page added to selection
+
+ def deselectPage( self, id, force = False, skip_redraw = False, noUpdate = False ):
+ if not id in self.selectedIds: return False # page isn't selected
+
+ if not force:
+ if len(self.selectedIds) <= 1: return False # don't deselect the last page
+
+ if self.displayedPage == id:
+ i = self.selectedIds.index(id)
+ if i == 0: self.owner.displayPage( self.selectedIds[1] )
+ else: self.owner.displayPage( self.selectedIds[i-1] )
+
+ self.selectedIds.remove( id )
+ if not skip_redraw:
+ ind = self.noteDB.getPageIndex( id )
+ self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ if not noUpdate:
+ self.owner.updatePageSelection( self.selectedIds )
+
+ return True # page removed from the selection
+
+ def selectPages( self, which ):
+ self._clearSelection()
+ self.selectedIds += which
+
+ self.owner.updatePageSelection( self.selectedIds )
+
+ def selectAll( self ):
+ self.selectedIds = self.noteDB.getTune()[:]
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ self.owner.updatePageSelection( self.selectedIds )
+
+ def _clearSelection( self ):
+ self.selectedIds = []
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ def getSelectedIds( self ):
+ return self.selectedIds
+
+ def getDisplayedIndex( self ):
+ return self.selectedIds.index( self.displayedPage )
+
+ def getFirstSelected( self ):
+ return self.selectedIds[0]
+
+ def getLastSelected( self ):
+ return self.selectedIds[-1]
+
+ #=======================================================
+ # NoteDB notifications
+
+ def notifyPageAdd( self, id, at ):
+ if not self.thumbnail.has_key(id):
+ self.thumbnail[id] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirtyRect[id] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirty[id] = True
+ self.selectPage( id )
+ self.updateSize()
+
+ def notifyPageDelete( self, which, safe ):
+ if self.displayedPage in which:
+ noUpdate = True
+ else:
+ noUpdate = False
+ for id in self.selectedIds:
+ if id in which:
+ self.deselectPage( id, True, True, noUpdate )
+ for id in which:
+ del self.thumbnail[id]
+ del self.thumbnailDirtyRect[id]
+ del self.thumbnailDirty[id]
+ if self.displayedPage in which:
+ self.displayedPage = -1
+ self.updateSize()
+
+ def notifyPageDuplicate( self, new, at ):
+ for id in new:
+ self.thumbnail[new[id]] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirtyRect[new[id]] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirty[new[id]] = True
+ self.updateSize()
+
+ def notifyPageMove( self, which, low, high ):
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ def notifyPageUpdate( self, page, parameter, value ):
+ if parameter == PARAMETER.PAGE_BEATS:
+ notes = self.noteDB.getNotesByPage( page, self )
+ for note in notes:
+ note.updateParameter( -1, -1 ) # force update transform
+
+ elif parameter == PARAMETER.PAGE_COLOR:
+ self.invalidate_thumbnail( page, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+
+ #=======================================================
+ # Drawing
+
+ def drawThumbnail( self, id, pixmap, rect ):
+ startX = rect.x
+ startY = rect.y
+ stopX = rect.x + rect.width
+ stopY = rect.y + rect.height
+
+ # draw background
+ pixmap.draw_drawable( self.gc, self.thumbnailBG[self.noteDB.getPage(id).color], startX, startY, startX, startY, rect.width, rect.height+1 )
+
+ # draw regular tracks
+ self.gc.foreground = self.lineColor
+ self.gc.set_line_attributes( 1, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ for i in range(self.drumIndex):
+ if startY >= self.trackRect[i+1][1]: continue
+ if stopY < self.trackRect[i][1]: break
+
+ # draw notes
+ notes = self.noteDB.getNotesByTrack( id, i, self )
+ for n in range( len(notes) ):
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+ # drum track
+ if stopY > self.trackRect[self.drumIndex][0]:
+ # draw notes
+ notes = self.noteDB.getNotesByTrack( id, self.drumIndex, self )
+ for n in range( len(notes) ):
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+
+ self.thumbnailDirty[id] = False
+
+
+ def draw( self, drawingArea, event ):
+
+ 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( self.clearMask )
+
+ # draw background
+ self.gc.foreground = self.bgColor
+ drawingArea.window.draw_rectangle( self.gc, True, startX, startY, event.area.width, event.area.height )
+
+ tracks = [ self.owner.getTrackSelected(i) for i in range(Config.NUMBER_OF_TRACKS) ]
+
+ # draw pages
+ self.gc.set_clip_mask( self.clipMask )
+
+ x = self.pageOffset
+ endx = x + Config.PAGE_THUMBNAIL_WIDTH
+ for pageId in self.noteDB.getTune():
+ if endx < startX:
+ x = endx
+ endx += Config.PAGE_THUMBNAIL_WIDTH
+ continue
+ if x > stopX: break
+
+ # draw thumbnail
+ if self.thumbnailDirty[pageId]:
+ self.gc.set_clip_origin( 0, 0 )
+ self.drawThumbnail( pageId, self.thumbnail[pageId], self.thumbnailDirtyRect[pageId] )
+ self.gc.set_clip_origin( x, self.pageY )
+ drawingArea.window.draw_drawable( self.gc, self.thumbnail[pageId], 0, 0, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+
+ # draw border if necessary
+ if pageId == self.displayedPage: # displayed page border
+ self.gc.set_function( gtk.gdk.INVERT )
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if tracks[i]:
+ drawingArea.window.draw_rectangle( self.gc, True, x + self.trackRect[i][0], self.pageY + self.trackRect[i][1], self.trackRect[i][2], self.trackRect[i][3] )
+ self.gc.set_function( gtk.gdk.COPY )
+ self.gc.foreground = self.displayedColor
+ self.gc.set_clip_origin( x - Config.PAGE_THUMBNAIL_WIDTH, self.pageY )
+ drawingArea.window.draw_rectangle( self.gc, True, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ elif pageId in self.selectedIds: # selected page border
+ self.gc.set_function( gtk.gdk.INVERT )
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if tracks[i]:
+ drawingArea.window.draw_rectangle( self.gc, True, x + self.trackRect[i][0], self.pageY + self.trackRect[i][1], self.trackRect[i][2], self.trackRect[i][3] )
+ self.gc.set_function( gtk.gdk.COPY )
+ self.gc.foreground = self.selectedColor
+ self.gc.set_clip_origin( x - Config.PAGE_THUMBNAIL_WIDTH, self.pageY )
+ drawingArea.window.draw_rectangle( self.gc, True, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+
+ x += Config.PAGE_THUMBNAIL_WIDTH
+
+ # draw drop marker
+ if self.dropAt >= 0:
+ self.gc.set_clip_rectangle( self.clearMask )
+ self.gc.set_line_attributes( self.dropWidth, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.lineColor
+ drawingArea.window.draw_line( self.gc, self.dropAtX, self.pageY+2, self.dropAtX, self.pageY+Config.PAGE_THUMBNAIL_HEIGHT-4 )
+
+ def invalidate_rect( self, x, y, width, height ):
+ if self.alloced == False: return
+ if x < self.visibleX: x = self.visibleX
+ if x + width > self.visibleEndX: width = self.visibleEndX - x
+ if width <= 0: return
+
+ self.dirtyRectToAdd.x = x
+ self.dirtyRectToAdd.y = y
+ self.dirtyRectToAdd.width = width
+ self.dirtyRectToAdd.height = height
+ if self.drawingArea.window:
+ self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True )
+ self.drawingAreaDirty = True
+
+ def invalidate_thumbnail( self, id, x, y, width, height ):
+ if not self.thumbnailDirty[id]:
+ self.thumbnailDirtyRect[id].x = x
+ self.thumbnailDirtyRect[id].y = y
+ self.thumbnailDirtyRect[id].width = width
+ self.thumbnailDirtyRect[id].height = height
+ self.thumbnailDirty[id] = True
+ else:
+ self.dirtyRectToAdd.x = x
+ self.dirtyRectToAdd.y = y
+ self.dirtyRectToAdd.width = width
+ self.dirtyRectToAdd.height = height
+ self.thumbnailDirtyRect[id] = self.thumbnailDirtyRect[id].union( self.dirtyRectToAdd )
+
+ ind = self.noteDB.getPageIndex( id )
+ self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ def ticksToPixels( self, beats, ticks ):
+ return int(round( ticks * self.pixelsPerTick[beats] ))
+ def pitchToPixels( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+ def pitchToPixelsDrum( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum ))
diff --git a/Edit/__init__.py b/Edit/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Edit/__init__.py
diff --git a/Edit/rm/BackgroundView.py b/Edit/rm/BackgroundView.py
new file mode 100644
index 0000000..ff6e75f
--- /dev/null
+++ b/Edit/rm/BackgroundView.py
@@ -0,0 +1,501 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from math import floor
+
+from Framework.Constants import Constants
+from GUI.GUIConstants import GUIConstants
+from GUI.Core.NoteParametersWindow import NoteParametersWindow
+
+from Framework.Core.Profiler import TP
+
+class SELECTNOTES:
+ ALL = -1
+ NONE = 0
+ ADD = 1
+ REMOVE = 2
+ EXCLUSIVE = 3
+
+#-------------------------------------------------------------
+# This is a TEMPORARY implementaion of the BackgroundView,
+# it was written quickly to get track selections working
+#-------------------------------------------------------------
+
+# TODO: Do I really have to subclass gtk.EventBox to get the button-press-event?
+# (I wasn't getting it subclassing directly from DrawingArea)
+class BackgroundView( gtk.EventBox ):
+ #-----------------------------------
+ # initialization
+ #-----------------------------------
+ def __init__( self, trackIDs, selectedTrackIDs, selectionChangedCallback, mutedTrackIDs, beatsPerPageAdjustment, trackDictionary, selectedPageIDs, updatePageCallback ):
+ gtk.EventBox.__init__( self )
+
+ self.drawingArea = gtk.DrawingArea()
+ self.add( self.drawingArea )
+
+ self.sizeInitialized = False
+
+ self.trackViews = {}
+ self.trackIDs = trackIDs
+ self.selectedTrackIDs = selectedTrackIDs
+ self.selectionChangedCallback = selectionChangedCallback
+ self.mutedTrackIDs = mutedTrackIDs
+ self.beatsPerPageAdjustment = beatsPerPageAdjustment
+ self.trackDictionary = trackDictionary
+ self.selectedPageIDs = selectedPageIDs
+ self.updatePageCallback = updatePageCallback
+
+ self.curAction = False # stores the current mouse action
+ self.curActionObject = False # stores the object that in handling the action
+
+ 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.drawingArea.connect( "expose-event", self.draw )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "button-release-event", self.handleButtonRelease )
+ self.connect( "motion-notify-event", self.handleMotion )
+
+ #-----------------------------------
+ # access methods
+ #-----------------------------------
+ def getTrackRect( self, trackID ):
+ return gtk.gdk.Rectangle( GUIConstants.BORDER_SIZE,
+ self.getTrackYLocation( trackID ),
+ self.getTrackWidth(),
+ self.getTrackHeight() )
+
+ def getTrackWidth( self ):
+ return self.get_allocation().width - 2 * ( GUIConstants.BORDER_SIZE + 2 )
+
+ def getFullHeight( self ):
+ return int( floor( self.get_allocation().height / len( self.trackIDs ) ) )
+
+ def getTrackHeight( self ):
+ return int( self.getFullHeight() - 2 * self.getTrackSpacing() )
+
+ #TODO-> trackIDs should probably be ordered!
+ # we're just using trackID as an index here (this will only work until you can remove tracks)
+ def getTrackYLocation( self, trackID ):
+ if self.getTrackHeight() < 0:
+ return 0
+ else:
+ trackIndex = trackID
+
+ trackHeight = int( floor( self.get_allocation().height / len( self.trackIDs ) ) )
+ trackBackgroundYValue = trackHeight * trackIndex
+ return trackBackgroundYValue + GUIConstants.BORDER_SIZE
+
+ def getTrackSpacing( self ):
+ return GUIConstants.TRACK_SPACING
+
+ #-----------------------------------
+ # callback methods
+ #-----------------------------------
+ def set_size_request( self, width, height ):
+ self.sizeInitialized = True
+ self.drawingArea.set_size_request( width, height )
+ self.height = height
+ self.width = width
+
+ numTracks = len(self.trackIDs)
+ trackSpacing = self.getTrackSpacing()
+ if numTracks: self.trackHeight = int( floor( (height - trackSpacing*(numTracks-1)) / numTracks ) )
+ else: self.trackHeight = 1
+ self.trackWidth = width
+
+ trackCount = 0
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].set_size_request( self.trackWidth, self.trackHeight )
+ self.trackViews[trackID].setPositionOffset( (0, trackCount*(self.trackHeight+trackSpacing)) )
+ trackCount += 1
+
+ def setCurrentTracks( self, trackViews ):
+
+ oldLen = len(self.trackViews)
+
+ if oldLen and trackViews != self.trackViews: self.clearSelectedNotes() # clear all the currently selected notes
+
+ self.trackViews = trackViews
+
+ numTracks = len(self.trackViews)
+ if oldLen != numTracks and self.sizeInitialized:
+ trackSpacing = self.getTrackSpacing()
+ if numTracks: self.trackHeight = int( floor( (self.height - trackSpacing*(numTracks-1)) / numTracks ) )
+ else: self.trackHeight = 1
+ trackCount = 0
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].set_size_request( self.trackWidth, self.trackHeight )
+ self.trackViews[trackID].setPositionOffset( (0, trackCount*(self.trackHeight+trackSpacing)) )
+ trackCount += 1
+
+ self.dirty()
+
+
+ def getNoteParameters( self ):
+ for trackID in self.selectedTrackIDs:
+ for pageID in self.selectedPageIDs:
+ for note in self.trackDictionary[ trackID ][ pageID ]:
+ newPitch = note.pitch + self.noteParameters.pitchAdjust.value
+ newAmplitude = note.amplitude * self.noteParameters.amplitudeAdjust.value
+ newPan = self.noteParameters.panAdjust.value
+ newReverbSend = note.reverbSend * self.noteParameters.reverbSendAdjust.value
+ newAttack = self.noteParameters.attackAdjust.value
+ newDecay = self.noteParameters.decayAdjust.value
+ newFilterType = self.noteParameters.filterType
+ newFilterCutoff = self.noteParameters.filterCutoff
+ newTied = self.noteParameters.tied
+ newOverlap = self.noteParameters.overlap
+
+ note.pitch = self.noteParametersBoundaries( newPitch, note.pitch, Constants.MINIMUM_PITCH, Constants.MAXIMUM_PITCH )
+ note.amplitude = self.noteParametersBoundaries( newAmplitude, note.amplitude, Constants.MINIMUM_AMPLITUDE, Constants.MAXIMUM_AMPLITUDE )
+ note.reverbSend = self.noteParametersBoundaries( newReverbSend, note.reverbSend, Constants.MINIMUM_AMPLITUDE,
+ Constants.MAXIMUM_AMPLITUDE )
+ if newPan != note.pan:
+ note.pan = newPan
+
+ if newAttack != note.attack:
+ note.attack = newAttack
+
+ if newDecay != note.decay:
+ note.decay = newDecay
+
+ if newFilterType != note.filterType:
+ note.filterType = newFilterType
+
+ if newFilterCutoff != note.filterCutoff:
+ note.filterCutoff = newFilterCutoff
+
+ if newTied != note.tied:
+ note.tied = newTied
+
+ if newOverlap != note.overlap:
+ note.overlap = newOverlap
+
+ self.updatePageCallback()
+
+ def noteParametersBoundaries( self, newValue, noteValue, minBoundary, maxBoundary ):
+ if newValue != noteValue:
+ if newValue >= minBoundary and newValue <= maxBoundary:
+ return newValue
+ elif newValue < minBoundary:
+ return minBoundary
+ elif newValue > maxBoundary:
+ return maxBoundary
+ else:
+ return noteValue
+
+ #-----------------------------------
+ # action and event methods
+ #-----------------------------------
+ def setCurrentAction( self, action, obj ):
+ if self.curAction:
+ print "BackgroundView - Action already in progress!"
+
+ 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()
+
+ def doneCurrentAction( self ):
+ if self.curAction == "note-drag-onset": self.doneNoteDrag()
+ elif self.curAction == "note-drag-duration": self.doneNoteDrag()
+ elif self.curAction == "note-drag-pitch": self.doneNoteDrag()
+
+ self.curAction = False
+ self.curActionObject = False
+
+ def toggleTrack( self, trackID, exclusive ):
+ if exclusive:
+ self.selectedTrackIDs.clear()
+ self.selectedTrackIDs.add( trackID )
+ else:
+ if trackID in self.selectedTrackIDs:
+ self.selectedTrackIDs.discard( trackID )
+ else:
+ self.selectedTrackIDs.add( trackID )
+
+ 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()
+ self.dirty()
+
+ def selectNotesByBar( self, selID, startX, stopX ):
+ beatCount = int(round( self.beatsPerPageAdjustment.value, 0 ))
+ for trackID in self.trackIDs:
+ if trackID == selID:
+ notes = self.trackViews[trackID].getNotesByBar( beatCount, startX, stopX )
+ self.trackViews[trackID].selectNotes( SELECTNOTES.EXCLUSIVE, notes )
+ else:
+ self.trackViews[trackID].selectNotes( SELECTNOTES.NONE, [] )
+ self.selectionChanged()
+
+ def selectNotesByTrack( self, selID ):
+ for trackID in self.trackIDs:
+ if trackID == selID: self.trackViews[trackID].selectNotes( SELECTNOTES.ALL, [] )
+ else: self.trackViews[trackID].selectNotes( SELECTNOTES.NONE, [] )
+ self.selectionChanged()
+
+ def selectNotes( self, noteDic ):
+ for trackID in self.trackIDs:
+ if trackID in noteDic: self.trackViews[trackID].selectNotes( SELECTNOTES.EXCLUSIVE, noteDic[trackID] )
+ else: self.trackViews[trackID].selectNotes( SELECTNOTES.NONE, [] )
+ self.selectionChanged()
+
+ def addNotesToSelection( self, noteDic ):
+ for trackID in self.trackIDs:
+ if trackID in noteDic: self.trackViews[trackID].selectNotes( SELECTNOTES.ADD, noteDic[trackID] )
+ self.selectionChanged()
+
+ def deselectNotes( self, noteDic ):
+ for trackID in self.trackIDs:
+ if trackID in noteDic: self.trackViews[trackID].selectNotes( SELECTNOTES.REMOVE, noteDic[trackID] )
+ self.selectionChanged()
+
+ def clearSelectedNotes( self ):
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].selectNotes( SELECTNOTES.NONE, [] )
+ self.selectionChanged()
+
+ def updateDragLimits( self ):
+ self.dragLimits = [ [-9999,9999], [-9999,9999], [-9999,9999] ] # initialize to big numbers!
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].updateDragLimits( self.dragLimits )
+
+ def noteDragOnset( self, event ):
+ dx = event.x - self.clickLoc[0]
+ dx = min( self.dragLimits[0][1], max( self.dragLimits[0][0], dx ) )
+ dy = 0
+ dw = 0
+
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].noteDrag( self, dx, dy, dw )
+ self.dirty()
+
+ def noteDragDuration( self, event ):
+ dx = 0
+ dy = 0
+ dw = event.x - self.clickLoc[0]
+ dw = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dw ) )
+
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].noteDrag( self, dx, dy, dw )
+ self.dirty()
+
+ def noteDragPitch( self, event ):
+ dx = 0
+ dy = event.y - self.clickLoc[1]
+ dy = min( self.dragLimits[1][1], max( self.dragLimits[1][0], dy ) )
+ dw = 0
+
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].noteDrag( self, dx, dy, dw )
+ self.dirty()
+
+ def doneNoteDrag( self ):
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].doneNoteDrag( self )
+
+ def updateMarquee( self, event ):
+ self.marqueeLoc = [ event.x, event.y ]
+ parentRect = self.get_allocation()
+ if self.marqueeLoc[0] < 0: self.marqueeLoc[0] = 0
+ elif self.marqueeLoc[0] > parentRect.width: self.marqueeLoc[0] = parentRect.width
+ if self.marqueeLoc[1] < 0: self.marqueeLoc[1] = 0
+ elif self.marqueeLoc[1] > parentRect.height: self.marqueeLoc[1] = parentRect.height
+
+ self.dirty()
+
+ def doneMarquee( self, event ):
+ if self.marqueeLoc:
+ start = [ min(self.clickLoc[0],self.marqueeLoc[0]), \
+ min(self.clickLoc[1],self.marqueeLoc[1]) ]
+ stop = [ max(self.clickLoc[0],self.marqueeLoc[0]), \
+ max(self.clickLoc[1],self.marqueeLoc[1]) ]
+
+ select = {}
+
+ trackSpacing = self.getTrackSpacing()
+ trackTop = 0
+ for trackID in self.trackIDs:
+ notes = self.trackViews[trackID].handleMarqueeSelect( self, start, stop )
+ if notes: select[trackID] = notes
+ trackTop += self.trackHeight + trackSpacing
+ if trackTop > stop[1]: break
+
+ self.selectNotes( select )
+
+ self.marqueeLoc = False
+ self.doneCurrentAction()
+
+ self.dirty()
+
+ def handleButtonPress( self, drawingArea, event ):
+
+ TP.ProfileBegin( "BV::handleButtonPress" )
+
+ 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 = [ event.x, event.y ]
+
+ trackSpacing = self.getTrackSpacing()
+ trackTop = 0
+ for trackID in self.trackIDs:
+ handled = self.trackViews[trackID].handleButtonPress( self, event )
+ trackTop += self.trackHeight + trackSpacing
+ if handled or trackTop > event.y: break
+
+ if handled:
+ if not self.curAction: self.curAction = True # it was handled maybe no action was declared, set curAction to True anyway
+ TP.ProfileEnd( "BV::handleButtonPress" )
+ return
+
+ if event.button == 3:
+ self.noteParameters = NoteParametersWindow( self.trackDictionary, self.getNoteParameters )
+ self.setCurrentAction( "noteParameters", False )
+
+ TP.ProfileEnd( "BV::handleButtonPress" )
+
+
+ def handleButtonRelease( self, drawingArea, event ):
+ TP.ProfileBegin( "BV::handleButtonRelease" )
+
+ if not self.curAction: #do track selection stuff here so that we can also handle marquee selection
+ trackSpacing = self.getTrackSpacing()
+ trackTop = 0
+ for trackID in self.trackIDs:
+ handled = self.trackViews[trackID].handleButtonRelease( self, event, self.buttonPressCount )
+ trackTop += self.trackHeight + trackSpacing
+ if handled or trackTop > event.y: break
+
+ if handled: self.dirty()
+
+ TP.ProfileEnd( "BV::handleButtonRelease" )
+ return
+
+ if not self.curActionObject: # there was no real action to carry out
+ self.curAction = False
+ TP.ProfileEnd( "BV::handleButtonRelease" )
+ return
+
+ if self.curActionObject != self:
+ if self.curActionObject.handleButtonRelease( self, event, self.buttonPressCount ):
+ self.dirty()
+ TP.ProfileEnd( "BV::handleButtonRelease" )
+ return
+
+
+ # we're doing the action ourselves
+
+ if self.curAction == "marquee": self.doneMarquee( event )
+
+ TP.ProfileEnd( "BV::handleButtonRelease" )
+ return
+
+ def handleMotion( self, drawingArea, event ):
+ TP.ProfileBegin( "BV::handleMotion" )
+
+ 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 )
+ TP.ProfileEnd( "BV::handleMotion" )
+ return
+
+ if self.curAction == "note-drag-duration":
+ self.noteDragDuration( event )
+ TP.ProfileEnd( "BV::handleMotion" )
+ return
+
+ if self.curAction == "note-drag-pitch":
+ self.noteDragPitch( event )
+ TP.ProfileEnd( "BV::handleMotion" )
+ return
+
+ # we're doing the action ourselves
+
+ if self.curAction == "marquee": self.updateMarquee( event )
+
+ TP.ProfileEnd( "BV::handleMotion" )
+ return
+
+ def TEMPOLDSTUFF(self):
+
+ #TODO change this to accomodate the space between tracks
+ trackHeight = ( drawingArea.get_allocation().height - 1 ) / len( self.trackIDs )
+ trackID = int( floor( event.y / trackHeight ) )
+
+ if event.type == gtk.gdk.BUTTON_PRESS:
+ #single click toggles track selection
+ if trackID in self.selectedTrackIDs:
+ self.selectedTrackIDs.discard( trackID )
+ else:
+ self.selectedTrackIDs.add( trackID )
+ elif event.type == gtk.gdk._2BUTTON_PRESS:
+ #double click selects a single track
+ self.selectedTrackIDs.clear()
+ self.selectedTrackIDs.add( trackID )
+
+ self.drawingArea.queue_draw()
+ self.selectionChangedCallback()
+ if event.button == 3:
+ self.noteParameters = NoteParametersWindow( self.trackDictionary, self.getNoteParameters )
+
+ #-----------------------------------
+ # drawing methods
+ #-----------------------------------
+ def draw( self, drawingArea, event ):
+ TP.ProfileBegin( "BackgroundView::draw" )
+
+ context = drawingArea.window.cairo_create()
+ context.set_antialias(0) # I don't know what to set this to to turn it off, and it doesn't seem to work anyway!?
+
+ #parentRect = self.get_allocation()
+
+ beatCount = int(round( self.beatsPerPageAdjustment.value, 0 ))
+
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].draw( context,
+ beatCount,
+ trackID in self.selectedTrackIDs )
+
+ # if self.curAction == "note-drag": # draw a cross at clickLoc
+ # lineW = 1
+ # context.set_line_width( lineW )
+ # lineWDIV2 = lineW/2.0
+ # context.set_source_rgb( 1, 1, 1 )
+
+ # context.move_to( self.clickLoc[0] + lineWDIV2 - 3, self.clickLoc[1] + lineWDIV2 )
+ # context.rel_line_to( 6, 0 )
+ # context.stroke()
+ # context.move_to( self.clickLoc[0] + lineWDIV2, self.clickLoc[1] + lineWDIV2 - 3)
+ # context.rel_line_to( 0, 6 )
+ # context.stroke()
+
+ if self.marqueeLoc: # draw the selection rect
+ lineW = 1
+ context.set_line_width( lineW )
+ lineWDIV2 = lineW/2.0
+
+ context.move_to( self.clickLoc[0] + lineWDIV2, self.clickLoc[1] + lineWDIV2 )
+ context.line_to( self.marqueeLoc[0] + lineWDIV2, self.clickLoc[1] + lineWDIV2 )
+ context.line_to( self.marqueeLoc[0] + lineWDIV2, self.marqueeLoc[1] + lineWDIV2 )
+ context.line_to( self.clickLoc[0] + lineWDIV2, self.marqueeLoc[1] + lineWDIV2 )
+ context.close_path()
+ context.set_source_rgb( 1, 1, 1 )
+ context.stroke()
+
+ TP.ProfileEnd( "BackgroundView::draw" )
+
+ def dirty( self ):
+ self.queue_draw()
+
+
diff --git a/Edit/rm/KeyboardInput.py b/Edit/rm/KeyboardInput.py
new file mode 100644
index 0000000..d1a0d83
--- /dev/null
+++ b/Edit/rm/KeyboardInput.py
@@ -0,0 +1,97 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from Framework import Note
+from Framework.CSound.CSoundConstants import CSoundConstants
+from Framework.Generation.GenerationConstants import GenerationConstants
+from GUI.Core.KeyMapping import KEY_MAP
+
+
+class KeyboardInput:
+ def __init__( self , getCurrentTick , getTrackInstruments , getTrackDictionary , getSelectedTrackIDs , mainWindowUpdateCallback , pagePlayerUpdateCallback , getCurrentPageIDCallback ):
+ self.active = False
+ self.record = False
+ self.monophonic = False
+ self.key_dict = dict()
+
+ self.getCurrentTick = getCurrentTick
+ self.getTrackInstruments = getTrackInstruments
+ self.getTrackDictionary = getTrackDictionary
+ self.getSelectedTrackIDs = getSelectedTrackIDs
+ self.mainWindowUpdateCallback = mainWindowUpdateCallback
+ self.pagePlayerUpdateCallback = pagePlayerUpdateCallback
+ self.getCurrentPageIDCallback = getCurrentPageIDCallback
+
+ def onKeyPress(self,widget,event):
+ if not self.active:
+ return
+ if self.record:
+ self.monophonic = False
+
+ key = event.hardware_keycode
+ # If the key is already in the dictionnary, exit function (to avoir key repeats)
+ if self.key_dict.has_key(key):
+ return
+ # Assign on which track the note will be created according to the number of keys pressed
+ track = len(self.key_dict)+10
+ if self.monophonic:
+ track = 10
+ # If the pressed key is in the keymap
+ if KEY_MAP.has_key(key):
+ # CsoundNote parameters
+ onset = self.getCurrentTick()
+ pitch = KEY_MAP[key]
+ duration = -1
+ instrument = self.getTrackInstruments()[0]
+ # get instrument from top selected track if a track is selected
+ if self.getSelectedTrackIDs():
+ instrument = self.getTrackInstruments()[min(self.getSelectedTrackIDs())]
+
+ if instrument == 'drum1kit':
+ if GenerationConstants.DRUMPITCH.has_key( pitch ):
+ instrument = CSoundConstants.DRUM1INSTRUMENTS[ GenerationConstants.DRUMPITCH[ pitch ] ]
+ else:
+ instrument = CSoundConstants.DRUM1INSTRUMENTS[ pitch ]
+ pitch = 36
+ duration = 100
+
+ if CSoundConstants.INSTRUMENTS[instrument].csoundInstrumentID == 102:
+ duration = 100
+
+ # Create and play the note
+ self.key_dict[key] = Note.note_new(onset = 0,
+ pitch = pitch,
+ amplitude = 1,
+ pan = 0.5,
+ duration = duration,
+ trackID = track,
+ fullDuration = False,
+ instrument = instrument,
+ instrumentFlag = instrument)
+ Note.note_play(self.key_dict[key])
+
+ def onKeyRelease(self,widget,event):
+ if not self.active:
+ return
+ key = event.hardware_keycode
+
+ if KEY_MAP.has_key(key):
+ self.key_dict[key]['duration'] = 0
+ self.key_dict[key]['amplitude'] = 0
+ self.key_dict[key]['dirty'] = True
+ Note.note_play(self.key_dict[key])
+ self.key_dict[key]['duration'] = self.getCurrentTick() - self.key_dict[key]['onset']
+ #print "onset",self.key_dict[key].onset
+ #print "dur",self.key_dict[key].duration
+ if self.record and len( self.getSelectedTrackIDs() ) != 0:
+ self.key_dict[key]['amplitude'] = 1
+ self.getTrackDictionary()[min(self.getSelectedTrackIDs())][self.getCurrentPageIDCallback()].append(self.key_dict[key])
+ self.mainWindowUpdateCallback()
+ self.pagePlayerUpdateCallback()
+ del self.key_dict[key]
+
+
+ def onButtonPress(self,widget,event):
+ pass
+
diff --git a/Edit/rm/NoteView.py b/Edit/rm/NoteView.py
new file mode 100644
index 0000000..ac139a1
--- /dev/null
+++ b/Edit/rm/NoteView.py
@@ -0,0 +1,253 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from Framework.Constants import Constants
+from Framework.CSound.CSoundConstants import CSoundConstants
+from GUI.GUIConstants import GUIConstants
+from GUI.Core.NoteParametersWindow import NoteParametersWindow
+
+from BackgroundView import SELECTNOTES
+
+#----------------------------------------------------------------------
+# TODO: currently we are only using CSoundNotes,
+# - this should updated to handle generic Note objects
+#----------------------------------------------------------------------
+
+#----------------------------------------------------------------------
+# A view for the (CSound)Note class
+#----------------------------------------------------------------------
+class NoteView:
+ #-----------------------------------
+ # initialization
+ # TODO: not sure if passing in beatsPerPageAdjustment is the best way to go about it
+ #-----------------------------------
+ def __init__( self, note, track, beatsPerPageAdjustment ):
+ self.note = note
+
+ self.track = track
+ self.beatsPerPageAdjustment = beatsPerPageAdjustment
+ self.posOffset = (0,0)
+
+ self.baseOnset = self.basePitch = self.baseDuration = self.lastDragX = self.lastDragY = self.lastDragW = 0 # note dragging properties
+
+ self.sampleNote = None
+
+ self.parentSize = False
+
+ self.selected = False
+ self.potentialDeselect = False
+
+
+ def getNoteParameters( self ):
+ self.note.pitch = self.noteParameters.pitchAdjust.value
+ self.note.amplitude = self.noteParameters.amplitudeAdjust.value
+ self.note.pan = self.noteParameters.panAdjust.value
+ self.note.attack = self.noteParameters.attackAdjust.value
+ self.note.decay = self.noteParameters.decayAdjust.value
+ self.note.reverbSend = self.noteParameters.reverbSendAdjust.value
+ self.note.filterType = self.noteParameters.filterType
+ self.note.filterCutoff = self.noteParameters.filterCutoff
+ self.note.tied = self.noteParameters.tied
+ self.note.overlap = self.noteParameters.overlap
+
+ def handleButtonPress( self, emitter, event ):
+ eX = event.x - self.x
+ eY = event.y - self.y
+
+ if eX < 0 or eX > self.width \
+ or eY < 0 or eY > self.height:
+ return False
+
+ if event.button == 3:
+ self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters )
+ return True
+
+ if event.type == gtk.gdk._2BUTTON_PRESS: # select bar
+ self.potentialDeselect = False
+ emitter.selectNotesByBar( self.track.getID(), self.x, self.x+self.width )
+ elif event.type == gtk.gdk._3BUTTON_PRESS: # select track
+ self.potentialDeselect = False
+ emitter.selectNotesByTrack( self.track.getID() )
+ else:
+ if self.getSelected(): # we already selected, might want to delected
+ self.potentialDeselect = True
+ else:
+ emitter.selectNotes( { self.track.getID(): [ self ] } )
+ self.updateSampleNote( self.note.pitch )
+
+ 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 )
+
+
+ emitter.dirty()
+
+ return True
+
+ def handleButtonRelease( self, emitter, event, buttonPressCount ):
+
+ if self.potentialDeselect:
+ self.potentialDeselect = False
+ emitter.deselectNotes( { self.track.getID(): [ self ] } )
+
+ self.clearSampleNote()
+
+ emitter.doneCurrentAction()
+
+ return True
+
+ def noteDrag( self, emitter, dx, dy, dw ):
+ self.potentialDeselect = False
+
+ ticksPerPixel = (round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT) / self.parentSize[0]
+ stepPerPixel = (Constants.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.parentSize[1] - self.height)
+ pitchPerStep = (Constants.MAXIMUM_PITCH-Constants.MINIMUM_PITCH)/(Constants.NUMBER_OF_POSSIBLE_PITCHES-1)
+
+ if dx != self.lastDragX:
+ self.lastDragX = dx
+ self.note.onset = self.baseOnset + int(dx*ticksPerPixel)
+
+ if dy != self.lastDragY:
+ self.lastDragY = dy
+ newPitch = self.basePitch + round(-dy*stepPerPixel)*pitchPerStep
+ self.note.pitch = newPitch
+ self.updateSampleNote( newPitch )
+
+ if dw != self.lastDragW:
+ self.lastDragW = dw
+ self.note.duration = self.baseDuration + int(dw*ticksPerPixel)
+
+ self.updateTransform( False )
+
+ def doneNoteDrag( self, emitter ):
+ self.baseOnset = self.note.onset
+ self.basePitch = self.note.pitch
+ self.baseDuration = self.note.duration
+
+ self.lastDragX = 0
+ self.lastDragY = 0
+ self.lastDragW = 0
+
+ self.clearSampleNote()
+
+
+ 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
+
+ #-----------------------------------
+ # draw
+ #-----------------------------------
+
+ def setPositionOffset( self, offset ):
+ self.posOffset = offset
+ if self.parentSize: self.updateTransform( False )
+
+ def draw( self, context ):
+ lineW = GUIConstants.NOTE_BORDER_SIZE
+ lineWDIV2 = lineW/2.0
+ context.set_line_width( lineW )
+
+ context.move_to( self.x + lineWDIV2, self.y + lineWDIV2 )
+ context.rel_line_to( self.width - lineW, 0 )
+ context.rel_line_to( 0, self.height - lineW )
+ context.rel_line_to( -self.width + lineW, 0 )
+ context.close_path()
+
+ #background
+ colour = 1 - ( ( self.note.amplitude * 0.7 ) + 0.3 )
+ context.set_source_rgb( colour, colour, colour )
+ context.fill_preserve()
+
+ #border
+ if self.selected: context.set_source_rgb( 1, 1, 1 )
+ else: context.set_source_rgb( 0, 0, 0 )
+ context.stroke()
+
+ #-----------------------------------
+ # update
+ #-----------------------------------
+
+ def updateTransform( self, parentSize ):
+ if parentSize: self.parentSize = parentSize
+ self.width = int( self.parentSize[0] * self.note.duration / (round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT) )
+ self.height = round( max( GUIConstants.MINIMUM_NOTE_HEIGHT, self.parentSize[1] / (Constants.NUMBER_OF_POSSIBLE_PITCHES-1) ) )
+ self.x = int( self.parentSize[0] * self.note.onset / (round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT) ) \
+ + self.posOffset[0]
+ self.y = round( (self.parentSize[1]-self.height) * ( Constants.MAXIMUM_PITCH - self.note.pitch ) / (Constants.NUMBER_OF_POSSIBLE_PITCHES-1) ) \
+ + self.posOffset[1]
+
+ def checkX( self, startx, stopx ):
+ if self.x >= startx and self.x < stopx: return True
+ else: return False
+
+ def getStartTick( self ):
+ return self.note.onset
+
+ def getEndTick( self ):
+ return self.note.onset + self.note.duration
+
+ def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound ):
+ pixelsPerTick = self.parentSize[0] / (round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT)
+ pixelsPerPitch = (self.parentSize[1] - self.height) / (Constants.MAXIMUM_PITCH - Constants.MINIMUM_PITCH)
+ left = (leftBound - self.note.onset) * pixelsPerTick
+ right = (rightBound - self.note.duration - self.note.onset) * pixelsPerTick
+ up = (self.note.pitch - Constants.MAXIMUM_PITCH) * pixelsPerPitch
+ down = (self.note.pitch - Constants.MINIMUM_PITCH) * pixelsPerPitch
+ short = (Constants.MINIMUM_NOTE_DURATION - self.note.duration) * pixelsPerTick
+ long = (widthBound - self.note.duration - self.note.onset) * pixelsPerTick
+
+ if dragLimits[0][0] < left: dragLimits[0][0] = left
+ if dragLimits[0][1] > right: dragLimits[0][1] = right
+ if dragLimits[1][0] < up: dragLimits[1][0] = up
+ if dragLimits[1][1] > down: dragLimits[1][1] = down
+ 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.onset
+ self.basePitch = self.note.pitch
+ self.baseDuration = self.note.duration
+
+ def updateSampleNote( self, pitch ):
+ if self.sampleNote == None:
+ self.sampleNote = self.note.clone()
+ #TODO clean this up:
+ if CSoundConstants.INSTRUMENTS[ self.sampleNote.instrumentFlag ].csoundInstrumentID == 103:
+ self.sampleNote.duration = 100
+ else:
+ self.sampleNote.duration = -1
+ self.sampleNote.play()
+
+ elif self.sampleNote.pitch != pitch:
+ self.sampleNote.pitch = pitch
+ self.sampleNote.play()
+
+ def clearSampleNote( self ):
+ if self.sampleNote != None:
+ self.sampleNote.duration = 0
+ self.sampleNote.play()
+ del self.sampleNote
+ self.sampleNote = None
+
+ #-----------------------------------
+ # Selection
+ #-----------------------------------
+
+ def setSelected( self, state ):
+ if self.selected != state:
+ self.selected = state
+ return True # state changed
+ return False # state is the same
+
+ def getSelected( self ):
+ return self.selected
diff --git a/Edit/rm/PageBankView.py b/Edit/rm/PageBankView.py
new file mode 100644
index 0000000..fedadef
--- /dev/null
+++ b/Edit/rm/PageBankView.py
@@ -0,0 +1,85 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from GUI.GUIConstants import GUIConstants
+from GUI.Core.PageView import PageView
+
+class PageBankView( gtk.Frame ):
+
+ NO_PAGE = -1
+
+ def __init__( self, selectPageCallback, pageDropCallback ):
+ gtk.Frame.__init__( self )
+ self.table = gtk.Table( 1, GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS )
+ self.add( self.table )
+ self.drag_dest_set( gtk.DEST_DEFAULT_ALL, [ ( "tune page", gtk.TARGET_SAME_APP, 11 )], gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE )
+ self.connect( "drag_data_received", self.dragDataReceived )
+ self.selectPageCallback = selectPageCallback
+ self.pageDropCallback = pageDropCallback
+ self.selectedPageIds = set([])
+ self.pageIndexDictionary = {}
+ self.pageViews = {}
+
+ def dragDataReceived( self, widget, context, x, y, selectionData, info, time):
+ self.pageDropCallback( selectionData.data )
+
+ def addPage( self, pageId, invokeCallback = True ):
+ pageIndex = len( self.pageViews.keys() )
+ self.pageIndexDictionary[ pageIndex ] = pageId
+
+ #TODO: resize table to suit number of pages?
+ #if pageIndex > ( self.table.n-rows * self.table.n_columns ):
+ # self.table.resize( self.table.n_rows + 1, self.table.n_columns )
+
+ pageView = PageView( pageIndex, self.selectPage, True )
+ self.pageViews[ pageIndex ] = pageView
+
+ columnIndex = pageIndex % GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS
+ rowIndex = int( pageIndex / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS )
+ self.table.attach( pageView, columnIndex, columnIndex + 1, rowIndex, rowIndex + 1, gtk.SHRINK, gtk.SHRINK )
+
+ self.updateSize( pageView )
+
+ pageView.drag_source_set( gtk.gdk.BUTTON1_MASK,
+ [ ( "bank page", gtk.TARGET_SAME_APP, 10 ) ],
+ gtk.gdk.ACTION_COPY )
+
+ self.selectPage( pageId, True, invokeCallback )
+
+ pageView.show()
+
+ def set_size_request( self, width, height ):
+ gtk.Frame.set_size_request( self, width, height )
+ self.table.set_size_request( width, height )
+ for pageId in self.pageViews.keys():
+ self.updateSize( self.pageViews[ pageId ] )
+
+ def updateSize( self, pageView ):
+ pageView.set_size_request( self.get_allocation().width / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS,
+ GUIConstants.PAGE_HEIGHT - 1 )
+
+ def selectPage( self, selectedPageId, invokeCallback = True, deselectOthers = True ):
+ if deselectOthers:
+ for pageId in self.pageViews.keys():
+ self.pageViews[ pageId ].setSelected( pageId == selectedPageId )
+ if pageId != selectedPageId:
+ self.selectedPageIds.discard( pageId )
+ else:
+ self.selectedPageIds.add( pageId )
+ #nb: pageId might be NO_PAGE, and selectedPageIds can be empty here
+
+ else:
+ self.pageViews[ selectedPageId ].toggleSelected()
+ if self.pageViews[ selectedPageId ].selected:
+ self.selectedPageIds.add( selectedPageId )
+ else:
+ self.selectedPageIds.discard( selectedPageId )
+
+ if invokeCallback:
+ self.selectPageCallback( selectedPageId )
+
+ def getSelectedPageIds( self ):
+ rval = filter( lambda id: self.pageViews[id].selected == True, self.pageViews.keys())
+ return rval
+
diff --git a/Edit/rm/PageView.py b/Edit/rm/PageView.py
new file mode 100644
index 0000000..00b650b
--- /dev/null
+++ b/Edit/rm/PageView.py
@@ -0,0 +1,65 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import pango
+
+from GUI.GUIConstants import GUIConstants
+
+class PageView( gtk.DrawingArea ):
+ def __init__( self, pageID, selectPageCallback, selected = False ):
+ gtk.DrawingArea.__init__( self )
+
+ self.pageID = pageID
+ self.selectPageCallback = selectPageCallback
+ self.selected = selected
+
+ self.add_events( gtk.gdk.BUTTON_PRESS_MASK )
+
+ self.connect( "expose-event", self.handleExposeEvent )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "drag_data_get", self.getData )
+
+ def handleButtonPress( self, widget, event ):
+ if event.button == 1 or event.button == 3:
+ self.selectPageCallback( self.pageID, event.button == 1 )
+
+ def getData( self, widget, context, selection, targetType, eventTime ):
+ return selection.set( gtk.gdk.SELECTION_PRIMARY, 32, "p %d" % self.pageID )
+
+ def toggleSelected( self ):
+ self.selected = not self.selected
+ self.queue_draw()
+
+ def setSelected( self, selected ):
+ if self.selected != selected:
+ self.selected = selected
+ self.queue_draw()
+
+ # TODO: this is temporary: replace with a visual representation of the page
+ def handleExposeEvent( self, drawingArea, event ):
+ size = self.get_allocation()
+ context = self.window.cairo_create()
+
+ if self.selected:
+ context.set_line_width( GUIConstants.PAGE_SELECTED_BORDER_SIZE )
+ else:
+ context.set_line_width( GUIConstants.PAGE_BORDER_SIZE )
+ context.move_to( 0, 0 )
+ context.rel_line_to( size.width, 0 )
+ context.rel_line_to( 0, size.height )
+ context.rel_line_to( -size.width, 0 )
+ context.close_path()
+
+ #blue background
+ context.set_source_rgb( 0.75, 0.75, 0.75 )
+ context.fill_preserve()
+
+ #black border
+ context.set_source_rgb( 0, 0, 0 )
+ context.stroke()
+
+ #text
+ layout = self.create_pango_layout( "%d" % ( self.pageID + 1 ) )
+ layout.set_font_description( pango.FontDescription( 'Sans 10' ) )
+ self.window.draw_layout( self.window.new_gc(), 5, 5, layout )
diff --git a/Edit/rm/PositionIndicator.py b/Edit/rm/PositionIndicator.py
new file mode 100644
index 0000000..aadc4f4
--- /dev/null
+++ b/Edit/rm/PositionIndicator.py
@@ -0,0 +1,47 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+#----------------------------------------------------------------------
+# A verical bar used to show the current point in time on a page
+# TODO: modify this class to change the current point in time
+# on click and drag
+#----------------------------------------------------------------------
+class PositionIndicator( gtk.DrawingArea ):
+ #-----------------------------------
+ # initialization
+ #-----------------------------------
+ def __init__( self, trackIDs, selectedTrackIDs, mutedTrackIDs ):
+ gtk.DrawingArea.__init__( self )
+
+ self.trackIDs = trackIDs
+ self.selectedTrackIDs = selectedTrackIDs
+ self.mutedTrackIDs = mutedTrackIDs
+
+ self.connect( "expose-event", self.draw )
+
+ def draw( self, drawingArea, event ):
+ indicatorSize = self.get_allocation()
+ trackHeight = indicatorSize.height / len( self.trackIDs )
+
+ context = drawingArea.window.cairo_create()
+
+ trackIndex = 0
+ for trackID in self.trackIDs:
+ height = trackIndex * trackHeight
+
+ context.move_to( 0, height )
+ context.rel_line_to( indicatorSize.width, 0 )
+ context.rel_line_to( 0, height + trackHeight )
+ context.rel_line_to( -indicatorSize.width, 0 )
+ context.close_path()
+
+ if trackID not in self.mutedTrackIDs:
+ context.set_source_rgb( 0, 0, 0 ) #black
+ else:
+ context.set_source_rgb( 0.6, 0.6, 0.6 ) #grey
+
+ context.fill_preserve()
+ context.stroke()
+
+ trackIndex += 1 \ No newline at end of file
diff --git a/Edit/rm/TrackView.py b/Edit/rm/TrackView.py
new file mode 100644
index 0000000..0b66abd
--- /dev/null
+++ b/Edit/rm/TrackView.py
@@ -0,0 +1,263 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from Framework.Constants import Constants
+from GUI.GUIConstants import GUIConstants
+
+from BackgroundView import SELECTNOTES
+from NoteView import NoteView
+
+
+#----------------------------------------------------------------------
+# This view class is used to show the contents of a NoteTrack
+# i.e. a Collection of Note objects
+#----------------------------------------------------------------------
+class TrackView:
+ #-----------------------------------
+ # initialization functions
+ #-----------------------------------
+ def __init__( self, trackID, beatsPerPageAdjustment ):
+ self.trackID = trackID
+ self.beatsPerPageAdjustment = beatsPerPageAdjustment
+ self.noteViews = []
+ self.posOffset = (0,0)
+ self.selectedNotes = []
+
+ def getID( self ):
+ return self.trackID
+
+ #-----------------------------------
+ # modification methods
+ #-----------------------------------
+ def setNotes( self, notes ):
+ self.clearNotes()
+
+ lineW = self.getBorderWidth()
+
+ for note in notes:
+ noteView = NoteView( note, self, self.beatsPerPageAdjustment )
+ self.noteViews.append( noteView )
+ noteView.setPositionOffset( (self.posOffset[0]+lineW, self.posOffset[1]+lineW ) )
+
+ self.updateNoteTransforms()
+
+ def clearNotes( self ):
+ del self.noteViews
+ self.noteViews = []
+ self.selectedNotes = []
+
+ def selectNotes( self, mode, which ):
+ if mode == SELECTNOTES.ALL:
+ for note in self.noteViews: note.setSelected( True )
+ self.selectedNotes = self.noteViews[:]
+ elif mode == SELECTNOTES.NONE:
+ for note in self.noteViews: note.setSelected( False )
+ self.selectedNotes = []
+ elif mode == SELECTNOTES.ADD:
+ for note in which:
+ if note.setSelected( True ):
+ self.selectedNotes.insert( 0, note )
+ elif mode == SELECTNOTES.REMOVE:
+ for note in which:
+ if note.setSelected( False ):
+ self.selectedNotes.remove( note )
+ elif mode == SELECTNOTES.EXCLUSIVE:
+ for note in self.noteViews:
+ if note in which:
+ if note.setSelected( True ):
+ self.selectedNotes.insert( 0, note )
+ else:
+ if note.setSelected( False ):
+ self.selectedNotes.remove( note )
+
+ def updateDragLimits( self, dragLimits ):
+ if not len(self.selectedNotes): return # no selected notes here
+
+ leftBound = 0
+ maxRightBound = round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT
+ last = len(self.noteViews)-1
+ for i in range(0,last):
+ if not self.noteViews[i].getSelected():
+ leftBound = self.noteViews[i].getEndTick()
+ else:
+ if not self.noteViews[i+1].getSelected():
+ rightBound = min( self.noteViews[i+1].getStartTick(), maxRightBound )
+ widthBound = rightBound
+ else:
+ rightBound = maxRightBound
+ widthBound = min( self.noteViews[i+1].getStartTick(), maxRightBound )
+ self.noteViews[i].updateDragLimits( dragLimits, leftBound, rightBound, widthBound )
+ if self.noteViews[last].getSelected():
+ self.noteViews[last].updateDragLimits( dragLimits, leftBound, maxRightBound, maxRightBound )
+
+ def getNotesByBar( self, beatCount, startX, stopX ):
+ beatWidth = self.getBeatLineSpacing( beatCount )
+ beatStart = self.getBeatLineStart()
+ while beatStart+beatWidth <= startX:
+ beatStart += beatWidth
+ beatStop = beatStart + beatWidth
+ while beatStop+beatWidth < stopX:
+ beatStop += beatWidth
+
+ notes = []
+ for note in self.noteViews:
+ if note.checkX( beatStart, beatStop ):
+ notes.insert(0,note)
+ return notes
+
+ #-----------------------------------
+ # event methods
+ #-----------------------------------
+
+ def handleButtonPress( self, emitter, event ):
+ eX = event.x - self.posOffset[0]
+ eY = event.y - self.posOffset[1]
+ if eX < 0 or eX > self.width or eY < 0 or eY > self.height:
+ return False
+
+ for note in self.noteViews:
+ handled = note.handleButtonPress( emitter, event )
+ if handled: return handled
+
+ return False
+
+ def handleButtonRelease( self, emitter, event, buttonPressCount ):
+ eX = event.x - self.posOffset[0]
+ eY = event.y - self.posOffset[1]
+
+ if eX < 0 or eX > self.width or eY < 0 or eY > self.height:
+ return False
+
+ if event.button == 1:
+ if buttonPressCount == 1: emitter.toggleTrack( self.trackID, False )
+ else: emitter.toggleTrack( self.trackID, True )
+
+ return True
+
+ def handleMarqueeSelect( self, emitter, start, stop ):
+ intersectionY = [ max(start[1],self.posOffset[1]), min(stop[1],self.posOffset[1]+self.height) ]
+ if intersectionY[0] > intersectionY[1]:
+ return False
+
+ intersectionX = [ max(start[0],self.posOffset[0]), min(stop[0],self.posOffset[0]+self.width) ]
+ if intersectionX[0] > intersectionX[1]:
+ return False
+
+
+ hits = []
+ for note in self.noteViews:
+ hit = note.handleMarqueeSelect( emitter,
+ [ intersectionX[0], intersectionY[0] ], \
+ [ intersectionX[1], intersectionY[1] ] )
+ if hit: hits.insert(0,note)
+
+ if len(hits): return hits
+
+ return False
+
+ def noteDrag( self, emitter, dx, dy, dw ):
+ for note in self.selectedNotes:
+ note.noteDrag( emitter, dx, dy, dw )
+
+ def doneNoteDrag( self, emitter ):
+ for note in self.selectedNotes:
+ note.doneNoteDrag( emitter )
+
+ #-----------------------------------
+ # drawing methods
+ #-----------------------------------
+
+ def getBorderWidth( self ): #should return a constant value, otherwise we have to recalculate sizing and positioning everyframe!
+ return GUIConstants.BORDER_SIZE
+
+ def getBeatLineWidth( self ):
+ return GUIConstants.BEAT_LINE_SIZE #should return a constant value, otherwise we have to recalculate sizing and positioning everyframe!
+
+ def getBeatLineSpacing( self, beatCount ):
+ return (self.width - 2*self.getBorderWidth() + self.getBeatLineWidth())/beatCount
+
+ def getBeatLineStart( self ):
+ return self.posOffset[0] + self.getBorderWidth() - self.getBeatLineWidth()/2.0
+
+ def setPositionOffset( self, offset ):
+ self.posOffset = offset
+
+ lineW = self.getBorderWidth()
+ for note in self.noteViews:
+ note.setPositionOffset( ( self.posOffset[0]+lineW, self.posOffset[1]+lineW ) )
+
+ def draw( self, context, beatCount, selected ):
+ #if selected: lineW = GUIConstants.SELECTED_BORDER_SIZE
+ #else: lineW = GUIConstants.BORDER_SIZE
+ lineW = self.getBorderWidth()
+ context.set_line_width( lineW )
+ lineWDIV2 = lineW/2.0
+
+ context.move_to( self.posOffset[0] + lineWDIV2, self.posOffset[1] + lineWDIV2 )
+ context.rel_line_to( self.width - lineW, 0 )
+ context.rel_line_to( 0, self.height - lineW )
+ context.rel_line_to( -self.width + lineW, 0 )
+ context.close_path()
+
+ #draw the background
+ context.set_source_rgb( 0.75, 0.75, 0.75 )
+ context.fill_preserve()
+
+ #draw the border
+ if selected: context.set_source_rgb( 1, 1, 1 )
+ else: context.set_source_rgb( 0, 0, 0 )
+ context.stroke()
+
+ #draw the beat lines
+ beatLineWidth = self.getBeatLineWidth()
+ context.set_line_width( beatLineWidth )
+ beatWidth = self.getBeatLineSpacing( beatCount )
+ beatStart = self.getBeatLineStart()
+ context.set_source_rgb( 0, 0, 0 )
+ for i in range(1,beatCount):
+ context.move_to( beatStart + i*beatWidth, self.posOffset[1] + lineW )
+ context.rel_line_to( 0, self.height - 2*lineW )
+ context.stroke()
+
+ #draw the notes
+ for note in self.noteViews:
+ note.draw( context )
+
+ #-----------------------------------
+ # sizing methods
+ #-----------------------------------
+
+ def updateNoteTransforms( self ):
+ width = self.width - 2*self.getBorderWidth()
+ height = self.height - 2*self.getBorderWidth() # adjust for actual note drawing area
+ for noteView in self.noteViews:
+ noteView.updateTransform( (width, height) )
+
+ def set_size_request( self, width, height ):
+ self.width = width
+ self.height = height
+ self.updateNoteTransforms()
+
+
+#unused for now...
+class NoteViewPool:
+ def __init__( self, parentContainer, beatsPerPageAdjustment ):
+ self.parentContainer = parentContainer
+ self.beatsPerPageAdjustment = beatsPerPageAdjustment
+ self.pool = []
+
+ def addNoteView( self, noteView ):
+ #noteView.hide()
+ self.pool.append( noteView )
+
+ def addNoteViews( self, noteViews ):
+ for noteView in noteViews:
+ self.addNoteView( noteView )
+
+ def getNoteView( self ):
+ poolSize = len( pool )
+ if poolSize != 0:
+ return pool.pop( poolSize )
+
+ return NoteView( None, self.parentContainer, self.beatsPerPageAdjustment )
diff --git a/Edit/rm/TunePageView.py b/Edit/rm/TunePageView.py
new file mode 100644
index 0000000..501504f
--- /dev/null
+++ b/Edit/rm/TunePageView.py
@@ -0,0 +1,17 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from PageView import PageView
+
+class TunePageView( PageView ):
+ def __init__( self, pageID, tuneIndex, selectPageCallback, selected = False ):
+ PageView.__init__( self, pageID, selectPageCallback, selected )
+
+ self.pageIndex = tuneIndex
+
+ def handleButtonPress( self, widget, data ):
+ self.selectPageCallback( self.tuneIndex )
+
+ def getData( self, widget, context, selection, targetType, eventTime ):
+ return selection.set( gtk.gdk.SELECTION_PRIMARY, 32, "t %d %d" % (self.pageID,self.pageIndex) )
diff --git a/Edit/rm/TuneView.py b/Edit/rm/TuneView.py
new file mode 100644
index 0000000..63cf468
--- /dev/null
+++ b/Edit/rm/TuneView.py
@@ -0,0 +1,123 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from GUI.GUIConstants import GUIConstants
+from GUI.Core.TunePageView import TunePageView
+
+def swap(l,i,j):
+ e = l[i]
+ l[i] = l[j]
+ l[j] = e
+
+class TuneView( gtk.ScrolledWindow ):
+
+ NO_PAGE = -1
+
+ def _page_width(self):
+ return self.pageContainer.get_allocation().width / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS
+
+ def __init__( self, selectPageCallback ):
+ gtk.ScrolledWindow.__init__( self )
+
+ #selectPageCallback(): currently connected to pagePlayer.setPlayTune, which skips to a given page of the tune.
+ self.selectPageCallback = selectPageCallback
+ self.selectedPageIndex = self.NO_PAGE
+
+ self.set_policy( gtk.POLICY_ALWAYS, gtk.POLICY_AUTOMATIC )
+ self.set_placement( gtk.CORNER_TOP_LEFT )
+
+ #self.pageViews: list of our custom PageView widgets
+ self.pageViews = []
+ self.pageContainer = gtk.HBox( False )
+ self.add_with_viewport( self.pageContainer )
+
+ #the old part
+ self.pageContainer.drag_dest_set( gtk.DEST_DEFAULT_ALL,
+ [ ( "bank page", gtk.TARGET_SAME_APP, 10 ),
+ ( "tune page", gtk.TARGET_SAME_APP, 11 )],
+ gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE )
+
+ self.pageContainer.connect( "drag_data_received", self.dragDataReceived )
+
+ #private method: called by gtk when pages get dragged onto the tune-view
+ def dragDataReceived( self, widget, context, x, y, selectionData, info, time ):
+ print 'dragDataReceived: ', selectionData.data, info, selectionData.data
+ recv = selectionData.data.split()
+ if recv[0] == 'p':
+ pageId = int( recv[1] )
+ self.addPage( pageId, min( x / self._page_width(), len( self.pageViews )) )
+ elif recv[0] == 't':
+ self.moveSelectedPage( min( x / self._page_width(), len( self.pageViews ) -1))
+ else:
+ raise 'ERROR'
+
+ #public method: called by MainWindow on file load
+ def syncFromPagePlayer(self):
+ raise 'never call this'
+ map( lambda pv:pv.destroy(), self.pageViews )
+ self.pageViews = []
+ tunePages = self.tunePagesCallback()
+ for i in range( len(tunePages)):
+ self.addPage( tunePages[i], i, False)
+
+
+ def addPage( self, pageID, position ):
+ #create a new widget
+ pageView = TunePageView( pageID, position, self.selectPage )
+ self.pageViews.insert( position, pageView )
+ self.pageContainer.pack_start( pageView, False )
+ self.pageContainer.reorder_child( pageView, position )
+
+ pageView.set_size_request( self.pageContainer.get_allocation().width / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS,
+ GUIConstants.PAGE_HEIGHT )
+ pageView.show()
+
+ for i in range( len(self.pageViews)) :
+ self.pageViews[i].tuneIndex = i
+ self.pageViews[i].setSelected( i == position)
+ self.selectPageCallback( pageID, position )
+ pageView.drag_source_set(
+ gtk.gdk.BUTTON1_MASK,
+ [ ( "tune page", gtk.TARGET_SAME_APP, 11 ) ],
+ gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE )
+
+ def moveSelectedPage( self, position):
+ self.pageContainer.reorder_child( self.pageViews[self.selectedPageIndex], position )
+ swap( self.pageViews, self.selectedPageIndex, position )
+ self.selectedPageIndex = position
+ for i in range( len(self.pageViews) ) :
+ self.pageViews[i].tuneIndex = i
+ self.pageViews[i].setSelected( i == position)
+
+ def removePage( self, position ):
+ pv = self.pageViews[position]
+ self.pageViews[position:position+1] = []
+ if self.selectedPageIndex >= position : self.selectedPageIndex -= 1
+ for i in range( len(self.pageViews)) :
+ self.pageViews[i].tuneIndex = i
+ self.pageViews[i].setSelected( i == position)
+ self.pageContainer.remove(pv)
+ del pv
+
+ def selectPage( self, selectedPageIndex, invokeCallback = True ):
+ if selectedPageIndex >= len( self.pageViews ): selectedPageIndex = self.NO_PAGE
+ self.selectedPageIndex = selectedPageIndex
+ if selectedPageIndex == self.NO_PAGE:
+ for pv in self.pageViews: pv.setSelected(False)
+ if invokeCallback: self.selectPageCallback( -1, -1 )
+ else:
+ if not self.pageViews[ selectedPageIndex ].selected:
+ map( lambda pv: pv.setSelected( pv.tuneIndex == selectedPageIndex), self.pageViews)
+ if invokeCallback: self.selectPageCallback( self.pageViews[selectedPageIndex].pageID, selectedPageIndex )
+
+ def set_size_request( self, width, height ):
+ gtk.ScrolledWindow.set_size_request( self, width, height )
+ map( lambda pv: pv.set_size_request( width / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS, GUIConstants.PAGE_HEIGHT ), self.pageViews)
+
+ def getPageId( self, idx):
+ return self.pageViews[idx].pageID
+
+ def getTune( self ):
+ return [ p.pageID for p in self.pageViews ]
+