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.

997 lines
29KB

  1. /*
  2. * Simple JACK Audio Meter
  3. * Copyright (C) 2011-2012 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the COPYING file
  16. */
  17. #include <QtCore/Qt>
  18. #ifndef Q_COMPILER_LAMBDA
  19. # define nullptr (0)
  20. #endif
  21. #define VERSION "0.8.1"
  22. #include "../jack_utils.hpp"
  23. #include "../midi_queue.hpp"
  24. #include "ui_xycontroller.h"
  25. #include <QtCore/QSettings>
  26. #include <QtCore/QTimer>
  27. #include <QtGui/QKeyEvent>
  28. #include <QtWidgets/QApplication>
  29. #include <QtWidgets/QGraphicsItem>
  30. #include <QtWidgets/QGraphicsScene>
  31. #include <QtWidgets/QGraphicsSceneEvent>
  32. #include <QtWidgets/QMainWindow>
  33. #include <QtWidgets/QMessageBox>
  34. // -------------------------------
  35. float abs_f(const float value)
  36. {
  37. return (value < 1.0f) ? -value : value;
  38. }
  39. jack_client_t* jClient = nullptr;
  40. jack_port_t* jMidiInPort = nullptr;
  41. jack_port_t* jMidiOutPort = nullptr;
  42. static Queue qMidiInData;
  43. static Queue qMidiOutData;
  44. QVector<QString> MIDI_CC_LIST;
  45. void MIDI_CC_LIST__init()
  46. {
  47. //MIDI_CC_LIST << "0x00 Bank Select";
  48. MIDI_CC_LIST << "0x01 Modulation";
  49. MIDI_CC_LIST << "0x02 Breath";
  50. MIDI_CC_LIST << "0x03 (Undefined)";
  51. MIDI_CC_LIST << "0x04 Foot";
  52. MIDI_CC_LIST << "0x05 Portamento";
  53. //MIDI_CC_LIST << "0x06 (Data Entry MSB)";
  54. MIDI_CC_LIST << "0x07 Volume";
  55. MIDI_CC_LIST << "0x08 Balance";
  56. MIDI_CC_LIST << "0x09 (Undefined)";
  57. MIDI_CC_LIST << "0x0A Pan";
  58. MIDI_CC_LIST << "0x0B Expression";
  59. MIDI_CC_LIST << "0x0C FX Control 1";
  60. MIDI_CC_LIST << "0x0D FX Control 2";
  61. MIDI_CC_LIST << "0x0E (Undefined)";
  62. MIDI_CC_LIST << "0x0F (Undefined)";
  63. MIDI_CC_LIST << "0x10 General Purpose 1";
  64. MIDI_CC_LIST << "0x11 General Purpose 2";
  65. MIDI_CC_LIST << "0x12 General Purpose 3";
  66. MIDI_CC_LIST << "0x13 General Purpose 4";
  67. MIDI_CC_LIST << "0x14 (Undefined)";
  68. MIDI_CC_LIST << "0x15 (Undefined)";
  69. MIDI_CC_LIST << "0x16 (Undefined)";
  70. MIDI_CC_LIST << "0x17 (Undefined)";
  71. MIDI_CC_LIST << "0x18 (Undefined)";
  72. MIDI_CC_LIST << "0x19 (Undefined)";
  73. MIDI_CC_LIST << "0x1A (Undefined)";
  74. MIDI_CC_LIST << "0x1B (Undefined)";
  75. MIDI_CC_LIST << "0x1C (Undefined)";
  76. MIDI_CC_LIST << "0x1D (Undefined)";
  77. MIDI_CC_LIST << "0x1E (Undefined)";
  78. MIDI_CC_LIST << "0x1F (Undefined)";
  79. //MIDI_CC_LIST << "0x20 *Bank Select";
  80. //MIDI_CC_LIST << "0x21 *Modulation";
  81. //MIDI_CC_LIST << "0x22 *Breath";
  82. //MIDI_CC_LIST << "0x23 *(Undefined)";
  83. //MIDI_CC_LIST << "0x24 *Foot";
  84. //MIDI_CC_LIST << "0x25 *Portamento";
  85. //MIDI_CC_LIST << "0x26 *(Data Entry MSB)";
  86. //MIDI_CC_LIST << "0x27 *Volume";
  87. //MIDI_CC_LIST << "0x28 *Balance";
  88. //MIDI_CC_LIST << "0x29 *(Undefined)";
  89. //MIDI_CC_LIST << "0x2A *Pan";
  90. //MIDI_CC_LIST << "0x2B *Expression";
  91. //MIDI_CC_LIST << "0x2C *FX *Control 1";
  92. //MIDI_CC_LIST << "0x2D *FX *Control 2";
  93. //MIDI_CC_LIST << "0x2E *(Undefined)";
  94. //MIDI_CC_LIST << "0x2F *(Undefined)";
  95. //MIDI_CC_LIST << "0x30 *General Purpose 1";
  96. //MIDI_CC_LIST << "0x31 *General Purpose 2";
  97. //MIDI_CC_LIST << "0x32 *General Purpose 3";
  98. //MIDI_CC_LIST << "0x33 *General Purpose 4";
  99. //MIDI_CC_LIST << "0x34 *(Undefined)";
  100. //MIDI_CC_LIST << "0x35 *(Undefined)";
  101. //MIDI_CC_LIST << "0x36 *(Undefined)";
  102. //MIDI_CC_LIST << "0x37 *(Undefined)";
  103. //MIDI_CC_LIST << "0x38 *(Undefined)";
  104. //MIDI_CC_LIST << "0x39 *(Undefined)";
  105. //MIDI_CC_LIST << "0x3A *(Undefined)";
  106. //MIDI_CC_LIST << "0x3B *(Undefined)";
  107. //MIDI_CC_LIST << "0x3C *(Undefined)";
  108. //MIDI_CC_LIST << "0x3D *(Undefined)";
  109. //MIDI_CC_LIST << "0x3E *(Undefined)";
  110. //MIDI_CC_LIST << "0x3F *(Undefined)";
  111. //MIDI_CC_LIST << "0x40 Damper On/Off"; // <63 off, >64 on
  112. //MIDI_CC_LIST << "0x41 Portamento On/Off"; // <63 off, >64 on
  113. //MIDI_CC_LIST << "0x42 Sostenuto On/Off"; // <63 off, >64 on
  114. //MIDI_CC_LIST << "0x43 Soft Pedal On/Off"; // <63 off, >64 on
  115. //MIDI_CC_LIST << "0x44 Legato Footswitch"; // <63 Normal, >64 Legato
  116. //MIDI_CC_LIST << "0x45 Hold 2"; // <63 off, >64 on
  117. MIDI_CC_LIST << "0x46 Control 1 [Variation]";
  118. MIDI_CC_LIST << "0x47 Control 2 [Timbre]";
  119. MIDI_CC_LIST << "0x48 Control 3 [Release]";
  120. MIDI_CC_LIST << "0x49 Control 4 [Attack]";
  121. MIDI_CC_LIST << "0x4A Control 5 [Brightness]";
  122. MIDI_CC_LIST << "0x4B Control 6 [Decay]";
  123. MIDI_CC_LIST << "0x4C Control 7 [Vib Rate]";
  124. MIDI_CC_LIST << "0x4D Control 8 [Vib Depth]";
  125. MIDI_CC_LIST << "0x4E Control 9 [Vib Delay]";
  126. MIDI_CC_LIST << "0x4F Control 10 [Undefined]";
  127. MIDI_CC_LIST << "0x50 General Purpose 5";
  128. MIDI_CC_LIST << "0x51 General Purpose 6";
  129. MIDI_CC_LIST << "0x52 General Purpose 7";
  130. MIDI_CC_LIST << "0x53 General Purpose 8";
  131. MIDI_CC_LIST << "0x54 Portamento Control";
  132. MIDI_CC_LIST << "0x5B FX 1 Depth [Reverb]";
  133. MIDI_CC_LIST << "0x5C FX 2 Depth [Tremolo]";
  134. MIDI_CC_LIST << "0x5D FX 3 Depth [Chorus]";
  135. MIDI_CC_LIST << "0x5E FX 4 Depth [Detune]";
  136. MIDI_CC_LIST << "0x5F FX 5 Depth [Phaser]";
  137. }
  138. // -------------------------------
  139. // XY Controller Scene
  140. class XYGraphicsScene : public QGraphicsScene
  141. {
  142. Q_OBJECT
  143. public:
  144. XYGraphicsScene(QWidget* parent)
  145. : QGraphicsScene(parent),
  146. m_parent(parent)
  147. {
  148. cc_x = 1;
  149. cc_y = 2;
  150. m_mouseLock = false;
  151. m_smooth = false;
  152. m_smooth_x = 0.0f;
  153. m_smooth_y = 0.0f;
  154. setBackgroundBrush(Qt::black);
  155. QPen cursorPen(QColor(255, 255, 255), 2);
  156. QColor cursorBrush(255, 255, 255, 50);
  157. m_cursor = addEllipse(QRectF(-10, -10, 20, 20), cursorPen, cursorBrush);
  158. QPen linePen(QColor(200, 200, 200, 100), 1, Qt::DashLine);
  159. m_lineH = addLine(-9999, 0, 9999, 0, linePen);
  160. m_lineV = addLine(0, -9999, 0, 9999, linePen);
  161. p_size = QRectF(-100, -100, 100, 100);
  162. }
  163. void setControlX(int x)
  164. {
  165. cc_x = x;
  166. }
  167. void setControlY(int y)
  168. {
  169. cc_y = y;
  170. }
  171. void setChannels(QList<int> channels)
  172. {
  173. m_channels = channels;
  174. }
  175. void setPosX(float x, bool forward=true)
  176. {
  177. if (m_mouseLock)
  178. return;
  179. float posX = x * (p_size.x() + p_size.width());
  180. m_cursor->setPos(posX, m_cursor->y());
  181. m_lineV->setX(posX);
  182. if (forward)
  183. {
  184. float value = posX / (p_size.x() + p_size.width());
  185. sendMIDI(&value, nullptr);
  186. }
  187. else
  188. m_smooth_x = posX;
  189. }
  190. void setPosY(float y, bool forward=true)
  191. {
  192. if (m_mouseLock)
  193. return;
  194. float posY = y * (p_size.y() + p_size.height());
  195. m_cursor->setPos(m_cursor->x(), posY);
  196. m_lineH->setY(posY);
  197. if (forward)
  198. {
  199. float value = posY / (p_size.y() + p_size.height());
  200. sendMIDI(nullptr, &value);
  201. }
  202. else
  203. m_smooth_y = posY;
  204. }
  205. void setSmooth(bool smooth)
  206. {
  207. m_smooth = smooth;
  208. }
  209. void setSmoothValues(float x, float y)
  210. {
  211. m_smooth_x = x * (p_size.x() + p_size.width());
  212. m_smooth_y = y * (p_size.y() + p_size.height());
  213. }
  214. void handleCC(int param, int value)
  215. {
  216. bool sendUpdate = false;
  217. float xp, yp;
  218. xp = yp = 0.0f;
  219. if (param == cc_x)
  220. {
  221. sendUpdate = true;
  222. xp = float(value)/63 - 1.0f;
  223. yp = m_cursor->y() / (p_size.y() + p_size.height());
  224. setPosX(xp, false);
  225. }
  226. if (param == cc_y)
  227. {
  228. sendUpdate = true;
  229. xp = m_cursor->x() / (p_size.x() + p_size.width());
  230. yp = float(value)/63 - 1.0f;
  231. setPosY(yp, false);
  232. }
  233. if (xp < -1.0f)
  234. xp = -1.0f;
  235. else if (xp > 1.0f)
  236. xp = 1.0f;
  237. if (yp < -1.0f)
  238. yp = -1.0f;
  239. else if (yp > 1.0f)
  240. yp = 1.0f;
  241. if (sendUpdate)
  242. emit cursorMoved(xp, yp);
  243. }
  244. void updateSize(QSize size)
  245. {
  246. p_size.setRect(-(float(size.width())/2), -(float(size.height())/2), size.width(), size.height());
  247. }
  248. void updateSmooth()
  249. {
  250. if (! m_smooth)
  251. return;
  252. if (m_cursor->x() == m_smooth_x && m_cursor->y() == m_smooth_y)
  253. return;
  254. if (abs_f(m_cursor->x() - m_smooth_x) <= 0.0005f)
  255. m_smooth_x = m_cursor->x();
  256. if (abs_f(m_cursor->y() - m_smooth_y) <= 0.0005f)
  257. m_smooth_y = m_cursor->y();
  258. if (m_cursor->x() == m_smooth_x && m_cursor->y() == m_smooth_y)
  259. return;
  260. float newX = float(m_smooth_x + m_cursor->x()*7) / 8;
  261. float newY = float(m_smooth_y + m_cursor->y()*7) / 8;
  262. QPointF pos(newX, newY);
  263. m_cursor->setPos(pos);
  264. m_lineH->setY(pos.y());
  265. m_lineV->setX(pos.x());
  266. float xp = pos.x() / (p_size.x() + p_size.width());
  267. float yp = pos.y() / (p_size.y() + p_size.height());
  268. sendMIDI(&xp, &yp);
  269. emit cursorMoved(xp, yp);
  270. }
  271. protected:
  272. void handleMousePos(QPointF pos)
  273. {
  274. if (! p_size.contains(pos))
  275. {
  276. if (pos.x() < p_size.x())
  277. pos.setX(p_size.x());
  278. else if (pos.x() > p_size.x() + p_size.width())
  279. pos.setX(p_size.x() + p_size.width());
  280. if (pos.y() < p_size.y())
  281. pos.setY(p_size.y());
  282. else if (pos.y() > p_size.y() + p_size.height())
  283. pos.setY(p_size.y() + p_size.height());
  284. }
  285. m_smooth_x = pos.x();
  286. m_smooth_y = pos.y();
  287. if (! m_smooth)
  288. {
  289. m_cursor->setPos(pos);
  290. m_lineH->setY(pos.y());
  291. m_lineV->setX(pos.x());
  292. float xp = pos.x() / (p_size.x() + p_size.width());
  293. float yp = pos.y() / (p_size.y() + p_size.height());
  294. sendMIDI(&xp, &yp);
  295. emit cursorMoved(xp, yp);
  296. }
  297. }
  298. void sendMIDI(float* xp=nullptr, float* yp=nullptr)
  299. {
  300. float rate = float(0xff) / 4;
  301. if (xp != nullptr)
  302. {
  303. int value = *xp * rate + rate;
  304. foreach (const int& channel, m_channels)
  305. qMidiOutData.put(0xB0 + channel - 1, cc_x, value);
  306. }
  307. if (yp != nullptr)
  308. {
  309. int value = *yp * rate + rate;
  310. foreach (const int& channel, m_channels)
  311. qMidiOutData.put(0xB0 + channel - 1, cc_y, value);
  312. }
  313. }
  314. void keyPressEvent(QKeyEvent* event)
  315. {
  316. event->accept();
  317. }
  318. void wheelEvent(QGraphicsSceneWheelEvent* event)
  319. {
  320. event->accept();
  321. }
  322. void mousePressEvent(QGraphicsSceneMouseEvent* event)
  323. {
  324. m_mouseLock = true;
  325. handleMousePos(event->scenePos());
  326. parent()->setCursor(Qt::CrossCursor);
  327. QGraphicsScene::mousePressEvent(event);
  328. }
  329. void mouseMoveEvent(QGraphicsSceneMouseEvent* event)
  330. {
  331. handleMousePos(event->scenePos());
  332. QGraphicsScene::mouseMoveEvent(event);
  333. }
  334. void mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
  335. {
  336. m_mouseLock = false;
  337. parent()->setCursor(Qt::ArrowCursor);
  338. QGraphicsScene::mouseReleaseEvent(event);
  339. }
  340. signals:
  341. void cursorMoved(float, float);
  342. private:
  343. int cc_x;
  344. int cc_y;
  345. QList<int> m_channels;
  346. bool m_mouseLock;
  347. bool m_smooth;
  348. float m_smooth_x;
  349. float m_smooth_y;
  350. QGraphicsEllipseItem* m_cursor;
  351. QGraphicsLineItem* m_lineH;
  352. QGraphicsLineItem* m_lineV;
  353. QRectF p_size;
  354. // fake parent
  355. QWidget* const m_parent;
  356. QWidget* parent() const
  357. {
  358. return m_parent;
  359. }
  360. };
  361. // -------------------------------
  362. // XY Controller Window
  363. namespace Ui {
  364. class XYControllerW;
  365. }
  366. class XYControllerW : public QMainWindow
  367. {
  368. Q_OBJECT
  369. public:
  370. XYControllerW()
  371. : QMainWindow(nullptr),
  372. settings("Cadence", "XY-Controller"),
  373. scene(this),
  374. ui(new Ui::XYControllerW)
  375. {
  376. ui->setupUi(this);
  377. // -------------------------------------------------------------
  378. // Internal stuff
  379. cc_x = 1;
  380. cc_y = 2;
  381. // -------------------------------------------------------------
  382. // Set-up GUI stuff
  383. ui->dial_x->setPixmap(2);
  384. ui->dial_y->setPixmap(2);
  385. ui->dial_x->setLabel("X");
  386. ui->dial_y->setLabel("Y");
  387. ui->keyboard->setOctaves(10);
  388. ui->graphicsView->setScene(&scene);
  389. ui->graphicsView->setRenderHints(QPainter::Antialiasing);
  390. foreach (const QString& MIDI_CC, MIDI_CC_LIST)
  391. {
  392. ui->cb_control_x->addItem(MIDI_CC);
  393. ui->cb_control_y->addItem(MIDI_CC);
  394. }
  395. // -------------------------------------------------------------
  396. // Load Settings
  397. loadSettings();
  398. // -------------------------------------------------------------
  399. // Connect actions to functions
  400. connect(ui->keyboard, SIGNAL(noteOn(int)), SLOT(slot_noteOn(int)));
  401. connect(ui->keyboard, SIGNAL(noteOff(int)), SLOT(slot_noteOff(int)));
  402. connect(ui->cb_smooth, SIGNAL(clicked(bool)), SLOT(slot_setSmooth(bool)));
  403. connect(ui->dial_x, SIGNAL(valueChanged(int)), SLOT(slot_updateSceneX(int)));
  404. connect(ui->dial_y, SIGNAL(valueChanged(int)), SLOT(slot_updateSceneY(int)));
  405. connect(ui->cb_control_x, SIGNAL(currentIndexChanged(QString)), SLOT(slot_checkCC_X(QString)));
  406. connect(ui->cb_control_y, SIGNAL(currentIndexChanged(QString)), SLOT(slot_checkCC_Y(QString)));
  407. connect(&scene, SIGNAL(cursorMoved(float,float)), SLOT(slot_sceneCursorMoved(float,float)));
  408. connect(ui->act_ch_01, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  409. connect(ui->act_ch_02, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  410. connect(ui->act_ch_03, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  411. connect(ui->act_ch_04, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  412. connect(ui->act_ch_05, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  413. connect(ui->act_ch_06, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  414. connect(ui->act_ch_07, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  415. connect(ui->act_ch_08, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  416. connect(ui->act_ch_09, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  417. connect(ui->act_ch_10, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  418. connect(ui->act_ch_11, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  419. connect(ui->act_ch_12, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  420. connect(ui->act_ch_13, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  421. connect(ui->act_ch_14, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  422. connect(ui->act_ch_15, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  423. connect(ui->act_ch_16, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  424. connect(ui->act_ch_all, SIGNAL(triggered()), SLOT(slot_checkChannel_all()));
  425. connect(ui->act_ch_none, SIGNAL(triggered()), SLOT(slot_checkChannel_none()));
  426. connect(ui->act_show_keyboard, SIGNAL(triggered(bool)), SLOT(slot_showKeyboard(bool)));
  427. connect(ui->act_about, SIGNAL(triggered()), SLOT(slot_about()));
  428. // -------------------------------------------------------------
  429. // Final stuff
  430. m_midiInTimerId = startTimer(30);
  431. QTimer::singleShot(0, this, SLOT(slot_updateScreen()));
  432. }
  433. void updateScreen()
  434. {
  435. scene.updateSize(ui->graphicsView->size());
  436. ui->graphicsView->centerOn(0, 0);
  437. int dial_x = ui->dial_x->value();
  438. int dial_y = ui->dial_y->value();
  439. slot_updateSceneX(dial_x);
  440. slot_updateSceneY(dial_y);
  441. scene.setSmoothValues(float(dial_x) / 100, float(dial_y) / 100);
  442. }
  443. protected slots:
  444. void slot_noteOn(int note)
  445. {
  446. foreach (const int& channel, m_channels)
  447. qMidiOutData.put(0x90 + channel - 1, note, 100);
  448. }
  449. void slot_noteOff(int note)
  450. {
  451. foreach (const int& channel, m_channels)
  452. qMidiOutData.put(0x80 + channel - 1, note, 0);
  453. }
  454. void slot_updateSceneX(int x)
  455. {
  456. scene.setSmoothValues(float(x) / 100, float(ui->dial_y->value()) / 100);
  457. scene.setPosX(float(x) / 100, bool(sender()));
  458. }
  459. void slot_updateSceneY(int y)
  460. {
  461. scene.setSmoothValues(float(ui->dial_x->value()) / 100, float(y) / 100);
  462. scene.setPosY(float(y) / 100, bool(sender()));
  463. }
  464. void slot_checkCC_X(QString text)
  465. {
  466. if (text.isEmpty())
  467. return;
  468. bool ok;
  469. int tmp_cc_x = text.split(" ").at(0).toInt(&ok, 16);
  470. if (ok)
  471. {
  472. cc_x = tmp_cc_x;
  473. scene.setControlX(cc_x);
  474. }
  475. }
  476. void slot_checkCC_Y(QString text)
  477. {
  478. if (text.isEmpty())
  479. return;
  480. bool ok;
  481. int tmp_cc_y = text.split(" ").at(0).toInt(&ok, 16);
  482. if (ok)
  483. {
  484. cc_y = tmp_cc_y;
  485. scene.setControlY(cc_y);
  486. }
  487. }
  488. void slot_checkChannel(bool clicked)
  489. {
  490. if (! sender())
  491. return;
  492. bool ok;
  493. int channel = ((QAction*)sender())->text().toInt(&ok);
  494. if (ok)
  495. {
  496. if (clicked && ! m_channels.contains(channel))
  497. m_channels.append(channel);
  498. else if ((! clicked) && m_channels.contains(channel))
  499. m_channels.removeOne(channel);
  500. scene.setChannels(m_channels);
  501. }
  502. }
  503. void slot_checkChannel_all()
  504. {
  505. ui->act_ch_01->setChecked(true);
  506. ui->act_ch_02->setChecked(true);
  507. ui->act_ch_03->setChecked(true);
  508. ui->act_ch_04->setChecked(true);
  509. ui->act_ch_05->setChecked(true);
  510. ui->act_ch_06->setChecked(true);
  511. ui->act_ch_07->setChecked(true);
  512. ui->act_ch_08->setChecked(true);
  513. ui->act_ch_09->setChecked(true);
  514. ui->act_ch_10->setChecked(true);
  515. ui->act_ch_11->setChecked(true);
  516. ui->act_ch_12->setChecked(true);
  517. ui->act_ch_13->setChecked(true);
  518. ui->act_ch_14->setChecked(true);
  519. ui->act_ch_15->setChecked(true);
  520. ui->act_ch_16->setChecked(true);
  521. #ifdef Q_COMPILER_INITIALIZER_LISTS
  522. m_channels = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
  523. #else
  524. m_channels.clear();
  525. for (int i=1; i <= 16; i++)
  526. m_channels << i;
  527. #endif
  528. scene.setChannels(m_channels);
  529. }
  530. void slot_checkChannel_none()
  531. {
  532. ui->act_ch_01->setChecked(false);
  533. ui->act_ch_02->setChecked(false);
  534. ui->act_ch_03->setChecked(false);
  535. ui->act_ch_04->setChecked(false);
  536. ui->act_ch_05->setChecked(false);
  537. ui->act_ch_06->setChecked(false);
  538. ui->act_ch_07->setChecked(false);
  539. ui->act_ch_08->setChecked(false);
  540. ui->act_ch_09->setChecked(false);
  541. ui->act_ch_10->setChecked(false);
  542. ui->act_ch_11->setChecked(false);
  543. ui->act_ch_12->setChecked(false);
  544. ui->act_ch_13->setChecked(false);
  545. ui->act_ch_14->setChecked(false);
  546. ui->act_ch_15->setChecked(false);
  547. ui->act_ch_16->setChecked(false);
  548. m_channels.clear();
  549. scene.setChannels(m_channels);
  550. }
  551. void slot_setSmooth(bool yesno)
  552. {
  553. scene.setSmooth(yesno);
  554. }
  555. void slot_sceneCursorMoved(float xp, float yp)
  556. {
  557. ui->dial_x->blockSignals(true);
  558. ui->dial_y->blockSignals(true);
  559. ui->dial_x->setValue(xp * 100);
  560. ui->dial_y->setValue(yp * 100);
  561. ui->dial_x->blockSignals(false);
  562. ui->dial_y->blockSignals(false);
  563. }
  564. void slot_showKeyboard(bool yesno)
  565. {
  566. ui->scrollArea->setVisible(yesno);
  567. QTimer::singleShot(0, this, SLOT(slot_updateScreen()));
  568. }
  569. void slot_about()
  570. {
  571. QMessageBox::about(this, tr("About XY Controller"), tr("<h3>XY Controller</h3>"
  572. "<br>Version %1"
  573. "<br>XY Controller is a simple XY widget that sends and receives data from Jack MIDI.<br>"
  574. "<br>Copyright (C) 2012-2013 falkTX").arg(VERSION));
  575. }
  576. void slot_updateScreen()
  577. {
  578. updateScreen();
  579. }
  580. protected:
  581. void saveSettings()
  582. {
  583. QVariantList varChannelList;
  584. foreach (const int& channel, m_channels)
  585. varChannelList << channel;
  586. settings.setValue("Geometry", saveGeometry());
  587. settings.setValue("ShowKeyboard", ui->scrollArea->isVisible());
  588. settings.setValue("Smooth", ui->cb_smooth->isChecked());
  589. settings.setValue("DialX", ui->dial_x->value());
  590. settings.setValue("DialY", ui->dial_y->value());
  591. settings.setValue("ControlX", cc_x);
  592. settings.setValue("ControlY", cc_y);
  593. settings.setValue("Channels", varChannelList);
  594. }
  595. void loadSettings()
  596. {
  597. restoreGeometry(settings.value("Geometry").toByteArray());
  598. bool showKeyboard = settings.value("ShowKeyboard", false).toBool();
  599. ui->act_show_keyboard->setChecked(showKeyboard);
  600. ui->scrollArea->setVisible(showKeyboard);
  601. bool smooth = settings.value("Smooth", false).toBool();
  602. ui->cb_smooth->setChecked(smooth);
  603. scene.setSmooth(smooth);
  604. ui->dial_x->setValue(settings.value("DialX", 50).toInt());
  605. ui->dial_y->setValue(settings.value("DialY", 50).toInt());
  606. cc_x = settings.value("ControlX", 1).toInt();
  607. cc_y = settings.value("ControlY", 2).toInt();
  608. scene.setControlX(cc_x);
  609. scene.setControlY(cc_y);
  610. m_channels.clear();
  611. if (settings.contains("Channels"))
  612. {
  613. QVariantList channels = settings.value("Channels").toList();
  614. foreach (const QVariant& var, channels)
  615. {
  616. bool ok;
  617. int channel = var.toInt(&ok);
  618. if (ok)
  619. m_channels.append(channel);
  620. }
  621. }
  622. else
  623. {
  624. #ifdef Q_COMPILER_INITIALIZER_LISTS
  625. m_channels = { 1 };
  626. #else
  627. m_channels << 1;
  628. #endif
  629. }
  630. scene.setChannels(m_channels);
  631. for (int i=0; i < MIDI_CC_LIST.size(); i++)
  632. {
  633. bool ok;
  634. int cc = MIDI_CC_LIST[i].split(" ").at(0).toInt(&ok, 16);
  635. if (ok)
  636. {
  637. if (cc_x == cc)
  638. ui->cb_control_x->setCurrentIndex(i);
  639. if (cc_y == cc)
  640. ui->cb_control_y->setCurrentIndex(i);
  641. }
  642. }
  643. if (m_channels.contains(1))
  644. ui->act_ch_01->setChecked(true);
  645. if (m_channels.contains(2))
  646. ui->act_ch_02->setChecked(true);
  647. if (m_channels.contains(3))
  648. ui->act_ch_03->setChecked(true);
  649. if (m_channels.contains(4))
  650. ui->act_ch_04->setChecked(true);
  651. if (m_channels.contains(5))
  652. ui->act_ch_05->setChecked(true);
  653. if (m_channels.contains(6))
  654. ui->act_ch_06->setChecked(true);
  655. if (m_channels.contains(7))
  656. ui->act_ch_07->setChecked(true);
  657. if (m_channels.contains(8))
  658. ui->act_ch_08->setChecked(true);
  659. if (m_channels.contains(9))
  660. ui->act_ch_09->setChecked(true);
  661. if (m_channels.contains(10))
  662. ui->act_ch_10->setChecked(true);
  663. if (m_channels.contains(11))
  664. ui->act_ch_11->setChecked(true);
  665. if (m_channels.contains(12))
  666. ui->act_ch_12->setChecked(true);
  667. if (m_channels.contains(13))
  668. ui->act_ch_13->setChecked(true);
  669. if (m_channels.contains(14))
  670. ui->act_ch_14->setChecked(true);
  671. if (m_channels.contains(15))
  672. ui->act_ch_15->setChecked(true);
  673. if (m_channels.contains(16))
  674. ui->act_ch_16->setChecked(true);
  675. }
  676. void timerEvent(QTimerEvent* event)
  677. {
  678. if (event->timerId() == m_midiInTimerId)
  679. {
  680. if (! qMidiInData.isEmpty())
  681. {
  682. unsigned char d1, d2, d3;
  683. qMidiInInternal.copyDataFrom(&qMidiInData);
  684. while (qMidiInInternal.get(&d1, &d2, &d3, false))
  685. {
  686. int channel = (d1 & 0x0F) + 1;
  687. int mode = d1 & 0xF0;
  688. if (m_channels.contains(channel))
  689. {
  690. if (mode == 0x80)
  691. ui->keyboard->sendNoteOff(d2, false);
  692. else if (mode == 0x90)
  693. ui->keyboard->sendNoteOn(d2, false);
  694. else if (mode == 0xB0)
  695. scene.handleCC(d2, d3);
  696. }
  697. }
  698. }
  699. scene.updateSmooth();
  700. }
  701. QMainWindow::timerEvent(event);
  702. }
  703. void resizeEvent(QResizeEvent* event)
  704. {
  705. updateScreen();
  706. QMainWindow::resizeEvent(event);
  707. }
  708. void closeEvent(QCloseEvent* event)
  709. {
  710. saveSettings();
  711. QMainWindow::closeEvent(event);
  712. }
  713. private:
  714. int cc_x;
  715. int cc_y;
  716. QList<int> m_channels;
  717. int m_midiInTimerId;
  718. QSettings settings;
  719. XYGraphicsScene scene;
  720. Ui::XYControllerW* const ui;
  721. Queue qMidiInInternal;
  722. };
  723. #include "xycontroller.moc"
  724. // -------------------------------
  725. int process_callback(const jack_nframes_t nframes, void*)
  726. {
  727. void* const midiInBuffer = jackbridge_port_get_buffer(jMidiInPort, nframes);
  728. void* const midiOutBuffer = jackbridge_port_get_buffer(jMidiOutPort, nframes);
  729. if (! (midiInBuffer && midiOutBuffer))
  730. return 1;
  731. // MIDI In
  732. jack_midi_event_t midiEvent;
  733. uint32_t midiEventCount = jackbridge_midi_get_event_count(midiInBuffer);
  734. qMidiInData.lock();
  735. for (uint32_t i=0; i < midiEventCount; i++)
  736. {
  737. if (! jackbridge_midi_event_get(&midiEvent, midiInBuffer, i))
  738. break;
  739. if (midiEvent.size == 1)
  740. qMidiInData.put(midiEvent.buffer[0], 0, 0, false);
  741. else if (midiEvent.size == 2)
  742. qMidiInData.put(midiEvent.buffer[0], midiEvent.buffer[1], 0, false);
  743. else if (midiEvent.size >= 3)
  744. qMidiInData.put(midiEvent.buffer[0], midiEvent.buffer[1], midiEvent.buffer[2], false);
  745. if (qMidiInData.isFull())
  746. break;
  747. }
  748. qMidiInData.unlock();
  749. // MIDI Out
  750. jackbridge_midi_clear_buffer(midiOutBuffer);
  751. qMidiOutData.lock();
  752. if (! qMidiOutData.isEmpty())
  753. {
  754. unsigned char d1, d2, d3, data[3];
  755. while (qMidiOutData.get(&d1, &d2, &d3, false))
  756. {
  757. data[0] = d1;
  758. data[1] = d2;
  759. data[2] = d3;
  760. jackbridge_midi_event_write(midiOutBuffer, 0, data, 3);
  761. }
  762. }
  763. qMidiOutData.unlock();
  764. return 0;
  765. }
  766. #ifdef HAVE_JACKSESSION
  767. void session_callback(jack_session_event_t* const event, void* const arg)
  768. {
  769. #ifdef Q_OS_LINUX
  770. QString filepath("cadence-xycontroller");
  771. Q_UNUSED(arg);
  772. #else
  773. QString filepath((char*)arg);
  774. #endif
  775. event->command_line = strdup(filepath.toUtf8().constData());
  776. jackbridge_session_reply(jClient, event);
  777. if (event->type == JackSessionSaveAndQuit)
  778. QApplication::instance()->quit();
  779. jackbridge_session_event_free(event);
  780. }
  781. #endif
  782. // -------------------------------
  783. int main(int argc, char* argv[])
  784. {
  785. MIDI_CC_LIST__init();
  786. #ifdef Q_OS_WIN
  787. QApplication::setGraphicsSystem("raster");
  788. #endif
  789. QApplication app(argc, argv);
  790. app.setApplicationName("XY-Controller");
  791. app.setApplicationVersion(VERSION);
  792. app.setOrganizationName("Cadence");
  793. app.setWindowIcon(QIcon(":/scalable/cadence.svg"));
  794. // JACK initialization
  795. jack_status_t jStatus;
  796. #ifdef HAVE_JACKSESSION
  797. jack_options_t jOptions = static_cast<jack_options_t>(JackNoStartServer|JackSessionID);
  798. #else
  799. jack_options_t jOptions = static_cast<jack_options_t>(JackNoStartServer);
  800. #endif
  801. jClient = jackbridge_client_open("XY-Controller", jOptions, &jStatus);
  802. if (! jClient)
  803. {
  804. std::string errorString(jackbridge_status_get_error_string(jStatus));
  805. QMessageBox::critical(nullptr, app.translate("XY-Controller", "Error"), app.translate("XY-Controller",
  806. "Could not connect to JACK, possible reasons:\n"
  807. "%1").arg(QString::fromStdString(errorString)));
  808. return 1;
  809. }
  810. jMidiInPort = jackbridge_port_register(jClient, "midi_in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
  811. jMidiOutPort = jackbridge_port_register(jClient, "midi_out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
  812. jackbridge_set_process_callback(jClient, process_callback, nullptr);
  813. #ifdef HAVE_JACKSESSION
  814. jackbridge_set_session_callback(jClient, session_callback, argv[0]);
  815. #endif
  816. jackbridge_activate(jClient);
  817. // Show GUI
  818. XYControllerW gui;
  819. gui.show();
  820. // App-Loop
  821. int ret = app.exec();
  822. jackbridge_deactivate(jClient);
  823. jackbridge_client_close(jClient);
  824. return ret;
  825. }