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.

582 lines
21KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # XY Controller for JACK, using jacklib
  4. # Copyright (C) 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 (self.m_mouseLock == False):
  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 (self.m_mouseLock == False):
  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 handleCC(self, param, value):
  78. if (param == self.cc_x):
  79. xp = (float(value)/63)-1.025
  80. yp = self.m_cursor.y()/(self.p_size.y()+self.p_size.height())
  81. self.setPosX(xp, False)
  82. elif (param == self.cc_y):
  83. xp = self.m_cursor.x()/(self.p_size.x()+self.p_size.width())
  84. yp = (float(value)/63)-1.025
  85. self.setPosY(yp, False)
  86. else:
  87. return
  88. self.emit(SIGNAL("cursorMoved(float, float)"), xp, yp)
  89. def handleMousePos(self, pos):
  90. if (not self.p_size.contains(pos)):
  91. if (pos.x() < self.p_size.x()):
  92. pos.setX(self.p_size.x())
  93. elif (pos.x() > self.p_size.x()+self.p_size.width()):
  94. pos.setX(self.p_size.x()+self.p_size.width())
  95. if (pos.y() < self.p_size.y()):
  96. pos.setY(self.p_size.y())
  97. elif (pos.y() > self.p_size.y()+self.p_size.height()):
  98. pos.setY(self.p_size.y()+self.p_size.height())
  99. self.m_smooth_x = pos.x()
  100. self.m_smooth_y = pos.y()
  101. if (self.m_smooth == False):
  102. self.m_cursor.setPos(pos)
  103. self.m_lineH.setY(pos.y())
  104. self.m_lineV.setX(pos.x())
  105. xp = pos.x()/(self.p_size.x()+self.p_size.width())
  106. yp = pos.y()/(self.p_size.y()+self.p_size.height())
  107. self.sendMIDI(xp, yp)
  108. self.emit(SIGNAL("cursorMoved(float, float)"), xp, yp)
  109. def sendMIDI(self, xp=None, yp=None):
  110. global jack_midi_out_data
  111. rate = float(0xff)/4
  112. if (xp != None):
  113. value = int((xp*rate)+rate)
  114. for channel in self.m_channels:
  115. jack_midi_out_data.put_nowait((0xB0+channel-1, self.cc_x, value))
  116. if (yp != None):
  117. value = int((yp*rate)+rate)
  118. for channel in self.m_channels:
  119. jack_midi_out_data.put_nowait((0xB0+channel-1, self.cc_y, value))
  120. def updateSize(self, size):
  121. self.p_size.setRect(-(size.width()/2), -(size.height()/2), size.width(), size.height())
  122. def updateSmooth(self):
  123. if (self.m_smooth):
  124. if (self.m_cursor.x() != self.m_smooth_x or self.m_cursor.y() != self.m_smooth_y):
  125. new_x = (self.m_smooth_x+self.m_cursor.x()*3)/4
  126. new_y = (self.m_smooth_y+self.m_cursor.y()*3)/4
  127. pos = QPointF(new_x, new_y)
  128. self.m_cursor.setPos(pos)
  129. self.m_lineH.setY(pos.y())
  130. self.m_lineV.setX(pos.x())
  131. xp = pos.x()/(self.p_size.x()+self.p_size.width())
  132. yp = pos.y()/(self.p_size.y()+self.p_size.height())
  133. self.sendMIDI(xp, yp)
  134. self.emit(SIGNAL("cursorMoved(float, float)"), xp, yp)
  135. def keyPressEvent(self, event):
  136. event.accept()
  137. def wheelEvent(self, event):
  138. event.accept()
  139. def mousePressEvent(self, event):
  140. self.m_mouseLock = True
  141. self.handleMousePos(event.scenePos())
  142. QGraphicsScene.mousePressEvent(self, event)
  143. def mouseMoveEvent(self, event):
  144. self.handleMousePos(event.scenePos())
  145. QGraphicsScene.mouseMoveEvent(self, event)
  146. def mouseReleaseEvent(self, event):
  147. self.m_mouseLock = False
  148. QGraphicsScene.mouseReleaseEvent(self, event)
  149. # XY Controller Window
  150. class XYControllerW(QMainWindow, ui_xycontroller.Ui_XYControllerW):
  151. def __init__(self, parent):
  152. QMainWindow.__init__(self, parent)
  153. self.setupUi(self)
  154. # -------------------------------------------------------------
  155. # Internal stuff
  156. self.cc_x = 1
  157. self.cc_y = 2
  158. self.m_channels = []
  159. # -------------------------------------------------------------
  160. # Set-up GUI stuff
  161. self.dial_x.setPixmap(2)
  162. self.dial_y.setPixmap(2)
  163. self.dial_x.setLabel("X")
  164. self.dial_y.setLabel("Y")
  165. self.keyboard.setOctaves(6)
  166. self.scene = XYGraphicsScene(self)
  167. self.graphicsView.setScene(self.scene)
  168. self.graphicsView.setRenderHints(QPainter.Antialiasing)
  169. for MIDI_CC in MIDI_CC_LIST:
  170. self.cb_control_x.addItem(MIDI_CC)
  171. self.cb_control_y.addItem(MIDI_CC)
  172. # -------------------------------------------------------------
  173. # Load Settings
  174. self.settings = QSettings("Cadence", "XY-Controller")
  175. self.loadSettings()
  176. # -------------------------------------------------------------
  177. # Connect actions to functions
  178. self.connect(self.keyboard, SIGNAL("noteOn(int)"), SLOT("slot_noteOn(int)"))
  179. self.connect(self.keyboard, SIGNAL("noteOff(int)"), SLOT("slot_noteOff(int)"))
  180. self.connect(self.cb_smooth, SIGNAL("clicked(bool)"), SLOT("slot_setSmooth(bool)"))
  181. self.connect(self.dial_x, SIGNAL("valueChanged(int)"), SLOT("slot_updateSceneX(int)"))
  182. self.connect(self.dial_y, SIGNAL("valueChanged(int)"), SLOT("slot_updateSceneY(int)"))
  183. self.connect(self.cb_control_x, SIGNAL("currentIndexChanged(QString)"), SLOT("slot_checkCC_X(QString)"))
  184. self.connect(self.cb_control_y, SIGNAL("currentIndexChanged(QString)"), SLOT("slot_checkCC_Y(QString)"))
  185. # FIXME
  186. self.connect(self.scene, SIGNAL("cursorMoved(float, float)"), self.slot_sceneCursorMoved)
  187. #self.connect(self.scene, SIGNAL("cursorMoved(float, float)"), SLOT("slot_sceneCursorMoved(float, float)"))
  188. self.connect(self.act_ch_01, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  189. self.connect(self.act_ch_02, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  190. self.connect(self.act_ch_03, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  191. self.connect(self.act_ch_04, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  192. self.connect(self.act_ch_05, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  193. self.connect(self.act_ch_06, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  194. self.connect(self.act_ch_07, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  195. self.connect(self.act_ch_08, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  196. self.connect(self.act_ch_09, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  197. self.connect(self.act_ch_10, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  198. self.connect(self.act_ch_11, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  199. self.connect(self.act_ch_12, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  200. self.connect(self.act_ch_13, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  201. self.connect(self.act_ch_14, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  202. self.connect(self.act_ch_15, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  203. self.connect(self.act_ch_16, SIGNAL("triggered(bool)"), SLOT("slot_checkChannel(bool)"))
  204. self.connect(self.act_ch_all, SIGNAL("triggered()"), SLOT("slot_checkChannel_all()"))
  205. self.connect(self.act_ch_none, SIGNAL("triggered()"), SLOT("slot_checkChannel_none()"))
  206. self.connect(self.act_show_keyboard, SIGNAL("triggered(bool)"), SLOT("slot_showKeyboard(bool)"))
  207. self.connect(self.act_about, SIGNAL("triggered()"), SLOT("slot_about()"))
  208. # -------------------------------------------------------------
  209. # Final stuff
  210. self.m_midiInTimerId = self.startTimer(50)
  211. QTimer.singleShot(0, self, SLOT("slot_updateScreen()"))
  212. def updateScreen(self):
  213. self.scene.updateSize(self.graphicsView.size())
  214. self.graphicsView.centerOn(0, 0)
  215. self.slot_updateSceneX(self.dial_x.value())
  216. self.slot_updateSceneY(self.dial_y.value())
  217. @pyqtSlot(int)
  218. def slot_noteOn(self, note):
  219. global jack_midi_out_data
  220. for channel in self.m_channels:
  221. jack_midi_out_data.put_nowait((0x90+channel-1, note, 100))
  222. @pyqtSlot(int)
  223. def slot_noteOff(self, note):
  224. global jack_midi_out_data
  225. for channel in self.m_channels:
  226. jack_midi_out_data.put_nowait((0x80+channel-1, note, 0))
  227. @pyqtSlot(int)
  228. def slot_updateSceneX(self, x):
  229. self.scene.setPosX(float(x)/100)
  230. @pyqtSlot(int)
  231. def slot_updateSceneY(self, y):
  232. self.scene.setPosY(float(y)/100)
  233. @pyqtSlot(str)
  234. def slot_checkCC_X(self, text):
  235. if (text):
  236. self.cc_x = int(text.split(" ")[0], 16)
  237. self.scene.setControlX(self.cc_x)
  238. @pyqtSlot(str)
  239. def slot_checkCC_Y(self, text):
  240. if (text):
  241. self.cc_y = int(text.split(" ")[0], 16)
  242. self.scene.setControlY(self.cc_y)
  243. @pyqtSlot(bool)
  244. def slot_checkChannel(self, clicked):
  245. channel = int(self.sender().text())
  246. if (clicked and channel not in self.m_channels):
  247. self.m_channels.append(channel)
  248. elif (not clicked and channel in self.m_channels):
  249. self.m_channels.remove(channel)
  250. self.scene.setChannels(self.m_channels)
  251. @pyqtSlot()
  252. def slot_checkChannel_all(self):
  253. self.act_ch_01.setChecked(True)
  254. self.act_ch_02.setChecked(True)
  255. self.act_ch_03.setChecked(True)
  256. self.act_ch_04.setChecked(True)
  257. self.act_ch_05.setChecked(True)
  258. self.act_ch_06.setChecked(True)
  259. self.act_ch_07.setChecked(True)
  260. self.act_ch_08.setChecked(True)
  261. self.act_ch_09.setChecked(True)
  262. self.act_ch_10.setChecked(True)
  263. self.act_ch_11.setChecked(True)
  264. self.act_ch_12.setChecked(True)
  265. self.act_ch_13.setChecked(True)
  266. self.act_ch_14.setChecked(True)
  267. self.act_ch_15.setChecked(True)
  268. self.act_ch_16.setChecked(True)
  269. self.m_channels = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
  270. self.scene.setChannels(self.m_channels)
  271. @pyqtSlot()
  272. def slot_checkChannel_none(self):
  273. self.act_ch_01.setChecked(False)
  274. self.act_ch_02.setChecked(False)
  275. self.act_ch_03.setChecked(False)
  276. self.act_ch_04.setChecked(False)
  277. self.act_ch_05.setChecked(False)
  278. self.act_ch_06.setChecked(False)
  279. self.act_ch_07.setChecked(False)
  280. self.act_ch_08.setChecked(False)
  281. self.act_ch_09.setChecked(False)
  282. self.act_ch_10.setChecked(False)
  283. self.act_ch_11.setChecked(False)
  284. self.act_ch_12.setChecked(False)
  285. self.act_ch_13.setChecked(False)
  286. self.act_ch_14.setChecked(False)
  287. self.act_ch_15.setChecked(False)
  288. self.act_ch_16.setChecked(False)
  289. self.m_channels = []
  290. self.scene.setChannels(self.m_channels)
  291. @pyqtSlot(bool)
  292. def slot_setSmooth(self, yesno):
  293. self.scene.setSmooth(yesno)
  294. @pyqtSlot(float, float)
  295. def slot_sceneCursorMoved(self, xp, yp):
  296. self.dial_x.setValue(xp*100)
  297. self.dial_y.setValue(yp*100)
  298. @pyqtSlot(bool)
  299. def slot_showKeyboard(self, yesno):
  300. self.scrollArea.setVisible(yesno)
  301. QTimer.singleShot(0, self, SLOT("slot_updateScreen()"))
  302. @pyqtSlot()
  303. def slot_about(self):
  304. QMessageBox.about(self, self.tr("About XY Controller"), self.tr("<h3>XY Controller</h3>"
  305. "<br>Version %s"
  306. "<br>XY Controller is a simple XY widget that sends and receives data from Jack MIDI.<br>"
  307. "<br>Copyright (C) 2012 falkTX" % (VERSION)))
  308. @pyqtSlot()
  309. def slot_updateScreen(self):
  310. self.updateScreen()
  311. def saveSettings(self):
  312. self.settings.setValue("Geometry", self.saveGeometry())
  313. self.settings.setValue("ShowKeyboard", self.scrollArea.isVisible())
  314. self.settings.setValue("Smooth", self.cb_smooth.isChecked())
  315. self.settings.setValue("DialX", self.dial_x.value())
  316. self.settings.setValue("DialY", self.dial_y.value())
  317. self.settings.setValue("ControlX", self.cc_x)
  318. self.settings.setValue("ControlY", self.cc_y)
  319. self.settings.setValue("Channels", self.m_channels)
  320. def loadSettings(self):
  321. self.restoreGeometry(self.settings.value("Geometry", ""))
  322. showKeyboard = self.settings.value("ShowKeyboard", False, type=bool)
  323. self.act_show_keyboard.setChecked(showKeyboard)
  324. self.scrollArea.setVisible(showKeyboard)
  325. smooth = self.settings.value("Smooth", False, type=bool)
  326. self.cb_smooth.setChecked(smooth)
  327. self.scene.setSmooth(smooth)
  328. self.dial_x.setValue(self.settings.value("DialX", 50, type=int))
  329. self.dial_y.setValue(self.settings.value("DialY", 50, type=int))
  330. self.cc_x = self.settings.value("ControlX", 1, type=int)
  331. self.cc_y = self.settings.value("ControlY", 2, type=int)
  332. self.m_channels = toList(self.settings.value("Channels", [1]))
  333. for i in range(len(self.m_channels)):
  334. self.m_channels[i] = int(self.m_channels[i])
  335. self.scene.setChannels(self.m_channels)
  336. for i in range(len(MIDI_CC_LIST)):
  337. cc = int(MIDI_CC_LIST[i].split(" ")[0], 16)
  338. if (self.cc_x == cc):
  339. self.cb_control_x.setCurrentIndex(i)
  340. if (self.cc_y == cc):
  341. self.cb_control_y.setCurrentIndex(i)
  342. if (1 in self.m_channels):
  343. self.act_ch_01.setChecked(True)
  344. if (2 in self.m_channels):
  345. self.act_ch_02.setChecked(True)
  346. if (3 in self.m_channels):
  347. self.act_ch_03.setChecked(True)
  348. if (4 in self.m_channels):
  349. self.act_ch_04.setChecked(True)
  350. if (5 in self.m_channels):
  351. self.act_ch_05.setChecked(True)
  352. if (6 in self.m_channels):
  353. self.act_ch_06.setChecked(True)
  354. if (7 in self.m_channels):
  355. self.act_ch_07.setChecked(True)
  356. if (8 in self.m_channels):
  357. self.act_ch_08.setChecked(True)
  358. if (9 in self.m_channels):
  359. self.act_ch_09.setChecked(True)
  360. if (10 in self.m_channels):
  361. self.act_ch_10.setChecked(True)
  362. if (11 in self.m_channels):
  363. self.act_ch_11.setChecked(True)
  364. if (12 in self.m_channels):
  365. self.act_ch_12.setChecked(True)
  366. if (13 in self.m_channels):
  367. self.act_ch_13.setChecked(True)
  368. if (14 in self.m_channels):
  369. self.act_ch_14.setChecked(True)
  370. if (15 in self.m_channels):
  371. self.act_ch_15.setChecked(True)
  372. if (16 in self.m_channels):
  373. self.act_ch_16.setChecked(True)
  374. def timerEvent(self, event):
  375. if (event.timerId() == self.m_midiInTimerId):
  376. global jack_midi_in_data
  377. if (jack_midi_in_data.empty() == False):
  378. while (True):
  379. try:
  380. mode, note, velo = jack_midi_in_data.get_nowait()
  381. except QuequeEmpty:
  382. break
  383. # TODO - filter by channel here
  384. #channel = mode - 0xB0+1
  385. #if (channel in self.m_channels):
  386. if (0x80 <= mode and mode <= 0x8F):
  387. self.keyboard.noteOff(note, False)
  388. elif (0x90 <= mode and mode < 0x9F):
  389. self.keyboard.noteOn(note, False)
  390. elif (0xB0 <= mode and mode < 0xBF):
  391. self.scene.handleCC(note, velo)
  392. jack_midi_in_data.task_done()
  393. self.scene.updateSmooth()
  394. QMainWindow.timerEvent(self, event)
  395. def resizeEvent(self, event):
  396. self.updateScreen()
  397. QMainWindow.resizeEvent(self, event)
  398. def closeEvent(self, event):
  399. self.saveSettings()
  400. QMainWindow.closeEvent(self, event)
  401. # -------------------------------------------------------------
  402. # JACK Stuff
  403. static_event = jacklib.jack_midi_event_t()
  404. static_mtype = jacklib.c_ubyte*3
  405. def jack_process_callback(nframes, arg):
  406. global jack_midi_in_port, jack_midi_out_port, jack_midi_in_data, jack_midi_out_data
  407. # MIDI In
  408. midi_in_buffer = jacklib.port_get_buffer(jack_midi_in_port, nframes)
  409. if (midi_in_buffer):
  410. event_count = jacklib.midi_get_event_count(midi_in_buffer)
  411. for i in range(event_count):
  412. if (jacklib.midi_event_get(jacklib.pointer(static_event), midi_in_buffer, i) == 0):
  413. if (static_event.size == 1):
  414. jack_midi_in_data.put_nowait((static_event.buffer[0], 0, 0))
  415. elif (static_event.size == 2):
  416. jack_midi_in_data.put_nowait((static_event.buffer[0], static_event.buffer[1], 0))
  417. elif (static_event.size >= 3):
  418. jack_midi_in_data.put_nowait((static_event.buffer[0], static_event.buffer[1], static_event.buffer[2]))
  419. if (jack_midi_in_data.full()):
  420. break
  421. # MIDI Out
  422. midi_out_buffer = jacklib.port_get_buffer(jack_midi_out_port, nframes)
  423. if (midi_out_buffer):
  424. jacklib.midi_clear_buffer(midi_out_buffer)
  425. if (jack_midi_out_data.empty() == False):
  426. while (True):
  427. try:
  428. mode, note, velo = jack_midi_out_data.get_nowait()
  429. except QuequeEmpty:
  430. break
  431. data = static_mtype(mode, note, velo)
  432. jacklib.midi_event_write(midi_out_buffer, 0, data, 3)
  433. jack_midi_out_data.task_done()
  434. return 0
  435. #--------------- main ------------------
  436. if __name__ == '__main__':
  437. # App initialization
  438. app = QApplication(sys.argv)
  439. app.setApplicationName("XY-Controller")
  440. app.setApplicationVersion(VERSION)
  441. app.setOrganizationName("falkTX")
  442. #app.setWindowIcon(QIcon(":/48x48/xy-controller.png"))
  443. # Start jack
  444. jack_status = jacklib.jack_status_t(0)
  445. jack_client = jacklib.client_open("XY-Controller", jacklib.JackNullOption, jacklib.pointer(jack_status))
  446. if not jack_client:
  447. 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))))
  448. sys.exit(1)
  449. jack_midi_in_port = jacklib.port_register(jack_client, "midi_in", jacklib.JACK_DEFAULT_MIDI_TYPE, jacklib.JackPortIsInput, 0)
  450. jack_midi_out_port = jacklib.port_register(jack_client, "midi_out", jacklib.JACK_DEFAULT_MIDI_TYPE, jacklib.JackPortIsOutput, 0)
  451. jacklib.set_process_callback(jack_client, jack_process_callback, None)
  452. jacklib.activate(jack_client)
  453. # Show GUI
  454. gui = XYControllerW(None)
  455. gui.show()
  456. # Set-up custom signal handling
  457. set_up_signals(gui)
  458. # App-Loop
  459. ret = app.exec_()
  460. # Close Jack
  461. if (jack_client):
  462. jacklib.deactivate(jack_client)
  463. jacklib.client_close(jack_client)
  464. # Exit properly
  465. sys.exit(ret)