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.

654 lines
16KB

  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. #include "carla_bridge.h"
  18. #include "carla_plugin.h"
  19. #include <QtCore/QFile>
  20. #ifndef __WINE__
  21. #include <QtCore/QTimer>
  22. #include <QtGui/QApplication>
  23. #include <QtGui/QDialog>
  24. #endif
  25. #define CARLA_PLUGIN CarlaBackend::CarlaPlugins[0]
  26. void toolkit_plugin_idle();
  27. ClientData* client = nullptr;
  28. // -------------------------------------------------------------------------
  29. // backend stuff
  30. CARLA_BACKEND_START_NAMESPACE
  31. short add_plugin_ladspa(const char* const filename, const char* const name, const char* const label, const void* const extra_stuff);
  32. short add_plugin_dssi(const char* const filename, const char* const name, const char* const label, const void* const extra_stuff);
  33. short add_plugin_lv2(const char* const filename, const char* const name, const char* const label);
  34. short add_plugin_vst(const char* const filename, const char* const name, const char* const label);
  35. CARLA_BACKEND_END_NAMESPACE
  36. using namespace CarlaBackend;
  37. // -------------------------------------------------------------------------
  38. // toolkit classes
  39. #ifdef __WINE__
  40. LRESULT WINAPI MainProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  41. {
  42. switch (msg)
  43. {
  44. case WM_CLOSE:
  45. if (client)
  46. client->queque_message(BRIDGE_MESSAGE_SHOW_GUI, 0, 0, 0.0);
  47. osc_send_configure(CARLA_BRIDGE_MSG_HIDE_GUI, "");
  48. return TRUE;
  49. }
  50. return DefWindowProc(hWnd, msg, wParam, lParam);
  51. }
  52. static bool close_now = false;
  53. static HINSTANCE hInst = nullptr;
  54. static HWND gui = nullptr;
  55. #else
  56. class PluginIdleTimer : public QTimer
  57. {
  58. public:
  59. PluginIdleTimer() {}
  60. void timerEvent(QTimerEvent*)
  61. {
  62. if (client)
  63. client->run_messages();
  64. toolkit_plugin_idle();
  65. }
  66. Q_SLOT void guiClosed()
  67. {
  68. //if (client)
  69. // client->queque_message(BRIDGE_MESSAGE_SHOW_GUI, 0, 0, 0.0);
  70. osc_send_configure(CARLA_BRIDGE_MSG_HIDE_GUI, "");
  71. }
  72. };
  73. static QApplication* app = nullptr;
  74. static QDialog* gui = nullptr;
  75. #endif
  76. #define nextShowMsgNULL 0
  77. #define nextShowMsgFALSE 1
  78. #define nextShowMsgTRUE 2
  79. static int nextShowMsg = nextShowMsgNULL;
  80. static const char* nextChunkFilePath = nullptr;
  81. // -------------------------------------------------------------------------
  82. // toolkit calls
  83. void toolkit_init()
  84. {
  85. #ifdef __WINE__
  86. #else
  87. static int argc = 0;
  88. static char* argv[] = { nullptr };
  89. app = new QApplication(argc, argv, true);
  90. #endif
  91. }
  92. void toolkit_plugin_idle()
  93. {
  94. if (nextShowMsg)
  95. {
  96. bool yesno = nextShowMsg - 1;
  97. CARLA_PLUGIN->showGui(yesno);
  98. if (gui)
  99. {
  100. #ifdef __WINE__
  101. ShowWindow(gui, yesno ? SW_SHOWNORMAL : SW_HIDE);
  102. UpdateWindow(gui);
  103. #else
  104. gui->setVisible(yesno);
  105. #endif
  106. }
  107. nextShowMsg = nextShowMsgNULL;
  108. }
  109. if (nextChunkFilePath)
  110. {
  111. QFile file(nextChunkFilePath);
  112. free((void*)nextChunkFilePath);
  113. nextChunkFilePath = nullptr;
  114. if (file.open(QIODevice::ReadOnly | QIODevice::Text))
  115. {
  116. QString stringData = file.readAll();
  117. file.remove();
  118. CARLA_PLUGIN->setChunkData(stringData.toUtf8().constData());
  119. }
  120. }
  121. CARLA_PLUGIN->idleGui();
  122. static PluginPostEvent postEvents[MAX_POST_EVENTS];
  123. CARLA_PLUGIN->postEventsCopy(postEvents);
  124. for (uint32_t i=0; i < MAX_POST_EVENTS; i++)
  125. {
  126. if (postEvents[i].type == PluginPostEventNull)
  127. break;
  128. switch (postEvents[i].type)
  129. {
  130. case PluginPostEventParameterChange:
  131. callback_action(CALLBACK_PARAMETER_CHANGED, 0, postEvents[i].index, 0, postEvents[i].value);
  132. break;
  133. case PluginPostEventProgramChange:
  134. callback_action(CALLBACK_PROGRAM_CHANGED, 0, postEvents[i].index, 0, 0.0);
  135. break;
  136. case PluginPostEventMidiProgramChange:
  137. callback_action(CALLBACK_MIDI_PROGRAM_CHANGED, 0, postEvents[i].index, 0, 0.0);
  138. break;
  139. case PluginPostEventNoteOn:
  140. callback_action(CALLBACK_NOTE_ON, 0, postEvents[i].index, postEvents[i].value, 0.0);
  141. break;
  142. case PluginPostEventNoteOff:
  143. callback_action(CALLBACK_NOTE_OFF, 0, postEvents[i].index, 0, 0.0);
  144. break;
  145. default:
  146. break;
  147. }
  148. }
  149. const ParameterData* paramData;
  150. for (uint32_t i=0; i < CARLA_PLUGIN->parameterCount(); i++)
  151. {
  152. paramData = CARLA_PLUGIN->parameterData(i);
  153. if (paramData->type == PARAMETER_OUTPUT && (paramData->hints & PARAMETER_IS_AUTOMABLE) > 0)
  154. osc_send_control(paramData->rindex, CARLA_PLUGIN->getParameterValue(i));
  155. }
  156. if (CARLA_PLUGIN->audioInCount() > 0)
  157. {
  158. osc_send_bridge_ains_peak(1, ains_peak[0]);
  159. osc_send_bridge_ains_peak(2, ains_peak[1]);
  160. }
  161. if (CARLA_PLUGIN->audioOutCount() > 0)
  162. {
  163. osc_send_bridge_aouts_peak(1, aouts_peak[0]);
  164. osc_send_bridge_aouts_peak(2, aouts_peak[1]);
  165. }
  166. }
  167. void toolkit_loop()
  168. {
  169. #ifdef __WINE__
  170. MSG msg;
  171. while (! close_now)
  172. {
  173. while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  174. DispatchMessage(&msg);
  175. client->run_messages();
  176. toolkit_plugin_idle();
  177. carla_msleep(50);
  178. }
  179. #else
  180. PluginIdleTimer timer;
  181. timer.start(50);
  182. if (gui)
  183. timer.connect(gui, SIGNAL(finished(int)), &timer, SLOT(guiClosed()));
  184. app->setQuitOnLastWindowClosed(false);
  185. app->exec();
  186. #endif
  187. }
  188. void toolkit_quit()
  189. {
  190. #ifdef __WINE__
  191. close_now = true;
  192. #else
  193. if (app)
  194. app->quit();
  195. #endif
  196. }
  197. void toolkit_window_show()
  198. {
  199. nextShowMsg = nextShowMsgTRUE;
  200. }
  201. void toolkit_window_hide()
  202. {
  203. nextShowMsg = nextShowMsgFALSE;
  204. }
  205. void toolkit_window_resize(int width, int height)
  206. {
  207. if (gui)
  208. {
  209. #ifdef __WINE__
  210. SetWindowPos(gui, 0, 0, 0, width + 6, height + 25, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  211. #else
  212. gui->setFixedSize(width, height);
  213. #endif
  214. }
  215. }
  216. // -------------------------------------------------------------------------
  217. // client stuff
  218. class PluginData : public ClientData
  219. {
  220. public:
  221. PluginData() : ClientData("")
  222. {
  223. }
  224. ~PluginData()
  225. {
  226. }
  227. // ---------------------------------------------------------------------
  228. // processing
  229. void set_parameter(int32_t rindex, double value)
  230. {
  231. if (CARLA_PLUGIN)
  232. CARLA_PLUGIN->setParameterValueByRIndex(rindex, value, true, true, false);
  233. }
  234. void set_program(uint32_t index)
  235. {
  236. if (CARLA_PLUGIN && index < CARLA_PLUGIN->programCount())
  237. CARLA_PLUGIN->setProgram(index, true, true, false, true);
  238. callback_action(CALLBACK_RELOAD_PARAMETERS, 0, 0, 0, 0.0);
  239. }
  240. void set_midi_program(uint32_t bank, uint32_t program)
  241. {
  242. if (CARLA_PLUGIN)
  243. CARLA_PLUGIN->setMidiProgramById(bank, program, true, true, false, true);
  244. callback_action(CALLBACK_RELOAD_PARAMETERS, 0, 0, 0, 0.0);
  245. }
  246. void note_on(uint8_t note, uint8_t velocity)
  247. {
  248. if (CARLA_PLUGIN)
  249. CARLA_PLUGIN->sendMidiSingleNote(note, velocity, true, true, false);
  250. }
  251. void note_off(uint8_t note)
  252. {
  253. if (CARLA_PLUGIN)
  254. CARLA_PLUGIN->sendMidiSingleNote(note, 0, true, true, false);
  255. }
  256. // plugin
  257. void save_now()
  258. {
  259. CARLA_PLUGIN->prepareForSave();
  260. for (uint32_t i=0; i < CARLA_PLUGIN->customDataCount(); i++)
  261. {
  262. const CustomData* const cdata = CARLA_PLUGIN->customData(i);
  263. osc_send_bridge_custom_data(customdatatype2str(cdata->type), cdata->key, cdata->value);
  264. }
  265. if (CARLA_PLUGIN->hints() & PLUGIN_USES_CHUNKS)
  266. {
  267. void* data = nullptr;
  268. int32_t dataSize = CARLA_PLUGIN->chunkData(&data);
  269. if (data && dataSize >= 4)
  270. {
  271. QString filePath;
  272. filePath += "/tmp/.CarlaChunk_";
  273. filePath += CARLA_PLUGIN->name();
  274. QFile file(filePath);
  275. if (file.open(QIODevice::WriteOnly))
  276. {
  277. QByteArray chunk((const char*)data, dataSize);
  278. file.write(chunk);
  279. file.close();
  280. osc_send_bridge_chunk_data(filePath.toUtf8().constData());
  281. }
  282. }
  283. }
  284. osc_send_configure(CARLA_BRIDGE_MSG_SAVED, "");
  285. }
  286. void set_custom_data(const char* const type, const char* const key, const char* const value)
  287. {
  288. if (CARLA_PLUGIN)
  289. CARLA_PLUGIN->setCustomData(customdatastr2type(type), key, value, false);
  290. }
  291. void set_chunk_data(const char* const filePath)
  292. {
  293. nextChunkFilePath = strdup(filePath);
  294. while (nextChunkFilePath)
  295. carla_msleep(25);
  296. }
  297. };
  298. // -------------------------------------------------------------------------
  299. void plugin_bridge_callback(CallbackType action, unsigned short, int value1, int value2, double value3)
  300. {
  301. switch (action)
  302. {
  303. case CALLBACK_PARAMETER_CHANGED:
  304. osc_send_control(value1, value3);
  305. break;
  306. case CALLBACK_PROGRAM_CHANGED:
  307. osc_send_program(value1);
  308. break;
  309. case CALLBACK_MIDI_PROGRAM_CHANGED:
  310. osc_send_midi_program(value1, value2, false);
  311. break;
  312. case CALLBACK_NOTE_ON:
  313. {
  314. uint8_t mdata[4] = { 0, MIDI_STATUS_NOTE_ON, (uint8_t)value1, (uint8_t)value2 };
  315. osc_send_midi(mdata);
  316. break;
  317. }
  318. case CALLBACK_NOTE_OFF:
  319. {
  320. uint8_t mdata[4] = { 0, MIDI_STATUS_NOTE_OFF, (uint8_t)value1, (uint8_t)value2 };
  321. osc_send_midi(mdata);
  322. break;
  323. }
  324. case CALLBACK_SHOW_GUI:
  325. if (value1 == 0)
  326. osc_send_configure(CARLA_BRIDGE_MSG_HIDE_GUI, "");
  327. break;
  328. case CALLBACK_RESIZE_GUI:
  329. if (client)
  330. client->queque_message(BRIDGE_MESSAGE_RESIZE_GUI, value1, value2, 0.0);
  331. break;
  332. case CALLBACK_RELOAD_PARAMETERS:
  333. if (CARLA_PLUGIN)
  334. {
  335. for (uint32_t i=0; i < CARLA_PLUGIN->parameterCount(); i++)
  336. {
  337. osc_send_control(i, CARLA_PLUGIN->getParameterValue(i));
  338. }
  339. }
  340. break;
  341. case CALLBACK_QUIT:
  342. if (client)
  343. client->queque_message(BRIDGE_MESSAGE_QUIT, 0, 0, 0.0);
  344. break;
  345. default:
  346. break;
  347. }
  348. }
  349. // -------------------------------------------------------------------------
  350. #ifdef __WINE__
  351. int WINAPI WinMain(HINSTANCE hInstX, HINSTANCE, LPSTR, int)
  352. {
  353. hInst = hInstX;
  354. #define MAXCMDTOKENS 128
  355. int argc;
  356. LPSTR argv[MAXCMDTOKENS];
  357. LPSTR p = GetCommandLine();
  358. char command[256];
  359. char *args;
  360. char *d, *e;
  361. argc = 0;
  362. args = (char *)malloc(lstrlen(p)+1);
  363. if (args == (char *)NULL) {
  364. qCritical("Insufficient memory in WinMain()");
  365. return 1;
  366. }
  367. // Parse command line handling quotes.
  368. d = args;
  369. while (*p)
  370. {
  371. // for each argument
  372. if (argc >= MAXCMDTOKENS - 1)
  373. break;
  374. e = d;
  375. while ((*p) && (*p != ' ')) {
  376. if (*p == '\042') {
  377. // Remove quotes, skipping over embedded spaces.
  378. // Doesn't handle embedded quotes.
  379. p++;
  380. while ((*p) && (*p != '\042'))
  381. *d++ =*p++;
  382. }
  383. else
  384. *d++ = *p;
  385. if (*p)
  386. p++;
  387. }
  388. *d++ = '\0';
  389. argv[argc++] = e;
  390. while ((*p) && (*p == ' '))
  391. p++; // Skip over trailing spaces
  392. }
  393. argv[argc] = 0;
  394. if (strlen(argv[0]) == 0)
  395. {
  396. GetModuleFileName(hInst, command, sizeof(command)-1);
  397. argv[0] = command;
  398. }
  399. #else
  400. int main(int argc, char* argv[])
  401. {
  402. #endif
  403. if (argc != 6)
  404. {
  405. qWarning("%s :: bad arguments", argv[0]);
  406. return 1;
  407. }
  408. const char* const osc_url = argv[1];
  409. const char* const stype = argv[2];
  410. const char* const filename = argv[3];
  411. const char* name = argv[4];
  412. const char* const label = argv[5];
  413. if (strcmp(name, "(none)") == 0)
  414. name = nullptr;
  415. short id;
  416. PluginType itype;
  417. if (strcmp(stype, "LADSPA") == 0)
  418. itype = PLUGIN_LADSPA;
  419. else if (strcmp(stype, "DSSI") == 0)
  420. itype = PLUGIN_DSSI;
  421. else if (strcmp(stype, "LV2") == 0)
  422. itype = PLUGIN_LV2;
  423. else if (strcmp(stype, "VST") == 0)
  424. itype = PLUGIN_VST;
  425. else
  426. {
  427. itype = PLUGIN_NONE;
  428. qWarning("Invalid plugin type '%s'", stype);
  429. return 1;
  430. }
  431. // Init backend
  432. set_callback_function(plugin_bridge_callback);
  433. set_last_error("no error");
  434. // Init engine
  435. QString engName = QString("%1 (master)").arg(label);
  436. engName.truncate(CarlaEngine::maxClientNameSize());
  437. CarlaEngine engine;
  438. engine.init(engName.toUtf8().constData());
  439. // Init toolkit
  440. toolkit_init();
  441. // Init plugin client
  442. client = new PluginData;
  443. // Init OSC
  444. osc_init(osc_url);
  445. osc_send_update();
  446. // Get plugin type
  447. switch (itype)
  448. {
  449. case PLUGIN_LADSPA:
  450. id = add_plugin_ladspa(filename, name, label, nullptr);
  451. break;
  452. case PLUGIN_DSSI:
  453. id = add_plugin_dssi(filename, name, label, nullptr);
  454. break;
  455. case PLUGIN_LV2:
  456. id = add_plugin_lv2(filename, name, label);
  457. break;
  458. case PLUGIN_VST:
  459. id = add_plugin_vst(filename, name, label);
  460. break;
  461. default:
  462. id = -1;
  463. break;
  464. }
  465. // Init plugin
  466. if (id == 0 && CARLA_PLUGIN)
  467. {
  468. // Create gui if needed
  469. GuiInfo guiInfo;
  470. CARLA_PLUGIN->getGuiInfo(&guiInfo);
  471. QString guiTitle = QString("%1 (GUI)").arg(CARLA_PLUGIN->name());
  472. #ifdef __WINE__
  473. if (guiInfo.type == GUI_INTERNAL_HWND)
  474. {
  475. WNDCLASSEX wclass;
  476. wclass.cbSize = sizeof(WNDCLASSEX);
  477. wclass.style = 0;
  478. wclass.lpfnWndProc = MainProc;
  479. wclass.cbClsExtra = 0;
  480. wclass.cbWndExtra = 0;
  481. wclass.hInstance = hInst;
  482. wclass.hIcon = LoadIcon(hInst, "carla");
  483. wclass.hCursor = LoadCursor(0, IDI_APPLICATION);
  484. wclass.lpszMenuName = "MENU_CARLA_BRIDGE";
  485. wclass.lpszClassName = "CLASS_CARLA_BRIDGE";
  486. wclass.hIconSm = 0;
  487. if (! RegisterClassEx(&wclass))
  488. {
  489. qCritical("Failed to register Wine application");
  490. return 1;
  491. }
  492. gui = CreateWindow("CLASS_CARLA_BRIDGE", guiTitle.toUtf8().constData(),
  493. WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX,
  494. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  495. 0, 0, hInst, 0);
  496. SetWindowPos(gui, 0, 0, 0, 6, 25, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  497. qDebug("Wine GUI created");
  498. #else
  499. if (guiInfo.type == GUI_INTERNAL_QT4 || guiInfo.type == GUI_INTERNAL_X11)
  500. {
  501. gui = new QDialog(nullptr);
  502. gui->resize(10, 10);
  503. gui->setWindowTitle(guiTitle);
  504. #endif
  505. CARLA_PLUGIN->setGuiData(0, gui);
  506. }
  507. // Report OK to backend
  508. osc_send_bridge_update();
  509. // Main loop
  510. toolkit_loop();
  511. // Remove & delete plugin
  512. carla_proc_lock();
  513. CARLA_PLUGIN->setEnabled(false);
  514. carla_proc_unlock();
  515. delete CARLA_PLUGIN;
  516. // Cleanup
  517. #ifndef __WINE__
  518. if (gui)
  519. {
  520. gui->close();
  521. delete gui;
  522. }
  523. delete app;
  524. #endif
  525. }
  526. else
  527. {
  528. qWarning("Plugin failed to load, error was:\n%s", get_last_error());
  529. return 1;
  530. }
  531. // delete old data
  532. if (nextChunkFilePath)
  533. {
  534. free((void*)nextChunkFilePath);
  535. nextChunkFilePath = nullptr;
  536. }
  537. // Close plugin client
  538. delete client;
  539. client = nullptr;
  540. // Close engine
  541. engine.close();
  542. // Close OSC
  543. osc_send_exiting();
  544. osc_close();
  545. return 0;
  546. }