Collection of tools useful for audio production
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

637 lines
23KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # XY Controller for JACK, using jacklib
  4. # Copyright (C) 2011-2012 Filipe Coelho <falktx@gmail.com>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the COPYING file
  17. # Imports (Global)
  18. from PyQt4.QtCore import pyqtSlot, Qt, QPointF, QRectF, QSettings, QTimer, QVariant
  19. from PyQt4.QtGui import QApplication, QColor, QIcon, QPainter, QPen, QGraphicsItem, QGraphicsScene, QMainWindow
  20. from queue import Queue, Empty as QuequeEmpty
  21. # Imports (Custom)
  22. import ui_xycontroller
  23. from shared import *
  24. from jacklib_helpers import *
  25. # Globals
  26. global jack_client, jack_midi_in_port, jack_midi_out_port, jack_midi_in_data, jack_midi_out_data
  27. jack_client = None
  28. jack_midi_in_port = None
  29. jack_midi_out_port = None
  30. jack_midi_in_data = Queue(512)
  31. jack_midi_out_data = Queue(512)
  32. # XY Controller Scene
  33. class XYGraphicsScene(QGraphicsScene):
  34. def __init__(self, parent):
  35. QGraphicsScene.__init__(self, parent)
  36. self.cc_x = 1
  37. self.cc_y = 2
  38. self.m_channels = []
  39. self.m_mouseLock = False
  40. self.m_smooth = False
  41. self.m_smooth_x = 0
  42. self.m_smooth_y = 0
  43. self.setBackgroundBrush(Qt.black)
  44. cursorPen = QPen(QColor(255, 255, 255), 2)
  45. cursorBrush = QColor(255, 255, 255, 50)
  46. self.m_cursor = self.addEllipse(QRectF(-10, -10, 20, 20), cursorPen, cursorBrush)
  47. linePen = QPen(QColor(200, 200, 200, 100), 1, Qt.DashLine)
  48. self.m_lineH = self.addLine(-9999, 0, 9999, 0, linePen)
  49. self.m_lineV = self.addLine(0, -9999, 0, 9999, linePen)
  50. self.p_size = QRectF(-100, -100, 100, 100)
  51. def setControlX(self, x):
  52. self.cc_x = x
  53. def setControlY(self, y):
  54. self.cc_y = y
  55. def setChannels(self, channels):
  56. self.m_channels = channels
  57. def setPosX(self, x, forward=True):
  58. if not self.m_mouseLock:
  59. pos_x = x * (self.p_size.x() + self.p_size.width())
  60. self.m_cursor.setPos(pos_x, self.m_cursor.y())
  61. self.m_lineV.setX(pos_x)
  62. if forward:
  63. self.sendMIDI(pos_x / (self.p_size.x() + self.p_size.width()), None)
  64. else:
  65. self.m_smooth_x = pos_x
  66. def setPosY(self, y, forward=True):
  67. if not self.m_mouseLock:
  68. pos_y = y * (self.p_size.y() + self.p_size.height())
  69. self.m_cursor.setPos(self.m_cursor.x(), pos_y)
  70. self.m_lineH.setY(pos_y)
  71. if forward:
  72. self.sendMIDI(None, pos_y / (self.p_size.y() + self.p_size.height()))
  73. else:
  74. self.m_smooth_y = pos_y
  75. def setSmooth(self, smooth):
  76. self.m_smooth = smooth
  77. def setSmoothValues(self, x, y):
  78. self.m_smooth_x = x * (self.p_size.x() + self.p_size.width())
  79. self.m_smooth_y = y * (self.p_size.y() + self.p_size.height())
  80. def handleCC(self, param, value):
  81. sendUpdate = False
  82. xp = yp = 0.0
  83. if param == self.cc_x:
  84. sendUpdate = True
  85. xp = (float(value) / 63) - 1.0
  86. yp = self.m_cursor.y() / (self.p_size.y() + self.p_size.height())
  87. if xp < -1.0:
  88. xp = -1.0
  89. elif xp > 1.0:
  90. xp = 1.0
  91. self.setPosX(xp, False)
  92. if param == self.cc_y:
  93. sendUpdate = True
  94. xp = self.m_cursor.x() / (self.p_size.x() + self.p_size.width())
  95. yp = (float(value) / 63) - 1.0
  96. if yp < -1.0:
  97. yp = -1.0
  98. elif yp > 1.0:
  99. yp = 1.0
  100. self.setPosY(yp, False)
  101. if sendUpdate:
  102. self.emit(SIGNAL("cursorMoved(double, double)"), xp, yp)
  103. def handleMousePos(self, pos):
  104. if not self.p_size.contains(pos):
  105. if pos.x() < self.p_size.x():
  106. pos.setX(self.p_size.x())
  107. elif pos.x() > self.p_size.x() + self.p_size.width():
  108. pos.setX(self.p_size.x() + self.p_size.width())
  109. if pos.y() < self.p_size.y():
  110. pos.setY(self.p_size.y())
  111. elif pos.y() > self.p_size.y() + self.p_size.height():
  112. pos.setY(self.p_size.y() + self.p_size.height())
  113. self.m_smooth_x = pos.x()
  114. self.m_smooth_y = pos.y()
  115. if not self.m_smooth:
  116. self.m_cursor.setPos(pos)
  117. self.m_lineH.setY(pos.y())
  118. self.m_lineV.setX(pos.x())
  119. xp = pos.x() / (self.p_size.x() + self.p_size.width())
  120. yp = pos.y() / (self.p_size.y() + self.p_size.height())
  121. self.sendMIDI(xp, yp)
  122. self.emit(SIGNAL("cursorMoved(double, double)"), xp, yp)
  123. def sendMIDI(self, xp=None, yp=None):
  124. global jack_midi_out_data
  125. rate = float(0xff) / 4
  126. if xp != None:
  127. value = int((xp * rate) + rate)
  128. for channel in self.m_channels:
  129. jack_midi_out_data.put_nowait((0xB0 + channel - 1, self.cc_x, value))
  130. if yp != None:
  131. value = int((yp * rate) + rate)
  132. for channel in self.m_channels:
  133. jack_midi_out_data.put_nowait((0xB0 + channel - 1, self.cc_y, value))
  134. def updateSize(self, size):
  135. self.p_size.setRect(-(size.width() / 2), -(size.height() / 2), size.width(), size.height())
  136. def updateSmooth(self):
  137. if self.m_smooth:
  138. if self.m_cursor.x() != self.m_smooth_x or self.m_cursor.y() != self.m_smooth_y:
  139. if abs(self.m_cursor.x() - self.m_smooth_x) <= 0.001:
  140. self.m_smooth_x = self.m_cursor.x()
  141. return
  142. elif abs(self.m_cursor.y() - self.m_smooth_y) <= 0.001:
  143. self.m_smooth_y = self.m_cursor.y()
  144. return
  145. new_x = (self.m_smooth_x + self.m_cursor.x() * 3) / 4
  146. new_y = (self.m_smooth_y + self.m_cursor.y() * 3) / 4
  147. pos = QPointF(new_x, new_y)
  148. self.m_cursor.setPos(pos)
  149. self.m_lineH.setY(pos.y())
  150. self.m_lineV.setX(pos.x())
  151. xp = pos.x() / (self.p_size.x() + self.p_size.width())
  152. yp = pos.y() / (self.p_size.y() + self.p_size.height())
  153. self.sendMIDI(xp, yp)
  154. self.emit(SIGNAL("cursorMoved(double, double)"), xp, yp)
  155. def keyPressEvent(self, event):
  156. event.accept()
  157. def wheelEvent(self, event):
  158. event.accept()
  159. def mousePressEvent(self, event):
  160. self.m_mouseLock = True
  161. self.handleMousePos(event.scenePos())
  162. QGraphicsScene.mousePressEvent(self, event)
  163. def mouseMoveEvent(self, event):
  164. self.handleMousePos(event.scenePos())
  165. QGraphicsScene.mouseMoveEvent(self, event)
  166. def mouseReleaseEvent(self, event):
  167. self.m_mouseLock = False
  168. QGraphicsScene.mouseReleaseEvent(self, event)
  169. # XY Controller Window
  170. class XYControllerW(QMainWindow, ui_xycontroller.Ui_XYControllerW):
  171. def __init__(self, parent=None):
  172. QMainWindow.__init__(self, parent)
  173. self.setupUi(self)
  174. # -------------------------------------------------------------
  175. # Internal stuff
  176. self.cc_x = 1
  177. self.cc_y = 2
  178. self.m_channels = []
  179. # -------------------------------------------------------------
  180. # Set-up GUI stuff
  181. self.dial_x.setPixmap(2)
  182. self.dial_y.setPixmap(2)
  183. self.dial_x.setLabel("X")
  184. self.dial_y.setLabel("Y")
  185. self.keyboard.setOctaves(6)
  186. self.scene = XYGraphicsScene(self)
  187. self.graphicsView.setScene(self.scene)
  188. self.graphicsView.setRenderHints(QPainter.Antialiasing)
  189. for MIDI_CC in MIDI_CC_LIST:
  190. self.cb_control_x.addItem(MIDI_CC)
  191. self.cb_control_y.addItem(MIDI_CC)
  192. # -------------------------------------------------------------
  193. # Load Settings
  194. self.settings = QSettings("Cadence", "XY-Controller")
  195. self.loadSettings()
  196. # -------------------------------------------------------------
  197. # Connect actions to functions
  198. self.connect(self.keyboard, SIGNAL("noteOn(int)"), SLOT("slot_noteOn(int)"))
  199. self.connect(self.keyboard, SIGNAL("noteOff(int)"), SLOT("slot_noteOff(int)"))
  200. self.connect(self.cb_smooth, SIGNAL("clicked(bool)"), SLOT("slot_setSmooth(bool)"))
  201. self.connect(self.dial_x, SIGNAL("valueChanged(int)"), SLOT("slot_updateSceneX(int)"))
  202. self.connect(self.dial_y, SIGNAL("valueChanged(int)"), SLOT("slot_updateSceneY(int)"))
  203. self.connect(self.cb_control_x, SIGNAL("currentIndexChanged(QString)"), SLOT("slot_checkCC_X(QString)"))
  204. self.connect(self.cb_control_y, SIGNAL("currentIndexChanged(QString)"), SLOT("slot_checkCC_Y(QString)"))
  205. self.connect(self.scene, SIGNAL("cursorMoved(double, double)"), SLOT("slot_sceneCursorMoved(double, double)"))
  206. self.connect(self.act_ch_01, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  207. self.connect(self.act_ch_02, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  208. self.connect(self.act_ch_03, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  209. self.connect(self.act_ch_04, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  210. self.connect(self.act_ch_05, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  211. self.connect(self.act_ch_06, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  212. self.connect(self.act_ch_07, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  213. self.connect(self.act_ch_08, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  214. self.connect(self.act_ch_09, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  215. self.connect(self.act_ch_10, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  216. self.connect(self.act_ch_11, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  217. self.connect(self.act_ch_12, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  218. self.connect(self.act_ch_13, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  219. self.connect(self.act_ch_14, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  220. self.connect(self.act_ch_15, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  221. self.connect(self.act_ch_16, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  222. self.connect(self.act_ch_all, SIGNAL("triggered()"), SLOT("slot_checkChannel_all()"))
  223. self.connect(self.act_ch_none, SIGNAL("triggered()"), SLOT("slot_checkChannel_none()"))
  224. self.connect(self.act_show_keyboard, SIGNAL("triggered(bool)"), SLOT("slot_showKeyboard(bool)"))
  225. self.connect(self.act_about, SIGNAL("triggered()"), SLOT("slot_about()"))
  226. # -------------------------------------------------------------
  227. # Final stuff
  228. self.m_midiInTimerId = self.startTimer(50)
  229. QTimer.singleShot(0, self, SLOT("slot_updateScreen()"))
  230. def updateScreen(self):
  231. self.scene.updateSize(self.graphicsView.size())
  232. self.graphicsView.centerOn(0, 0)
  233. dial_x = self.dial_x.value()
  234. dial_y = self.dial_y.value()
  235. self.slot_updateSceneX(dial_x)
  236. self.slot_updateSceneY(dial_y)
  237. self.scene.setSmoothValues(float(dial_x) / 100, float(dial_y) / 100)
  238. @pyqtSlot(int)
  239. def slot_noteOn(self, note):
  240. global jack_midi_out_data
  241. for channel in self.m_channels:
  242. jack_midi_out_data.put_nowait((0x90 + channel - 1, note, 100))
  243. @pyqtSlot(int)
  244. def slot_noteOff(self, note):
  245. global jack_midi_out_data
  246. for channel in self.m_channels:
  247. jack_midi_out_data.put_nowait((0x80 + channel - 1, note, 0))
  248. @pyqtSlot(int)
  249. def slot_updateSceneX(self, x):
  250. self.scene.setPosX(float(x) / 100, not self.dial_x.isSliderDown())
  251. @pyqtSlot(int)
  252. def slot_updateSceneY(self, y):
  253. self.scene.setPosY(float(y) / 100, not self.dial_y.isSliderDown())
  254. @pyqtSlot(str)
  255. def slot_checkCC_X(self, text):
  256. if text:
  257. self.cc_x = int(text.split(" ")[0], 16)
  258. self.scene.setControlX(self.cc_x)
  259. @pyqtSlot(str)
  260. def slot_checkCC_Y(self, text):
  261. if text:
  262. self.cc_y = int(text.split(" ")[0], 16)
  263. self.scene.setControlY(self.cc_y)
  264. @pyqtSlot(bool)
  265. def slot_checkChannel(self, clicked):
  266. channel = int(self.sender().text())
  267. if clicked and channel not in self.m_channels:
  268. self.m_channels.append(channel)
  269. elif not clicked and channel in self.m_channels:
  270. self.m_channels.remove(channel)
  271. self.scene.setChannels(self.m_channels)
  272. @pyqtSlot()
  273. def slot_checkChannel_all(self):
  274. self.act_ch_01.setChecked(True)
  275. self.act_ch_02.setChecked(True)
  276. self.act_ch_03.setChecked(True)
  277. self.act_ch_04.setChecked(True)
  278. self.act_ch_05.setChecked(True)
  279. self.act_ch_06.setChecked(True)
  280. self.act_ch_07.setChecked(True)
  281. self.act_ch_08.setChecked(True)
  282. self.act_ch_09.setChecked(True)
  283. self.act_ch_10.setChecked(True)
  284. self.act_ch_11.setChecked(True)
  285. self.act_ch_12.setChecked(True)
  286. self.act_ch_13.setChecked(True)
  287. self.act_ch_14.setChecked(True)
  288. self.act_ch_15.setChecked(True)
  289. self.act_ch_16.setChecked(True)
  290. self.m_channels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
  291. self.scene.setChannels(self.m_channels)
  292. @pyqtSlot()
  293. def slot_checkChannel_none(self):
  294. self.act_ch_01.setChecked(False)
  295. self.act_ch_02.setChecked(False)
  296. self.act_ch_03.setChecked(False)
  297. self.act_ch_04.setChecked(False)
  298. self.act_ch_05.setChecked(False)
  299. self.act_ch_06.setChecked(False)
  300. self.act_ch_07.setChecked(False)
  301. self.act_ch_08.setChecked(False)
  302. self.act_ch_09.setChecked(False)
  303. self.act_ch_10.setChecked(False)
  304. self.act_ch_11.setChecked(False)
  305. self.act_ch_12.setChecked(False)
  306. self.act_ch_13.setChecked(False)
  307. self.act_ch_14.setChecked(False)
  308. self.act_ch_15.setChecked(False)
  309. self.act_ch_16.setChecked(False)
  310. self.m_channels = []
  311. self.scene.setChannels(self.m_channels)
  312. @pyqtSlot(bool)
  313. def slot_setSmooth(self, yesno):
  314. self.scene.setSmooth(yesno)
  315. @pyqtSlot(float, float)
  316. def slot_sceneCursorMoved(self, xp, yp):
  317. self.dial_x.blockSignals(True)
  318. self.dial_y.blockSignals(True)
  319. self.dial_x.setValue(xp * 100)
  320. self.dial_y.setValue(yp * 100)
  321. self.dial_x.blockSignals(False)
  322. self.dial_y.blockSignals(False)
  323. @pyqtSlot(bool)
  324. def slot_showKeyboard(self, yesno):
  325. self.scrollArea.setVisible(yesno)
  326. QTimer.singleShot(0, self, SLOT("slot_updateScreen()"))
  327. @pyqtSlot()
  328. def slot_about(self):
  329. QMessageBox.about(self, self.tr("About XY Controller"), self.tr("<h3>XY Controller</h3>"
  330. "<br>Version %s"
  331. "<br>XY Controller is a simple XY widget that sends and receives data from Jack MIDI.<br>"
  332. "<br>Copyright (C) 2012 falkTX" % VERSION))
  333. @pyqtSlot()
  334. def slot_updateScreen(self):
  335. self.updateScreen()
  336. def saveSettings(self):
  337. self.settings.setValue("Geometry", self.saveGeometry())
  338. self.settings.setValue("ShowKeyboard", self.scrollArea.isVisible())
  339. self.settings.setValue("Smooth", self.cb_smooth.isChecked())
  340. self.settings.setValue("DialX", self.dial_x.value())
  341. self.settings.setValue("DialY", self.dial_y.value())
  342. self.settings.setValue("ControlX", self.cc_x)
  343. self.settings.setValue("ControlY", self.cc_y)
  344. self.settings.setValue("Channels", self.m_channels)
  345. def loadSettings(self):
  346. self.restoreGeometry(self.settings.value("Geometry", ""))
  347. showKeyboard = self.settings.value("ShowKeyboard", False, type=bool)
  348. self.act_show_keyboard.setChecked(showKeyboard)
  349. self.scrollArea.setVisible(showKeyboard)
  350. smooth = self.settings.value("Smooth", False, type=bool)
  351. self.cb_smooth.setChecked(smooth)
  352. self.scene.setSmooth(smooth)
  353. self.dial_x.setValue(self.settings.value("DialX", 50, type=int))
  354. self.dial_y.setValue(self.settings.value("DialY", 50, type=int))
  355. self.cc_x = self.settings.value("ControlX", 1, type=int)
  356. self.cc_y = self.settings.value("ControlY", 2, type=int)
  357. self.scene.setControlX(self.cc_x)
  358. self.scene.setControlY(self.cc_y)
  359. self.m_channels = toList(self.settings.value("Channels", [1]))
  360. for i in range(len(self.m_channels)):
  361. self.m_channels[i] = int(self.m_channels[i])
  362. self.scene.setChannels(self.m_channels)
  363. for i in range(len(MIDI_CC_LIST)):
  364. cc = int(MIDI_CC_LIST[i].split(" ")[0], 16)
  365. if self.cc_x == cc:
  366. self.cb_control_x.setCurrentIndex(i)
  367. if self.cc_y == cc:
  368. self.cb_control_y.setCurrentIndex(i)
  369. if 1 in self.m_channels:
  370. self.act_ch_01.setChecked(True)
  371. if 2 in self.m_channels:
  372. self.act_ch_02.setChecked(True)
  373. if 3 in self.m_channels:
  374. self.act_ch_03.setChecked(True)
  375. if 4 in self.m_channels:
  376. self.act_ch_04.setChecked(True)
  377. if 5 in self.m_channels:
  378. self.act_ch_05.setChecked(True)
  379. if 6 in self.m_channels:
  380. self.act_ch_06.setChecked(True)
  381. if 7 in self.m_channels:
  382. self.act_ch_07.setChecked(True)
  383. if 8 in self.m_channels:
  384. self.act_ch_08.setChecked(True)
  385. if 9 in self.m_channels:
  386. self.act_ch_09.setChecked(True)
  387. if 10 in self.m_channels:
  388. self.act_ch_10.setChecked(True)
  389. if 11 in self.m_channels:
  390. self.act_ch_11.setChecked(True)
  391. if 12 in self.m_channels:
  392. self.act_ch_12.setChecked(True)
  393. if 13 in self.m_channels:
  394. self.act_ch_13.setChecked(True)
  395. if 14 in self.m_channels:
  396. self.act_ch_14.setChecked(True)
  397. if 15 in self.m_channels:
  398. self.act_ch_15.setChecked(True)
  399. if 16 in self.m_channels:
  400. self.act_ch_16.setChecked(True)
  401. def timerEvent(self, event):
  402. if event.timerId() == self.m_midiInTimerId:
  403. global jack_midi_in_data
  404. if not jack_midi_in_data.empty():
  405. while True:
  406. try:
  407. data1, data2, data3 = jack_midi_in_data.get_nowait()
  408. except QuequeEmpty:
  409. break
  410. channel = (data1 & 0x0F) + 1
  411. mode = data1 & 0xF0
  412. if channel in self.m_channels:
  413. if mode == 0x80:
  414. self.keyboard.sendNoteOff(data2, False)
  415. elif mode == 0x90:
  416. self.keyboard.sendNoteOn(data2, False)
  417. elif mode == 0xB0:
  418. self.scene.handleCC(data2, data3)
  419. jack_midi_in_data.task_done()
  420. self.scene.updateSmooth()
  421. QMainWindow.timerEvent(self, event)
  422. def resizeEvent(self, event):
  423. self.updateScreen()
  424. QMainWindow.resizeEvent(self, event)
  425. def closeEvent(self, event):
  426. self.saveSettings()
  427. QMainWindow.closeEvent(self, event)
  428. # -------------------------------------------------------------
  429. # JACK Stuff
  430. static_event = jacklib.jack_midi_event_t()
  431. static_mtype = jacklib.c_ubyte * 3
  432. def jack_process_callback(nframes, arg):
  433. global jack_midi_in_port, jack_midi_out_port, jack_midi_in_data, jack_midi_out_data
  434. # MIDI In
  435. midi_in_buffer = jacklib.port_get_buffer(jack_midi_in_port, nframes)
  436. if midi_in_buffer:
  437. event_count = jacklib.midi_get_event_count(midi_in_buffer)
  438. for i in range(event_count):
  439. if jacklib.midi_event_get(jacklib.pointer(static_event), midi_in_buffer, i) == 0:
  440. if static_event.size == 1:
  441. jack_midi_in_data.put_nowait((static_event.buffer[0], 0, 0))
  442. elif static_event.size == 2:
  443. jack_midi_in_data.put_nowait((static_event.buffer[0], static_event.buffer[1], 0))
  444. elif static_event.size >= 3:
  445. jack_midi_in_data.put_nowait((static_event.buffer[0], static_event.buffer[1], static_event.buffer[2]))
  446. if jack_midi_in_data.full():
  447. break
  448. # MIDI Out
  449. midi_out_buffer = jacklib.port_get_buffer(jack_midi_out_port, nframes)
  450. if midi_out_buffer:
  451. jacklib.midi_clear_buffer(midi_out_buffer)
  452. if not jack_midi_out_data.empty():
  453. while True:
  454. try:
  455. mode, note, velo = jack_midi_out_data.get_nowait()
  456. except QuequeEmpty:
  457. break
  458. data = static_mtype(mode, note, velo)
  459. jacklib.midi_event_write(midi_out_buffer, 0, data, 3)
  460. jack_midi_out_data.task_done()
  461. return 0
  462. def jack_session_callback(event, arg):
  463. if WINDOWS:
  464. filepath = os.path.join(sys.argv[0])
  465. else:
  466. if sys.argv[0].startswith("/"):
  467. filepath = "jack_xycontroller"
  468. else:
  469. filepath = os.path.join(sys.path[0], "xycontroller.py")
  470. event.command_line = str(filepath).encode("utf-8")
  471. jacklib.session_reply(jack_client, event)
  472. if event.type == jacklib.JackSessionSaveAndQuit:
  473. app.quit()
  474. #jacklib.session_event_free(event)
  475. #--------------- main ------------------
  476. if __name__ == '__main__':
  477. # App initialization
  478. app = QApplication(sys.argv)
  479. app.setApplicationName("XY-Controller")
  480. app.setApplicationVersion(VERSION)
  481. app.setOrganizationName("falkTX")
  482. #app.setWindowIcon(QIcon(":/48x48/xy-controller.png"))
  483. # Start jack
  484. jack_status = jacklib.jack_status_t(0)
  485. jack_client = jacklib.client_open("XY-Controller", jacklib.JackSessionID, jacklib.pointer(jack_status))
  486. if not jack_client:
  487. QMessageBox.critical(None, app.translate("XYControllerW", "Error"), app.translate("XYControllerW",
  488. "Could not connect to JACK, possible errors:\n%s" % (get_jack_status_error_string(jack_status))))
  489. sys.exit(1)
  490. jack_midi_in_port = jacklib.port_register(jack_client, "midi_in", jacklib.JACK_DEFAULT_MIDI_TYPE, jacklib.JackPortIsInput, 0)
  491. jack_midi_out_port = jacklib.port_register(jack_client, "midi_out", jacklib.JACK_DEFAULT_MIDI_TYPE, jacklib.JackPortIsOutput, 0)
  492. jacklib.set_session_callback(jack_client, jack_session_callback, None)
  493. jacklib.set_process_callback(jack_client, jack_process_callback, None)
  494. jacklib.activate(jack_client)
  495. # Show GUI
  496. gui = XYControllerW()
  497. gui.show()
  498. # Set-up custom signal handling
  499. set_up_signals(gui)
  500. # App-Loop
  501. ret = app.exec_()
  502. # Close Jack
  503. if jack_client:
  504. jacklib.deactivate(jack_client)
  505. jacklib.client_close(jack_client)
  506. # Exit properly
  507. sys.exit(ret)