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.

1128 lines
31KB

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