Browse Source

Add initial code for c++ xycontroller

tags/v0.9.0
falkTX 13 years ago
parent
commit
e5a867fcb8
9 changed files with 1973 additions and 12 deletions
  1. +4
    -3
      .gitignore
  2. +6
    -9
      c++/jackmeter/Makefile
  3. +245
    -0
      c++/widgets/pixmapdial.cpp
  4. +87
    -0
      c++/widgets/pixmapdial.h
  5. +506
    -0
      c++/widgets/pixmapkeyboard.cpp
  6. +84
    -0
      c++/widgets/pixmapkeyboard.h
  7. +72
    -0
      c++/xycontroller/Makefile
  8. +33
    -0
      c++/xycontroller/qtcreator/xycontroller.pro
  9. +936
    -0
      c++/xycontroller/xycontroller.cpp

+ 4
- 3
.gitignore View File

@@ -34,7 +34,11 @@ c++/carla-backend/doxygen/
c++/carla-includes/vst/
c++/jackmeter/cadence_jackmeter

c++/*/*-build-*-Debug/
c++/*/*-build-*-Release/

src/resources_rc.py
resources/qrc_resources.cpp


# incoming
@@ -46,7 +50,4 @@ c++/carla-backend/carla_backend_lv2.cpp
c++/carla-backend/carla_backend_vst.cpp
c++/carla-bridge/qt-plugin/
c++/flmixer/
c++/xycontroller/
c++/widgets/ledbutton.*
c++/widgets/pixmapdial.*
c++/widgets/pixmapkeyboard.*

+ 6
- 9
c++/jackmeter/Makefile View File

@@ -6,26 +6,23 @@

CXX ?= g++
STRIP ?= strip
PKGCONFIG ?= pkg-config

BASE_FLAGS = -O2 -ffast-math -fomit-frame-pointer -fPIC -mtune=generic -msse -mfpmath=sse -Wall

BUILD_FLAGS = $(BASE_FLAGS) -std=c++0x $(CXXFLAGS)
BUILD_FLAGS += $(shell $(PKGCONFIG) --cflags QtCore QtGui jack)
BUILD_FLAGS += $(shell pkg-config --cflags QtCore QtGui jack)
BUILD_FLAGS += -DNDEBUG -DQT_NO_DEBUG -DQT_NO_DEBUG_STREAM -DQT_NO_DEBUG_OUTPUT

LINK_FLAGS = $(LDFLAGS)
LINK_FLAGS += $(shell $(PKGCONFIG) --libs QtCore QtGui jack)
LINK_FLAGS += $(shell pkg-config --libs QtCore QtGui jack)

OBJS = jackmeter.o \
../widgets/digitalpeakmeter.o

HAVE_JACKSESSION = $(shell pkg-config --atleast-version=0.121.0 jack && echo true)

ifeq ($(HAVE_JACKSESSION),true)
ifeq ($(shell pkg-config --atleast-version=0.121.0 jack && echo true),true)
BUILD_FLAGS += -DHAVE_JACKSESSION
endif

OBJS = jackmeter.o \
../widgets/digitalpeakmeter.o

# --------------------------------------------------------------

all: cadence_jackmeter


+ 245
- 0
c++/widgets/pixmapdial.cpp View File

@@ -0,0 +1,245 @@
/*
* Pixmap Dial, a custom Qt4 widget
* Copyright (C) 2011-2012 Filipe Coelho <falktx@gmail.com>
*
* 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 COPYING file
*/

#include "pixmapdial.h"

#include <QtCore/QTimer>
#include <QtGui/QPainter>

PixmapDial::PixmapDial(QWidget* parent):
QDial(parent)
{
m_pixmap.load(":/bitmaps/dial_01d.png");
m_pixmap_n_str = "01";
m_custom_paint = CUSTOM_PAINT_NULL;

m_hovered = false;
m_hover_step = HOVER_MIN;

if (m_pixmap.width() > m_pixmap.height())
m_orientation = HORIZONTAL;
else
m_orientation = VERTICAL;

m_label = "";
m_label_pos = QPointF(0.0, 0.0);
m_label_width = 0;
m_label_height = 0;
m_label_gradient = QLinearGradient(0, 0, 0, 1);

if (palette().window().color().lightness() > 100)
{
// Light background
QColor c = palette().dark().color();
m_color1 = c;
m_color2 = QColor(c.red(), c.green(), c.blue(), 0);
m_colorT[0] = palette().buttonText().color();
m_colorT[1] = palette().mid().color();
}
else
{
// Dark background
m_color1 = QColor(0, 0, 0, 255);
m_color2 = QColor(0, 0, 0, 0);
m_colorT[0] = Qt::white;
m_colorT[1] = Qt::darkGray;
}

updateSizes();
}

int PixmapDial::getSize() const
{
return p_size;
}

void PixmapDial::setCustomPaint(CustomPaint paint)
{
m_custom_paint = paint;
update();
}

void PixmapDial::setEnabled(bool enabled)
{
if (isEnabled() != enabled)
{
m_pixmap.load(QString(":/dial_%1%2.png").arg(m_pixmap_n_str).arg(enabled ? "" : "d"));
updateSizes();
update();
}
QDial::setEnabled(enabled);
}

void PixmapDial::setLabel(QString label)
{
m_label = label;

m_label_width = QFontMetrics(font()).width(label);
m_label_height = QFontMetrics(font()).height();

m_label_pos.setX((p_size/2)-(m_label_width/2));
m_label_pos.setY(p_size+m_label_height);

m_label_gradient.setColorAt(0.0, m_color1);
m_label_gradient.setColorAt(0.6, m_color1);
m_label_gradient.setColorAt(1.0, m_color2);

m_label_gradient.setStart(0, p_size/2);
m_label_gradient.setFinalStop(0, p_size+m_label_height+5);

m_label_gradient_rect = QRectF(p_size*1/8, p_size/2, p_size*6/8, p_size+m_label_height+5);
update();
}

void PixmapDial::setPixmap(int pixmap_id)
{
if (pixmap_id > 10)
m_pixmap_n_str = QString::number(pixmap_id);
else
m_pixmap_n_str = QString("0%1").arg(pixmap_id);

m_pixmap.load(QString(":/bitmaps/dial_%1%2.png").arg(m_pixmap_n_str).arg(isEnabled() ? "" : "d"));

if (m_pixmap.width() > m_pixmap.height())
m_orientation = HORIZONTAL;
else
m_orientation = VERTICAL;

updateSizes();
update();
}

QSize PixmapDial::minimumSizeHint() const
{
return QSize(p_size, p_size);
}

QSize PixmapDial::sizeHint() const
{
return QSize(p_size, p_size);
}

void PixmapDial::updateSizes()
{
p_width = m_pixmap.width();
p_height = m_pixmap.height();

if (p_width < 1)
p_width = 1;

if (p_height < 1)
p_height = 1;

if (m_orientation == HORIZONTAL)
{
p_size = p_height;
p_count = p_width/p_height;
}
else
{
p_size = p_width;
p_count = p_height/p_width;
}

setMinimumSize(p_size, p_size + m_label_height + 5);
setMaximumSize(p_size, p_size + m_label_height + 5);
}

void PixmapDial::enterEvent(QEvent* event)
{
m_hovered = true;
if (m_hover_step == HOVER_MIN)
m_hover_step = HOVER_MIN + 1;
QDial::enterEvent(event);
}

void PixmapDial::leaveEvent(QEvent* event)
{
m_hovered = false;
if (m_hover_step == HOVER_MAX)
m_hover_step = HOVER_MAX - 1;
QDial::leaveEvent(event);
}

