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.

756 lines
18KB

  1. /*
  2. * Carla Plugin bridge code
  3. * Copyright (C) 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. #ifdef BUILD_BRIDGE_PLUGIN
  18. #include "carla_bridge_client.h"
  19. #include "carla_plugin.h"
  20. #include <QtCore/QFile>
  21. #ifndef __WINE__
  22. #include <QtCore/QTimerEvent>
  23. #include <QtGui/QApplication>
  24. #include <QtGui/QDialog>
  25. #endif
  26. #ifdef __WINE__
  27. static HINSTANCE hInstGlobal = nullptr;
  28. #else
  29. static int qargc = 0;
  30. static char* qargv[] = { nullptr };
  31. #endif
  32. CARLA_BRIDGE_START_NAMESPACE
  33. // -------------------------------------------------------------------------
  34. // client
  35. class CarlaPluginClient : public CarlaClient
  36. {
  37. public:
  38. CarlaPluginClient(CarlaToolkit* const toolkit)
  39. : CarlaClient(toolkit)
  40. {
  41. engine = nullptr;
  42. plugin = nullptr;
  43. }
  44. ~CarlaPluginClient()
  45. {
  46. }
  47. void setStuff(CarlaBackend::CarlaEngine* const engine_, CarlaBackend::CarlaPlugin* const plugin_)
  48. {
  49. engine = engine_;
  50. plugin = plugin_;
  51. }
  52. // ---------------------------------------------------------------------
  53. // processing
  54. void setParameter(const int32_t rindex, const double value)
  55. {
  56. Q_ASSERT(plugin);
  57. if (! plugin)
  58. return;
  59. plugin->setParameterValueByRIndex(rindex, value, true, true, false);
  60. }
  61. void setProgram(const uint32_t index)
  62. {
  63. Q_ASSERT(plugin && index < plugin->programCount());
  64. if (! plugin)
  65. return;
  66. if (index >= plugin->programCount())
  67. return;
  68. plugin->setProgram(index, true, true, false, true);
  69. }
  70. void setMidiProgram(const uint32_t bank, const uint32_t program)
  71. {
  72. Q_ASSERT(plugin);
  73. if (! plugin)
  74. return;
  75. plugin->setMidiProgramById(bank, program, true, true, false, true);
  76. }
  77. void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo)
  78. {
  79. Q_ASSERT(plugin);
  80. Q_ASSERT(velo > 0);
  81. if (! plugin)
  82. return;
  83. plugin->sendMidiSingleNote(channel, note, velo, true, true, false);
  84. }
  85. void noteOff(const uint8_t channel, const uint8_t note)
  86. {
  87. Q_ASSERT(plugin);
  88. if (! plugin)
  89. return;
  90. plugin->sendMidiSingleNote(channel, note, 0, true, true, false);
  91. }
  92. // ---------------------------------------------------------------------
  93. // plugin
  94. void saveNow()
  95. {
  96. Q_ASSERT(plugin);
  97. if (! plugin)
  98. return;
  99. plugin->prepareForSave();
  100. #if 0
  101. for (uint32_t i=0; i < CARLA_PLUGIN->customDataCount(); i++)
  102. {
  103. const CustomData* const cdata = CARLA_PLUGIN->customData(i);
  104. osc_send_bridge_custom_data(customdatatype2str(cdata->type), cdata->key, cdata->value);
  105. }
  106. if (CARLA_PLUGIN->hints() & PLUGIN_USES_CHUNKS)
  107. {
  108. void* data = nullptr;
  109. int32_t dataSize = CARLA_PLUGIN->chunkData(&data);
  110. if (data && dataSize >= 4)
  111. {
  112. QString filePath;
  113. filePath += "/tmp/.CarlaChunk_";
  114. filePath += CARLA_PLUGIN->name();
  115. QFile file(filePath);
  116. if (file.open(QIODevice::WriteOnly))
  117. {
  118. QByteArray chunk((const char*)data, dataSize);
  119. file.write(chunk);
  120. file.close();
  121. osc_send_bridge_chunk_data(filePath.toUtf8().constData());
  122. }
  123. }
  124. }
  125. osc_send_configure(CARLA_BRIDGE_MSG_SAVED, "");
  126. #endif
  127. }
  128. void setCustomData(const char* const type, const char* const key, const char* const value)
  129. {
  130. Q_ASSERT(plugin);
  131. if (! plugin)
  132. return;
  133. plugin->setCustomData(CarlaBackend::getCustomDataStringType(type), key, value, true);
  134. }
  135. void setChunkData(const char* const filePath)
  136. {
  137. Q_ASSERT(plugin);
  138. if (! plugin)
  139. return;
  140. Q_UNUSED(filePath);
  141. #if 0
  142. nextChunkFilePath = strdup(filePath);
  143. while (nextChunkFilePath)
  144. carla_msleep(25);
  145. #endif
  146. }
  147. // ---------------------------------------------------------------------
  148. // idle
  149. void idle()
  150. {
  151. Q_ASSERT(plugin);
  152. if (! plugin)
  153. return;
  154. plugin->idleGui();
  155. //plugin->showGui(true);
  156. }
  157. // ---------------------------------------------------------------------
  158. // callback
  159. void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const double value3)
  160. {
  161. switch (action)
  162. {
  163. case CarlaBackend::CALLBACK_PARAMETER_VALUE_CHANGED:
  164. //osc_send_control(value1, value3);
  165. break;
  166. case CarlaBackend::CALLBACK_PROGRAM_CHANGED:
  167. //osc_send_program(value1);
  168. break;
  169. case CarlaBackend::CALLBACK_MIDI_PROGRAM_CHANGED:
  170. //osc_send_midi_program(value1, value2, false);
  171. break;
  172. case CarlaBackend::CALLBACK_NOTE_ON:
  173. {
  174. //uint8_t mdata[4] = { 0, MIDI_STATUS_NOTE_ON, (uint8_t)value1, (uint8_t)value2 };
  175. //osc_send_midi(mdata);
  176. break;
  177. }
  178. case CarlaBackend::CALLBACK_NOTE_OFF:
  179. {
  180. //uint8_t mdata[4] = { 0, MIDI_STATUS_NOTE_OFF, (uint8_t)value1, (uint8_t)value2 };
  181. //osc_send_midi(mdata);
  182. break;
  183. }
  184. case CarlaBackend::CALLBACK_SHOW_GUI:
  185. //if (value1 == 0)
  186. //sendOscConfigure(CarlaBackend::CARLA_BRIDGE_MSG_HIDE_GUI, "");
  187. //quequeMessage(MESSAGE_QUIT, 0, 0, 0.0);
  188. break;
  189. case CarlaBackend::CALLBACK_RESIZE_GUI:
  190. qDebug("resize callback-------------------------------------------------------------------------------");
  191. quequeMessage(MESSAGE_RESIZE_GUI, value1, value2, 0.0);
  192. //if (m_toolkit)
  193. // m_toolkit->resize(value1, value2);
  194. break;
  195. case CarlaBackend::CALLBACK_RELOAD_PARAMETERS:
  196. //if (CARLA_PLUGIN)
  197. //{
  198. // for (uint32_t i=0; i < CARLA_PLUGIN->parameterCount(); i++)
  199. // {
  200. // osc_send_control(i, CARLA_PLUGIN->getParameterValue(i));
  201. // }
  202. //}
  203. break;
  204. case CarlaBackend::CALLBACK_QUIT:
  205. //quequeMessage(MESSAGE_QUIT, 0, 0, 0.0);
  206. break;
  207. default:
  208. break;
  209. }
  210. Q_UNUSED(value1);
  211. Q_UNUSED(value2);
  212. Q_UNUSED(value3);
  213. }
  214. // ---------------------------------------------------------------------
  215. static void callback(void* const ptr, CarlaBackend::CallbackType const action, const unsigned short, const int value1, const int value2, const double value3)
  216. {
  217. Q_ASSERT(ptr);
  218. if (! ptr)
  219. return;
  220. CarlaPluginClient* const client = (CarlaPluginClient*)ptr;
  221. client->handleCallback(action, value1, value2, value3);
  222. }
  223. private:
  224. CarlaBackend::CarlaEngine* engine;
  225. CarlaBackend::CarlaPlugin* plugin;
  226. };
  227. // -------------------------------------------------------------------------
  228. // toolkit
  229. #ifndef __WINE__
  230. class BridgeApplication : public QApplication
  231. {
  232. public:
  233. BridgeApplication()
  234. : QApplication(qargc, qargv)
  235. {
  236. msgTimer = 0;
  237. m_client = nullptr;
  238. }
  239. void exec(CarlaPluginClient* const client)
  240. {
  241. m_client = client;
  242. msgTimer = startTimer(50);
  243. QApplication::exec();
  244. }
  245. protected:
  246. void timerEvent(QTimerEvent* const event)
  247. {
  248. if (event->timerId() == msgTimer)
  249. {
  250. if (m_client)
  251. {
  252. m_client->idle();
  253. if (! m_client->runMessages())
  254. killTimer(msgTimer);
  255. }
  256. }
  257. QApplication::timerEvent(event);
  258. }
  259. private:
  260. int msgTimer;
  261. CarlaPluginClient* m_client;
  262. };
  263. #endif
  264. class CarlaToolkitPlugin : public CarlaToolkit
  265. {
  266. public:
  267. CarlaToolkitPlugin()
  268. : CarlaToolkit("carla-bridge-plugin")
  269. {
  270. qDebug("CarlaToolkitPlugin::CarlaToolkitPlugin()");
  271. #ifdef __WINE__
  272. closeNow = false;
  273. hwnd = nullptr;
  274. #else
  275. app = nullptr;
  276. dialog = nullptr;
  277. #endif
  278. }
  279. ~CarlaToolkitPlugin()
  280. {
  281. qDebug("CarlaToolkitPlugin::~CarlaToolkitPlugin()");
  282. #ifdef __WINE__
  283. Q_ASSERT(! closeNow);
  284. #else
  285. Q_ASSERT(! app);
  286. #endif
  287. }
  288. void init()
  289. {
  290. qDebug("CarlaToolkitPlugin::init()");
  291. #ifdef __WINE__
  292. Q_ASSERT(! closeNow);
  293. WNDCLASSEXA wc;
  294. wc.cbSize = sizeof(WNDCLASSEXA);
  295. wc.style = 0;
  296. wc.lpfnWndProc = windowProcA;
  297. wc.cbClsExtra = 0;
  298. wc.cbWndExtra = 0;
  299. wc.hInstance = hInstGlobal;
  300. wc.hIcon = LoadIconA(nullptr, IDI_APPLICATION);
  301. wc.hCursor = LoadCursorA(nullptr, IDC_ARROW);
  302. wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
  303. wc.lpszMenuName = "MENU_CARLA_BRIDGE";
  304. wc.lpszClassName = "CLASS_CARLA_BRIDGE";
  305. wc.hIconSm = nullptr;
  306. RegisterClassExA(&wc);
  307. #else
  308. Q_ASSERT(! app);
  309. app = new BridgeApplication;
  310. #endif
  311. }
  312. void exec(CarlaClient* const client, const bool showGui)
  313. {
  314. qDebug("CarlaToolkitPlugin::exec(%p)", client);
  315. Q_ASSERT(client);
  316. m_client = client;
  317. m_client->sendOscUpdate();
  318. m_client->sendOscBridgeUpdate();
  319. if (showGui)
  320. show();
  321. #ifdef __WINE__
  322. Q_ASSERT(! closeNow);
  323. MSG msg;
  324. CarlaPluginClient* const pluginClient = (CarlaPluginClient*)client;
  325. while (! closeNow)
  326. {
  327. //pluginClient->runMessages();
  328. //pluginClient->idle();
  329. //if (closeNow)
  330. // break;
  331. while (GetMessageA(&msg, hwnd, 0, 0) > 0)
  332. {
  333. //if (PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE))
  334. //{
  335. //TranslateMessage(&msg);
  336. DispatchMessageA(&msg);
  337. pluginClient->runMessages();
  338. pluginClient->idle();
  339. }
  340. //if (closeNow)
  341. // break;
  342. //carla_msleep(50);
  343. }
  344. #else
  345. Q_ASSERT(app);
  346. app->exec((CarlaPluginClient*)client);
  347. #endif
  348. }
  349. void quit()
  350. {
  351. qDebug("CarlaToolkitPlugin::quit()");
  352. #ifdef __WINE__
  353. if (closeNow && hwnd)
  354. {
  355. DestroyWindow(hwnd);
  356. hwnd = nullptr;
  357. }
  358. closeNow = true;
  359. #else
  360. Q_ASSERT(app);
  361. if (dialog)
  362. {
  363. dialog->close();
  364. delete dialog;
  365. dialog = nullptr;
  366. }
  367. if (app)
  368. {
  369. if (! app->closingDown())
  370. app->quit();
  371. delete app;
  372. app = nullptr;
  373. }
  374. #endif
  375. }
  376. void show()
  377. {
  378. qDebug("CarlaToolkitPlugin::show()");
  379. #ifdef __WINE__
  380. Q_ASSERT(hwnd);
  381. if (hwnd)
  382. {
  383. ShowWindow(hwnd, SW_SHOWNORMAL);
  384. UpdateWindow(hwnd);
  385. }
  386. #else
  387. Q_ASSERT(dialog);
  388. if (dialog)
  389. dialog->show();
  390. #endif
  391. }
  392. void hide()
  393. {
  394. qDebug("CarlaToolkitPlugin::hide()");
  395. #ifdef __WINE__
  396. Q_ASSERT(hwnd);
  397. if (hwnd)
  398. ShowWindow(hwnd, SW_HIDE);
  399. #else
  400. Q_ASSERT(dialog);
  401. if (dialog)
  402. dialog->show();
  403. #endif
  404. }
  405. void resize(int width, int height)
  406. {
  407. qDebug("CarlaToolkitPlugin::resize(%i, %i)", width, height);
  408. #ifdef __WINE__
  409. Q_ASSERT(hwnd);
  410. if (hwnd)
  411. SetWindowPos(hwnd, 0, 0, 0, width + 6, height + 25, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  412. #else
  413. Q_ASSERT(dialog);
  414. if (dialog)
  415. dialog->setFixedSize(width, height);
  416. #endif
  417. }
  418. // ---------------------------------------------------------------------
  419. void createWindow(const char* const pluginName)
  420. {
  421. #ifdef __WINE__
  422. hwnd = CreateWindowA("CLASS_CARLA_BRIDGE", pluginName, WS_OVERLAPPEDWINDOW &~ WS_THICKFRAME &~ WS_MAXIMIZEBOX,
  423. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  424. HWND_DESKTOP, nullptr, hInstGlobal, nullptr);
  425. SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)this);
  426. SetWindowPos(hwnd, 0, 0, 0, 1100 + 6, 600 + 25, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  427. #else
  428. dialog = new QDialog(nullptr);
  429. dialog->resize(10, 10);
  430. //window->setLayout(new QVBoxLayout(dialog);
  431. dialog->setWindowTitle(QString("%1 (GUI)").arg(pluginName));
  432. #endif
  433. }
  434. CarlaBackend::GuiDataHandle getWindowHandle() const
  435. {
  436. #ifdef __WINE__
  437. return hwnd;
  438. #else
  439. return dialog;
  440. #endif
  441. }
  442. // ---------------------------------------------------------------------
  443. private:
  444. #ifdef __WINE__
  445. bool closeNow;
  446. HWND hwnd;
  447. void handleWindowCloseMessageA()
  448. {
  449. //m_client->quequeMessage(MESSAGE_QUIT, 0, 0, 0.0);
  450. //m_client->quequeMessage(MESSAGE_SHOW_GUI, 0, 0, 0.0);
  451. closeNow = true;
  452. m_client->sendOscConfigure(CarlaBackend::CARLA_BRIDGE_MSG_HIDE_GUI, "");
  453. }
  454. static LRESULT CALLBACK windowProcA(HWND _hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  455. {
  456. qDebug("windowProcA(%p, %i, %li, %li)", _hwnd, message, wParam, lParam);
  457. if (message == WM_CLOSE || message == WM_DESTROY)
  458. {
  459. CarlaToolkitPlugin* const toolkit = (CarlaToolkitPlugin*)GetWindowLongPtrA(_hwnd, GWLP_USERDATA);
  460. Q_ASSERT(toolkit);
  461. if (toolkit)
  462. {
  463. toolkit->handleWindowCloseMessageA();
  464. return TRUE;
  465. }
  466. }
  467. return DefWindowProcA(_hwnd, message, wParam, lParam);
  468. }
  469. #else
  470. BridgeApplication* app;
  471. QDialog* dialog;
  472. #endif
  473. };
  474. CarlaToolkit* CarlaToolkit::createNew(const char* const)
  475. {
  476. return new CarlaToolkitPlugin;
  477. }
  478. // -------------------------------------------------------------------------
  479. CARLA_BRIDGE_END_NAMESPACE
  480. #ifdef __WINE__
  481. int WINAPI WinMain(HINSTANCE hInstX, HINSTANCE, LPSTR, int)
  482. {
  483. hInstGlobal = hInstX;
  484. #define MAXCMDTOKENS 128
  485. int argc;
  486. LPSTR argv[MAXCMDTOKENS];
  487. LPSTR p = GetCommandLine();
  488. char command[256];
  489. char *args;
  490. char *d, *e;
  491. argc = 0;
  492. args = (char *)malloc(lstrlen(p)+1);
  493. if (args == (char *)NULL) {
  494. qCritical("Insufficient memory in WinMain()");
  495. return 1;
  496. }
  497. // Parse command line handling quotes.
  498. d = args;
  499. while (*p)
  500. {
  501. // for each argument
  502. if (argc >= MAXCMDTOKENS - 1)
  503. break;
  504. e = d;
  505. while ((*p) && (*p != ' ')) {
  506. if (*p == '\042') {
  507. // Remove quotes, skipping over embedded spaces.
  508. // Doesn't handle embedded quotes.
  509. p++;
  510. while ((*p) && (*p != '\042'))
  511. *d++ =*p++;
  512. }
  513. else
  514. *d++ = *p;
  515. if (*p)
  516. p++;
  517. }
  518. *d++ = '\0';
  519. argv[argc++] = e;
  520. while ((*p) && (*p == ' '))
  521. p++; // Skip over trailing spaces
  522. }
  523. argv[argc] = 0;
  524. if (strlen(argv[0]) == 0)
  525. {
  526. GetModuleFileName(hInstGlobal, command, sizeof(command)-1);
  527. argv[0] = command;
  528. }
  529. #else
  530. int main(int argc, char* argv[])
  531. {
  532. #endif
  533. if (argc != 6)
  534. {
  535. qWarning("usage: %s <osc-url|\"null\"> <type> <filename> <name|\"(none)\"> <label>", argv[0]);
  536. return 1;
  537. }
  538. const char* const oscUrl = argv[1];
  539. const char* const stype = argv[2];
  540. const char* const filename = argv[3];
  541. const char* name = argv[4];
  542. const char* const label = argv[5];
  543. const bool useOsc = strcmp(oscUrl, "null");
  544. if (strcmp(name, "(none)") == 0)
  545. name = nullptr;
  546. CarlaBackend::PluginType itype;
  547. if (strcmp(stype, "LADSPA") == 0)
  548. itype = CarlaBackend::PLUGIN_LADSPA;
  549. else if (strcmp(stype, "DSSI") == 0)
  550. itype = CarlaBackend::PLUGIN_DSSI;
  551. else if (strcmp(stype, "LV2") == 0)
  552. itype = CarlaBackend::PLUGIN_LV2;
  553. else if (strcmp(stype, "VST") == 0)
  554. itype = CarlaBackend::PLUGIN_VST;
  555. else
  556. {
  557. itype = CarlaBackend::PLUGIN_NONE;
  558. qWarning("Invalid plugin type '%s'", stype);
  559. return 1;
  560. }
  561. // Init toolkit
  562. CarlaBridge::CarlaToolkitPlugin toolkit;
  563. toolkit.init();
  564. // Init client
  565. CarlaBridge::CarlaPluginClient client(&toolkit);
  566. // Init OSC
  567. if (useOsc && ! client.oscInit(oscUrl))
  568. {
  569. toolkit.quit();
  570. return -1;
  571. }
  572. // Init backend engine
  573. CarlaBackend::CarlaEngineJack engine;
  574. engine.setCallback(client.callback, &client);
  575. // bridge client <-> engine
  576. client.registerOscEngine(&engine);
  577. // Init engine
  578. QString engName = QString("%1 (master)").arg(label);
  579. engName.truncate(engine.maxClientNameSize());
  580. engine.init(engName.toUtf8().constData());
  581. /// Init plugin
  582. short id = engine.addPlugin(itype, filename, name, label);
  583. if (id >= 0 && id < CarlaBackend::MAX_PLUGINS)
  584. {
  585. CarlaBackend::CarlaPlugin* const plugin = engine.getPlugin(id);
  586. client.setStuff(&engine, plugin);
  587. {
  588. // create window if needed
  589. toolkit.createWindow(plugin->name());
  590. plugin->setGuiData(toolkit.getWindowHandle());
  591. }
  592. if (! useOsc)
  593. {
  594. plugin->setActive(true, false, false);
  595. plugin->showGui(true);
  596. }
  597. }
  598. else
  599. {
  600. qWarning("Plugin failed to load, error was:\n%s", CarlaBackend::getLastError());
  601. return 1;
  602. }
  603. toolkit.exec(&client, !useOsc);
  604. engine.removeAllPlugins();
  605. engine.close();
  606. // Close OSC
  607. if (useOsc)
  608. {
  609. client.sendOscExiting();
  610. client.oscClose();
  611. }
  612. // Close toolkit
  613. toolkit.quit();
  614. return 0;
  615. }
  616. #endif // BUILD_BRIDGE_PLUGIN