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.

519 lines
13KB

  1. /*
  2. * Carla UI bridge code
  3. * Copyright (C) 2011-2012 Filipe Coelho <falktx@falktx.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 BRIDGE_VST
  18. #include "carla_bridge_client.hpp"
  19. #include "carla_vst_utils.hpp"
  20. #include "carla_midi.h"
  21. #include <QtCore/QTimerEvent>
  22. #include <QtGui/QDialog>
  23. #ifdef Q_WS_X11
  24. # include <QtGui/QX11Info>
  25. #endif
  26. CARLA_BRIDGE_START_NAMESPACE
  27. // -------------------------------------------------------------------------
  28. // fake values
  29. uint32_t bufferSize = 512;
  30. double sampleRate = 44100.0;
  31. class CarlaVstClient : public CarlaClient, QObject
  32. {
  33. public:
  34. CarlaVstClient(CarlaToolkit* const toolkit)
  35. : CarlaClient(toolkit),
  36. QObject(nullptr)
  37. {
  38. effect = nullptr;
  39. idleTimer = 0;
  40. // make client valid
  41. unique1 = unique2 = rand();
  42. }
  43. ~CarlaVstClient()
  44. {
  45. // make client invalid
  46. unique2 += 1;
  47. }
  48. // ---------------------------------------------------------------------
  49. // ui initialization
  50. bool init(const char* binary, const char*)
  51. {
  52. // -----------------------------------------------------------------
  53. // init
  54. CarlaClient::init(binary, nullptr);
  55. // -----------------------------------------------------------------
  56. // open DLL
  57. if (! libOpen(binary))
  58. {
  59. qWarning("%s", libError());
  60. return false;
  61. }
  62. // -----------------------------------------------------------------
  63. // get DLL main entry
  64. VST_Function vstFn = (VST_Function)libSymbol("VSTPluginMain");
  65. if (! vstFn)
  66. vstFn = (VST_Function)libSymbol("main");
  67. if (! vstFn)
  68. return false;
  69. // -----------------------------------------------------------------
  70. // initialize plugin
  71. effect = vstFn(hostCallback);
  72. if (! (effect && effect->magic == kEffectMagic))
  73. return false;
  74. // -----------------------------------------------------------------
  75. // initialize VST stuff
  76. #ifdef VESTIGE_HEADER
  77. effect->ptr1 = this;
  78. #else
  79. effect->resvd1 = (intptr_t)this;
  80. #endif
  81. int32_t value = 0;
  82. #ifdef Q_WS_X11
  83. value = (int64_t)QX11Info::display();
  84. #endif
  85. effect->dispatcher(effect, effOpen, 0, 0, nullptr, 0.0f);
  86. #if ! VST_FORCE_DEPRECATED
  87. effect->dispatcher(effect, effSetBlockSizeAndSampleRate, 0, bufferSize, nullptr, sampleRate);
  88. #endif
  89. effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr, sampleRate);
  90. effect->dispatcher(effect, effSetBlockSize, 0, bufferSize, nullptr, 0.0f);
  91. effect->dispatcher(effect, effSetProcessPrecision, 0, kVstProcessPrecision32, nullptr, 0.0f);
  92. if (effect->dispatcher(effect, effEditOpen, 0, value, getContainerId(), 0.0f) != 1)
  93. {
  94. effect->dispatcher(effect, effClose, 0, 0, nullptr, 0.0f);
  95. return false;
  96. }
  97. // -----------------------------------------------------------------
  98. // initialize gui stuff
  99. ERect* vstRect = nullptr;
  100. effect->dispatcher(effect, effEditGetRect, 0, 0, &vstRect, 0.0f);
  101. if (vstRect)
  102. {
  103. int width = vstRect->right - vstRect->left;
  104. int height = vstRect->bottom - vstRect->top;
  105. if (width > 0 && height > 0)
  106. toolkitResize(width, height);
  107. }
  108. idleTimer = startTimer(50);
  109. return true;
  110. }
  111. void close()
  112. {
  113. CarlaClient::close();
  114. if (effect)
  115. {
  116. effect->dispatcher(effect, effEditClose, 0, 0, nullptr, 0.0f);
  117. effect->dispatcher(effect, effClose, 0, 0, nullptr, 0.0f);
  118. }
  119. }
  120. // ---------------------------------------------------------------------
  121. // ui management
  122. void* getWidget() const
  123. {
  124. return nullptr; // VST always uses reparent
  125. }
  126. bool isResizable() const
  127. {
  128. return false;
  129. }
  130. bool needsReparent() const
  131. {
  132. return true;
  133. }
  134. // ---------------------------------------------------------------------
  135. // processing
  136. void setParameter(const int32_t rindex, const double value)
  137. {
  138. if (effect)
  139. effect->setParameter(effect, rindex, value);
  140. }
  141. void setProgram(const uint32_t index)
  142. {
  143. if (effect)
  144. effect->dispatcher(effect, effSetProgram, 0, index, nullptr, 0.0f);
  145. }
  146. void setMidiProgram(const uint32_t, const uint32_t)
  147. {
  148. }
  149. void noteOn(const uint8_t, const uint8_t, const uint8_t)
  150. {
  151. }
  152. void noteOff(const uint8_t, const uint8_t)
  153. {
  154. }
  155. // ---------------------------------------------------------------------
  156. void handleAudioMasterAutomate(const uint32_t index, const float value)
  157. {
  158. effect->setParameter(effect, index, value);
  159. sendOscControl(index, value);
  160. }
  161. intptr_t handleAudioMasterGetCurrentProcessLevel()
  162. {
  163. return kVstProcessLevelUser;
  164. }
  165. intptr_t handleAudioMasterProcessEvents(const VstEvents* const vstEvents)
  166. {
  167. for (int32_t i=0; i < vstEvents->numEvents; i++)
  168. {
  169. if (! vstEvents->events[i])
  170. break;
  171. const VstMidiEvent* const vstMidiEvent = (const VstMidiEvent*)vstEvents->events[i];
  172. if (vstMidiEvent->type != kVstMidiType)
  173. {
  174. uint8_t status = vstMidiEvent->midiData[0];
  175. // Fix bad note-off
  176. if (MIDI_IS_STATUS_NOTE_ON(status) && vstMidiEvent->midiData[2] == 0)
  177. status -= 0x10;
  178. uint8_t midiBuf[4] = { 0, status, (uint8_t)vstMidiEvent->midiData[1], (uint8_t)vstMidiEvent->midiData[2] };
  179. sendOscMidi(midiBuf);
  180. }
  181. }
  182. return 1;
  183. }
  184. intptr_t handleAdioMasterSizeWindow(int32_t width, int32_t height)
  185. {
  186. toolkitResize(width, height);
  187. return 1;
  188. }
  189. void handleAudioMasterUpdateDisplay()
  190. {
  191. sendOscConfigure("reloadprograms", "");
  192. }
  193. // ---------------------------------------------------------------------
  194. static intptr_t hostCanDo(const char* const feature)
  195. {
  196. qDebug("CarlaVstClient::hostCanDo(\"%s\")", feature);
  197. if (strcmp(feature, "supplyIdle") == 0)
  198. return 1;
  199. if (strcmp(feature, "sendVstEvents") == 0)
  200. return 1;
  201. if (strcmp(feature, "sendVstMidiEvent") == 0)
  202. return 1;
  203. if (strcmp(feature, "sendVstMidiEventFlagIsRealtime") == 0)
  204. return -1;
  205. if (strcmp(feature, "sendVstTimeInfo") == 0)
  206. return 1;
  207. if (strcmp(feature, "receiveVstEvents") == 0)
  208. return 1;
  209. if (strcmp(feature, "receiveVstMidiEvent") == 0)
  210. return 1;
  211. if (strcmp(feature, "receiveVstTimeInfo") == 0)
  212. return -1;
  213. if (strcmp(feature, "reportConnectionChanges") == 0)
  214. return -1;
  215. if (strcmp(feature, "acceptIOChanges") == 0)
  216. return 1;
  217. if (strcmp(feature, "sizeWindow") == 0)
  218. return 1;
  219. if (strcmp(feature, "offline") == 0)
  220. return -1;
  221. if (strcmp(feature, "openFileSelector") == 0)
  222. return -1;
  223. if (strcmp(feature, "closeFileSelector") == 0)
  224. return -1;
  225. if (strcmp(feature, "startStopProcess") == 0)
  226. return 1;
  227. if (strcmp(feature, "supportShell") == 0)
  228. return -1;
  229. if (strcmp(feature, "shellCategory") == 0)
  230. return -1;
  231. // unimplemented
  232. qWarning("CarlaVstClient::hostCanDo(\"%s\") - unknown feature", feature);
  233. return 0;
  234. }
  235. static intptr_t VSTCALLBACK hostCallback(AEffect* const effect, const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
  236. {
  237. #if DEBUG
  238. qDebug("CarlaVstClient::hostCallback(%p, %s, %i, " P_INTPTR ", %p, %f", effect, vstMasterOpcode2str(opcode), index, value, ptr, opt);
  239. #endif
  240. // Check if 'resvd1' points to this client
  241. CarlaVstClient* self = nullptr;
  242. #ifdef VESTIGE_HEADER
  243. if (effect && effect->ptr1)
  244. {
  245. self = (CarlaVstClient*)effect->ptr1;
  246. #else
  247. if (effect && effect->resvd1)
  248. {
  249. self = (CarlaVstClient*)effect->resvd1;
  250. #endif
  251. if (self->unique1 != self->unique2)
  252. self = nullptr;
  253. }
  254. intptr_t ret = 0;
  255. switch (opcode)
  256. {
  257. case audioMasterAutomate:
  258. if (self)
  259. self->handleAudioMasterAutomate(index, opt);
  260. break;
  261. case audioMasterVersion:
  262. ret = kVstVersion;
  263. break;
  264. case audioMasterCurrentId:
  265. // TODO
  266. break;
  267. case audioMasterIdle:
  268. //if (effect)
  269. // effect->dispatcher(effect, effEditIdle, 0, 0, nullptr, 0.0f);
  270. break;
  271. case audioMasterGetTime:
  272. static VstTimeInfo_R timeInfo;
  273. memset(&timeInfo, 0, sizeof(VstTimeInfo_R));
  274. timeInfo.sampleRate = sampleRate;
  275. // Tempo
  276. timeInfo.tempo = 120.0;
  277. timeInfo.flags |= kVstTempoValid;
  278. // Time Signature
  279. timeInfo.timeSigNumerator = 4;
  280. timeInfo.timeSigDenominator = 4;
  281. timeInfo.flags |= kVstTimeSigValid;
  282. ret = (intptr_t)&timeInfo;
  283. break;
  284. case audioMasterProcessEvents:
  285. if (self && ptr)
  286. ret = self->handleAudioMasterProcessEvents((const VstEvents*)ptr);
  287. break;
  288. #if ! VST_FORCE_DEPRECATED
  289. case audioMasterTempoAt:
  290. // Deprecated in VST SDK 2.4
  291. ret = 120 * 10000;
  292. break;
  293. #endif
  294. case audioMasterSizeWindow:
  295. if (self && index > 0 && value > 0)
  296. ret = self->handleAdioMasterSizeWindow(index, value);
  297. break;
  298. case audioMasterGetSampleRate:
  299. ret = sampleRate;
  300. break;
  301. case audioMasterGetBlockSize:
  302. ret = bufferSize;
  303. break;
  304. case audioMasterGetCurrentProcessLevel:
  305. ret = kVstProcessLevelUser;
  306. break;
  307. case audioMasterGetAutomationState:
  308. ret = kVstAutomationReadWrite;
  309. break;
  310. case audioMasterGetVendorString:
  311. if (ptr)
  312. strcpy((char*)ptr, "Cadence");
  313. break;
  314. case audioMasterGetProductString:
  315. if (ptr)
  316. strcpy((char*)ptr, "Carla-Bridge");
  317. break;
  318. case audioMasterGetVendorVersion:
  319. ret = 0x050; // 0.5.0
  320. break;
  321. case audioMasterCanDo:
  322. if (ptr)
  323. ret = hostCanDo((const char*)ptr);
  324. break;
  325. case audioMasterGetLanguage:
  326. ret = kVstLangEnglish;
  327. break;
  328. case audioMasterUpdateDisplay:
  329. if (self)
  330. self->handleAudioMasterUpdateDisplay();
  331. break;
  332. default:
  333. #ifdef DEBUG
  334. qDebug("CarlaVstClient::hostCallback(%p, %s, %i, " P_INTPTR ", %p, %f", effect, vstMasterOpcode2str(opcode), index, value, ptr, opt);
  335. #endif
  336. break;
  337. }
  338. return ret;
  339. }
  340. protected:
  341. void timerEvent(QTimerEvent* const event)
  342. {
  343. if (event->timerId() == idleTimer && effect)
  344. {
  345. effect->dispatcher(effect, effIdle, 0, 0, nullptr, 0.0f);
  346. effect->dispatcher(effect, effEditIdle, 0, 0, nullptr, 0.0f);
  347. }
  348. QObject::timerEvent(event);
  349. }
  350. private:
  351. int unique1;
  352. AEffect* effect;
  353. int idleTimer;
  354. int unique2;
  355. };
  356. CARLA_BRIDGE_END_NAMESPACE
  357. int main(int argc, char* argv[])
  358. {
  359. using namespace CarlaBridge;
  360. if (argc != 4)
  361. {
  362. qWarning("usage: %s <osc-url|\"null\"> <binary> <ui-title>", argv[0]);
  363. return 1;
  364. }
  365. const char* oscUrl = argv[1];
  366. const char* binary = argv[2];
  367. const char* uiTitle = argv[3];
  368. const bool useOsc = strcmp(oscUrl, "null");
  369. // try to get sampleRate value
  370. const char* const sampleRateStr = getenv("CARLA_SAMPLE_RATE");
  371. if (sampleRateStr)
  372. sampleRate = atof(sampleRateStr);
  373. // Init toolkit
  374. CarlaToolkit* const toolkit = CarlaToolkit::createNew(uiTitle);
  375. toolkit->init();
  376. // Init VST-UI
  377. CarlaVstClient client(toolkit);
  378. // Init OSC
  379. if (useOsc && ! client.oscInit(oscUrl))
  380. {
  381. toolkit->quit();
  382. delete toolkit;
  383. return -1;
  384. }
  385. // Load UI
  386. int ret;
  387. if (client.init(binary, nullptr))
  388. {
  389. toolkit->exec(&client, !useOsc);
  390. ret = 0;
  391. }
  392. else
  393. {
  394. qCritical("Failed to load VST UI");
  395. ret = 1;
  396. }
  397. // Close OSC
  398. if (useOsc)
  399. {
  400. client.oscClose();
  401. }
  402. // Close VST-UI
  403. client.close();
  404. // Close toolkit
  405. toolkit->quit();
  406. delete toolkit;
  407. return ret;
  408. }
  409. #endif