void PixmapDial::paintEvent(QPaintEvent*)
{
QPainter painter(this);

if (! m_label.isEmpty())
{
painter.setPen(m_color2);
painter.setBrush(m_label_gradient);
painter.drawRect(m_label_gradient_rect);

painter.setPen(isEnabled() ? m_colorT[0] : m_colorT[1]);
painter.drawText(m_label_pos, m_label);
}

QRectF target, source;

if (isEnabled())
{
float current = value()-minimum();
float divider = maximum()-minimum();

if (divider == 0.0f)
return;

target = QRectF(0.0, 0.0, p_size, p_size);
float value = current/divider;

int xpos, ypos, per = int((p_count-1) * value);

if (m_orientation == HORIZONTAL)
{
xpos = p_size*per;
ypos = 0.0;
}
else
{
xpos = 0.0;
ypos = p_size*per;
}

source = QRectF(xpos, ypos, p_size, p_size);
painter.drawPixmap(target, m_pixmap, source);

// Custom knobs (Dry/Wet and Volume)
// TODO

// Custom knobs (L and R)
// TODO

if (HOVER_MIN > m_hover_step && m_hover_step < HOVER_MAX)
{
m_hover_step += m_hovered ? 1 : -1;
QTimer::singleShot(20, this, SLOT(update()));
}
}
else
{
target = QRectF(0.0, 0.0, p_size, p_size);
source = target;
painter.drawPixmap(target, m_pixmap, source);
}
}

void PixmapDial::resizeEvent(QResizeEvent* event)
{
updateSizes();
QDial::resizeEvent(event);
}

+ 87
- 0
c++/widgets/pixmapdial.h View File

@@ -0,0 +1,87 @@
/*
* Pixmap Dial, a custom Qt4 widget
* Copyright (C) 2011-2012 Filipe Coelho <falktx@gmail.com>
*
* 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 COPYING file
*/

#ifndef PIXMAPDIAL_H
#define PIXMAPDIAL_H

#include <QtGui/QDial>
#include <QtGui/QPixmap>

class PixmapDial : public QDial
{
public:
enum Orientation {
HORIZONTAL = 0,
VERTICAL = 1
};

enum CustomPaint {
CUSTOM_PAINT_NULL = 0,
CUSTOM_PAINT_CARLA_WET = 1,
CUSTOM_PAINT_CARLA_VOL = 2,
CUSTOM_PAINT_CARLA_L = 3,
CUSTOM_PAINT_CARLA_R = 4
};

PixmapDial(QWidget* parent);

int getSize() const;
void setCustomPaint(CustomPaint paint);
void setEnabled(bool enabled);
void setLabel(QString label);
void setPixmap(int pixmap_id);

QSize minimumSizeHint() const;
QSize sizeHint() const;

protected:
void updateSizes();

void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void paintEvent(QPaintEvent* event);
void resizeEvent(QResizeEvent* event);

private:
QPixmap m_pixmap;
QString m_pixmap_n_str;

CustomPaint m_custom_paint;
Orientation m_orientation;

bool m_hovered;
int m_hover_step;

QString m_label;
QPointF m_label_pos;
int m_label_width;
int m_label_height;

QLinearGradient m_label_gradient;
QRectF m_label_gradient_rect;

QColor m_color1;
QColor m_color2;
QColor m_colorT[2];

int p_width, p_height, p_size, p_count;

static const int HOVER_MIN = 0;
static const int HOVER_MAX = 9;
};

#endif // PIXMAPDIAL_H

+ 506
- 0
c++/widgets/pixmapkeyboard.cpp View File

@@ -0,0 +1,506 @@
/*
* Pixmap Keyboard, a custom Qt4 widget
* Copyright (C) 2011-2012 Filipe Coelho <falktx@gmail.com>
*
* 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 COPYING file
*/

#include "pixmapkeyboard.h"

#include <QtCore/QMap>
#include <QtCore/QTimer>
#include <QtGui/QKeyEvent>
#include <QtGui/QMouseEvent>
#include <QtGui/QPainter>

static QMap<int, QRectF> midi_key2rect_map_horizontal;
static QMap<int, QRectF> midi_key2rect_map_vertical;
static QMap<int, int> midi_keyboard2key_map;
static QVector<int> blackNotes;

void midi_map_init()
{
static bool init = false;
if (init) return;
init = true;

// midi_key2rect_map_horizontal ------
midi_key2rect_map_horizontal[0] = QRectF(0, 0, 18, 64); // C
midi_key2rect_map_horizontal[1] = QRectF(13, 0, 11, 42); // C#
midi_key2rect_map_horizontal[2] = QRectF(18, 0, 25, 64); // D
midi_key2rect_map_horizontal[3] = QRectF(37, 0, 11, 42); // D#
midi_key2rect_map_horizontal[4] = QRectF(42, 0, 18, 64); // E
midi_key2rect_map_horizontal[5] = QRectF(60, 0, 18, 64); // F
midi_key2rect_map_horizontal[6] = QRectF(73, 0, 11, 42); // F#
midi_key2rect_map_horizontal[7] = QRectF(78, 0, 25, 64); // G
midi_key2rect_map_horizontal[8] = QRectF(97, 0, 11, 42); // G#
midi_key2rect_map_horizontal[9] = QRectF(102, 0, 25, 64); // A
midi_key2rect_map_horizontal[10] = QRectF(121, 0, 11, 42); // A#
midi_key2rect_map_horizontal[11] = QRectF(126, 0, 18, 64); // B

// midi_key2rect_map_vertical --------
midi_key2rect_map_vertical[11] = QRectF(0, 0, 64, 18); // B
midi_key2rect_map_vertical[10] = QRectF(0, 14, 42, 7); // A#
midi_key2rect_map_vertical[9] = QRectF(0, 18, 64, 24); // A
midi_key2rect_map_vertical[8] = QRectF(0, 38, 42, 7); // G#
midi_key2rect_map_vertical[7] = QRectF(0, 42, 64, 24); // G
midi_key2rect_map_vertical[6] = QRectF(0, 62, 42, 7); // F#
midi_key2rect_map_vertical[5] = QRectF(0, 66, 64, 18); // F
midi_key2rect_map_vertical[4] = QRectF(0, 84, 64, 18); // E
midi_key2rect_map_vertical[3] = QRectF(0, 98, 42, 7); // D#
midi_key2rect_map_vertical[2] = QRectF(0, 102, 64, 24); // D
midi_key2rect_map_vertical[1] = QRectF(0, 122, 42, 7); // C#
midi_key2rect_map_vertical[0] = QRectF(0, 126, 64, 18); // C

// midi_keyboard2key_map -------------
// 3th octave
midi_keyboard2key_map[Qt::Key_Z] = 48;
midi_keyboard2key_map[Qt::Key_S] = 49;
midi_keyboard2key_map[Qt::Key_X] = 50;
midi_keyboard2key_map[Qt::Key_D] = 51;
midi_keyboard2key_map[Qt::Key_C] = 52;
midi_keyboard2key_map[Qt::Key_V] = 53;
midi_keyboard2key_map[Qt::Key_G] = 54;
midi_keyboard2key_map[Qt::Key_B] = 55;
midi_keyboard2key_map[Qt::Key_H] = 56;
midi_keyboard2key_map[Qt::Key_N] = 57;
midi_keyboard2key_map[Qt::Key_J] = 58;
midi_keyboard2key_map[Qt::Key_M] = 59;
// 4th octave
midi_keyboard2key_map[Qt::Key_Q] = 60;
midi_keyboard2key_map[Qt::Key_2] = 61;
midi_keyboard2key_map[Qt::Key_W] = 62;
midi_keyboard2key_map[Qt::Key_3] = 63;
midi_keyboard2key_map[Qt::Key_E] = 64;
midi_keyboard2key_map[Qt::Key_R] = 65;
midi_keyboard2key_map[Qt::Key_5] = 66;
midi_keyboard2key_map[Qt::Key_T] = 67;
midi_keyboard2key_map[Qt::Key_6] = 68;
midi_keyboard2key_map[Qt::Key_Y] = 69;
midi_keyboard2key_map[Qt::Key_7] = 70;
midi_keyboard2key_map[Qt::Key_U] = 71;

blackNotes << 1;
blackNotes << 3;
blackNotes << 6;
blackNotes << 8;
blackNotes << 10;
}

