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.

667 lines
17KB

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