Browse Source

midi pattern: quick&dirty note audition, fix octave offset

Signed-off-by: falkTX <falktx@falktx.com>
tags/v2.3.0-RC1
falkTX 4 years ago
parent
commit
8301366ae1
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
4 changed files with 109 additions and 28 deletions
  1. +10
    -6
      source/frontend/midipattern-ui
  2. +38
    -11
      source/frontend/widgets/pianoroll.py
  3. +43
    -6
      source/native-plugins/midi-base.hpp
  4. +18
    -5
      source/native-plugins/midi-pattern.cpp

+ 10
- 6
source/frontend/midipattern-ui View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# A piano roll viewer/editor
# Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2014-2015 Perry Nguyen
#
# This program is free software; you can redistribute it and/or
@@ -69,6 +69,7 @@ class MidiPatternW(ExternalUI, QMainWindow):
self.ui.act_edit_select_all.triggered.connect(self.slot_editSelectAll)

self.ui.piano.midievent.connect(self.sendMsg)
self.ui.piano.noteclicked.connect(self.sendTemporaryNote)
self.ui.piano.measureupdate.connect(self.updateMeasureBox)
self.ui.piano.modeupdate.connect(self.ui.modeIndicator.changeMode)
self.ui.piano.modeupdate.connect(self.slot_modeChanged)
@@ -237,6 +238,9 @@ class MidiPatternW(ExternalUI, QMainWindow):
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 sendTemporaryNote(self, note, on):
self.send(["midi-note", note, on])

def msgCallback(self, msg):
msg = charPtrToString(msg)

@@ -246,15 +250,15 @@ class MidiPatternW(ExternalUI, QMainWindow):

elif msg == "midievent-add":
# adds single midi event
time = int(self.readlineblock())
size = int(self.readlineblock())
data = tuple(int(self.readlineblock()) for x in range(size))
time = self.readlineblock_int()
size = self.readlineblock_int()
data = tuple(self.readlineblock_int() for x in range(size))

self.handleMidiEvent(time, size, data)

elif msg == "transport":
playing, frame, bar, beat, tick = tuple(int(i) for i in self.readlineblock().split(":"))
bpm = float(self.readlineblock())
bpm = self.readlineblock_float()
playing = bool(int(playing))

old_frame = self.fTransportInfo['frame']
@@ -326,7 +330,7 @@ class MidiPatternW(ExternalUI, QMainWindow):
if __name__ == '__main__':
import resources_rc

pathBinaries, pathResources = getPaths()
pathBinaries, _ = getPaths()
gCarla.utils = CarlaUtils(os.path.join(pathBinaries, "libcarla_utils." + DLL_EXTENSION))
gCarla.utils.set_process_name("MidiPattern")



+ 38
- 11
source/frontend/widgets/pianoroll.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# A piano roll viewer/editor
# Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2014-2015 Perry Nguyen
#
# This program is free software; you can redistribute it and/or
@@ -21,7 +21,7 @@
# Imports (Global)

from PyQt5.QtCore import Qt, QRectF, QPointF, pyqtSignal
from PyQt5.QtGui import QColor, QFont, QPen, QPainter
from PyQt5.QtGui import QColor, QFont, QPen, QPainter, QTransform
from PyQt5.QtWidgets import QGraphicsItem, QGraphicsLineItem, QGraphicsOpacityEffect, QGraphicsRectItem, QGraphicsSimpleTextItem
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView
from PyQt5.QtWidgets import QWidget, QLabel, QComboBox, QHBoxLayout, QVBoxLayout, QStyle
@@ -246,11 +246,12 @@ class NoteItem(QGraphicsRectItem):


class PianoKeyItem(QGraphicsRectItem):
def __init__(self, width, height, parent):
def __init__(self, width, height, note, parent):
QGraphicsRectItem.__init__(self, 0, 0, width, height, parent)
self.setPen(QPen(QColor(0,0,0,80)))
self.width = width
self.height = height
self.note = note
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
self.setAcceptHoverEvents(True)
self.hover_brush = QColor(200, 0, 0)
@@ -285,6 +286,7 @@ class PianoKeyItem(QGraphicsRectItem):
class PianoRoll(QGraphicsScene):
'''the piano roll'''