PixmapKeyboard::PixmapKeyboard(QWidget* parent):
QWidget(parent),
m_font("Monospace", 8, QFont::Normal)
{
midi_map_init();

m_octaves = 6;
m_lastMouseNote = -1;
m_needsUpdate = false;

setCursor(Qt::PointingHandCursor);
setMode(HORIZONTAL);
}

void PixmapKeyboard::sendNoteOn(int note, bool sendSignal)
{
if (note >= 0 && note <= 127 && ! m_enabledKeys.contains(note))
{
m_enabledKeys.append(note);
if (sendSignal)
emit noteOn(note);

m_needsUpdate = true;
QTimer::singleShot(0, this, SLOT(updateOnce()));
}

if (m_enabledKeys.count() == 1)
emit notesOn();
}

void PixmapKeyboard::sendNoteOff(int note, bool sendSignal)
{
if (note >= 0 && note <= 127 && m_enabledKeys.contains(note))
{
m_enabledKeys.removeOne(note);
if (sendSignal)
emit noteOff(note);

m_needsUpdate = true;
QTimer::singleShot(0, this, SLOT(updateOnce()));
}

if (m_enabledKeys.count() == 0)
emit notesOff();
}

void PixmapKeyboard::setMode(Orientation mode, Color color)
{
if (color == COLOR_CLASSIC)
{
m_colorStr = "classic";
}
else if (color == COLOR_ORANGE)
{
m_colorStr = "orange";
}
else
{
qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color", mode, color);
return setMode(mode);
}

if (mode == HORIZONTAL)
{
m_midi_map = &midi_key2rect_map_horizontal;
m_pixmap.load(QString(":/bitmaps/kbd_h_%1.png").arg(m_colorStr));
m_pixmap_mode = HORIZONTAL;
p_width = m_pixmap.width();
p_height = m_pixmap.height() / 2;
}
else if (mode == VERTICAL)
{
m_midi_map = &midi_key2rect_map_vertical;
m_pixmap.load(QString(":/bitmaps/kbd_v_%1.png").arg(m_colorStr));
m_pixmap_mode = VERTICAL;
p_width = m_pixmap.width() / 2;
p_height = m_pixmap.height();
}
else
{
qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode", mode, color);
return setMode(HORIZONTAL);
}

setOctaves(m_octaves);
}

void PixmapKeyboard::setOctaves(int octaves)
{
if (octaves < 1)
octaves = 1;
else if (octaves > 6)
octaves = 6;
m_octaves = octaves;

if (m_pixmap_mode == HORIZONTAL)
{
setMinimumSize(p_width * m_octaves, p_height);
setMaximumSize(p_width * m_octaves, p_height);
}
else if (m_pixmap_mode == VERTICAL)
{
setMinimumSize(p_width, p_height * m_octaves);
setMaximumSize(p_width, p_height * m_octaves);
}

update();
}

void PixmapKeyboard::keyPressEvent(QKeyEvent* event)
{
int qKey = event->key();

if (midi_keyboard2key_map.keys().contains(qKey))
sendNoteOn(midi_keyboard2key_map[qKey]);

QWidget::keyPressEvent(event);
}

void PixmapKeyboard::keyReleaseEvent(QKeyEvent* event)
{
int qKey = event->key();

if (midi_keyboard2key_map.keys().contains(qKey))
sendNoteOff(midi_keyboard2key_map[qKey]);

QWidget::keyReleaseEvent(event);
}

void PixmapKeyboard::mousePressEvent(QMouseEvent* event)
{
m_lastMouseNote = -1;
handleMousePos(event->pos());
setFocus();
QWidget::mousePressEvent(event);
}

void PixmapKeyboard::mouseMoveEvent(QMouseEvent* event)
{
handleMousePos(event->pos());
QWidget::mousePressEvent(event);
}

void PixmapKeyboard::mouseReleaseEvent(QMouseEvent* event)
{
if (m_lastMouseNote != -1)
{
sendNoteOff(m_lastMouseNote);
m_lastMouseNote = -1;
}
QWidget::mouseReleaseEvent(event);
}

void PixmapKeyboard::handleMousePos(const QPoint& pos)
{
int note, octave;
QPointF n_pos;

if (m_pixmap_mode == HORIZONTAL)
{
if (pos.x() < 0 or pos.x() > m_octaves * 144)
return;
octave = pos.x() / p_width;
n_pos = QPointF(pos.x() % p_width, pos.y());
}
else if (m_pixmap_mode == VERTICAL)
{
if (pos.y() < 0 or pos.y() > m_octaves * 144)
return;
octave = m_octaves - pos.y() / p_height;
n_pos = QPointF(pos.x(), pos.y() % p_height);
}
else
return;

octave += 3;

if ((*m_midi_map)[1].contains(n_pos)) // C#
note = 1;
else if ((*m_midi_map)[3].contains(n_pos)) // D#
note = 3;
else if ((*m_midi_map)[6].contains(n_pos)) // F#
note = 6;
else if ((*m_midi_map)[8].contains(n_pos)) // G#
note = 8;
else if ((*m_midi_map)[10].contains(n_pos))// A#
note = 10;
else if ((*m_midi_map)[0].contains(n_pos)) // C
note = 0;
else if ((*m_midi_map)[2].contains(n_pos)) // D
note = 2;
else if ((*m_midi_map)[4].contains(n_pos)) // E
note = 4;
else if ((*m_midi_map)[5].contains(n_pos)) // F
note = 5;
else if ((*m_midi_map)[7].contains(n_pos)) // G
note = 7;
else if ((*m_midi_map)[9].contains(n_pos)) // A
note = 9;
else if ((*m_midi_map)[11].contains(n_pos))// B
note = 11;
else
note = -1;

if (note != -1)
{
note += octave * 12;
if (m_lastMouseNote != note)
{
sendNoteOff(m_lastMouseNote);
sendNoteOn(note);
}
}
else
sendNoteOff(m_lastMouseNote);

m_lastMouseNote = note;
}

