diff --git a/src/clickablelabel.py b/src/clickablelabel.py
index 3279ac1..d0e5828 100644
--- a/src/clickablelabel.py
+++ b/src/clickablelabel.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Clickable Label, a custom Qt4 widget
diff --git a/src/digitalpeakmeter.py b/src/digitalpeakmeter.py
index 70da2e0..f90bc2c 100644
--- a/src/digitalpeakmeter.py
+++ b/src/digitalpeakmeter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Digital Peak Meter, a custom Qt4 widget
diff --git a/src/jacklib.py b/src/jacklib.py
index 28cb782..5e049f3 100644
--- a/src/jacklib.py
+++ b/src/jacklib.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# JACK ctypes definitions for usage in python applications
diff --git a/src/jacklib_helpers.py b/src/jacklib_helpers.py
index 60e8cdf..7933e65 100644
--- a/src/jacklib_helpers.py
+++ b/src/jacklib_helpers.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Helper functions for extra jacklib functionality
diff --git a/src/jackmeter.py b/src/jackmeter.py
index 15a107f..fe1cc38 100644
--- a/src/jackmeter.py
+++ b/src/jackmeter.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Simple JACK Audio Meter
@@ -75,7 +75,7 @@ def reconnect_inputs():
need_reconnect = False
class MeterW(DigitalPeakMeter):
- def __init__(self, parent):
+ def __init__(self, parent=None):
DigitalPeakMeter.__init__(self, parent)
client_name = str(jacklib.get_client_name(client), encoding="ascii")
@@ -125,7 +125,7 @@ if __name__ == '__main__':
reconnect_inputs()
# Show GUI
- gui = MeterW(None)
+ gui = MeterW()
gui.resize(70, 600)
gui.show()
diff --git a/src/ladspa_rdf.py b/src/ladspa_rdf.py
index 99fdc9a..42c01d7 100644
--- a/src/ladspa_rdf.py
+++ b/src/ladspa_rdf.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# LADSPA RDF python support
diff --git a/src/logs.py b/src/logs.py
old mode 100644
new mode 100755
index 8224c8f..e55e022
--- a/src/logs.py
+++ b/src/logs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# JACK, A2J, LASH and LADISH Logs Viewer
@@ -360,7 +360,7 @@ if __name__ == '__main__':
app = QApplication(sys.argv)
# Show GUI
- gui = LogsW(None, Qt.WindowFlags())
+ gui = LogsW(None)
gui.show()
set_up_signals(gui)
diff --git a/src/pixmapdial.py b/src/pixmapdial.py
index e1b981a..f603fd2 100644
--- a/src/pixmapdial.py
+++ b/src/pixmapdial.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Pixmap Dial, a custom Qt4 widget
diff --git a/src/pixmapkeyboard.py b/src/pixmapkeyboard.py
index a68e8dd..26165c1 100644
--- a/src/pixmapkeyboard.py
+++ b/src/pixmapkeyboard.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Pixmap Keyboard, a custom Qt4 widget
@@ -92,7 +92,7 @@ class PixmapKeyboard(QWidget):
VERTICAL = 1
def __init__(self, parent):
- super(PixmapKeyboard, self).__init__(parent)
+ QWidget.__init__(self, parent)
self.m_octaves = 6
self.m_lastMouseNote = -1
diff --git a/src/render.py b/src/render.py
old mode 100644
new mode 100755
index ff8ad12..83d457f
--- a/src/render.py
+++ b/src/render.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# JACK-Capture frontend, with freewheel and transport support
@@ -17,7 +17,7 @@
# For a full copy of the GNU General Public License see the COPYING file
# Imports (Global)
-from PyQt4.QtCore import pyqtSlot, Qt, QProcess, QTime, QTimer
+from PyQt4.QtCore import pyqtSlot, QProcess, QTime, QTimer
from PyQt4.QtGui import QDialog
from time import sleep
@@ -31,8 +31,8 @@ jack_client = None
# Render Window
class RenderW(QDialog, ui_render.Ui_RenderW):
- def __init__(self, parent, flags):
- QDialog.__init__(self, parent, flags)
+ def __init__(self, parent):
+ QDialog.__init__(self, parent)
self.setupUi(self)
# -------------------------------------------------------------
@@ -290,7 +290,7 @@ if __name__ == '__main__':
sys.exit(1)
# Show GUI
- gui = RenderW(None, Qt.WindowFlags())
+ gui = RenderW(None)
gui.setWindowIcon(getIcon("media-record", 48))
gui.show()
diff --git a/src/shared.py b/src/shared.py
index ab250c2..1064aa1 100644
--- a/src/shared.py
+++ b/src/shared.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Common/Shared code
@@ -56,7 +56,7 @@ if (PATH_env == None):
PATH = ("/bin", "/sbin", "/usr/local/bin", "/usr/local/sbin", "/usr/bin", "/usr/sbin", "/usr/games")
else:
PATH = PATH_env.split(os.pathsep)
- del PATH_env
+del PATH_env
MIDI_CC_LIST = (
#"0x00 Bank Select",
@@ -151,6 +151,15 @@ MIDI_CC_LIST = (
"0x5F FX 5 Depth [Phaser]"
)
+# Convert a value to a list
+def toList(value):
+ if value is None:
+ return []
+ elif not isinstance(value, list):
+ return [value]
+ else:
+ return value
+
# Get Icon from user theme, using our own as backup (Oxygen)
def getIcon(icon, size=16):
return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.png" % (size, size, icon)))
@@ -163,12 +172,12 @@ def getAndSetPath(self, currentPath, lineEdit):
return newPath
# Custom MessageBox
-def CustomMessageBox(self, icon, title, text, extra_text="", buttons=QMessageBox.Yes|QMessageBox.No, defButton=QMessageBox.No):
+def CustomMessageBox(self, icon, title, text, extraText="", buttons=QMessageBox.Yes|QMessageBox.No, defButton=QMessageBox.No):
msgBox = QMessageBox(self)
msgBox.setIcon(icon)
msgBox.setWindowTitle(title)
msgBox.setText(text)
- msgBox.setInformativeText(extra_text)
+ msgBox.setInformativeText(extraText)
msgBox.setStandardButtons(buttons)
msgBox.setDefaultButton(defButton)
return msgBox.exec_()
diff --git a/src/ui/xycontroller.ui b/src/ui/xycontroller.ui
new file mode 100644
index 0000000..3cf6785
--- /dev/null
+++ b/src/ui/xycontroller.ui
@@ -0,0 +1,499 @@
+
+
+ XYControllerW
+
+
+
+ 0
+ 0
+ 588
+ 498
+
+
+
+ XY Controller
+
+
+
+ -
+
+
-
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+ -
+
+
-
+
+
+ -100
+
+
+ 100
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 30
+
+
+
+
+ -
+
+
+ -100
+
+
+ 100
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
-
+
+
+ X Controls:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Y Controls:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Smooth
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 70
+
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ true
+
+
+ Qt::AlignCenter
+
+
+
+
+ 0
+ 6
+ 864
+ 64
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 864
+ 64
+
+
+
+
+ 864
+ 64
+
+
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 864
+ 64
+
+
+
+
+ 864
+ 64
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &About
+
+
+
+
+ true
+
+
+ Show MIDI &Keyboard
+
+
+
+
+ (All)
+
+
+
+
+ true
+
+
+ 1
+
+
+
+
+ true
+
+
+ 2
+
+
+
+
+ true
+
+
+ 3
+
+
+
+
+ true
+
+
+ 4
+
+
+
+
+ true
+
+
+ 5
+
+
+
+
+ true
+
+
+ 6
+
+
+
+
+ true
+
+
+ 7
+
+
+
+
+ true
+
+
+ 8
+
+
+
+
+ true
+
+
+ 9
+
+
+
+
+ true
+
+
+ 10
+
+
+
+
+ true
+
+
+ 11
+
+
+
+
+ true
+
+
+ 12
+
+
+
+
+ true
+
+
+ 13
+
+
+
+
+ true
+
+
+ 14
+
+
+
+
+ true
+
+
+ 15
+
+
+
+
+ true
+
+
+ 16
+
+
+
+
+ &New
+
+
+
+
+ &Open...
+
+
+
+
+ &Save
+
+
+
+
+ Save &As...
+
+
+
+
+
+ :/16x16/application-exit.png:/16x16/application-exit.png
+
+
+ &Quit
+
+
+
+
+ (None)
+
+
+
+
+
+ PixmapKeyboard
+ QWidget
+
+ 1
+
+
+ PixmapDial
+ QDial
+
+
+
+
+
+
+
+
+ act_quit
+ triggered()
+ XYControllerW
+ close()
+
+
+ -1
+ -1
+
+
+ 239
+ 222
+
+
+
+
+
diff --git a/src/xycontroller.py b/src/xycontroller.py
new file mode 100755
index 0000000..36ed2e4
--- /dev/null
+++ b/src/xycontroller.py
@@ -0,0 +1,581 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# XY Controller for JACK, using jacklib
+# Copyright (C) 2012 Filipe Coelho
+#
+# 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
+
+# Imports (Global)
+from PyQt4.QtCore import pyqtSlot, Qt, QPointF, QRectF, QSettings, QTimer, QVariant
+from PyQt4.QtGui import QApplication, QColor, QIcon, QPainter, QPen, QGraphicsItem, QGraphicsScene, QMainWindow
+from queue import Queue, Empty as QuequeEmpty
+
+# Imports (Custom)
+import ui_xycontroller
+from shared import *
+from jacklib_helpers import *
+
+# Globals
+global jack_client, jack_midi_in_port, jack_midi_out_port, jack_midi_in_data, jack_midi_out_data
+jack_client = None
+jack_midi_in_port = None
+jack_midi_out_port = None
+jack_midi_in_data = Queue(512)
+jack_midi_out_data = Queue(512)
+
+# XY Controller Scene
+class XYGraphicsScene(QGraphicsScene):
+ def __init__(self, parent):
+ QGraphicsScene.__init__(self, parent)
+
+ self.cc_x = 1
+ self.cc_y = 2
+ self.m_channels = []
+ self.m_mouseLock = False
+ self.m_smooth = False
+ self.m_smooth_x = 0
+ self.m_smooth_y = 0
+
+ self.setBackgroundBrush(Qt.black)
+
+ cursorPen = QPen(QColor(255,255,255), 2)
+ cursorBrush = QColor(255,255,255,50)
+ self.m_cursor = self.addEllipse(QRectF(-10, -10, 20, 20), cursorPen, cursorBrush)
+
+ linePen = QPen(QColor(200,200,200,100), 1, Qt.DashLine)
+ self.m_lineH = self.addLine(-9999, 0, 9999, 0, linePen)
+ self.m_lineV = self.addLine(0, -9999, 0, 9999, linePen)
+
+ self.p_size = QRectF(-100, -100, 100, 100)
+
+ def setControlX(self, x):
+ self.cc_x = x
+
+ def setControlY(self, y):
+ self.cc_y = y
+
+ def setChannels(self, channels):
+ self.m_channels = channels
+
+ def setPosX(self, x, forward=True):
+ if (self.m_mouseLock == False):
+ pos_x = x*(self.p_size.x()+self.p_size.width())
+ self.m_cursor.setPos(pos_x, self.m_cursor.y())
+ self.m_lineV.setX(pos_x)
+
+ if (forward):
+ self.sendMIDI(pos_x/(self.p_size.x()+self.p_size.width()), None)
+ else:
+ self.m_smooth_x = pos_x
+
+ def setPosY(self, y, forward=True):
+ if (self.m_mouseLock == False):
+ pos_y = y*(self.p_size.y()+self.p_size.height())
+ self.m_cursor.setPos(self.m_cursor.x(), pos_y)
+ self.m_lineH.setY(pos_y)
+
+ if (forward):
+ self.sendMIDI(None, pos_y/(self.p_size.y()+self.p_size.height()))
+ else:
+ self.m_smooth_y = pos_y
+
+ def setSmooth(self, smooth):
+ self.m_smooth = smooth
+
+ def handleCC(self, param, value):
+ if (param == self.cc_x):
+ xp = (float(value)/63)-1.025
+ yp = self.m_cursor.y()/(self.p_size.y()+self.p_size.height())
+ self.setPosX(xp, False)
+
+ elif (param == self.cc_y):
+ xp = self.m_cursor.x()/(self.p_size.x()+self.p_size.width())
+ yp = (float(value)/63)-1.025
+ self.setPosY(yp, False)
+
+ else:
+ return
+
+ self.emit(SIGNAL("cursorMoved(float, float)"), xp, yp)
+
+ def handleMousePos(self, pos):
+ if (not self.p_size.contains(pos)):
+ if (pos.x() < self.p_size.x()):
+ pos.setX(self.p_size.x())
+ elif (pos.x() > self.p_size.x()+self.p_size.width()):
+ pos.setX(self.p_size.x()+self.p_size.width())
+
+ if (pos.y() < self.p_size.y()):
+ pos.setY(self.p_size.y())
+ elif (pos.y() > self.p_size.y()+self.p_size.height()):
+ pos.setY(self.p_size.y()+self.p_size.height())
+
+ self.m_smooth_x = pos.x()
+ self.m_smooth_y = pos.y()
+
+ if (self.m_smooth == False):
+ self.m_cursor.setPos(pos)
+ self.m_lineH.setY(pos.y())
+ self.m_lineV.setX(pos.x())
+
+ xp = pos.x()/(self.p_size.x()+self.p_size.width())
+ yp = pos.y()/(self.p_size.y()+self.p_size.height())
+
+ self.sendMIDI(xp, yp)
+ self.emit(SIGNAL("cursorMoved(float, float)"), xp, yp)
+
+ def sendMIDI(self, xp=None, yp=None):
+ global jack_midi_out_data
+ rate = float(0xff)/4
+
+ if (xp != None):
+ value = int((xp*rate)+rate)
+ for channel in self.m_channels:
+ jack_midi_out_data.put_nowait((0xB0+channel-1, self.cc_x, value))
+
+ if (yp != None):
+ value = int((yp*rate)+rate)
+ for channel in self.m_channels:
+ jack_midi_out_data.put_nowait((0xB0+channel-1, self.cc_y, value))
+
+ def updateSize(self, size):
+ self.p_size.setRect(-(size.width()/2), -(size.height()/2), size.width(), size.height())
+
+ def updateSmooth(self):
+ if (self.m_smooth):
+ if (self.m_cursor.x() != self.m_smooth_x or self.m_cursor.y() != self.m_smooth_y):
+ new_x = (self.m_smooth_x+self.m_cursor.x()*3)/4
+ new_y = (self.m_smooth_y+self.m_cursor.y()*3)/4
+ pos = QPointF(new_x, new_y)
+
+ self.m_cursor.setPos(pos)
+ self.m_lineH.setY(pos.y())
+ self.m_lineV.setX(pos.x())
+
+ xp = pos.x()/(self.p_size.x()+self.p_size.width())
+ yp = pos.y()/(self.p_size.y()+self.p_size.height())
+
+ self.sendMIDI(xp, yp)
+ self.emit(SIGNAL("cursorMoved(float, float)"), xp, yp)
+
+ def keyPressEvent(self, event):
+ event.accept()
+
+ def wheelEvent(self, event):
+ event.accept()
+
+ def mousePressEvent(self, event):
+ self.m_mouseLock = True
+ self.handleMousePos(event.scenePos())
+ QGraphicsScene.mousePressEvent(self, event)
+
+ def mouseMoveEvent(self, event):
+ self.handleMousePos(event.scenePos())
+ QGraphicsScene.mouseMoveEvent(self, event)
+
+ def mouseReleaseEvent(self, event):
+ self.m_mouseLock = False
+ QGraphicsScene.mouseReleaseEvent(self, event)
+
+# XY Controller Window
+class XYControllerW(QMainWindow, ui_xycontroller.Ui_XYControllerW):
+ def __init__(self, parent):
+ QMainWindow.__init__(self, parent)
+ self.setupUi(self)
+
+ # -------------------------------------------------------------
+ # Internal stuff
+
+ self.cc_x = 1
+ self.cc_y = 2
+ self.m_channels = []
+
+ # -------------------------------------------------------------
+ # Set-up GUI stuff
+
+ self.dial_x.setPixmap(2)
+ self.dial_y.setPixmap(2)
+ self.dial_x.setLabel("X")
+ self.dial_y.setLabel("Y")
+ self.keyboard.setOctaves(6)
+
+ self.scene = XYGraphicsScene(self)
+ self.graphicsView.setScene(self.scene)
+ self.graphicsView.setRenderHints(QPainter.Antialiasing)
+
+ for MIDI_CC in MIDI_CC_LIST:
+ self.cb_control_x.addItem(MIDI_CC)
+ self.cb_control_y.addItem(MIDI_CC)
+
+ # -------------------------------------------------------------
+ # Load Settings
+
+ self.settings = QSettings("Cadence", "XY-Controller")
+ self.loadSettings()
+
+ # -------------------------------------------------------------
+ # Connect actions to functions
+
+ self.connect(self.keyboard, SIGNAL("noteOn(int)"), SLOT("slot_noteOn(int)"))
+ self.connect(self.keyboard, SIGNAL("noteOff(int)"), SLOT("slot_noteOff(int)"))
+
+ self.connect(self.cb_smooth, SIGNAL("clicked(bool)"), SLOT("slot_setSmooth(bool)"))
+
+ self.connect(self.dial_x, SIGNAL("valueChanged(int)"), SLOT("slot_updateSceneX(int)"))
+ self.connect(self.dial_y, SIGNAL("valueChanged(int)"), SLOT("slot_updateSceneY(int)"))
+
+ self.connect(self.cb_control_x, SIGNAL("currentIndexChanged(QString)"), SLOT("slot_checkCC_X(QString)"))
+ self.connect(self.cb_control_y, SIGNAL("currentIndexChanged(QString)"), SLOT("slot_checkCC_Y(QString)"))
+
+ # FIXME
+ self.connect(self.scene, SIGNAL("cursorMoved(float, float)"), self.slot_sceneCursorMoved)
+ #self.connect(self.scene, SIGNAL("cursorMoved(float, float)"), SLOT("slot_sceneCursorMoved(float, float)"))
+
+ self.connect(self.act_ch_01, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_02, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_03, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_04, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_05, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_06, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_07, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_08, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_09, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_10, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_11, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_12, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_13, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_14, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_15, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_16, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
+ self.connect(self.act_ch_all, SIGNAL("triggered()"), SLOT("slot_checkChannel_all()"))
+ self.connect(self.act_ch_none, SIGNAL("triggered()"), SLOT("slot_checkChannel_none()"))
+
+ self.connect(self.act_show_keyboard, SIGNAL("triggered(bool)"), SLOT("slot_showKeyboard(bool)"))
+ self.connect(self.act_about, SIGNAL("triggered()"), SLOT("slot_about()"))
+
+ # -------------------------------------------------------------
+ # Final stuff
+
+ self.m_midiInTimerId = self.startTimer(50)
+ QTimer.singleShot(0, self, SLOT("slot_updateScreen()"))
+
+ def updateScreen(self):
+ self.scene.updateSize(self.graphicsView.size())
+ self.graphicsView.centerOn(0, 0)
+
+ self.slot_updateSceneX(self.dial_x.value())
+ self.slot_updateSceneY(self.dial_y.value())
+
+ @pyqtSlot(int)
+ def slot_noteOn(self, note):
+ global jack_midi_out_data
+ for channel in self.m_channels:
+ jack_midi_out_data.put_nowait((0x90+channel-1, note, 100))
+
+ @pyqtSlot(int)
+ def slot_noteOff(self, note):
+ global jack_midi_out_data
+ for channel in self.m_channels:
+ jack_midi_out_data.put_nowait((0x80+channel-1, note, 0))
+
+ @pyqtSlot(int)
+ def slot_updateSceneX(self, x):
+ self.scene.setPosX(float(x)/100)
+
+ @pyqtSlot(int)
+ def slot_updateSceneY(self, y):
+ self.scene.setPosY(float(y)/100)
+
+ @pyqtSlot(str)
+ def slot_checkCC_X(self, text):
+ if (text):
+ self.cc_x = int(text.split(" ")[0], 16)
+ self.scene.setControlX(self.cc_x)
+
+ @pyqtSlot(str)
+ def slot_checkCC_Y(self, text):
+ if (text):
+ self.cc_y = int(text.split(" ")[0], 16)
+ self.scene.setControlY(self.cc_y)
+
+ @pyqtSlot(bool)
+ def slot_checkChannel(self, clicked):
+ channel = int(self.sender().text())
+ if (clicked and channel not in self.m_channels):
+ self.m_channels.append(channel)
+ elif (not clicked and channel in self.m_channels):
+ self.m_channels.remove(channel)
+ self.scene.setChannels(self.m_channels)
+
+ @pyqtSlot()
+ def slot_checkChannel_all(self):
+ self.act_ch_01.setChecked(True)
+ self.act_ch_02.setChecked(True)
+ self.act_ch_03.setChecked(True)
+ self.act_ch_04.setChecked(True)
+ self.act_ch_05.setChecked(True)
+ self.act_ch_06.setChecked(True)
+ self.act_ch_07.setChecked(True)
+ self.act_ch_08.setChecked(True)
+ self.act_ch_09.setChecked(True)
+ self.act_ch_10.setChecked(True)
+ self.act_ch_11.setChecked(True)
+ self.act_ch_12.setChecked(True)
+ self.act_ch_13.setChecked(True)
+ self.act_ch_14.setChecked(True)
+ self.act_ch_15.setChecked(True)
+ self.act_ch_16.setChecked(True)
+ self.m_channels = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
+ self.scene.setChannels(self.m_channels)
+
+ @pyqtSlot()
+ def slot_checkChannel_none(self):
+ self.act_ch_01.setChecked(False)
+ self.act_ch_02.setChecked(False)
+ self.act_ch_03.setChecked(False)
+ self.act_ch_04.setChecked(False)
+ self.act_ch_05.setChecked(False)
+ self.act_ch_06.setChecked(False)
+ self.act_ch_07.setChecked(False)
+ self.act_ch_08.setChecked(False)
+ self.act_ch_09.setChecked(False)
+ self.act_ch_10.setChecked(False)
+ self.act_ch_11.setChecked(False)
+ self.act_ch_12.setChecked(False)
+ self.act_ch_13.setChecked(False)
+ self.act_ch_14.setChecked(False)
+ self.act_ch_15.setChecked(False)
+ self.act_ch_16.setChecked(False)
+ self.m_channels = []
+ self.scene.setChannels(self.m_channels)
+
+ @pyqtSlot(bool)
+ def slot_setSmooth(self, yesno):
+ self.scene.setSmooth(yesno)
+
+ @pyqtSlot(float, float)
+ def slot_sceneCursorMoved(self, xp, yp):
+ self.dial_x.setValue(xp*100)
+ self.dial_y.setValue(yp*100)
+
+ @pyqtSlot(bool)
+ def slot_showKeyboard(self, yesno):
+ self.scrollArea.setVisible(yesno)
+ QTimer.singleShot(0, self, SLOT("slot_updateScreen()"))
+
+ @pyqtSlot()
+ def slot_about(self):
+ QMessageBox.about(self, self.tr("About XY Controller"), self.tr("XY Controller
"
+ "
Version %s"
+ "
XY Controller is a simple XY widget that sends and receives data from Jack MIDI.
"
+ "
Copyright (C) 2012 falkTX" % (VERSION)))
+
+ @pyqtSlot()
+ def slot_updateScreen(self):
+ self.updateScreen()
+
+ def saveSettings(self):
+ self.settings.setValue("Geometry", self.saveGeometry())
+ self.settings.setValue("ShowKeyboard", self.scrollArea.isVisible())
+ self.settings.setValue("Smooth", self.cb_smooth.isChecked())
+ self.settings.setValue("DialX", self.dial_x.value())
+ self.settings.setValue("DialY", self.dial_y.value())
+ self.settings.setValue("ControlX", self.cc_x)
+ self.settings.setValue("ControlY", self.cc_y)
+ self.settings.setValue("Channels", self.m_channels)
+
+ def loadSettings(self):
+ self.restoreGeometry(self.settings.value("Geometry", ""))
+
+ showKeyboard = self.settings.value("ShowKeyboard", False, type=bool)
+ self.act_show_keyboard.setChecked(showKeyboard)
+ self.scrollArea.setVisible(showKeyboard)
+
+ smooth = self.settings.value("Smooth", False, type=bool)
+ self.cb_smooth.setChecked(smooth)
+ self.scene.setSmooth(smooth)
+
+ self.dial_x.setValue(self.settings.value("DialX", 50, type=int))
+ self.dial_y.setValue(self.settings.value("DialY", 50, type=int))
+ self.cc_x = self.settings.value("ControlX", 1, type=int)
+ self.cc_y = self.settings.value("ControlY", 2, type=int)
+ self.m_channels = toList(self.settings.value("Channels", [1]))
+
+ for i in range(len(self.m_channels)):
+ self.m_channels[i] = int(self.m_channels[i])
+
+ self.scene.setChannels(self.m_channels)
+
+ for i in range(len(MIDI_CC_LIST)):
+ cc = int(MIDI_CC_LIST[i].split(" ")[0], 16)
+ if (self.cc_x == cc):
+ self.cb_control_x.setCurrentIndex(i)
+ if (self.cc_y == cc):
+ self.cb_control_y.setCurrentIndex(i)
+
+ if (1 in self.m_channels):
+ self.act_ch_01.setChecked(True)
+ if (2 in self.m_channels):
+ self.act_ch_02.setChecked(True)
+ if (3 in self.m_channels):
+ self.act_ch_03.setChecked(True)
+ if (4 in self.m_channels):
+ self.act_ch_04.setChecked(True)
+ if (5 in self.m_channels):
+ self.act_ch_05.setChecked(True)
+ if (6 in self.m_channels):
+ self.act_ch_06.setChecked(True)
+ if (7 in self.m_channels):
+ self.act_ch_07.setChecked(True)
+ if (8 in self.m_channels):
+ self.act_ch_08.setChecked(True)
+ if (9 in self.m_channels):
+ self.act_ch_09.setChecked(True)
+ if (10 in self.m_channels):
+ self.act_ch_10.setChecked(True)
+ if (11 in self.m_channels):
+ self.act_ch_11.setChecked(True)
+ if (12 in self.m_channels):
+ self.act_ch_12.setChecked(True)
+ if (13 in self.m_channels):
+ self.act_ch_13.setChecked(True)
+ if (14 in self.m_channels):
+ self.act_ch_14.setChecked(True)
+ if (15 in self.m_channels):
+ self.act_ch_15.setChecked(True)
+ if (16 in self.m_channels):
+ self.act_ch_16.setChecked(True)
+
+ def timerEvent(self, event):
+ if (event.timerId() == self.m_midiInTimerId):
+ global jack_midi_in_data
+ if (jack_midi_in_data.empty() == False):
+ while (True):
+ try:
+ mode, note, velo = jack_midi_in_data.get_nowait()
+ except QuequeEmpty:
+ break
+
+ # TODO - filter by channel here
+ #channel = mode - 0xB0+1
+ #if (channel in self.m_channels):
+ if (0x80 <= mode and mode <= 0x8F):
+ self.keyboard.noteOff(note, False)
+ elif (0x90 <= mode and mode < 0x9F):
+ self.keyboard.noteOn(note, False)
+ elif (0xB0 <= mode and mode < 0xBF):
+ self.scene.handleCC(note, velo)
+
+ jack_midi_in_data.task_done()
+
+ self.scene.updateSmooth()
+
+ QMainWindow.timerEvent(self, event)
+
+ def resizeEvent(self, event):
+ self.updateScreen()
+ QMainWindow.resizeEvent(self, event)
+
+ def closeEvent(self, event):
+ self.saveSettings()
+ QMainWindow.closeEvent(self, event)
+
+# -------------------------------------------------------------
+# JACK Stuff
+
+static_event = jacklib.jack_midi_event_t()
+static_mtype = jacklib.c_ubyte*3
+
+def jack_process_callback(nframes, arg):
+ global jack_midi_in_port, jack_midi_out_port, jack_midi_in_data, jack_midi_out_data
+
+ # MIDI In
+ midi_in_buffer = jacklib.port_get_buffer(jack_midi_in_port, nframes)
+
+ if (midi_in_buffer):
+ event_count = jacklib.midi_get_event_count(midi_in_buffer)
+
+ for i in range(event_count):
+ if (jacklib.midi_event_get(jacklib.pointer(static_event), midi_in_buffer, i) == 0):
+ if (static_event.size == 1):
+ jack_midi_in_data.put_nowait((static_event.buffer[0], 0, 0))
+ elif (static_event.size == 2):
+ jack_midi_in_data.put_nowait((static_event.buffer[0], static_event.buffer[1], 0))
+ elif (static_event.size >= 3):
+ jack_midi_in_data.put_nowait((static_event.buffer[0], static_event.buffer[1], static_event.buffer[2]))
+
+ if (jack_midi_in_data.full()):
+ break
+
+ # MIDI Out
+ midi_out_buffer = jacklib.port_get_buffer(jack_midi_out_port, nframes)
+
+ if (midi_out_buffer):
+ jacklib.midi_clear_buffer(midi_out_buffer)
+
+ if (jack_midi_out_data.empty() == False):
+ while (True):
+ try:
+ mode, note, velo = jack_midi_out_data.get_nowait()
+ except QuequeEmpty:
+ break
+
+ data = static_mtype(mode, note, velo)
+ jacklib.midi_event_write(midi_out_buffer, 0, data, 3)
+
+ jack_midi_out_data.task_done()
+
+ return 0
+
+
+#--------------- main ------------------
+if __name__ == '__main__':
+
+ # App initialization
+ app = QApplication(sys.argv)
+ app.setApplicationName("XY-Controller")
+ app.setApplicationVersion(VERSION)
+ app.setOrganizationName("falkTX")
+ #app.setWindowIcon(QIcon(":/48x48/xy-controller.png"))
+
+ # Start jack
+ jack_status = jacklib.jack_status_t(0)
+ jack_client = jacklib.client_open("XY-Controller", jacklib.JackNullOption, jacklib.pointer(jack_status))
+
+ if not jack_client:
+ QMessageBox.critical(None, app.translate("RenderW", "Error"), app.translate("RenderW", "Could not connect to JACK, possible errors:\n%s" % (get_jack_status_error_string(jack_status))))
+ sys.exit(1)
+
+ jack_midi_in_port = jacklib.port_register(jack_client, "midi_in", jacklib.JACK_DEFAULT_MIDI_TYPE, jacklib.JackPortIsInput, 0)
+ jack_midi_out_port = jacklib.port_register(jack_client, "midi_out", jacklib.JACK_DEFAULT_MIDI_TYPE, jacklib.JackPortIsOutput, 0)
+ jacklib.set_process_callback(jack_client, jack_process_callback, None)
+ jacklib.activate(jack_client)
+
+ # Show GUI
+ gui = XYControllerW(None)
+ gui.show()
+
+ # Set-up custom signal handling
+ set_up_signals(gui)
+
+ # App-Loop
+ ret = app.exec_()
+
+ # Close Jack
+ if (jack_client):
+ jacklib.deactivate(jack_client)
+ jacklib.client_close(jack_client)
+
+ # Exit properly
+ sys.exit(ret)