noteclicked = pyqtSignal(int,bool)
midievent = pyqtSignal(list)
measureupdate = pyqtSignal(int)
modeupdate = pyqtSignal(str)
@@ -342,6 +344,7 @@ class PianoRoll(QGraphicsScene):
self.piano = None
self.header = None
self.play_head = None
self.last_piano_note = None

self.setGridDiv()
self.default_length = 1. / self.grid_div
@@ -454,6 +457,14 @@ class PianoRoll(QGraphicsScene):

def mousePressEvent(self, event):
QGraphicsScene.mousePressEvent(self, event)

if self.piano.contains(event.scenePos()):
item = self.itemAt(event.scenePos(), QTransform())
if isinstance(item, PianoKeyItem) and item.note is not None:
self.last_piano_note = item.note
self.noteclicked.emit(self.last_piano_note, True)
return

if not (any(key.pressed for key in self.piano_keys)
or any(note.pressed for note in self.notes)):
for note in self.selected_notes:
@@ -469,6 +480,7 @@ class PianoRoll(QGraphicsScene):
self.marquee = QGraphicsRectItem(self.marquee_rect)
self.marquee.setBrush(QColor(255, 255, 255, 100))
self.addItem(self.marquee)

else:
for s_note in self.notes:
if s_note.pressed and s_note in self.selected_notes:
@@ -484,6 +496,10 @@ class PianoRoll(QGraphicsScene):

def mouseMoveEvent(self, event):
QGraphicsScene.mouseMoveEvent(self, event)

if self.last_piano_note is not None:
return

self.mousePos = event.scenePos()
if not (any((key.pressed for key in self.piano_keys))):
m_pos = event.scenePos()
@@ -544,6 +560,13 @@ class PianoRoll(QGraphicsScene):
note.moveEvent(event)

def mouseReleaseEvent(self, event):
QGraphicsScene.mouseReleaseEvent

if self.last_piano_note is not None:
self.noteclicked.emit(self.last_piano_note, False)
self.last_piano_note = None
return

if not (any((key.pressed for key in self.piano_keys)) or any((note.pressed for note in self.notes))):
if event.button() == Qt.LeftButton:
if self.place_ghost and self.insert_mode:
@@ -557,6 +580,7 @@ class PianoRoll(QGraphicsScene):
elif self.marquee_select:
self.marquee_select = False
self.removeItem(self.marquee)

elif not self.marquee_select:
for note in self.selected_notes:
old_info = note.note[:]
@@ -586,32 +610,35 @@ class PianoRoll(QGraphicsScene):
self.piano.setPos(0, self.header_height)
self.addItem(self.piano)

key = PianoKeyItem(piano_keys_width, self.note_height, self.piano)
label = QGraphicsSimpleTextItem('C8', key)
key = PianoKeyItem(piano_keys_width, self.note_height, 78, self.piano)
label = QGraphicsSimpleTextItem('C9', key)
label.setPos(18, 1)
label.setFont(piano_label)
key.setBrush(QColor(255, 255, 255))
for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1):
for i in range(self.end_octave - self.start_octave, 0, -1):
for j in range(self.notes_in_octave, 0, -1):
note = (self.end_octave - i + 3) * 12 - j
if j in black_notes:
key = PianoKeyItem(piano_keys_width/1.4, self.note_height, self.piano)
key = PianoKeyItem(piano_keys_width/1.4, self.note_height, note, self.piano)
key.setBrush(QColor(0, 0, 0))
key.setZValue(1.0)
key.setPos(0, self.note_height * j + self.octave_height * (i - 1))
elif (j - 1) and (j + 1) in black_notes:
key = PianoKeyItem(piano_keys_width, self.note_height * 2, self.piano)
key = PianoKeyItem(piano_keys_width, self.note_height * 2, note, self.piano)
key.setBrush(QColor(255, 255, 255))
key.setPos(0, self.note_height * j + self.octave_height * (i - 1) - self.note_height/2.)
elif (j - 1) in black_notes:
key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, self.piano)
key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, note, self.piano)
key.setBrush(QColor(255, 255, 255))
key.setPos(0, self.note_height * j + self.octave_height * (i - 1) - self.note_height/2.)
elif (j + 1) in black_notes:
key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, self.piano)
key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, note, self.piano)
key.setBrush(QColor(255, 255, 255))
key.setPos(0, self.note_height * j + self.octave_height * (i - 1))
if j == 12:
label = QGraphicsSimpleTextItem('{}{}'.format(labels[j - 1], self.end_octave - i), key )
label = QGraphicsSimpleTextItem('{}{}'.format(labels[j - 1],
self.end_octave - i + 1),
key)
label.setPos(18, 6)
label.setFont(piano_label)
self.piano_keys.append(key)