void PixmapKeyboard::paintEvent(QPaintEvent*)
{
QPainter painter(this);

// -------------------------------------------------------------
// Paint clean keys (as background)

for (int octave=0; octave < m_octaves; octave++)
{
QRectF target;

if (m_pixmap_mode == HORIZONTAL)
target = QRectF(p_width * octave, 0, p_width, p_height);
else if (m_pixmap_mode == VERTICAL)
target = QRectF(0, p_height * octave, p_width, p_height);
else
return;

QRectF source = QRectF(0, 0, p_width, p_height);
painter.drawPixmap(target, m_pixmap, source);
}

// -------------------------------------------------------------
// Paint (white) pressed keys

bool paintedWhite = false;

for (int i=0; i < m_enabledKeys.count(); i++)
{
int octave, note = m_enabledKeys[i];
QRectF pos = _getRectFromMidiNote(note);

if (_isNoteBlack(note))
continue;

if (note < 35)
// cannot paint this note
continue;
else if (note < 48)
octave = 0;
else if (note < 60)
octave = 1;
else if (note < 72)
octave = 2;
else if (note < 84)
octave = 3;
else if (note < 96)
octave = 4;
else if (note < 108)
octave = 5;
else
// cannot paint this note either
continue;

if (m_pixmap_mode == VERTICAL)
octave = m_octaves - octave - 1;

QRectF target, source;

if (m_pixmap_mode == HORIZONTAL)
{
target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
source = QRectF(pos.x(), p_height, pos.width(), pos.height());
}
else if (m_pixmap_mode == VERTICAL)
{
target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
source = QRectF(p_width, pos.y(), pos.width(), pos.height());
}
else
return;

paintedWhite = true;
painter.drawPixmap(target, m_pixmap, source);
}

// -------------------------------------------------------------
// Clear white keys border

if (paintedWhite)
{
for (int octave=0; octave < m_octaves; octave++)
{
foreach (int note, blackNotes)
{
QRectF target, source;
QRectF pos = _getRectFromMidiNote(note);
if (m_pixmap_mode == HORIZONTAL)
{
target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
source = QRectF(pos.x(), 0, pos.width(), pos.height());
}
else if (m_pixmap_mode == VERTICAL)
{
target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
source = QRectF(0, pos.y(), pos.width(), pos.height());
}
else
return;

painter.drawPixmap(target, m_pixmap, source);
}
}
}

// -------------------------------------------------------------
// Paint (black) pressed keys

for (int i=0; i < m_enabledKeys.count(); i++)
{
int octave, note = m_enabledKeys[i];
QRectF pos = _getRectFromMidiNote(note);

if (! _isNoteBlack(note))
continue;

if (note < 35)
// cannot paint this note
continue;
else if (note < 48)
octave = 0;
else if (note < 60)
octave = 1;
else if (note < 72)
octave = 2;
else if (note < 84)
octave = 3;
else if (note < 96)
octave = 4;
else if (note < 108)
octave = 5;
else
// cannot paint this note either
continue;

if (m_pixmap_mode == VERTICAL)
octave = m_octaves - octave - 1;

QRectF target, source;

if (m_pixmap_mode == HORIZONTAL)
{
target = QRectF(pos.x() + (p_width * octave), 0, pos.width(), pos.height());
source = QRectF(pos.x(), p_height, pos.width(), pos.height());
}
else if (m_pixmap_mode == VERTICAL)
{
target = QRectF(pos.x(), pos.y() + (p_height * octave), pos.width(), pos.height());
source = QRectF(p_width, pos.y(), pos.width(), pos.height());
}
else
return;

painter.drawPixmap(target, m_pixmap, source);
}

// Paint C-number note info
painter.setFont(m_font);
painter.setPen(Qt::black);

for (int i=0; i < m_octaves; i++)
{
if (m_pixmap_mode == HORIZONTAL)
painter.drawText(i * 144, 48, 18, 18, Qt::AlignCenter, QString("C%1").arg(i + 2));
else if (m_pixmap_mode == VERTICAL)
painter.drawText(45, (m_octaves * 144) - (i * 144) - 16, 18, 18, Qt::AlignCenter, QString("C%1").arg(i + 2));
}
}

void PixmapKeyboard::updateOnce()
{
if (m_needsUpdate)
{
update();
m_needsUpdate = false;
}
}

bool PixmapKeyboard::_isNoteBlack(int note)
{
int baseNote = note % 12;
return blackNotes.contains(baseNote);
}

QRectF PixmapKeyboard::_getRectFromMidiNote(int note)
{
return (*m_midi_map)[note % 12];
}

+ 84
- 0
c++/widgets/pixmapkeyboard.h View File

@@ -0,0 +1,84 @@
/*
* Pixmap Keyboard, a custom Qt4 widget
* Copyright (C) 2011-2012 Filipe Coelho <falktx@gmail.com>
*
* 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 COPYING file
*/

#ifndef PIXMAPKEYBOARD_H
#define PIXMAPKEYBOARD_H

#include <QtGui/QPixmap>
#include <QtGui/QWidget>

class PixmapKeyboard : public QWidget
{
Q_OBJECT

public:
enum Color {
COLOR_CLASSIC = 0,
COLOR_ORANGE = 1
};

enum Orientation {
HORIZONTAL = 0,
VERTICAL = 1
};

PixmapKeyboard(QWidget* parent);

void sendNoteOn(int note, bool sendSignal=true);
void sendNoteOff(int note, bool sendSignal=true);

void setMode(Orientation mode, Color color=COLOR_ORANGE);
void setOctaves(int octaves);

signals:
void noteOn(int);
void noteOff(int);
void notesOn();
void notesOff();

protected:
void keyPressEvent(QKeyEvent*);
void keyReleaseEvent(QKeyEvent*);
void mousePressEvent(QMouseEvent*);
void mouseMoveEvent(QMouseEvent*);
void mouseReleaseEvent(QMouseEvent*);
void handleMousePos(const QPoint&);
void paintEvent(QPaintEvent*);

private Q_SLOTS:
void updateOnce();

private:
QPixmap m_pixmap;
Orientation m_pixmap_mode;

QString m_colorStr;
QFont m_font;

int m_octaves;
int m_lastMouseNote;
int p_width, p_height;

bool m_needsUpdate;
QList<int> m_enabledKeys;
QMap<int, QRectF> *m_midi_map;

bool _isNoteBlack(int note);
QRectF _getRectFromMidiNote(int note);
};

#endif // PIXMAPKEYBOARD_H

+ 72
- 0
c++/xycontroller/Makefile View File

@@ -0,0 +1,72 @@
#!/usr/bin/make -f
# Makefile for xycontroller #
# ------------------------------------ #
# Created by falkTX
#

CXX ?= g++
MOC ?= moc
RCC ?= rcc
UIC ?= uic
STRIP ?= strip
WINDRES ?= windres

BASE_FLAGS = -O2 -ffast-math -fomit-frame-pointer -fPIC -mtune=generic -msse -mfpmath=sse -Wall -I../widgets

BUILD_FLAGS = $(BASE_FLAGS) -std=c++0x $(CXXFLAGS)
BUILD_FLAGS += $(shell pkg-config --cflags QtCore QtGui jack)
BUILD_FLAGS += -DNDEBUG -DQT_NO_DEBUG -DQT_NO_DEBUG_STREAM -DQT_NO_DEBUG_OUTPUT

LINK_FLAGS = $(LDFLAGS)
LINK_FLAGS += $(shell pkg-config --libs QtCore QtGui jack)

ifeq ($(shell pkg-config --atleast-version=0.121.0 jack && echo true),true)
BUILD_FLAGS += -DHAVE_JACKSESSION
endif

FILES = \
xycontroller.moc \
ui_xycontroller.h \
../widgets/moc_pixmapkeyboard.cpp \
../../resources/qrc_resources.cpp

OBJS = xycontroller.o \
../widgets/pixmapdial.o \
../widgets/pixmapkeyboard.o \
../widgets/moc_pixmapkeyboard.o \
../../resources/qrc_resources.o

# --------------------------------------------------------------

