| 
							- #!/usr/bin/env python3
 - # -*- coding: utf-8 -*-
 - 
 - # A piano roll viewer/editor
 - # Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
 - # Copyright (C) 2014-2015 Perry Nguyen
 - #
 - # This program is free software; you can redistribute it and/or
 - # modify it under the terms of the GNU General Public License as
 - # published by the Free Software Foundation; either version 2 of
 - # the License, or any later version.
 - #
 - # This program is distributed in the hope that it will be useful,
 - # but WITHOUT ANY WARRANTY; without even the implied warranty of
 - # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 - # GNU General Public License for more details.
 - #
 - # For a full copy of the GNU General Public License see the doc/GPL.txt file.
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Config)
 - 
 - from carla_config import *
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Global)
 - 
 - if config_UseQt5:
 -     from PyQt5.QtCore import pyqtSlot, Qt, QEvent
 -     from PyQt5.QtGui import QKeyEvent
 -     from PyQt5.QtWidgets import QMainWindow
 - else:
 -     from PyQt4.QtCore import pyqtSlot, Qt, QEvent
 -     from PyQt4.QtGui import QKeyEvent, QMainWindow
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Custom)
 - 
 - from carla_shared import *
 - from carla_utils import *
 - from widgets.pianoroll import *
 - 
 - import ui_midipattern
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (ExternalUI)
 - 
 - from carla_app import CarlaApplication
 - from externalui import ExternalUI
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - 
 - class MidiPatternW(ExternalUI, QMainWindow):
 -     PPQ = 48.0
 - 
 -     def __init__(self):
 -         ExternalUI.__init__(self)
 -         QMainWindow.__init__(self)
 -         self.ui = ui_midipattern.Ui_MidiPatternW()
 -         self.ui.setupUi(self)
 -         self.ui.piano = self.ui.graphicsView.piano
 - 
 -         # to be filled with note-on events, while waiting for their matching note-off
 -         self.fPendingNoteOns = [] # (channel, note, velocity, time)
 - 
 -         self.fTransportInfo = {
 -             "playing": False,
 -             "frame": 0,
 -             "bar": 0,
 -             "beat": 0,
 -             "tick": 0,
 -             "bpm": 120.0,
 -             "sigNum": 4.0,
 -             "sigDenom": 4.0
 -         }
 - 
 -         self.ui.act_edit_insert.triggered.connect(self.slot_editInsertMode)
 -         self.ui.act_edit_velocity.triggered.connect(self.slot_editVelocityMode)
 -         self.ui.act_edit_select_all.triggered.connect(self.slot_editSelectAll)
 - 
 -         self.ui.piano.midievent.connect(self.sendMsg)
 -         self.ui.piano.measureupdate.connect(self.updateMeasureBox)
 -         self.ui.piano.modeupdate.connect(self.ui.modeIndicator.changeMode)
 -         self.ui.piano.modeupdate.connect(self.slot_modeChanged)
 - 
 -         self.ui.timeSigBox.currentIndexChanged[int].connect(self.slot_paramChanged)
 -         self.ui.measureBox.currentIndexChanged[int].connect(self.slot_paramChanged)
 -         self.ui.defaultLengthBox.currentIndexChanged[int].connect(self.slot_paramChanged)
 -         self.ui.quantizeBox.currentIndexChanged[int].connect(self.slot_paramChanged)
 - 
 -         self.ui.timeSigBox.currentIndexChanged[str].connect(self.ui.piano.setTimeSig)
 -         self.ui.measureBox.currentIndexChanged[str].connect(self.ui.piano.setMeasures)
 -         self.ui.defaultLengthBox.currentIndexChanged[str].connect(self.ui.piano.setDefaultLength)
 -         self.ui.quantizeBox.currentIndexChanged[str].connect(self.ui.piano.setGridDiv)
 -         self.ui.hSlider.valueChanged.connect(self.ui.graphicsView.setZoomX)
 -         self.ui.vSlider.valueChanged.connect(self.ui.graphicsView.setZoomY)
 - 
 -         self.ui.graphicsView.setFocus()
 - 
 -         self.fIdleTimer = self.startTimer(30)
 -         self.setWindowTitle(self.fUiName)
 -         self.ready()
 - 
 -     def slot_editInsertMode(self):
 -         ev = QKeyEvent(QEvent.User, Qt.Key_F, Qt.NoModifier)
 -         self.ui.piano.keyPressEvent(ev)
 - 
 -     def slot_editVelocityMode(self):
 -         ev = QKeyEvent(QEvent.User, Qt.Key_D, Qt.NoModifier)
 -         self.ui.piano.keyPressEvent(ev)
 - 
 -     def slot_editSelectAll(self):
 -         ev = QKeyEvent(QEvent.User, Qt.Key_A, Qt.NoModifier)
 -         self.ui.piano.keyPressEvent(ev)
 - 
 -     def slot_modeChanged(self, mode):
 -         if mode == "insert_mode":
 -             self.ui.act_edit_insert.setChecked(True)
 -             self.ui.act_edit_velocity.setChecked(False)
 -         elif mode == "velocity_mode":
 -             self.ui.act_edit_insert.setChecked(False)
 -             self.ui.act_edit_velocity.setChecked(True)
 -         else:
 -             self.ui.act_edit_insert.setChecked(False)
 -             self.ui.act_edit_velocity.setChecked(False)
 - 
 -     def slot_paramChanged(self, index):
 -         sender = self.sender()
 - 
 -         if sender == self.ui.timeSigBox:
 -             param = 0
 -         elif sender == self.ui.measureBox:
 -             param = 1
 -             index += 1
 -         elif sender == self.ui.defaultLengthBox:
 -             param = 2
 -         elif sender == self.ui.quantizeBox:
 -             param = 3
 -         else:
 -             return
 - 
 -         self.sendControl(param, index)
 - 
 -     # -------------------------------------------------------------------
 -     # DSP Callbacks
 - 
 -     def dspParameterChanged(self, index, value):
 -         value = int(value)
 - 
 -         if index == 0: # TimeSig
 -             self.ui.timeSigBox.setCurrentIndex(value)
 -         elif index == 1: # Measures
 -             self.ui.measureBox.setCurrentIndex(value-1)
 -         elif index == 2: # DefLength
 -             self.ui.defaultLengthBox.setCurrentIndex(value)
 -         elif index == 3: # Quantize
 -             self.ui.quantizeBox.setCurrentIndex(value)
 - 
 -     def dspStateChanged(self, key, value):
 -         pass
 - 
 -     # -------------------------------------------------------------------
 -     # ExternalUI Callbacks
 - 
 -     def uiShow(self):
 -         self.show()
 - 
 -     def uiFocus(self):
 -         self.setWindowState((self.windowState() & ~Qt.WindowMinimized) | Qt.WindowActive)
 -         self.show()
 - 
 -         self.raise_()
 -         self.activateWindow()
 - 
 -     def uiHide(self):
 -         self.hide()
 - 
 -     def uiQuit(self):
 -         self.closeExternalUI()
 -         self.close()
 -         app.quit()
 - 
 -     def uiTitleChanged(self, uiTitle):
 -         self.setWindowTitle(uiTitle)
 - 
 -     # -------------------------------------------------------------------
 -     # Qt events
 - 
 -     def timerEvent(self, event):
 -         if event.timerId() == self.fIdleTimer:
 -             self.idleExternalUI()
 -         QMainWindow.timerEvent(self, event)
 - 
 -     def closeEvent(self, event):
 -         self.closeExternalUI()
 -         QMainWindow.closeEvent(self, event)
 - 
 -         # there might be other qt windows open which will block the UI from quitting
 -         app.quit()
 - 
 -     # -------------------------------------------------------------------
 -     # Custom callback
 - 
 -     def updateMeasureBox(self, index):
 -         self.measureBox.setCurrentIndex(index-1)
 - 
 -     def sendMsg(self, data):
 -         msg = data[0]
 -         if msg == "midievent-remove":
 -             note, start, length, vel = data[1:5]
 -             note_start = start * 60. / self.fTransportInfo["bpm"] * 4. / self.fTransportInfo["sigDenom"] * self.PPQ
 -             note_stop = note_start + length * 60. / self.fTransportInfo["bpm"] * 4. * self.fTransportInfo["sigNum"] / self.fTransportInfo["sigDenom"] * self.PPQ
 -             self.send([msg, note_start, 3, MIDI_STATUS_NOTE_ON, note, vel])
 -             self.send([msg, note_stop, 3, MIDI_STATUS_NOTE_OFF, note, vel])
 - 
 -         elif msg == "midievent-add":
 -             note, start, length, vel = data[1:5]
 -             note_start = start * 60. / self.fTransportInfo["bpm"] * self.PPQ
 -             note_stop = note_start + length * 60. / self.fTransportInfo["bpm"] * 4. * self.fTransportInfo["sigNum"] / self.fTransportInfo["sigDenom"] * self.PPQ
 -             self.send([msg, note_start, 3, MIDI_STATUS_NOTE_ON, note, vel])
 -             self.send([msg, note_stop, 3, MIDI_STATUS_NOTE_OFF, note, vel])
 - 
 -     def msgCallback(self, msg):
 -         msg = charPtrToString(msg)
 - 
 -         if msg == "midi-clear-all":
 -             # clear all notes
 -             self.ui.piano.clearNotes()
 - 
 -         elif msg == "midievent-add":
 -             # adds single midi event
 -             time = int(self.readlineblock())
 -             size = int(self.readlineblock())
 -             data = []
 - 
 -             for x in range(size):
 -                 data.append(int(self.readlineblock()))
 - 
 -             self.handleMidiEvent(time, size, data)
 - 
 -         elif msg == "transport":
 -             playing = bool(self.readlineblock() == "true")
 -             frame, bar, beat, tick = [int(i) for i in self.readlineblock().split(":")]
 -             bpm, sigNum, sigDenom = [float(i) for i in self.readlineblock().split(":")]
 - 
 -             if beat != self.fTransportInfo["beat"]:
 -                 print(beat)
 - 
 -             old_frame = self.fTransportInfo['frame']
 - 
 -             self.fTransportInfo = {
 -                 "playing": playing,
 -                 "frame": frame,
 -                 "bar": bar,
 -                 "beat": beat,
 -                 "tick": tick,
 -                 "bpm": bpm,
 -                 "sigNum": sigNum,
 -                 "sigDenom": sigDenom
 -             }
 - 
 -             if old_frame != frame:
 -                 self.ui.piano.movePlayHead(self.fTransportInfo)
 - 
 -         else:
 -             ExternalUI.msgCallback(self, msg)
 - 
 -     # -------------------------------------------------------------------
 -     # Internal stuff
 - 
 -     def handleMidiEvent(self, time, size, data):
 -         #print("Got MIDI Event on UI", time, size, data)
 - 
 -         # NOTE: for now time comes in frames, which might not be desirable
 -         #       we'll convert it to a smaller value for now (seconds)
 -         #       later on we can have time as PPQ or similar
 - 
 -         time /= self.PPQ
 - 
 -         status  = MIDI_GET_STATUS_FROM_DATA(data)
 -         channel = MIDI_GET_CHANNEL_FROM_DATA(data)
 -         if status == MIDI_STATUS_NOTE_ON:
 -             note = data[1]
 -             velo = data[2]
 - 
 -             # append (channel, note, velo, time) for later
 -             self.fPendingNoteOns.append((channel, note, velo, time))
 - 
 -         elif status == MIDI_STATUS_NOTE_OFF:
 -             note = data[1]
 -             velo = data[2]
 - 
 -             # find previous note-on that matches this note and channel
 -             for noteOnMsg in self.fPendingNoteOns:
 -                 channel_, note_, velo_, time_ = noteOnMsg
 - 
 -                 if channel_ != channel:
 -                     continue
 -                 if note_ != note:
 -                     continue
 - 
 -                 # found it
 -                 #print("{} {} {} {}\n".format(note, time_, time-time_, velo_))
 -                 start = time_ / 60. * self.fTransportInfo["bpm"] / 4. * self.fTransportInfo["sigDenom"]
 -                 length = (time - time_) / 60. * self.fTransportInfo["bpm"] / 4. / self.fTransportInfo["sigNum"] * self.fTransportInfo["sigDenom"]
 -                 self.ui.piano.drawNote(note, start, length, velo_)
 - 
 -                 # remove from list
 -                 self.fPendingNoteOns.remove(noteOnMsg)
 -                 break
 - 
 - #--------------- main ------------------
 - if __name__ == '__main__':
 -     import resources_rc
 - 
 -     pathBinaries, pathResources = getPaths()
 -     gCarla.utils = CarlaUtils(os.path.join(pathBinaries, "libcarla_utils." + DLL_EXTENSION))
 -     gCarla.utils.set_process_name("MidiPattern")
 - 
 -     app = CarlaApplication("MidiPattern")
 -     gui = MidiPatternW()
 -     app.exit_exec()
 
 
  |