|
- #!/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 tatch
- #
- # 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 Qt, QRectF
- from PyQt5.QtGui import QColor, QFont, QPen
- from PyQt5.QtWidgets import QGraphicsItem, QGraphicsLineItem, QGraphicsOpacityEffect, QGraphicsRectItem, QGraphicsSimpleTextItem
- from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView
- else:
- from PyQt4.QtCore import Qt, QRectF
- from PyQt4.QtGui import QColor, QFont, QPen
- from PyQt4.QtGui import QGraphicsItem, QGraphicsLineItem, QGraphicsOpacityEffect, QGraphicsRectItem, QGraphicsSimpleTextItem
- from PyQt4.QtGui import QGraphicsScene, QGraphicsView
-
- # ------------------------------------------------------------------------------------------------------------
- # Imports (Custom)
-
- from carla_shared import *
- from carla_utils import *
-
- # ------------------------------------------------------------------------------------------------------------
- # Imports (ExternalUI)
-
- from carla_app import CarlaApplication
- from externalui import ExternalUI
-
- # ------------------------------------------------------------------------------------------------------------
- # MIDI definitions, copied from CarlaMIDI.h
-
- MAX_MIDI_CHANNELS = 16
- MAX_MIDI_NOTE = 128
- MAX_MIDI_VALUE = 128
- MAX_MIDI_CONTROL = 120 # 0x77
-
- MIDI_STATUS_BIT = 0xF0
- MIDI_CHANNEL_BIT = 0x0F
-
- # MIDI Messages List
- MIDI_STATUS_NOTE_OFF = 0x80 # note (0-127), velocity (0-127)
- MIDI_STATUS_NOTE_ON = 0x90 # note (0-127), velocity (0-127)
- MIDI_STATUS_POLYPHONIC_AFTERTOUCH = 0xA0 # note (0-127), pressure (0-127)
- MIDI_STATUS_CONTROL_CHANGE = 0xB0 # see 'Control Change Messages List'
- MIDI_STATUS_PROGRAM_CHANGE = 0xC0 # program (0-127), none
- MIDI_STATUS_CHANNEL_PRESSURE = 0xD0 # pressure (0-127), none
- MIDI_STATUS_PITCH_WHEEL_CONTROL = 0xE0 # LSB (0-127), MSB (0-127)
-
- # MIDI Message type
- def MIDI_IS_CHANNEL_MESSAGE(status): return status >= MIDI_STATUS_NOTE_OFF and status < MIDI_STATUS_BIT
- def MIDI_IS_SYSTEM_MESSAGE(status): return status >= MIDI_STATUS_BIT and status <= 0xFF
- def MIDI_IS_OSC_MESSAGE(status): return status == '/' or status == '#'
-
- # MIDI Channel message type
- def MIDI_IS_STATUS_NOTE_OFF(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_NOTE_OFF
- def MIDI_IS_STATUS_NOTE_ON(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_NOTE_ON
- def MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_POLYPHONIC_AFTERTOUCH
- def MIDI_IS_STATUS_CONTROL_CHANGE(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_CONTROL_CHANGE
- def MIDI_IS_STATUS_PROGRAM_CHANGE(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_PROGRAM_CHANGE
- def MIDI_IS_STATUS_CHANNEL_PRESSURE(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_CHANNEL_PRESSURE
- def MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_PITCH_WHEEL_CONTROL
-
- # MIDI Utils
- def MIDI_GET_STATUS_FROM_DATA(data): return data[0] & MIDI_STATUS_BIT if MIDI_IS_CHANNEL_MESSAGE(data[0]) else data[0]
- def MIDI_GET_CHANNEL_FROM_DATA(data): return data[0] & MIDI_CHANNEL_BIT if MIDI_IS_CHANNEL_MESSAGE(data[0]) else 0
-
- # ------------------------------------------------------------------------------------------------------------
- # Global variables and functions
-
- global_piano_roll_snap = True
- global_piano_roll_grid_width = 1000.0
- global_piano_roll_grid_max_start_time = 999.0
- global_piano_roll_note_height = 10
- global_piano_roll_snap_value = global_piano_roll_grid_width / 64.0
- global_piano_roll_note_count = 120
- global_piano_keys_width = 34
- global_piano_roll_header_height = 20
- global_piano_roll_total_height = 1000 #this gets changed
-
- global_piano_roll_quantize_choices = ['None','1/4','1/3','1/2','1']
-
- def piano_roll_quantize(a_index):
- global global_piano_roll_snap_value
- global global_piano_roll_snap
- if a_index == 0:
- global_piano_roll_snap = False
- elif a_index == 1:
- global_piano_roll_snap_value = global_piano_roll_grid_width / 16.0
- global_piano_roll_snap = True
- elif a_index == 2:
- global_piano_roll_snap_value = global_piano_roll_grid_width / 12.0
- global_piano_roll_snap = True
- elif a_index == 3:
- global_piano_roll_snap_value = global_piano_roll_grid_width / 8.0
- global_piano_roll_snap = True
- elif a_index == 4:
- global_piano_roll_snap_value = global_piano_roll_grid_width / 4.0
- global_piano_roll_snap = True
-
- def snap(a_pos_x, a_pos_y = None):
- if global_piano_roll_snap:
- f_pos_x = int((a_pos_x - global_piano_keys_width) / global_piano_roll_snap_value) * global_piano_roll_snap_value + global_piano_keys_width
- if a_pos_y:
- f_pos_y = int((a_pos_y - global_piano_roll_header_height) / global_piano_roll_note_height) * global_piano_roll_note_height + global_piano_roll_header_height
- return (f_pos_x, f_pos_y)
- else:
- return f_pos_x
-
- # ------------------------------------------------------------------------------------------------------------
- # Graphics Items
-
- class note_item(QGraphicsRectItem):
- '''a note on the pianoroll sequencer'''
- def __init__(self, a_length, a_note_height, a_note_num):
- QGraphicsRectItem.__init__(self, 0, 0, a_length, a_note_height)
- self.setFlag(QGraphicsItem.ItemIsMovable)
- self.setFlag(QGraphicsItem.ItemIsSelectable)
- self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
- self.setAcceptHoverEvents(True)
- self.o_brush = QColor(100, 0, 0)
- self.hover_brush = QColor(200, 200, 100)
- self.s_brush = QColor(200, 100, 100)
- self.setBrush(self.o_brush)
- self.note_height = a_note_height
- self.note_num = a_note_num
- self.pressed = False
- self.selected = False
-
- def select(self):
- self.setSelected(True)
- self.selected = True
- self.setBrush(self.s_brush)
-
- def deselect(self):
- self.setSelected(False)
- self.selected = False
- self.setBrush(self.o_brush)
-
- def hoverEnterEvent(self, a_event):
- QGraphicsRectItem.hoverEnterEvent(self, a_event)
- if not self.selected:
- self.setBrush(self.hover_brush)
-
- def hoverLeaveEvent(self, a_event):
- QGraphicsRectItem.hoverLeaveEvent(self, a_event)
- if not self.selected:
- self.setBrush(self.o_brush)
- elif self.selected:
- self.setBrush(self.s_brush)
-
- def mousePressEvent(self, a_event):
- a_event.setAccepted(True)
- QGraphicsRectItem.mousePressEvent(self, a_event)
- self.select()
- self.pressed = True
-
- def mouseMoveEvent(self, a_event):
- QGraphicsRectItem.mouseMoveEvent(self, a_event)
- f_pos_x = self.pos().x()
- f_pos_y = self.pos().y()
- if f_pos_x < global_piano_keys_width:
- f_pos_x = global_piano_keys_width
- elif f_pos_x > global_piano_roll_grid_max_start_time:
- f_pos_x = global_piano_roll_grid_max_start_time
- if f_pos_y < global_piano_roll_header_height:
- f_pos_y = global_piano_roll_header_height
- elif f_pos_y > global_piano_roll_total_height:
- f_pos_y = global_piano_roll_total_height
- (f_pos_x, f_pos_y,) = snap(f_pos_x, f_pos_y)
- self.setPos(f_pos_x, f_pos_y)
-
- def mouseReleaseEvent(self, a_event):
- a_event.setAccepted(True)
- QGraphicsRectItem.mouseReleaseEvent(self, a_event)
- self.pressed = False
- if a_event.button() == Qt.LeftButton:
- (f_pos_x, f_pos_y,) = snap(self.pos().x(), self.pos().y())
- self.setPos(f_pos_x, f_pos_y)
- f_new_note_start = (f_pos_x - global_piano_keys_width) * 0.001 * 4.0
- f_new_note_num = int(global_piano_roll_note_count - (f_pos_y - global_piano_roll_header_height) / global_piano_roll_note_height)
- #print("note start: ", str(f_new_note_start))
- print("MIDI number: ", str(f_new_note_num))
-
-
- class piano_key_item(QGraphicsRectItem):
- def __init__(self, width, height, parent):
- QGraphicsRectItem.__init__(self, 0, 0, width, height, parent)
- self.width = width
- self.height = height
- self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
- self.setAcceptHoverEvents(True)
- self.hover_brush = QColor(200, 0, 0)
- self.click_brush = QColor(255, 100, 100)
- self.pressed = False
-
- def hoverEnterEvent(self, a_event):
- QGraphicsRectItem.hoverEnterEvent(self, a_event)
- self.o_brush = self.brush()
- self.setBrush(self.hover_brush)
-
- def hoverLeaveEvent(self, a_event):
- if self.pressed:
- self.pressed = False
- self.setBrush(self.hover_brush)
- QGraphicsRectItem.hoverLeaveEvent(self, a_event)
- self.setBrush(self.o_brush)
-
- def mousePressEvent(self, a_event):
- self.pressed = True
- self.setBrush(self.click_brush)
-
- def mouseMoveEvent(self, a_event):
- """this may eventually do something"""
- pass
-
- def mouseReleaseEvent(self, a_event):
- self.pressed = False
- QGraphicsRectItem.mouseReleaseEvent(self, a_event)
- self.setBrush(self.hover_brush)
-
- # ------------------------------------------------------------------------------------------------------------
- # External UI
-
- class piano_roll(ExternalUI, QGraphicsView):
- '''the piano roll'''
- def __init__(self, a_item_length = 4, a_grid_div = 4):
- ExternalUI.__init__(self)
- QGraphicsView.__init__(self)
-
- global global_piano_roll_total_height
- self.item_length = float(a_item_length)
- self.viewer_width = 1000
- self.grid_div = a_grid_div
- self.end_octave = 8
- self.start_octave = -2
- self.notes_in_octave = 12
- self.total_notes = (self.end_octave - self.start_octave) * self.notes_in_octave + 1
- self.note_height = global_piano_roll_note_height
- self.octave_height = self.notes_in_octave * self.note_height
- self.header_height = global_piano_roll_header_height
- self.piano_height = self.note_height * self.total_notes
- self.padding = 2
- self.piano_width = global_piano_keys_width - self.padding
- self.piano_height = self.note_height * self.total_notes
- global_piano_roll_total_height = self.piano_height - self.note_height + global_piano_roll_header_height
- self.scene = QGraphicsScene(self)
- self.scene.mousePressEvent = self.sceneMousePressEvent
- self.scene.mouseReleaseEvent = self.sceneMouseReleaseEvent
- self.scene.mouseMoveEvent = self.sceneMouseMoveEvent
- self.scene.setBackgroundBrush(QColor(100, 100, 100))
- self.setScene(self.scene)
- self.setAlignment(Qt.AlignLeft)
- self.notes = []
- self.selected_notes = []
- self.piano_keys = []
- self.ghost_vel = 100
- self.show_ghost = True
- self.marquee_select = False
- self.insert_mode = False
- self.place_ghost = False
- self.draw_piano()
- self.draw_header()
- self.draw_grid()
-
- # to be filled with note-on events, while waiting for their matching note-off
- self.fPendingNoteOns = [] # (channel, note, velocity, time)
-
- self.fIdleTimer = self.startTimer(30)
- self.setWindowTitle(self.fUiName)
- self.ready()
-
- # -------------------------------------------------------------------
- # DSP Callbacks
-
- def dspParameterChanged(self, index, value):
- pass
-
- 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()
- QGraphicsView.timerEvent(self, event)
-
- def closeEvent(self, event):
- self.closeExternalUI()
- QGraphicsView.closeEvent(self, event)
-
- # -------------------------------------------------------------------
- # Custom callback
-
- def msgCallback(self, msg):
- #try:
- self.msgCallback2(msg)
- #except:
- #print("Custom msgCallback error, skipped for", msg)
-
- def msgCallback2(self, msg):
- msg = charPtrToString(msg)
-
- if msg == "midi-clear-all":
- # clear all notes
- self.clear_drawn_items()
-
- 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 == "show":
- self.uiShow()
-
- elif msg == "focus":
- self.uiFocus()
-
- elif msg == "hide":
- self.uiHide()
-
- elif msg == "quit":
- self.fQuitReceived = True
- self.uiQuit()
-
- elif msg == "uiTitle":
- uiTitle = self.readlineblock()
- self.uiTitleChanged(uiTitle)
-
- else:
- print("unknown message: \"" + 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.getSampleRate()
-
- 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
- self.draw_note(note, time_, time - time_, velo_)
-
- # remove from list
- self.fPendingNoteOns.remove(noteOnMsg)
- break
-
- def make_ghostnote(self, pos_x, pos_y):
- """creates the ghostnote that is placed on the scene before the real one is."""
- f_length = self.beat_width / 4
- (f_start, f_note,) = snap(pos_x, pos_y)
- self.ghost_rect_orig = QRectF(f_start, f_note, f_length, self.note_height)
- self.ghost_rect = QRectF(self.ghost_rect_orig)
- self.ghost_note = QGraphicsRectItem(self.ghost_rect)
- self.ghost_note.setBrush(QColor(230, 221, 45, 100))
- self.scene.addItem(self.ghost_note)
-
- def keyPressEvent(self, a_event):
- QGraphicsView.keyPressEvent(self, a_event)
- if a_event.key() == Qt.Key_B:
- if not self.insert_mode:
- self.insert_mode = True
- if self.show_ghost:
- self.make_ghostnote(self.piano_width, self.header_height)
- elif self.insert_mode:
- self.insert_mode = False
- if self.place_ghost:
- self.place_ghost = False
- self.scene.removeItem(self.ghost_note)
- elif self.show_ghost:
- self.scene.removeItem(self.ghost_note)
- if a_event.key() == Qt.Key_Delete or a_event.key() == Qt.Key_Backspace:
- for note in self.selected_notes:
- note.deselect()
- self.scene.removeItem(note)
-
- def sceneMousePressEvent(self, a_event):
- QGraphicsScene.mousePressEvent(self.scene, a_event)
- if not (any(key.pressed for key in self.piano_keys) or any(note.pressed for note in self.notes)):
- #if not self.scene.mouseGrabberItem():
- for note in self.selected_notes:
- note.deselect()
-
- self.selected_notes = []
- self.f_pos = a_event.scenePos()
- if a_event.button() == Qt.LeftButton:
- if self.insert_mode:
- self.place_ghost = True
- else:
- self.marquee_select = True
- self.marquee_rect = QRectF(self.f_pos.x(), self.f_pos.y(), 1, 1)
- self.marquee = QGraphicsRectItem(self.marquee_rect)
- self.marquee.setBrush(QColor(255, 255, 255, 100))
- self.scene.addItem(self.marquee)
- else:
- out = False
- for note in self.notes:
- if note.pressed and note in self.selected_notes:
- break
- elif note.pressed and note not in self.selected_notes:
- s_note = note
- out = True
- break
- if out == True:
- for note in self.selected_notes:
- note.deselect()
- self.selected_notes = [s_note]
- elif out == False:
- for note in self.selected_notes:
- note.mousePressEvent(a_event)
-
- def sceneMouseMoveEvent(self, a_event):
- QGraphicsScene.mouseMoveEvent(self.scene, a_event)
- #if not (any((key.pressed for key in self.piano_keys)) or any((note.pressed for note in self.notes))):
- if not (any((key.pressed for key in self.piano_keys))):
- #if not self.scene.mouseGrabberItem():
- m_pos = a_event.scenePos()
- if self.insert_mode and self.place_ghost: #placing a note
- #bind velocity to vertical mouse movement
- self.ghost_vel = self.ghost_vel + (a_event.lastScenePos().y() - m_pos.y())/10
- if self.ghost_vel < 0:
- self.ghost_vel = 0
- elif self.ghost_vel > 127:
- self.ghost_vel = 127
- #print "velocity:", self.ghost_vel
-
- m_width = self.ghost_rect.x() + self.ghost_rect_orig.width()
- if m_pos.x() < m_width:
- m_pos.setX(m_width)
- m_new_x = snap(m_pos.x())
- self.ghost_rect.setRight(m_new_x)
- self.ghost_note.setRect(self.ghost_rect)
- else:
- if m_pos.x() < self.piano_width:
- m_pos.setX(self.piano_width)
- elif m_pos.x() > self.viewer_width:
- m_pos.setX(self.viewer_width)
- if m_pos.y() < self.header_height:
- m_pos.setY(self.header_height)
-
- if self.insert_mode and self.show_ghost: #ghostnote follows mouse around
- (m_new_x, m_new_y,) = snap(m_pos.x(), m_pos.y())
- self.ghost_rect.moveTo(m_new_x, m_new_y)
- self.ghost_note.setRect(self.ghost_rect)
- elif self.marquee_select:
- if self.f_pos.x() < m_pos.x() and self.f_pos.y() < m_pos.y():
- self.marquee_rect.setBottomRight(m_pos)
- elif self.f_pos.x() < m_pos.x() and self.f_pos.y() > m_pos.y():
- self.marquee_rect.setTopRight(m_pos)
- elif self.f_pos.x() > m_pos.x() and self.f_pos.y() < m_pos.y():
- self.marquee_rect.setBottomLeft(m_pos)
- elif self.f_pos.x() > m_pos.x() and self.f_pos.y() > m_pos.y():
- self.marquee_rect.setTopLeft(m_pos)
- self.marquee.setRect(self.marquee_rect)
- self.selected_notes = []
- for item in self.scene.collidingItems(self.marquee):
- if item in self.notes:
- self.selected_notes.append(item)
-
- for note in self.notes:
- if note in self.selected_notes:
- note.select()
- else:
- note.deselect()
-
- elif not self.marquee_select:
- for note in self.selected_notes:
- note.mouseMoveEvent(a_event)
-
- def sceneMouseReleaseEvent(self, a_event):
- QGraphicsScene.mouseReleaseEvent(self.scene, a_event)
- if not (any((key.pressed for key in self.piano_keys)) or any((note.pressed for note in self.notes))):
- #if not self.scene.mouseGrabberItem():
- if a_event.button() == Qt.LeftButton:
- if self.place_ghost and self.insert_mode:
- self.place_ghost = False
- f_final_scenePos = a_event.scenePos().x()
- f_final_scenePos = snap(f_final_scenePos)
- f_delta = f_final_scenePos - self.ghost_rect.x()
- if f_delta < self.beat_width / 8:
- f_delta = self.beat_width / 4
- f_length = f_delta / self.viewer_width * 4
- f_start = (self.ghost_rect.x() - self.piano_width - self.padding) / self.beat_width
- f_note = self.total_notes - (self.ghost_rect.y() - self.header_height) / self.note_height - 1
- self.draw_note(f_note, f_start, f_length, self.ghost_vel)
- self.ghost_rect = QRectF(self.ghost_rect_orig)
- self.ghost_note.setRect(self.ghost_rect)
- elif self.marquee_select:
- self.marquee_select = False
- self.scene.removeItem(self.marquee)
- elif not self.marquee_select:
- for n in self.selected_notes:
- n.mouseReleaseEvent(a_event)
-
- def draw_header(self):
- self.header = QGraphicsRectItem(0, 0, self.viewer_width, self.header_height)
- #self.header.setZValue(1.0)
- self.header.setPos(self.piano_width + self.padding, 0)
- self.scene.addItem(self.header)
- self.beat_width = self.viewer_width / self.item_length
- self.value_width = self.beat_width / self.grid_div
-
- def draw_piano(self):
- f_labels = ['B','Bb','A','Ab','G','Gb','F','E','Eb','D','Db','C']
- f_black_notes = [2,4,6,9,11]
- f_piano_label = QFont()
- f_piano_label.setPointSize(8)
- self.piano = QGraphicsRectItem(0, 0, self.piano_width, self.piano_height)
- self.piano.setPos(0, self.header_height)
- self.scene.addItem(self.piano)
- f_key = piano_key_item(self.piano_width, self.note_height, self.piano)
- f_label = QGraphicsSimpleTextItem('C8', f_key)
- f_label.setPos(4, 0)
- f_label.setFont(f_piano_label)
- f_key.setBrush(QColor(255, 255, 255))
- for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1):
- for j in range(self.notes_in_octave, 0, -1):
- f_key = piano_key_item(self.piano_width, self.note_height, self.piano)
- f_key.setPos(0, self.note_height * j + self.octave_height * (i - 1))
- if j == 12:
- f_label = QGraphicsSimpleTextItem('%s%d' % (f_labels[(j - 1)], self.end_octave - i), f_key)
- f_label.setPos(4, 0)
- f_label.setFont(f_piano_label)
- if j in f_black_notes:
- f_key.setBrush(QColor(0, 0, 0))
- else:
- f_key.setBrush(QColor(255, 255, 255))
- self.piano_keys.append(f_key)
-
- def draw_grid(self):
- f_black_notes = [2,4,6,9,11]
- for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1):
- for j in range(self.notes_in_octave, 0, -1):
- f_scale_bar = QGraphicsRectItem(0, 0, self.viewer_width, self.note_height, self.piano)
- f_scale_bar.setPos(self.piano_width + self.padding, self.note_height * j + self.octave_height * (i - 1))
- if j not in f_black_notes:
- f_scale_bar.setBrush(QColor(230, 230, 230, 100))
- f_beat_pen = QPen()
- f_beat_pen.setWidth(2)
- f_line_pen = QPen()
- f_line_pen.setColor(QColor(0, 0, 0, 40))
- for i in range(0, int(self.item_length) + 1):
- f_beat = QGraphicsLineItem(0, 0, 0, self.piano_height + self.header_height - f_beat_pen.width(), self.header)
- f_beat.setPos(self.beat_width * i, 0.5 * f_beat_pen.width())
- f_beat.setPen(f_beat_pen)
- if i < self.item_length:
- f_number = QGraphicsSimpleTextItem('%d' % (i + 1), self.header)
- f_number.setPos(self.beat_width * i + 5, 2)
- f_number.setBrush(Qt.white)
- for j in range(0, self.grid_div):
- f_line = QGraphicsLineItem(0, 0, 0, self.piano_height, self.header)
- f_line.setZValue(1.0)
- if float(j) == self.grid_div / 2.0:
- f_line.setLine(0, 0, 0, self.piano_height)
- f_line.setPos(self.beat_width * i + self.value_width * j, self.header_height)
- else:
- f_line.setPos(self.beat_width * i + self.value_width * j, self.header_height)
- f_line.setPen(f_line_pen)
-
- def set_zoom(self, scale_x, scale_y):
- self.scale(scale_x, scale_y)
-
- def clear_drawn_items(self):
- self.scene.clear()
- self.draw_header()
- self.draw_piano()
- self.draw_grid()
-
- def draw_item(self, a_item):
- """ Draw all notes in an instance of the item class"""
- for f_notes in a_item.notes:
- self.draw_note(f_note)
-
- def draw_note(self, a_note, a_start=None, a_length=None, a_velocity=None):
- """a_note is the midi number, a_start is 1-4.99, a_length is in beats, a_velocity is 0-127"""
- f_start = self.piano_width + self.padding + self.beat_width * a_start
- f_length = self.beat_width * (a_length * 0.25 * self.item_length)
- f_note = self.header_height + self.note_height * (self.total_notes - a_note - 1)
- f_note_item = note_item(f_length, self.note_height, a_note)
- f_note_item.setPos(f_start, f_note)
- f_vel_opacity = QGraphicsOpacityEffect()
- f_vel_opacity.setOpacity(a_velocity * 0.007874016 * 0.6 + 0.3)
- f_note_item.setGraphicsEffect(f_vel_opacity)
- self.scene.addItem(f_note_item)
- self.notes.append(f_note_item)
-
- #--------------- 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("MidiSequencer")
-
- app = CarlaApplication("MidiSequencer")
- gui = piano_roll()
- app.exit_exec()
|