all: cadence_xycontroller

cadence_xycontroller: $(FILES) $(OBJS)
$(CXX) $(OBJS) $(LINK_FLAGS) -o $@ && strip $@

cadence_xycontroller.exe: $(FILES) $(OBJS) icon.o
$(CXX) $(OBJS) icon.o -static -mwindows $(LINK_FLAGS) -o $@ && strip $@

# --------------------------------------------------------------

xycontroller.moc: xycontroller.cpp
$(MOC) $< -o $@

ui_xycontroller.h: ../../src/ui/xycontroller.ui
$(UIC) $< -o $@

icon.o: ../../resources/ico/cadence.rc
$(WINDRES) -i $< -o $@ -O coff

../widgets/moc_pixmapkeyboard.cpp: ../widgets/pixmapkeyboard.h
$(MOC) $< -o $@

../../resources/qrc_resources.cpp: ../../resources/resources.qrc
$(RCC) -name resources $< -o $@

# --------------------------------------------------------------

.cpp.o:
$(CXX) -c $< $(BUILD_FLAGS) -o $@

clean:
rm -f $(FILES) $(OBJS) icon.o cadence_xycontroller*

+ 33
- 0
c++/xycontroller/qtcreator/xycontroller.pro View File

@@ -0,0 +1,33 @@
# QtCreator project file

QT = core gui

CONFIG = debug link_pkgconfig qt resources uic warn_on
PKGCONFIG = jack

TARGET = xycontroller
TEMPLATE = app
VERSION = 0.5.0

DEFINES = HAVE_JACKSESSION

SOURCES = \
../xycontroller.cpp \
../../widgets/pixmapdial.cpp \
../../widgets/pixmapkeyboard.cpp

HEADERS = \
../../jack_utils.h \
../../widgets/pixmapdial.h \
../../widgets/pixmapkeyboard.h

FORMS = \
../../../src/ui/xycontroller.ui

RESOURCES = \
../../../resources/resources.qrc

INCLUDEPATH = \
../../widgets

QMAKE_CXXFLAGS *= -std=c++0x

+ 936
- 0
c++/xycontroller/xycontroller.cpp View File

@@ -0,0 +1,936 @@
/*
* Simple JACK Audio Meter
* Copyright (C) 2011-2012 Filipe Coelho <falktx@gmail.com>
*
* 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 COPYING file
*/

#include <QtCore/Qt>

#ifndef Q_COMPILER_LAMBDA
# define nullptr (0)
#endif

#define VERSION "0.5.0"

//#include "../jack_utils.h"
#include "ui_xycontroller.h"

//#include <QtCore/QMutex>
#include <QtCore/QSettings>
#include <QtCore/QTimer>
#include <QtGui/QApplication>
#include <QtGui/QGraphicsItem>
#include <QtGui/QGraphicsScene>
#include <QtGui/QGraphicsSceneEvent>
#include <QtGui/QKeyEvent>
#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>

// -------------------------------

class Queue {
public:
Queue(int size_) :
size(size_)
{
}

~Queue()
{
}

void put_nowait(int, int, int)
{
//const QMutexLocker m(&mutex);
}

private:
const int size;
//QMutex mutex;
};

qreal abs_q(const qreal value)
{
if (value < 1.0)
return -value;
return value;
}

#if 0
jack_client_t* jack_client = nullptr;
jack_port_t* jack_midi_in_port = nullptr;
jack_port_t* jack_midi_out_port = nullptr;
#endif
Queue jack_midi_in_data = Queue(512);
Queue jack_midi_out_data = Queue(512);

QVector<QString> MIDI_CC_LIST;
void MIDI_CC_LIST__init()
{
//MIDI_CC_LIST << "0x00 Bank Select";
MIDI_CC_LIST << "0x01 Modulation";
MIDI_CC_LIST << "0x02 Breath";
MIDI_CC_LIST << "0x03 (Undefined)";
MIDI_CC_LIST << "0x04 Foot";
MIDI_CC_LIST << "0x05 Portamento";
//MIDI_CC_LIST << "0x06 (Data Entry MSB)";
MIDI_CC_LIST << "0x07 Volume";
MIDI_CC_LIST << "0x08 Balance";
MIDI_CC_LIST << "0x09 (Undefined)";
MIDI_CC_LIST << "0x0A Pan";
MIDI_CC_LIST << "0x0B Expression";
MIDI_CC_LIST << "0x0C FX Control 1";
MIDI_CC_LIST << "0x0D FX Control 2";
MIDI_CC_LIST << "0x0E (Undefined)";
MIDI_CC_LIST << "0x0F (Undefined)";
MIDI_CC_LIST << "0x10 General Purpose 1";
MIDI_CC_LIST << "0x11 General Purpose 2";
MIDI_CC_LIST << "0x12 General Purpose 3";
MIDI_CC_LIST << "0x13 General Purpose 4";
MIDI_CC_LIST << "0x14 (Undefined)";
MIDI_CC_LIST << "0x15 (Undefined)";
MIDI_CC_LIST << "0x16 (Undefined)";
MIDI_CC_LIST << "0x17 (Undefined)";
MIDI_CC_LIST << "0x18 (Undefined)";
MIDI_CC_LIST << "0x19 (Undefined)";
MIDI_CC_LIST << "0x1A (Undefined)";
MIDI_CC_LIST << "0x1B (Undefined)";
MIDI_CC_LIST << "0x1C (Undefined)";
MIDI_CC_LIST << "0x1D (Undefined)";
MIDI_CC_LIST << "0x1E (Undefined)";
MIDI_CC_LIST << "0x1F (Undefined)";
//MIDI_CC_LIST << "0x20 *Bank Select";
//MIDI_CC_LIST << "0x21 *Modulation";
//MIDI_CC_LIST << "0x22 *Breath";
//MIDI_CC_LIST << "0x23 *(Undefined)";
//MIDI_CC_LIST << "0x24 *Foot";
//MIDI_CC_LIST << "0x25 *Portamento";
//MIDI_CC_LIST << "0x26 *(Data Entry MSB)";
//MIDI_CC_LIST << "0x27 *Volume";
//MIDI_CC_LIST << "0x28 *Balance";
//MIDI_CC_LIST << "0x29 *(Undefined)";
//MIDI_CC_LIST << "0x2A *Pan";
//MIDI_CC_LIST << "0x2B *Expression";
//MIDI_CC_LIST << "0x2C *FX *Control 1";
//MIDI_CC_LIST << "0x2D *FX *Control 2";
//MIDI_CC_LIST << "0x2E *(Undefined)";
//MIDI_CC_LIST << "0x2F *(Undefined)";
//MIDI_CC_LIST << "0x30 *General Purpose 1";
//MIDI_CC_LIST << "0x31 *General Purpose 2";
//MIDI_CC_LIST << "0x32 *General Purpose 3";
//MIDI_CC_LIST << "0x33 *General Purpose 4";
//MIDI_CC_LIST << "0x34 *(Undefined)";
//MIDI_CC_LIST << "0x35 *(Undefined)";
//MIDI_CC_LIST << "0x36 *(Undefined)";
//MIDI_CC_LIST << "0x37 *(Undefined)";
//MIDI_CC_LIST << "0x38 *(Undefined)";
//MIDI_CC_LIST << "0x39 *(Undefined)";
//MIDI_CC_LIST << "0x3A *(Undefined)";
//MIDI_CC_LIST << "0x3B *(Undefined)";
//MIDI_CC_LIST << "0x3C *(Undefined)";
//MIDI_CC_LIST << "0x3D *(Undefined)";
//MIDI_CC_LIST << "0x3E *(Undefined)";
//MIDI_CC_LIST << "0x3F *(Undefined)";
//MIDI_CC_LIST << "0x40 Damper On/Off"; // <63 off, >64 on
//MIDI_CC_LIST << "0x41 Portamento On/Off"; // <63 off, >64 on
//MIDI_CC_LIST << "0x42 Sostenuto On/Off"; // <63 off, >64 on
//MIDI_CC_LIST << "0x43 Soft Pedal On/Off"; // <63 off, >64 on
//MIDI_CC_LIST << "0x44 Legato Footswitch"; // <63 Normal, >64 Legato
//MIDI_CC_LIST << "0x45 Hold 2"; // <63 off, >64 on
MIDI_CC_LIST << "0x46 Control 1 [Variation]";
MIDI_CC_LIST << "0x47 Control 2 [Timbre]";
MIDI_CC_LIST << "0x48 Control 3 [Release]";
MIDI_CC_LIST << "0x49 Control 4 [Attack]";
MIDI_CC_LIST << "0x4A Control 5 [Brightness]";
MIDI_CC_LIST << "0x4B Control 6 [Decay]";
MIDI_CC_LIST << "0x4C Control 7 [Vib Rate]";
MIDI_CC_LIST << "0x4D Control 8 [Vib Depth]";
MIDI_CC_LIST << "0x4E Control 9 [Vib Delay]";
MIDI_CC_LIST << "0x4F Control 10 [Undefined]";
MIDI_CC_LIST << "0x50 General Purpose 5";
MIDI_CC_LIST << "0x51 General Purpose 6";
MIDI_CC_LIST << "0x52 General Purpose 8";
MIDI_CC_LIST << "0x53 General Purpose 9";
MIDI_CC_LIST << "0x54 Portamento Control";
MIDI_CC_LIST << "0x5B FX 1 Depth [Reverb]";
MIDI_CC_LIST << "0x5C FX 2 Depth [Tremolo]";
MIDI_CC_LIST << "0x5D FX 3 Depth [Chorus]";
MIDI_CC_LIST << "0x5E FX 4 Depth [Detune]";
MIDI_CC_LIST << "0x5F FX 5 Depth [Phaser]";
}