+ 43
- 6
source/native-plugins/midi-base.hpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -59,9 +59,18 @@ public:
fStartTime(0),
fReadMutex(),
fWriteMutex(),
fData()
fData(),
fTemporary()
{
CARLA_SAFE_ASSERT(kPlayer != nullptr);

carla_zeroStructs(fTemporary, 2);

fTemporary[0].data[0] = MIDI_STATUS_NOTE_OFF;
fTemporary[1].data[0] = MIDI_STATUS_NOTE_ON;
fTemporary[1].data[2] = 100;

fTemporary[0].size = fTemporary[1].size = 3;
}

~MidiPattern() noexcept
@@ -183,6 +192,12 @@ public:
appendSorted(rawEvent);
}

void flagTemporaryNote(const uint8_t note, const bool on)
{
fTemporary[on ? 1 : 0].time = 1;
fTemporary[on ? 1 : 0].data[1] = note;
}

// -------------------------------------------------------------------
// remove data

@@ -240,6 +255,8 @@ public:
{
long double ldtime;

playTemporary();

const CarlaMutexTryLocker cmtl(fReadMutex);

if (cmtl.wasNotLocked())
@@ -266,6 +283,21 @@ public:
return true;
}

void playTemporary()
{
if (fTemporary[0].time != 0)
{
fTemporary[0].time = 0;
kPlayer->writeMidiEvent(fMidiPort, 0.0, &fTemporary[0]);
}

if (fTemporary[1].time != 0)
{
fTemporary[1].time = 0;
kPlayer->writeMidiEvent(fMidiPort, 0.0, &fTemporary[1]);
}
}

// -------------------------------------------------------------------
// configure

@@ -303,12 +335,15 @@ public:

const CarlaMutexLocker cmlw(fWriteMutex);

if (fData.count() == 0)
return nullptr;

char* const data((char*)std::calloc(1, fData.count()*maxMsgSize+1));
char* const data((char*)std::calloc(1, fData.count() * maxMsgSize + 1));
CARLA_SAFE_ASSERT_RETURN(data != nullptr, nullptr);

if (fData.count() == 0)
{
*data = '\0';
return data;
}

char* dataWrtn = data;
int wrtn;

@@ -453,6 +488,8 @@ private:
CarlaMutex fWriteMutex;
LinkedList<const RawMidiEvent*> fData;

RawMidiEvent fTemporary[2];

void appendSorted(const RawMidiEvent* const event)
{
const CarlaMutexLocker cmlw(fWriteMutex);


+ 18
- 5
source/native-plugins/midi-pattern.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Native Plugins
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -281,6 +281,10 @@ protected:
fLastFrame = fTimeInfo.frame;
fLastPosition = static_cast<float>(playPos);
}
else
{
fMidiOut.playTemporary();
}
}

// -------------------------------------------------------------------
@@ -389,6 +393,17 @@ protected:
return true;
}

if (std::strcmp(msg, "midi-note") == 0)
{
uint8_t note;
bool on;
CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(note), true);
CARLA_SAFE_ASSERT_RETURN(readNextLineAsBool(on), true);

fMidiOut.flagTemporaryNote(note, on);
return true;
}

if (std::strcmp(msg, "midievent-add") == 0)
{
uint64_t time;
@@ -406,8 +421,7 @@ protected:
data[i] = dvalue;
}

fMidiOut.addRaw(time /* * TICKS_PER_BEAT */, data, size);

fMidiOut.addRaw(time, data, size);
return true;
}

@@ -428,8 +442,7 @@ protected:
data[i] = dvalue;
}

fMidiOut.removeRaw(time /* * TICKS_PER_BEAT */, data, size);

fMidiOut.removeRaw(time, data, size);
return true;
}



Loading…
Cancel
Save