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.

1130 lines
32KB

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