// -------------------------------
// XY Controller Scene

class XYGraphicsScene : public QGraphicsScene
{
Q_OBJECT

public:
XYGraphicsScene(QWidget* parent) : QGraphicsScene(parent), m_parent(parent)
{
cc_x = 1;
cc_y = 2;

m_mouseLock = false;
m_smooth = false;
m_smooth_x = 0;
m_smooth_y = 0;

setBackgroundBrush(Qt::black);

QPen cursorPen(QColor(255, 255, 255), 2);
QColor cursorBrush(255, 255, 255, 50);
m_cursor = addEllipse(QRectF(-10, -10, 20, 20), cursorPen, cursorBrush);

QPen linePen(QColor(200, 200, 200, 100), 1, Qt::DashLine);
m_lineH = addLine(-9999, 0, 9999, 0, linePen);
m_lineV = addLine(0, -9999, 0, 9999, linePen);

p_size = QRectF(-100, -100, 100, 100);
}

~XYGraphicsScene()
{
}

void setControlX(int x)
{
cc_x = x;
}

void setControlY(int y)
{
cc_y = y;
}

void setChannels(QList<int> channels)
{
m_channels = channels;
}

void setPosX(qreal x, bool forward=true)
{
if (! m_mouseLock)
{
qreal pos_x = x * (p_size.x() + p_size.width());
m_cursor->setPos(pos_x, m_cursor->y());
m_lineV->setX(pos_x);

if (forward)
{
qreal value = pos_x / (p_size.x() + p_size.width());
sendMIDI(&value, nullptr);
}
else
m_smooth_x = pos_x;
}
}

void setPosY(qreal y, bool forward=true)
{
if (! m_mouseLock)
{
qreal pos_y = y * (p_size.y() + p_size.height());
m_cursor->setPos(m_cursor->x(), pos_y);
m_lineH->setY(pos_y);

if (forward)
{
qreal value = pos_y / (p_size.y() + p_size.height());
sendMIDI(nullptr, &value);
}
else
m_smooth_y = pos_y;
}
}

void setSmooth(bool smooth)
{
m_smooth = smooth;
}

void setSmoothValues(int x, int y)
{
m_smooth_x = x * (p_size.x() + p_size.width());
m_smooth_y = y * (p_size.y() + p_size.height());
}

void handleCC(int param, int value)
{
bool sendUpdate = false;
qreal xp, yp;
xp = yp = 0.0;

if (param == cc_x)
{
sendUpdate = true;
xp = (float(value) / 63) - 1.0;
yp = m_cursor->y() / (p_size.y() + p_size.height());

if (xp < -1.0)
xp = -1.0;
else if (xp > 1.0)
xp = 1.0;

setPosX(xp, false);
}

if (param == cc_y)
{
sendUpdate = true;
xp = m_cursor->x() / (p_size.x() + p_size.width());
yp = (float(value) / 63) - 1.0;

if (yp < -1.0)
yp = -1.0;
else if (yp > 1.0)
yp = 1.0;

setPosY(yp, false);
}

if (sendUpdate)
emit cursorMoved(xp, yp);
}

void updateSize(QSize size)
{
p_size.setRect(-(size.width() / 2), -(size.height() / 2), size.width(), size.height());
}

void updateSmooth()
{
if (! m_smooth)
return;

if (m_cursor->x() != m_smooth_x || m_cursor->y() != m_smooth_y)
{
if (abs(m_cursor->x() - m_smooth_x) <= 0.001)
{
m_smooth_x = m_cursor->x();
return;
}
else if (abs(m_cursor->y() - m_smooth_y) <= 0.001)
{
m_smooth_y = m_cursor->y();
return;
}

qreal new_x = (m_smooth_x + m_cursor->x() * 3) / 4;
qreal new_y = (m_smooth_y + m_cursor->y() * 3) / 4;
QPointF pos(new_x, new_y);

m_cursor->setPos(pos);
m_lineH->setY(pos.y());
m_lineV->setX(pos.x());

qreal xp = pos.x() / (p_size.x() + p_size.width());
qreal yp = pos.y() / (p_size.y() + p_size.height());

sendMIDI(&xp, &yp);
emit cursorMoved(xp, yp);
}
}

protected:
void handleMousePos(QPointF pos)
{
if (! p_size.contains(pos))
{
if (pos.x() < p_size.x())
pos.setX(p_size.x());
else if (pos.x() > p_size.x() + p_size.width())
pos.setX(p_size.x() + p_size.width());

if (pos.y() < p_size.y())
pos.setY(p_size.y());
else if (pos.y() > p_size.y() + p_size.height())
pos.setY(p_size.y() + p_size.height());
}

m_smooth_x = pos.x();
m_smooth_y = pos.y();

if (! m_smooth)
{
m_cursor->setPos(pos);
m_lineH->setY(pos.y());
m_lineV->setX(pos.x());

qreal xp = pos.x() / (p_size.x() + p_size.width());
qreal yp = pos.y() / (p_size.y() + p_size.height());

sendMIDI(&xp, &yp);

emit cursorMoved(xp, yp);
}
}

void sendMIDI(qreal* xp=nullptr, qreal* yp=nullptr)
{
qreal rate = qreal(0xff) / 4;

if (xp != nullptr)
{
int value = *xp * rate + rate;
foreach (const int& channel, m_channels)
jack_midi_out_data.put_nowait(0xB0 + channel - 1, cc_x, value);
}

if (yp != nullptr)
{
int value = *yp * rate + rate;
foreach (const int& channel, m_channels)
jack_midi_out_data.put_nowait(0xB0 + channel - 1, cc_y, value);
}
}

void keyPressEvent(QKeyEvent* event)
{
event->accept();
}

void wheelEvent(QGraphicsSceneWheelEvent* event)
{
event->accept();
}

void mousePressEvent(QGraphicsSceneMouseEvent* event)
{
m_mouseLock = true;
handleMousePos(event->scenePos());
parent()->setCursor(Qt::CrossCursor);
QGraphicsScene::mousePressEvent(event);
}

void mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
handleMousePos(event->scenePos());
QGraphicsScene::mouseMoveEvent(event);
}

void mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
m_mouseLock = false;
parent()->setCursor(Qt::ArrowCursor);
QGraphicsScene::mouseReleaseEvent(event);
}

QWidget* parent() const
{
return m_parent;
}

signals:
void cursorMoved(qreal, qreal);

private:
int cc_x;
int cc_y;
QList<int> m_channels;

bool m_mouseLock;
bool m_smooth;
int m_smooth_x;
int m_smooth_y;

QGraphicsEllipseItem* m_cursor;
QGraphicsLineItem* m_lineH;
QGraphicsLineItem* m_lineV;

QRectF p_size;

QWidget* const m_parent;
};

// -------------------------------
// XY Controller Window

namespace Ui {
class XYControllerW;
}

class XYControllerW : public QMainWindow
{
Q_OBJECT

public:
XYControllerW() :
QMainWindow(nullptr),
scene(this),
settings("Cadence", "XY-Controller"),
ui(new Ui::XYControllerW)
{
ui->setupUi(this);

// -------------------------------------------------------------
// Internal stuff

cc_x = 1;
cc_y = 2;

// -------------------------------------------------------------
// Set-up GUI stuff

ui->dial_x->setPixmap(2);
ui->dial_y->setPixmap(2);
ui->dial_x->setLabel("X");
ui->dial_y->setLabel("Y");
ui->keyboard->setOctaves(6);

ui->graphicsView->setScene(&scene);
ui->graphicsView->setRenderHints(QPainter::Antialiasing);

foreach (const QString& MIDI_CC, MIDI_CC_LIST)
{
ui->cb_control_x->addItem(MIDI_CC);
ui->cb_control_y->addItem(MIDI_CC);
}

// -------------------------------------------------------------
// Load Settings

loadSettings();

// -------------------------------------------------------------
// Connect actions to functions

connect(ui->keyboard, SIGNAL(noteOn(int)), SLOT(noteOn(int)));
connect(ui->keyboard, SIGNAL(noteOff(int)), SLOT(noteOff(int)));

connect(ui->cb_smooth, SIGNAL(clicked(bool)), SLOT(setSmooth(bool)));

connect(ui->dial_x, SIGNAL(valueChanged(int)), SLOT(updateSceneX(int)));
connect(ui->dial_y, SIGNAL(valueChanged(int)), SLOT(updateSceneY(int)));

connect(ui->cb_control_x, SIGNAL(currentIndexChanged(QString)), SLOT(checkCC_X(QString)));
connect(ui->cb_control_y, SIGNAL(currentIndexChanged(QString)), SLOT(checkCC_Y(QString)));

connect(&scene, SIGNAL(cursorMoved(qreal, qreal)), SLOT(sceneCursorMoved(qreal, qreal)));

connect(ui->act_ch_01, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_02, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_03, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_04, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_05, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_06, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_07, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_08, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_09, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_10, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_11, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_12, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_13, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_14, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_15, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_16, SIGNAL(triggered(bool)), SLOT(checkChannel(bool)));
connect(ui->act_ch_all, SIGNAL(triggered()), SLOT(checkChannel_all()));
connect(ui->act_ch_none, SIGNAL(triggered()), SLOT(checkChannel_none()));

connect(ui->act_show_keyboard, SIGNAL(triggered(bool)), SLOT(showKeyboard(bool)));
connect(ui->act_about, SIGNAL(triggered()), SLOT(about()));

// -------------------------------------------------------------
// Final stuff

m_midiInTimerId = startTimer(50);
QTimer::singleShot(0, this, SLOT(updateScreen()));
}

protected slots:
void noteOn(int note)
{
foreach (const int& channel, m_channels)
jack_midi_out_data.put_nowait(0x90 + channel - 1, note, 100);
}

void noteOff(int note)
{
foreach (const int& channel, m_channels)
jack_midi_out_data.put_nowait(0x80 + channel - 1, note, 0);
}

void updateSceneX(int x)
{
scene.setPosX(float(x) / 100, bool(sender()));
}

void updateSceneY(int y)
{
scene.setPosY(float(y) / 100, bool(sender()));
}

void checkCC_X(QString text)
{
if (! text.isEmpty())
{
bool ok;
int tmp_cc_x = text.split(" ").at(0).toInt(&ok, 16);

if (ok)
{
cc_x = tmp_cc_x;
scene.setControlX(cc_x);
}
}
}

void checkCC_Y(QString text)
{
if (! text.isEmpty())
{
bool ok;
int tmp_cc_y = text.split(" ").at(0).toInt(&ok, 16);

if (ok)
{
cc_y = tmp_cc_y;
scene.setControlY(cc_y);
}
}
}

void checkChannel(bool clicked)
{
if (! sender())
return;

bool ok;
int channel = ((QAction*)sender())->text().toInt(&ok);

if (ok)
{
if (clicked && ! m_channels.contains(channel))
m_channels.append(channel);
else if ((! clicked) && m_channels.contains(channel))
m_channels.removeOne(channel);
scene.setChannels(m_channels);
}
}

void checkChannel_all()
{
ui->act_ch_01->setChecked(true);
ui->act_ch_02->setChecked(true);
ui->act_ch_03->setChecked(true);
ui->act_ch_04->setChecked(true);
ui->act_ch_05->setChecked(true);
ui->act_ch_06->setChecked(true);
ui->act_ch_07->setChecked(true);
ui->act_ch_08->setChecked(true);
ui->act_ch_09->setChecked(true);
ui->act_ch_10->setChecked(true);
ui->act_ch_11->setChecked(true);
ui->act_ch_12->setChecked(true);
ui->act_ch_13->setChecked(true);
ui->act_ch_14->setChecked(true);
ui->act_ch_15->setChecked(true);
ui->act_ch_16->setChecked(true);

#ifdef Q_COMPILER_INITIALIZER_LISTS
m_channels = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
#else
m_channels.clear();

for (int i=1; i <= 16; i++)
m_channels << i;
#endif
scene.setChannels(m_channels);
}

void checkChannel_none()
{
ui->act_ch_01->setChecked(false);
ui->act_ch_02->setChecked(false);
ui->act_ch_03->setChecked(false);
ui->act_ch_04->setChecked(false);
ui->act_ch_05->setChecked(false);
ui->act_ch_06->setChecked(false);
ui->act_ch_07->setChecked(false);
ui->act_ch_08->setChecked(false);
ui->act_ch_09->setChecked(false);
ui->act_ch_10->setChecked(false);
ui->act_ch_11->setChecked(false);
ui->act_ch_12->setChecked(false);
ui->act_ch_13->setChecked(false);
ui->act_ch_14->setChecked(false);
ui->act_ch_15->setChecked(false);
ui->act_ch_16->setChecked(false);

m_channels.clear();
scene.setChannels(m_channels);
}

void setSmooth(bool yesno)
{
scene.setSmooth(yesno);
}

void sceneCursorMoved(qreal xp, qreal yp)
{
ui->dial_x->blockSignals(true);
ui->dial_y->blockSignals(true);

ui->dial_x->setValue(xp * 100);
ui->dial_y->setValue(yp * 100);

ui->dial_x->blockSignals(false);
ui->dial_y->blockSignals(false);
}

void showKeyboard(bool yesno)
{
ui->scrollArea->setVisible(yesno);
QTimer::singleShot(0, this, SLOT(updateScreen()));
}

void about()
{
QMessageBox::about(this, tr("About XY Controller"), tr("<h3>XY Controller</h3>"
"<br>Version %1"
"<br>XY Controller is a simple XY widget that sends and receives data from Jack MIDI.<br>"
"<br>Copyright (C) 2012 falkTX").arg(VERSION));
}

void updateScreen()
{
scene.updateSize(ui->graphicsView->size());
ui->graphicsView->centerOn(0, 0);

int dial_x = ui->dial_x->value();
int dial_y = ui->dial_y->value();
updateSceneX(dial_x);
updateSceneY(dial_y);
scene.setSmoothValues(float(dial_x) / 100, float(dial_y) / 100);
}

protected:
void saveSettings()
{
QVariantList varChannelList;
foreach (const int& channel, m_channels)
varChannelList << channel;

settings.setValue("Geometry", saveGeometry());
settings.setValue("ShowKeyboard", ui->scrollArea->isVisible());
settings.setValue("Smooth", ui->cb_smooth->isChecked());
settings.setValue("DialX", ui->dial_x->value());
settings.setValue("DialY", ui->dial_y->value());
settings.setValue("ControlX", cc_x);
settings.setValue("ControlY", cc_y);
settings.setValue("Channels", varChannelList);
}

void loadSettings()
{
restoreGeometry(settings.value("Geometry").toByteArray());

bool showKeyboard = settings.value("ShowKeyboard", false).toBool();
ui->act_show_keyboard->setChecked(showKeyboard);
ui->scrollArea->setVisible(showKeyboard);

bool smooth = settings.value("Smooth", false).toBool();
ui->cb_smooth->setChecked(smooth);
scene.setSmooth(smooth);

ui->dial_x->setValue(settings.value("DialX", 50).toInt());
ui->dial_y->setValue(settings.value("DialY", 50).toInt());

cc_x = settings.value("ControlX", 1).toInt();
cc_y = settings.value("ControlY", 2).toInt();
scene.setControlX(cc_x);
scene.setControlY(cc_y);

m_channels.clear();

if (settings.contains("Channels"))
{
QVariantList channels = settings.value("Channels").toList();


foreach (const QVariant& var, channels)
{
bool ok;
int channel = var.toInt(&ok);

if (ok)
m_channels.append(channel);
}
}
else
#ifdef Q_COMPILER_INITIALIZER_LISTS
m_channels = { 1 };
#else
m_channels << 1;
#endif

scene.setChannels(m_channels);

for (int i=0; i < MIDI_CC_LIST.size(); i++)
{
bool ok;
int cc = MIDI_CC_LIST[i].split(" ").at(0).toInt(&ok, 16);

if (ok)
{
if (cc_x == cc)
ui->cb_control_x->setCurrentIndex(i);
if (cc_y == cc)
ui->cb_control_y->setCurrentIndex(i);
}
}

if (m_channels.contains(1))
ui->act_ch_01->setChecked(true);
if (m_channels.contains(2))
ui->act_ch_02->setChecked(true);
if (m_channels.contains(3))
ui->act_ch_03->setChecked(true);
if (m_channels.contains(4))
ui->act_ch_04->setChecked(true);
if (m_channels.contains(5))
ui->act_ch_05->setChecked(true);
if (m_channels.contains(6))
ui->act_ch_06->setChecked(true);
if (m_channels.contains(7))
ui->act_ch_07->setChecked(true);
if (m_channels.contains(8))
ui->act_ch_08->setChecked(true);
if (m_channels.contains(9))
ui->act_ch_09->setChecked(true);
if (m_channels.contains(10))
ui->act_ch_10->setChecked(true);
if (m_channels.contains(11))
ui->act_ch_11->setChecked(true);
if (m_channels.contains(12))
ui->act_ch_12->setChecked(true);
if (m_channels.contains(13))
ui->act_ch_13->setChecked(true);
if (m_channels.contains(14))
ui->act_ch_14->setChecked(true);
if (m_channels.contains(15))
ui->act_ch_15->setChecked(true);
if (m_channels.contains(16))
ui->act_ch_16->setChecked(true);
}
void timerEvent(QTimerEvent* event)
{
if (event->timerId() == m_midiInTimerId)
{
//if not jack_midi_in_data.empty():
//while True:
//try:
// data1, data2, data3 = jack_midi_in_data.get_nowait()
//except QuequeEmpty:
// break

//channel = (data1 & 0x0F) + 1
//mode = data1 & 0xF0

//if channel in self.m_channels:
// if mode == 0x80:
// self.keyboard.sendNoteOff(data2, False)
// elif mode == 0x90:
// self.keyboard.sendNoteOn(data2, False)
// elif mode == 0xB0:
// self.scene.handleCC(data2, data3)

//jack_midi_in_data.task_done()

scene.updateSmooth();
}

QMainWindow::timerEvent(event);
}

void resizeEvent(QResizeEvent* event)
{
updateScreen();
QMainWindow::resizeEvent(event);
}

void closeEvent(QCloseEvent* event)
{
saveSettings();
QMainWindow::closeEvent(event);
}

private:
int cc_x;
int cc_y;
QList<int> m_channels;

int m_midiInTimerId;

XYGraphicsScene scene;
QSettings settings;

Ui::XYControllerW* ui;
};

