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.

1133 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 = 128;
  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. {
  361. m_smooth_x = m_cursor->x();
  362. return;
  363. }
  364. if (abs_f(m_cursor->y() - m_smooth_y) <= 0.0005f)
  365. {
  366. m_smooth_y = m_cursor->y();
  367. return;
  368. }
  369. float newX = float(m_smooth_x + m_cursor->x()*7) / 8;
  370. float newY = float(m_smooth_y + m_cursor->y()*7) / 8;
  371. QPointF pos(newX, newY);
  372. m_cursor->setPos(pos);
  373. m_lineH->setY(pos.y());
  374. m_lineV->setX(pos.x());
  375. float xp = pos.x() / (p_size.x() + p_size.width());
  376. float yp = pos.y() / (p_size.y() + p_size.height());
  377. sendMIDI(&xp, &yp);
  378. emit cursorMoved(xp, yp);
  379. }
  380. protected:
  381. void handleMousePos(QPointF pos)
  382. {
  383. if (! p_size.contains(pos))
  384. {
  385. if (pos.x() < p_size.x())
  386. pos.setX(p_size.x());
  387. else if (pos.x() > p_size.x() + p_size.width())
  388. pos.setX(p_size.x() + p_size.width());
  389. if (pos.y() < p_size.y())
  390. pos.setY(p_size.y());
  391. else if (pos.y() > p_size.y() + p_size.height())
  392. pos.setY(p_size.y() + p_size.height());
  393. }
  394. m_smooth_x = pos.x();
  395. m_smooth_y = pos.y();
  396. if (! m_smooth)
  397. {
  398. m_cursor->setPos(pos);
  399. m_lineH->setY(pos.y());
  400. m_lineV->setX(pos.x());
  401. float xp = pos.x() / (p_size.x() + p_size.width());
  402. float yp = pos.y() / (p_size.y() + p_size.height());
  403. sendMIDI(&xp, &yp);
  404. emit cursorMoved(xp, yp);
  405. }
  406. }
  407. void sendMIDI(float* xp=nullptr, float* yp=nullptr)
  408. {
  409. float rate = float(0xff) / 4;
  410. if (xp != nullptr)
  411. {
  412. int value = *xp * rate + rate;
  413. foreach (const int& channel, m_channels)
  414. qMidiOutData.put(0xB0 + channel - 1, cc_x, value);
  415. }
  416. if (yp != nullptr)
  417. {
  418. int value = *yp * rate + rate;
  419. foreach (const int& channel, m_channels)
  420. qMidiOutData.put(0xB0 + channel - 1, cc_y, value);
  421. }
  422. }
  423. void keyPressEvent(QKeyEvent* event)
  424. {
  425. event->accept();
  426. }
  427. void wheelEvent(QGraphicsSceneWheelEvent* event)
  428. {
  429. event->accept();
  430. }
  431. void mousePressEvent(QGraphicsSceneMouseEvent* event)
  432. {
  433. m_mouseLock = true;
  434. handleMousePos(event->scenePos());
  435. parent()->setCursor(Qt::CrossCursor);
  436. QGraphicsScene::mousePressEvent(event);
  437. }
  438. void mouseMoveEvent(QGraphicsSceneMouseEvent* event)
  439. {
  440. handleMousePos(event->scenePos());
  441. QGraphicsScene::mouseMoveEvent(event);
  442. }
  443. void mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
  444. {
  445. m_mouseLock = false;
  446. parent()->setCursor(Qt::ArrowCursor);
  447. QGraphicsScene::mouseReleaseEvent(event);
  448. }
  449. signals:
  450. void cursorMoved(float, float);
  451. private:
  452. int cc_x;
  453. int cc_y;
  454. QList<int> m_channels;
  455. bool m_mouseLock;
  456. bool m_smooth;
  457. float m_smooth_x;
  458. float m_smooth_y;
  459. QGraphicsEllipseItem* m_cursor;
  460. QGraphicsLineItem* m_lineH;
  461. QGraphicsLineItem* m_lineV;
  462. QRectF p_size;
  463. // fake parent
  464. QWidget* const m_parent;
  465. QWidget* parent() const
  466. {
  467. return m_parent;
  468. }
  469. };
  470. // -------------------------------
  471. // XY Controller Window
  472. namespace Ui {
  473. class XYControllerW;
  474. }
  475. class XYControllerW : public QMainWindow
  476. {
  477. Q_OBJECT
  478. public:
  479. XYControllerW()
  480. : QMainWindow(nullptr),
  481. settings("Cadence", "XY-Controller"),
  482. scene(this),
  483. ui(new Ui::XYControllerW)
  484. {
  485. ui->setupUi(this);
  486. // -------------------------------------------------------------
  487. // Internal stuff
  488. cc_x = 1;
  489. cc_y = 2;
  490. // -------------------------------------------------------------
  491. // Set-up GUI stuff
  492. ui->dial_x->setPixmap(2);
  493. ui->dial_y->setPixmap(2);
  494. ui->dial_x->setLabel("X");
  495. ui->dial_y->setLabel("Y");
  496. ui->keyboard->setOctaves(6);
  497. ui->graphicsView->setScene(&scene);
  498. ui->graphicsView->setRenderHints(QPainter::Antialiasing);
  499. foreach (const QString& MIDI_CC, MIDI_CC_LIST)
  500. {
  501. ui->cb_control_x->addItem(MIDI_CC);
  502. ui->cb_control_y->addItem(MIDI_CC);
  503. }
  504. // -------------------------------------------------------------
  505. // Load Settings
  506. loadSettings();
  507. // -------------------------------------------------------------
  508. // Connect actions to functions
  509. connect(ui->keyboard, SIGNAL(noteOn(int)), SLOT(slot_noteOn(int)));
  510. connect(ui->keyboard, SIGNAL(noteOff(int)), SLOT(slot_noteOff(int)));
  511. connect(ui->cb_smooth, SIGNAL(clicked(bool)), SLOT(slot_setSmooth(bool)));
  512. connect(ui->dial_x, SIGNAL(valueChanged(int)), SLOT(slot_updateSceneX(int)));
  513. connect(ui->dial_y, SIGNAL(valueChanged(int)), SLOT(slot_updateSceneY(int)));
  514. connect(ui->cb_control_x, SIGNAL(currentIndexChanged(QString)), SLOT(slot_checkCC_X(QString)));
  515. connect(ui->cb_control_y, SIGNAL(currentIndexChanged(QString)), SLOT(slot_checkCC_Y(QString)));
  516. connect(&scene, SIGNAL(cursorMoved(float,float)), SLOT(slot_sceneCursorMoved(float,float)));
  517. connect(ui->act_ch_01, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  518. connect(ui->act_ch_02, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  519. connect(ui->act_ch_03, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  520. connect(ui->act_ch_04, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  521. connect(ui->act_ch_05, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  522. connect(ui->act_ch_06, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  523. connect(ui->act_ch_07, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  524. connect(ui->act_ch_08, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  525. connect(ui->act_ch_09, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  526. connect(ui->act_ch_10, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  527. connect(ui->act_ch_11, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  528. connect(ui->act_ch_12, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  529. connect(ui->act_ch_13, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  530. connect(ui->act_ch_14, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  531. connect(ui->act_ch_15, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  532. connect(ui->act_ch_16, SIGNAL(triggered(bool)), SLOT(slot_checkChannel(bool)));
  533. connect(ui->act_ch_all, SIGNAL(triggered()), SLOT(slot_checkChannel_all()));
  534. connect(ui->act_ch_none, SIGNAL(triggered()), SLOT(slot_checkChannel_none()));
  535. connect(ui->act_show_keyboard, SIGNAL(triggered(bool)), SLOT(slot_showKeyboard(bool)));
  536. connect(ui->act_about, SIGNAL(triggered()), SLOT(slot_about()));
  537. // -------------------------------------------------------------
  538. // Final stuff
  539. m_midiInTimerId = startTimer(30);
  540. QTimer::singleShot(0, this, SLOT(slot_updateScreen()));
  541. }
  542. void updateScreen()
  543. {
  544. scene.updateSize(ui->graphicsView->size());
  545. ui->graphicsView->centerOn(0, 0);
  546. int dial_x = ui->dial_x->value();
  547. int dial_y = ui->dial_y->value();
  548. slot_updateSceneX(dial_x);
  549. slot_updateSceneY(dial_y);
  550. scene.setSmoothValues(float(dial_x) / 100, float(dial_y) / 100);
  551. }
  552. protected slots:
  553. void slot_noteOn(int note)
  554. {
  555. foreach (const int& channel, m_channels)
  556. qMidiOutData.put(0x90 + channel - 1, note, 100);
  557. }
  558. void slot_noteOff(int note)
  559. {
  560. foreach (const int& channel, m_channels)
  561. qMidiOutData.put(0x80 + channel - 1, note, 0);
  562. }
  563. void slot_updateSceneX(int x)
  564. {
  565. scene.setSmoothValues(float(x) / 100, float(ui->dial_y->value()) / 100);
  566. scene.setPosX(float(x) / 100, bool(sender()));
  567. }
  568. void slot_updateSceneY(int y)
  569. {
  570. scene.setSmoothValues(float(ui->dial_x->value()) / 100, float(y) / 100);
  571. scene.setPosY(float(y) / 100, bool(sender()));
  572. }
  573. void slot_checkCC_X(QString text)
  574. {
  575. if (text.isEmpty())
  576. return;
  577. bool ok;
  578. int tmp_cc_x = text.split(" ").at(0).toInt(&ok, 16);
  579. if (ok)
  580. {
  581. cc_x = tmp_cc_x;
  582. scene.setControlX(cc_x);
  583. }
  584. }
  585. void slot_checkCC_Y(QString text)
  586. {
  587. if (text.isEmpty())
  588. return;
  589. bool ok;
  590. int tmp_cc_y = text.split(" ").at(0).toInt(&ok, 16);
  591. if (ok)
  592. {
  593. cc_y = tmp_cc_y;
  594. scene.setControlY(cc_y);
  595. }
  596. }
  597. void slot_checkChannel(bool clicked)
  598. {
  599. if (! sender())
  600. return;
  601. bool ok;
  602. int channel = ((QAction*)sender())->text().toInt(&ok);
  603. if (ok)
  604. {
  605. if (clicked && ! m_channels.contains(channel))
  606. m_channels.append(channel);
  607. else if ((! clicked) && m_channels.contains(channel))
  608. m_channels.removeOne(channel);
  609. scene.setChannels(m_channels);
  610. }
  611. }
  612. void slot_checkChannel_all()
  613. {
  614. ui->act_ch_01->setChecked(true);
  615. ui->act_ch_02->setChecked(true);
  616. ui->act_ch_03->setChecked(true);
  617. ui->act_ch_04->setChecked(true);
  618. ui->act_ch_05->setChecked(true);
  619. ui->act_ch_06->setChecked(true);
  620. ui->act_ch_07->setChecked(true);
  621. ui->act_ch_08->setChecked(true);
  622. ui->act_ch_09->setChecked(true);
  623. ui->act_ch_10->setChecked(true);
  624. ui->act_ch_11->setChecked(true);
  625. ui->act_ch_12->setChecked(true);
  626. ui->act_ch_13->setChecked(true);
  627. ui->act_ch_14->setChecked(true);
  628. ui->act_ch_15->setChecked(true);
  629. ui->act_ch_16->setChecked(true);
  630. #ifdef Q_COMPILER_INITIALIZER_LISTS
  631. m_channels = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
  632. #else
  633. m_channels.clear();
  634. for (int i=1; i <= 16; i++)
  635. m_channels << i;
  636. #endif
  637. scene.setChannels(m_channels);
  638. }
  639. void slot_checkChannel_none()
  640. {
  641. ui->act_ch_01->setChecked(false);
  642. ui->act_ch_02->setChecked(false);
  643. ui->act_ch_03->setChecked(false);
  644. ui->act_ch_04->setChecked(false);
  645. ui->act_ch_05->setChecked(false);
  646. ui->act_ch_06->setChecked(false);
  647. ui->act_ch_07->setChecked(false);
  648. ui->act_ch_08->setChecked(false);
  649. ui->act_ch_09->setChecked(false);
  650. ui->act_ch_10->setChecked(false);
  651. ui->act_ch_11->setChecked(false);
  652. ui->act_ch_12->setChecked(false);
  653. ui->act_ch_13->setChecked(false);
  654. ui->act_ch_14->setChecked(false);
  655. ui->act_ch_15->setChecked(false);
  656. ui->act_ch_16->setChecked(false);
  657. m_channels.clear();
  658. scene.setChannels(m_channels);
  659. }
  660. void slot_setSmooth(bool yesno)
  661. {
  662. scene.setSmooth(yesno);
  663. }
  664. void slot_sceneCursorMoved(float xp, float yp)
  665. {
  666. ui->dial_x->blockSignals(true);
  667. ui->dial_y->blockSignals(true);
  668. ui->dial_x->setValue(xp * 100);
  669. ui->dial_y->setValue(yp * 100);
  670. ui->dial_x->blockSignals(false);
  671. ui->dial_y->blockSignals(false);
  672. }
  673. void slot_showKeyboard(bool yesno)
  674. {
  675. ui->scrollArea->setVisible(yesno);
  676. QTimer::singleShot(0, this, SLOT(slot_updateScreen()));
  677. }
  678. void slot_about()
  679. {
  680. QMessageBox::about(this, tr("About XY Controller"), tr("<h3>XY Controller</h3>"
  681. "<br>Version %1"
  682. "<br>XY Controller is a simple XY widget that sends and receives data from Jack MIDI.<br>"
  683. "<br>Copyright (C) 2012 falkTX").arg(VERSION));
  684. }
  685. void slot_updateScreen()
  686. {
  687. updateScreen();
  688. }
  689. protected:
  690. void saveSettings()
  691. {
  692. QVariantList varChannelList;
  693. foreach (const int& channel, m_channels)
  694. varChannelList << channel;
  695. settings.setValue("Geometry", saveGeometry());
  696. settings.setValue("ShowKeyboard", ui->scrollArea->isVisible());
  697. settings.setValue("Smooth", ui->cb_smooth->isChecked());
  698. settings.setValue("DialX", ui->dial_x->value());
  699. settings.setValue("DialY", ui->dial_y->value());
  700. settings.setValue("ControlX", cc_x);
  701. settings.setValue("ControlY", cc_y);
  702. settings.setValue("Channels", varChannelList);
  703. }
  704. void loadSettings()
  705. {
  706. restoreGeometry(settings.value("Geometry").toByteArray());
  707. bool showKeyboard = settings.value("ShowKeyboard", false).toBool();
  708. ui->act_show_keyboard->setChecked(showKeyboard);
  709. ui->scrollArea->setVisible(showKeyboard);
  710. bool smooth = settings.value("Smooth", false).toBool();
  711. ui->cb_smooth->setChecked(smooth);
  712. scene.setSmooth(smooth);
  713. ui->dial_x->setValue(settings.value("DialX", 50).toInt());
  714. ui->dial_y->setValue(settings.value("DialY", 50).toInt());
  715. cc_x = settings.value("ControlX", 1).toInt();
  716. cc_y = settings.value("ControlY", 2).toInt();
  717. scene.setControlX(cc_x);
  718. scene.setControlY(cc_y);
  719. m_channels.clear();
  720. if (settings.contains("Channels"))
  721. {
  722. QVariantList channels = settings.value("Channels").toList();
  723. foreach (const QVariant& var, channels)
  724. {
  725. bool ok;
  726. int channel = var.toInt(&ok);
  727. if (ok)
  728. m_channels.append(channel);
  729. }
  730. }
  731. else
  732. #ifdef Q_COMPILER_INITIALIZER_LISTS
  733. m_channels = { 1 };
  734. #else
  735. m_channels << 1;
  736. #endif
  737. scene.setChannels(m_channels);
  738. for (int i=0; i < MIDI_CC_LIST.size(); i++)
  739. {
  740. bool ok;
  741. int cc = MIDI_CC_LIST[i].split(" ").at(0).toInt(&ok, 16);
  742. if (ok)
  743. {
  744. if (cc_x == cc)
  745. ui->cb_control_x->setCurrentIndex(i);
  746. if (cc_y == cc)
  747. ui->cb_control_y->setCurrentIndex(i);
  748. }
  749. }
  750. if (m_channels.contains(1))
  751. ui->act_ch_01->setChecked(true);
  752. if (m_channels.contains(2))
  753. ui->act_ch_02->setChecked(true);
  754. if (m_channels.contains(3))
  755. ui->act_ch_03->setChecked(true);
  756. if (m_channels.contains(4))
  757. ui->act_ch_04->setChecked(true);
  758. if (m_channels.contains(5))
  759. ui->act_ch_05->setChecked(true);
  760. if (m_channels.contains(6))
  761. ui->act_ch_06->setChecked(true);
  762. if (m_channels.contains(7))
  763. ui->act_ch_07->setChecked(true);
  764. if (m_channels.contains(8))
  765. ui->act_ch_08->setChecked(true);
  766. if (m_channels.contains(9))
  767. ui->act_ch_09->setChecked(true);
  768. if (m_channels.contains(10))
  769. ui->act_ch_10->setChecked(true);
  770. if (m_channels.contains(11))
  771. ui->act_ch_11->setChecked(true);
  772. if (m_channels.contains(12))
  773. ui->act_ch_12->setChecked(true);
  774. if (m_channels.contains(13))
  775. ui->act_ch_13->setChecked(true);
  776. if (m_channels.contains(14))
  777. ui->act_ch_14->setChecked(true);
  778. if (m_channels.contains(15))
  779. ui->act_ch_15->setChecked(true);
  780. if (m_channels.contains(16))
  781. ui->act_ch_16->setChecked(true);
  782. }
  783. void timerEvent(QTimerEvent* event)
  784. {
  785. if (event->timerId() == m_midiInTimerId)
  786. {
  787. if (! qMidiInData.isEmpty())
  788. {
  789. unsigned char d1, d2, d3;
  790. qMidiInInternal.copyDataFrom(&qMidiInData);
  791. while (qMidiInInternal.get(&d1, &d2, &d3, false))
  792. {
  793. int channel = (d1 & 0x0F) + 1;
  794. int mode = d1 & 0xF0;
  795. if (m_channels.contains(channel))
  796. {
  797. if (mode == 0x80)
  798. ui->keyboard->sendNoteOff(d2, false);
  799. else if (mode == 0x90)
  800. ui->keyboard->sendNoteOn(d2, false);
  801. else if (mode == 0xB0)
  802. scene.handleCC(d2, d3);
  803. }
  804. }
  805. }
  806. scene.updateSmooth();
  807. }
  808. QMainWindow::timerEvent(event);
  809. }
  810. void resizeEvent(QResizeEvent* event)
  811. {
  812. updateScreen();
  813. QMainWindow::resizeEvent(event);
  814. }
  815. void closeEvent(QCloseEvent* event)
  816. {
  817. saveSettings();
  818. QMainWindow::closeEvent(event);
  819. }
  820. private:
  821. int cc_x;
  822. int cc_y;
  823. QList<int> m_channels;
  824. int m_midiInTimerId;
  825. QSettings settings;
  826. XYGraphicsScene scene;
  827. Ui::XYControllerW* const ui;
  828. Queue qMidiInInternal;
  829. };
  830. #include "xycontroller.moc"
  831. // -------------------------------
  832. int process_callback(const jack_nframes_t nframes, void*)
  833. {
  834. void* const midiInBuffer = jack_port_get_buffer(jMidiInPort, nframes);
  835. void* const midiOutBuffer = jack_port_get_buffer(jMidiOutPort, nframes);
  836. if (! (midiInBuffer && midiOutBuffer))
  837. return 1;
  838. // MIDI In
  839. jack_midi_event_t midiEvent;
  840. uint32_t midiEventCount = jack_midi_get_event_count(midiInBuffer);
  841. qMidiInData.lock();
  842. for (uint32_t i=0; i < midiEventCount; i++)
  843. {
  844. if (jack_midi_event_get(&midiEvent, midiInBuffer, i) != 0)
  845. break;
  846. if (midiEvent.size == 1)
  847. qMidiInData.put(midiEvent.buffer[0], 0, 0, false);
  848. else if (midiEvent.size == 2)
  849. qMidiInData.put(midiEvent.buffer[0], midiEvent.buffer[1], 0, false);
  850. else if (midiEvent.size >= 3)
  851. qMidiInData.put(midiEvent.buffer[0], midiEvent.buffer[1], midiEvent.buffer[2], false);
  852. if (qMidiInData.isFull())
  853. break;
  854. }
  855. qMidiInData.unlock();
  856. // MIDI Out
  857. jack_midi_clear_buffer(midiOutBuffer);
  858. qMidiOutData.lock();
  859. if (! qMidiOutData.isEmpty())
  860. {
  861. unsigned char d1, d2, d3, data[3];
  862. while (qMidiOutData.get(&d1, &d2, &d3, false))
  863. {
  864. data[0] = d1;
  865. data[1] = d2;
  866. data[2] = d3;
  867. jack_midi_event_write(midiOutBuffer, 0, data, 3);
  868. }
  869. }
  870. qMidiOutData.unlock();
  871. return 0;
  872. }
  873. #ifdef HAVE_JACKSESSION
  874. void session_callback(jack_session_event_t* const event, void* const arg)
  875. {
  876. #ifdef Q_OS_LINUX
  877. QString filepath("cadence_xycontroller");
  878. Q_UNUSED(arg);
  879. #else
  880. QString filepath((char*)arg);
  881. #endif
  882. event->command_line = strdup(filepath.toUtf8().constData());
  883. jack_session_reply(jClient, event);
  884. if (event->type == JackSessionSaveAndQuit)
  885. QApplication::instance()->quit();
  886. jack_session_event_free(event);
  887. }
  888. #endif
  889. // -------------------------------
  890. int main(int argc, char* argv[])
  891. {
  892. MIDI_CC_LIST__init();
  893. #ifdef Q_OS_WIN
  894. QApplication::setGraphicsSystem("raster");
  895. #endif
  896. QApplication app(argc, argv);
  897. app.setApplicationName("XY-Controller");
  898. app.setApplicationVersion(VERSION);
  899. app.setOrganizationName("Cadence");
  900. //app.setWindowIcon(QIcon(":/48x48/xy-controller.png"));
  901. // JACK initialization
  902. jack_status_t jStatus;
  903. #ifdef HAVE_JACKSESSION
  904. jack_options_t jOptions = static_cast<JackOptions>(JackNoStartServer|JackSessionID);
  905. #else
  906. jack_options_t jOptions = static_cast<JackOptions>(JackNoStartServer);
  907. #endif
  908. jClient = jack_client_open("XY-Controller", jOptions, &jStatus);
  909. if (! jClient)
  910. {
  911. std::string errorString(jack_status_get_error_string(jStatus));
  912. QMessageBox::critical(nullptr, app.translate("XY-Controller", "Error"), app.translate("XY-Controller",
  913. "Could not connect to JACK, possible reasons:\n"
  914. "%1").arg(QString::fromStdString(errorString)));
  915. return 1;
  916. }
  917. jMidiInPort = jack_port_register(jClient, "midi_in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
  918. jMidiOutPort = jack_port_register(jClient, "midi_out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
  919. jack_set_process_callback(jClient, process_callback, nullptr);
  920. #ifdef HAVE_JACKSESSION
  921. jack_set_session_callback(jClient, session_callback, argv[0]);
  922. #endif
  923. jack_activate(jClient);
  924. // Show GUI
  925. XYControllerW gui;
  926. gui.show();
  927. // App-Loop
  928. int ret = app.exec();
  929. jack_deactivate(jClient);
  930. jack_client_close(jClient);
  931. return ret;
  932. }