#include "xycontroller.moc"

// -------------------------------

// -------------------------------

int main(int argc, char* argv[])
{
MIDI_CC_LIST__init();

QApplication app(argc, argv);
app.setApplicationName("XY-Controller");
app.setApplicationVersion(VERSION);
app.setOrganizationName("Cadence");
//app.setWindowIcon(QIcon(":/48x48/xy-controller.png"));

#if 0
// JACK initialization
jack_status_t jStatus;
jack_options_t jOptions = static_cast<JackOptions>(JackNoStartServer/*|JackSessionID*/);
jack_client = jack_client_open("XY-Controller", jOptions, &jStatus);

if (! jack_client)
{
std::string errorString(jack_status_get_error_string(jStatus));
QMessageBox::critical(nullptr, app.translate("XY-Controller", "Error"), app.translate("XY-Controller",
"Could not connect to JACK, possible reasons:\n"
"%1").arg(QString::fromStdString(errorString)));
return 1;
}

jack_midi_in_port = jack_port_register(jack_client, "midi_in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
jack_midi_out_port = jack_port_register(jack_client, "midi_out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);

//jack_set_process_callback(jClient, process_callback, nullptr);
//jack_set_port_connect_callback(jClient, port_callback, nullptr);
//jack_set_session_callback(jClient, session_callback, argv[0]);
jack_activate(jack_client);
#endif

// Show GUI
XYControllerW gui;
gui.show();

// App-Loop
int ret = app.exec();

#if 0
jack_deactivate(jack_client);
jack_client_close(jack_client);
#endif

return ret;
}

Loading…
Cancel
Save