Collection of DPF-based plugins for packaging
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.

1146 lines
36KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Lesser General Public
  7. * License as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser General Public License for more details.
  13. *
  14. * For a full copy of the license see the LGPL.txt file
  15. */
  16. #include "DistrhoPluginInternal.hpp"
  17. #if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD)
  18. # include "../DistrhoPluginUtils.hpp"
  19. #endif
  20. #if DISTRHO_PLUGIN_HAS_UI
  21. # include "DistrhoUIInternal.hpp"
  22. # include "../extra/RingBuffer.hpp"
  23. #else
  24. # include "../extra/Sleep.hpp"
  25. #endif
  26. #ifdef DPF_RUNTIME_TESTING
  27. # include "../extra/Thread.hpp"
  28. #endif
  29. #if defined(HAVE_JACK) && defined(STATIC_BUILD) && !defined(DISTRHO_OS_WASM)
  30. # define JACKBRIDGE_DIRECT
  31. #endif
  32. #include "jackbridge/JackBridge.cpp"
  33. #include "lv2/lv2.h"
  34. #ifdef DISTRHO_OS_MAC
  35. # define Point CocoaPoint
  36. # include <CoreFoundation/CoreFoundation.h>
  37. # undef Point
  38. #endif
  39. #ifndef DISTRHO_OS_WINDOWS
  40. # include <signal.h>
  41. # include <unistd.h>
  42. #endif
  43. #ifdef __SSE2_MATH__
  44. # include <xmmintrin.h>
  45. #endif
  46. #ifndef JACK_METADATA_ORDER
  47. # define JACK_METADATA_ORDER "http://jackaudio.org/metadata/order"
  48. #endif
  49. #ifndef JACK_METADATA_PRETTY_NAME
  50. # define JACK_METADATA_PRETTY_NAME "http://jackaudio.org/metadata/pretty-name"
  51. #endif
  52. #ifndef JACK_METADATA_PORT_GROUP
  53. # define JACK_METADATA_PORT_GROUP "http://jackaudio.org/metadata/port-group"
  54. #endif
  55. #ifndef JACK_METADATA_SIGNAL_TYPE
  56. # define JACK_METADATA_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type"
  57. #endif
  58. // -----------------------------------------------------------------------
  59. START_NAMESPACE_DISTRHO
  60. #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
  61. static const sendNoteFunc sendNoteCallback = nullptr;
  62. #endif
  63. #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_STATE
  64. static const setStateFunc setStateCallback = nullptr;
  65. #endif
  66. #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  67. static const writeMidiFunc writeMidiCallback = nullptr;
  68. #endif
  69. #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  70. static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
  71. #endif
  72. // -----------------------------------------------------------------------
  73. static volatile bool gCloseSignalReceived = false;
  74. #ifdef DISTRHO_OS_WINDOWS
  75. static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept
  76. {
  77. if (dwCtrlType == CTRL_C_EVENT)
  78. {
  79. gCloseSignalReceived = true;
  80. return TRUE;
  81. }
  82. return FALSE;
  83. }
  84. static void initSignalHandler()
  85. {
  86. SetConsoleCtrlHandler(winSignalHandler, TRUE);
  87. }
  88. #else
  89. static void closeSignalHandler(int) noexcept
  90. {
  91. gCloseSignalReceived = true;
  92. }
  93. static void initSignalHandler()
  94. {
  95. struct sigaction sig;
  96. memset(&sig, 0, sizeof(sig));
  97. sig.sa_handler = closeSignalHandler;
  98. sig.sa_flags = SA_RESTART;
  99. sigemptyset(&sig.sa_mask);
  100. sigaction(SIGINT, &sig, nullptr);
  101. sigaction(SIGTERM, &sig, nullptr);
  102. }
  103. #endif
  104. // -----------------------------------------------------------------------
  105. #if DISTRHO_PLUGIN_HAS_UI
  106. class PluginJack : public DGL_NAMESPACE::IdleCallback
  107. #else
  108. class PluginJack
  109. #endif
  110. {
  111. public:
  112. PluginJack(jack_client_t* const client, const uintptr_t winId)
  113. : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr),
  114. #if DISTRHO_PLUGIN_HAS_UI
  115. fUI(this,
  116. winId,
  117. d_nextSampleRate,
  118. nullptr, // edit param
  119. setParameterValueCallback,
  120. setStateCallback,
  121. sendNoteCallback,
  122. nullptr, // window size
  123. nullptr, // file request
  124. nullptr, // bundle
  125. fPlugin.getInstancePointer(),
  126. 0.0),
  127. #endif
  128. fClient(client)
  129. {
  130. #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  131. char strBuf[0xff+1];
  132. strBuf[0xff] = '\0';
  133. # if DISTRHO_PLUGIN_NUM_INPUTS > 0
  134. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  135. {
  136. const AudioPort& port(fPlugin.getAudioPort(true, i));
  137. fPortAudioIns[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  138. setAudioPortMetadata(port, fPortAudioIns[i], i);
  139. }
  140. # endif
  141. # if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  142. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  143. {
  144. std::snprintf(strBuf, 0xff, "out%i", i+1);
  145. const AudioPort& port(fPlugin.getAudioPort(false, i));
  146. fPortAudioOuts[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  147. setAudioPortMetadata(port, fPortAudioOuts[i], DISTRHO_PLUGIN_NUM_INPUTS+i);
  148. }
  149. # endif
  150. #endif
  151. fPortEventsIn = jackbridge_port_register(fClient, "events-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
  152. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  153. fPortMidiOut = jackbridge_port_register(fClient, "midi-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
  154. fPortMidiOutBuffer = nullptr;
  155. #endif
  156. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  157. if (fPlugin.getProgramCount() > 0)
  158. {
  159. fPlugin.loadProgram(0);
  160. # if DISTRHO_PLUGIN_HAS_UI
  161. fUI.programLoaded(0);
  162. # endif
  163. }
  164. # if DISTRHO_PLUGIN_HAS_UI
  165. fProgramChanged = -1;
  166. # endif
  167. #endif
  168. if (const uint32_t count = fPlugin.getParameterCount())
  169. {
  170. fLastOutputValues = new float[count];
  171. std::memset(fLastOutputValues, 0, sizeof(float)*count);
  172. #if DISTRHO_PLUGIN_HAS_UI
  173. fParametersChanged = new bool[count];
  174. std::memset(fParametersChanged, 0, sizeof(bool)*count);
  175. #endif
  176. for (uint32_t i=0; i < count; ++i)
  177. {
  178. #if DISTRHO_PLUGIN_HAS_UI
  179. if (! fPlugin.isParameterOutput(i))
  180. fUI.parameterChanged(i, fPlugin.getParameterValue(i));
  181. #endif
  182. }
  183. }
  184. else
  185. {
  186. fLastOutputValues = nullptr;
  187. #if DISTRHO_PLUGIN_HAS_UI
  188. fParametersChanged = nullptr;
  189. #endif
  190. }
  191. jackbridge_set_thread_init_callback(fClient, jackThreadInitCallback, this);
  192. jackbridge_set_buffer_size_callback(fClient, jackBufferSizeCallback, this);
  193. jackbridge_set_sample_rate_callback(fClient, jackSampleRateCallback, this);
  194. jackbridge_set_process_callback(fClient, jackProcessCallback, this);
  195. jackbridge_on_shutdown(fClient, jackShutdownCallback, this);
  196. fPlugin.activate();
  197. jackbridge_activate(fClient);
  198. std::fflush(stdout);
  199. #if DISTRHO_PLUGIN_HAS_UI
  200. if (const char* const name = jackbridge_get_client_name(fClient))
  201. fUI.setWindowTitle(name);
  202. else
  203. fUI.setWindowTitle(fPlugin.getName());
  204. fUI.exec(this);
  205. #else
  206. while (! gCloseSignalReceived)
  207. d_sleep(1);
  208. // unused
  209. (void)winId;
  210. #endif
  211. }
  212. ~PluginJack()
  213. {
  214. if (fClient != nullptr)
  215. jackbridge_deactivate(fClient);
  216. if (fLastOutputValues != nullptr)
  217. {
  218. delete[] fLastOutputValues;
  219. fLastOutputValues = nullptr;
  220. }
  221. #if DISTRHO_PLUGIN_HAS_UI
  222. if (fParametersChanged != nullptr)
  223. {
  224. delete[] fParametersChanged;
  225. fParametersChanged = nullptr;
  226. }
  227. #endif
  228. fPlugin.deactivate();
  229. if (fClient == nullptr)
  230. return;
  231. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  232. jackbridge_port_unregister(fClient, fPortMidiOut);
  233. fPortMidiOut = nullptr;
  234. #endif
  235. jackbridge_port_unregister(fClient, fPortEventsIn);
  236. fPortEventsIn = nullptr;
  237. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  238. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  239. {
  240. jackbridge_port_unregister(fClient, fPortAudioIns[i]);
  241. fPortAudioIns[i] = nullptr;
  242. }
  243. #endif
  244. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  245. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  246. {
  247. jackbridge_port_unregister(fClient, fPortAudioOuts[i]);
  248. fPortAudioOuts[i] = nullptr;
  249. }
  250. #endif
  251. jackbridge_client_close(fClient);
  252. }
  253. // -------------------------------------------------------------------
  254. protected:
  255. #if DISTRHO_PLUGIN_HAS_UI
  256. void idleCallback() override
  257. {
  258. if (gCloseSignalReceived)
  259. return fUI.quit();
  260. # if DISTRHO_PLUGIN_WANT_PROGRAMS
  261. if (fProgramChanged >= 0)
  262. {
  263. fUI.programLoaded(fProgramChanged);
  264. fProgramChanged = -1;
  265. }
  266. # endif
  267. for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
  268. {
  269. if (fPlugin.isParameterOutput(i))
  270. {
  271. const float value = fPlugin.getParameterValue(i);
  272. if (d_isEqual(fLastOutputValues[i], value))
  273. continue;
  274. fLastOutputValues[i] = value;
  275. fUI.parameterChanged(i, value);
  276. }
  277. else if (fParametersChanged[i])
  278. {
  279. fParametersChanged[i] = false;
  280. fUI.parameterChanged(i, fPlugin.getParameterValue(i));
  281. }
  282. }
  283. fUI.exec_idle();
  284. }
  285. #endif
  286. void jackBufferSize(const jack_nframes_t nframes)
  287. {
  288. fPlugin.setBufferSize(nframes, true);
  289. }
  290. void jackSampleRate(const jack_nframes_t nframes)
  291. {
  292. fPlugin.setSampleRate(nframes, true);
  293. }
  294. void jackProcess(const jack_nframes_t nframes)
  295. {
  296. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  297. const float* audioIns[DISTRHO_PLUGIN_NUM_INPUTS];
  298. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  299. audioIns[i] = (const float*)jackbridge_port_get_buffer(fPortAudioIns[i], nframes);
  300. #else
  301. static const float** audioIns = nullptr;
  302. #endif
  303. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  304. float* audioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
  305. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  306. audioOuts[i] = (float*)jackbridge_port_get_buffer(fPortAudioOuts[i], nframes);
  307. #else
  308. static float** audioOuts = nullptr;
  309. #endif
  310. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  311. jack_position_t pos;
  312. fTimePosition.playing = (jackbridge_transport_query(fClient, &pos) == JackTransportRolling);
  313. if (pos.unique_1 == pos.unique_2)
  314. {
  315. fTimePosition.frame = pos.frame;
  316. if (pos.valid & JackPositionBBT)
  317. {
  318. fTimePosition.bbt.valid = true;
  319. fTimePosition.bbt.bar = pos.bar;
  320. fTimePosition.bbt.beat = pos.beat;
  321. fTimePosition.bbt.tick = pos.tick;
  322. #ifdef JACK_TICK_DOUBLE
  323. if (pos.valid & JackTickDouble)
  324. fTimePosition.bbt.tick = pos.tick_double;
  325. else
  326. #endif
  327. fTimePosition.bbt.tick = pos.tick;
  328. fTimePosition.bbt.barStartTick = pos.bar_start_tick;
  329. fTimePosition.bbt.beatsPerBar = pos.beats_per_bar;
  330. fTimePosition.bbt.beatType = pos.beat_type;
  331. fTimePosition.bbt.ticksPerBeat = pos.ticks_per_beat;
  332. fTimePosition.bbt.beatsPerMinute = pos.beats_per_minute;
  333. }
  334. else
  335. fTimePosition.bbt.valid = false;
  336. }
  337. else
  338. {
  339. fTimePosition.bbt.valid = false;
  340. fTimePosition.frame = 0;
  341. }
  342. fPlugin.setTimePosition(fTimePosition);
  343. #endif
  344. updateParameterTriggers();
  345. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  346. fPortMidiOutBuffer = jackbridge_port_get_buffer(fPortMidiOut, nframes);
  347. jackbridge_midi_clear_buffer(fPortMidiOutBuffer);
  348. #endif
  349. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  350. uint32_t midiEventCount = 0;
  351. MidiEvent midiEvents[512];
  352. # if DISTRHO_PLUGIN_HAS_UI
  353. while (fNotesRingBuffer.isDataAvailableForReading())
  354. {
  355. uint8_t midiData[3];
  356. if (! fNotesRingBuffer.readCustomData(midiData, 3))
  357. break;
  358. MidiEvent& midiEvent(midiEvents[midiEventCount++]);
  359. midiEvent.frame = 0;
  360. midiEvent.size = 3;
  361. std::memcpy(midiEvent.data, midiData, 3);
  362. if (midiEventCount == 512)
  363. break;
  364. }
  365. # endif
  366. #else
  367. static const uint32_t midiEventCount = 0;
  368. #endif
  369. void* const midiInBuf = jackbridge_port_get_buffer(fPortEventsIn, nframes);
  370. if (const uint32_t eventCount = std::min(512u - midiEventCount, jackbridge_midi_get_event_count(midiInBuf)))
  371. {
  372. jack_midi_event_t jevent;
  373. for (uint32_t i=0; i < eventCount; ++i)
  374. {
  375. if (! jackbridge_midi_event_get(&jevent, midiInBuf, i))
  376. break;
  377. // Check if message is control change on channel 1
  378. if (jevent.buffer[0] == 0xB0 && jevent.size == 3)
  379. {
  380. const uint8_t control = jevent.buffer[1];
  381. const uint8_t value = jevent.buffer[2];
  382. /* NOTE: This is not optimal, we're iterating all parameters on every CC message.
  383. Since the JACK standalone is more of a test tool, this will do for now. */
  384. for (uint32_t j=0, paramCount=fPlugin.getParameterCount(); j < paramCount; ++j)
  385. {
  386. if (fPlugin.isParameterOutput(j))
  387. continue;
  388. if (fPlugin.getParameterMidiCC(j) != control)
  389. continue;
  390. const float scaled = static_cast<float>(value)/127.0f;
  391. const float fvalue = fPlugin.getParameterRanges(j).getUnnormalizedValue(scaled);
  392. fPlugin.setParameterValue(j, fvalue);
  393. #if DISTRHO_PLUGIN_HAS_UI
  394. fParametersChanged[j] = true;
  395. #endif
  396. break;
  397. }
  398. }
  399. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  400. // Check if message is program change on channel 1
  401. else if (jevent.buffer[0] == 0xC0 && jevent.size == 2)
  402. {
  403. const uint8_t program = jevent.buffer[1];
  404. if (program < fPlugin.getProgramCount())
  405. {
  406. fPlugin.loadProgram(program);
  407. # if DISTRHO_PLUGIN_HAS_UI
  408. fProgramChanged = program;
  409. # endif
  410. }
  411. }
  412. #endif
  413. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  414. MidiEvent& midiEvent(midiEvents[midiEventCount++]);
  415. midiEvent.frame = jevent.time;
  416. midiEvent.size = static_cast<uint32_t>(jevent.size);
  417. if (midiEvent.size > MidiEvent::kDataSize)
  418. midiEvent.dataExt = jevent.buffer;
  419. else
  420. std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size);
  421. #endif
  422. }
  423. }
  424. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  425. fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount);
  426. #else
  427. fPlugin.run(audioIns, audioOuts, nframes);
  428. #endif
  429. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  430. fPortMidiOutBuffer = nullptr;
  431. #endif
  432. }
  433. void jackShutdown()
  434. {
  435. d_stderr("jack has shutdown, quitting now...");
  436. fClient = nullptr;
  437. #if DISTRHO_PLUGIN_HAS_UI
  438. fUI.quit();
  439. #endif
  440. }
  441. // -------------------------------------------------------------------
  442. #if DISTRHO_PLUGIN_HAS_UI
  443. void setParameterValue(const uint32_t index, const float value)
  444. {
  445. fPlugin.setParameterValue(index, value);
  446. }
  447. # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  448. void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
  449. {
  450. uint8_t midiData[3];
  451. midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel;
  452. midiData[1] = note;
  453. midiData[2] = velocity;
  454. fNotesRingBuffer.writeCustomData(midiData, 3);
  455. fNotesRingBuffer.commitWrite();
  456. }
  457. # endif
  458. # if DISTRHO_PLUGIN_WANT_STATE
  459. void setState(const char* const key, const char* const value)
  460. {
  461. fPlugin.setState(key, value);
  462. }
  463. # endif
  464. #endif // DISTRHO_PLUGIN_HAS_UI
  465. // NOTE: no trigger support for JACK, simulate it here
  466. void updateParameterTriggers()
  467. {
  468. float defValue;
  469. for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
  470. {
  471. if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) != kParameterIsTrigger)
  472. continue;
  473. defValue = fPlugin.getParameterRanges(i).def;
  474. if (d_isNotEqual(defValue, fPlugin.getParameterValue(i)))
  475. fPlugin.setParameterValue(i, defValue);
  476. }
  477. }
  478. // -------------------------------------------------------------------
  479. private:
  480. PluginExporter fPlugin;
  481. #if DISTRHO_PLUGIN_HAS_UI
  482. UIExporter fUI;
  483. #endif
  484. jack_client_t* fClient;
  485. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  486. jack_port_t* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS];
  487. #endif
  488. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  489. jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
  490. #endif
  491. jack_port_t* fPortEventsIn;
  492. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  493. jack_port_t* fPortMidiOut;
  494. void* fPortMidiOutBuffer;
  495. #endif
  496. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  497. TimePosition fTimePosition;
  498. #endif
  499. // Temporary data
  500. float* fLastOutputValues;
  501. #if DISTRHO_PLUGIN_HAS_UI
  502. // Store DSP changes to send to UI
  503. bool* fParametersChanged;
  504. # if DISTRHO_PLUGIN_WANT_PROGRAMS
  505. int fProgramChanged;
  506. # endif
  507. # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  508. SmallStackRingBuffer fNotesRingBuffer;
  509. # endif
  510. #endif
  511. void setAudioPortMetadata(const AudioPort& port, jack_port_t* const jackport, const uint32_t index)
  512. {
  513. DISTRHO_SAFE_ASSERT_RETURN(jackport != nullptr,);
  514. const jack_uuid_t uuid = jackbridge_port_uuid(jackport);
  515. if (uuid == JACK_UUID_EMPTY_INITIALIZER)
  516. return;
  517. jackbridge_set_property(fClient, uuid, JACK_METADATA_PRETTY_NAME, port.name, "text/plain");
  518. {
  519. char strBuf[0xff];
  520. snprintf(strBuf, sizeof(0xff)-1, "%u", index);
  521. jackbridge_set_property(fClient, uuid, JACK_METADATA_ORDER, strBuf, "http://www.w3.org/2001/XMLSchema#integer");
  522. }
  523. if (port.groupId != kPortGroupNone)
  524. {
  525. const PortGroupWithId& portGroup(fPlugin.getPortGroupById(port.groupId));
  526. jackbridge_set_property(fClient, uuid, JACK_METADATA_PORT_GROUP, portGroup.name, "text/plain");
  527. }
  528. if (port.hints & kAudioPortIsCV)
  529. {
  530. jackbridge_set_property(fClient, uuid, JACK_METADATA_SIGNAL_TYPE, "CV", "text/plain");
  531. }
  532. else
  533. {
  534. jackbridge_set_property(fClient, uuid, JACK_METADATA_SIGNAL_TYPE, "AUDIO", "text/plain");
  535. return;
  536. }
  537. // set cv ranges
  538. const bool cvPortScaled = port.hints & kCVPortHasScaledRange;
  539. if (port.hints & kCVPortHasBipolarRange)
  540. {
  541. if (cvPortScaled)
  542. {
  543. jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "-5", "http://www.w3.org/2001/XMLSchema#integer");
  544. jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "5", "http://www.w3.org/2001/XMLSchema#integer");
  545. }
  546. else
  547. {
  548. jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "-1", "http://www.w3.org/2001/XMLSchema#integer");
  549. jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "1", "http://www.w3.org/2001/XMLSchema#integer");
  550. }
  551. }
  552. else if (port.hints & kCVPortHasNegativeUnipolarRange)
  553. {
  554. if (cvPortScaled)
  555. {
  556. jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "-10", "http://www.w3.org/2001/XMLSchema#integer");
  557. jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "0", "http://www.w3.org/2001/XMLSchema#integer");
  558. }
  559. else
  560. {
  561. jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "-1", "http://www.w3.org/2001/XMLSchema#integer");
  562. jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "0", "http://www.w3.org/2001/XMLSchema#integer");
  563. }
  564. }
  565. else if (port.hints & kCVPortHasPositiveUnipolarRange)
  566. {
  567. if (cvPortScaled)
  568. {
  569. jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "0", "http://www.w3.org/2001/XMLSchema#integer");
  570. jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "10", "http://www.w3.org/2001/XMLSchema#integer");
  571. }
  572. else
  573. {
  574. jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "0", "http://www.w3.org/2001/XMLSchema#integer");
  575. jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "1", "http://www.w3.org/2001/XMLSchema#integer");
  576. }
  577. }
  578. }
  579. // -------------------------------------------------------------------
  580. // Callbacks
  581. #define thisPtr ((PluginJack*)ptr)
  582. static void jackThreadInitCallback(void*)
  583. {
  584. #if defined(__SSE2_MATH__)
  585. _mm_setcsr(_mm_getcsr() | 0x8040);
  586. #elif defined(__aarch64__)
  587. uint64_t c;
  588. __asm__ __volatile__("mrs %0, fpcr \n"
  589. "orr %0, %0, #0x1000000\n"
  590. "msr fpcr, %0 \n"
  591. "isb \n"
  592. : "=r"(c) :: "memory");
  593. #elif defined(__arm__) && !defined(__SOFTFP__)
  594. uint32_t c;
  595. __asm__ __volatile__("vmrs %0, fpscr \n"
  596. "orr %0, %0, #0x1000000\n"
  597. "vmsr fpscr, %0 \n"
  598. : "=r"(c) :: "memory");
  599. #endif
  600. }
  601. static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr)
  602. {
  603. thisPtr->jackBufferSize(nframes);
  604. return 0;
  605. }
  606. static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr)
  607. {
  608. thisPtr->jackSampleRate(nframes);
  609. return 0;
  610. }
  611. static int jackProcessCallback(jack_nframes_t nframes, void* ptr)
  612. {
  613. thisPtr->jackProcess(nframes);
  614. return 0;
  615. }
  616. static void jackShutdownCallback(void* ptr)
  617. {
  618. thisPtr->jackShutdown();
  619. }
  620. #if DISTRHO_PLUGIN_HAS_UI
  621. static void setParameterValueCallback(void* ptr, uint32_t index, float value)
  622. {
  623. thisPtr->setParameterValue(index, value);
  624. }
  625. # if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  626. static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
  627. {
  628. thisPtr->sendNote(channel, note, velocity);
  629. }
  630. # endif
  631. # if DISTRHO_PLUGIN_WANT_STATE
  632. static void setStateCallback(void* ptr, const char* key, const char* value)
  633. {
  634. thisPtr->setState(key, value);
  635. }
  636. # endif
  637. #endif // DISTRHO_PLUGIN_HAS_UI
  638. #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  639. bool requestParameterValueChange(const uint32_t index, const float value)
  640. {
  641. DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), false);
  642. fPlugin.setParameterValue(index, value);
  643. # if DISTRHO_PLUGIN_HAS_UI
  644. fParametersChanged[index] = true;
  645. # endif
  646. return true;
  647. }
  648. static bool requestParameterValueChangeCallback(void* ptr, const uint32_t index, const float value)
  649. {
  650. return thisPtr->requestParameterValueChange(index, value);
  651. }
  652. #endif
  653. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  654. bool writeMidi(const MidiEvent& midiEvent)
  655. {
  656. DISTRHO_SAFE_ASSERT_RETURN(fPortMidiOutBuffer != nullptr, false);
  657. return jackbridge_midi_event_write(fPortMidiOutBuffer,
  658. midiEvent.frame,
  659. midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data,
  660. midiEvent.size);
  661. }
  662. static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
  663. {
  664. return thisPtr->writeMidi(midiEvent);
  665. }
  666. #endif
  667. #undef thisPtr
  668. };
  669. // -----------------------------------------------------------------------
  670. #ifdef DPF_RUNTIME_TESTING
  671. class PluginProcessTestingThread : public Thread
  672. {
  673. PluginExporter& plugin;
  674. public:
  675. PluginProcessTestingThread(PluginExporter& p) : plugin(p) {}
  676. protected:
  677. void run() override
  678. {
  679. plugin.setBufferSize(256);
  680. plugin.activate();
  681. float buffer[256];
  682. const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS > 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1];
  683. float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS > 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1];
  684. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  685. inputs[i] = buffer;
  686. for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  687. outputs[i] = buffer;
  688. while (! shouldThreadExit())
  689. {
  690. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  691. plugin.run(inputs, outputs, 128, nullptr, 0);
  692. #else
  693. plugin.run(inputs, outputs, 128);
  694. #endif
  695. d_msleep(100);
  696. }
  697. plugin.deactivate();
  698. }
  699. };
  700. bool runSelfTests()
  701. {
  702. // simple plugin creation first
  703. {
  704. d_nextBufferSize = 512;
  705. d_nextSampleRate = 44100.0;
  706. PluginExporter plugin(nullptr, nullptr, nullptr, nullptr);
  707. d_nextBufferSize = 0;
  708. d_nextSampleRate = 0.0;
  709. }
  710. // keep values for all tests now
  711. d_nextBufferSize = 512;
  712. d_nextSampleRate = 44100.0;
  713. // simple processing
  714. {
  715. d_nextPluginIsSelfTest = true;
  716. PluginExporter plugin(nullptr, nullptr, nullptr, nullptr);
  717. d_nextPluginIsSelfTest = false;
  718. #if DISTRHO_PLUGIN_HAS_UI
  719. UIExporter ui(nullptr, 0, plugin.getSampleRate(),
  720. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  721. plugin.getInstancePointer(), 0.0);
  722. ui.showAndFocus();
  723. #endif
  724. plugin.activate();
  725. plugin.deactivate();
  726. plugin.setBufferSize(128);
  727. plugin.setSampleRate(48000);
  728. plugin.activate();
  729. float buffer[128] = {};
  730. const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS > 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1];
  731. float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS > 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1];
  732. for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  733. inputs[i] = buffer;
  734. for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  735. outputs[i] = buffer;
  736. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  737. plugin.run(inputs, outputs, 128, nullptr, 0);
  738. #else
  739. plugin.run(inputs, outputs, 128);
  740. #endif
  741. plugin.deactivate();
  742. #if DISTRHO_PLUGIN_HAS_UI
  743. ui.plugin_idle();
  744. #endif
  745. }
  746. return true;
  747. // multi-threaded processing with UI
  748. {
  749. PluginExporter pluginA(nullptr, nullptr, nullptr, nullptr);
  750. PluginExporter pluginB(nullptr, nullptr, nullptr, nullptr);
  751. PluginExporter pluginC(nullptr, nullptr, nullptr, nullptr);
  752. PluginProcessTestingThread procTestA(pluginA);
  753. PluginProcessTestingThread procTestB(pluginB);
  754. PluginProcessTestingThread procTestC(pluginC);
  755. procTestA.startThread();
  756. procTestB.startThread();
  757. procTestC.startThread();
  758. // wait 2s
  759. d_sleep(2);
  760. // stop the 2nd instance now
  761. procTestB.stopThread(5000);
  762. #if DISTRHO_PLUGIN_HAS_UI
  763. // start UI in the middle of this
  764. {
  765. UIExporter uiA(nullptr, 0, pluginA.getSampleRate(),
  766. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  767. pluginA.getInstancePointer(), 0.0);
  768. UIExporter uiB(nullptr, 0, pluginA.getSampleRate(),
  769. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  770. pluginB.getInstancePointer(), 0.0);
  771. UIExporter uiC(nullptr, 0, pluginA.getSampleRate(),
  772. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  773. pluginC.getInstancePointer(), 0.0);
  774. // show UIs
  775. uiB.showAndFocus();
  776. uiA.showAndFocus();
  777. uiC.showAndFocus();
  778. // idle for 3s
  779. for (int i=0; i<30; i++)
  780. {
  781. uiC.plugin_idle();
  782. uiB.plugin_idle();
  783. uiA.plugin_idle();
  784. d_msleep(100);
  785. }
  786. }
  787. #endif
  788. procTestA.stopThread(5000);
  789. procTestC.stopThread(5000);
  790. }
  791. return true;
  792. }
  793. #endif // DPF_RUNTIME_TESTING
  794. END_NAMESPACE_DISTRHO
  795. // -----------------------------------------------------------------------
  796. int main(int argc, char* argv[])
  797. {
  798. USE_NAMESPACE_DISTRHO;
  799. initSignalHandler();
  800. #if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD)
  801. // find plugin bundle
  802. static String bundlePath;
  803. if (bundlePath.isEmpty())
  804. {
  805. String tmpPath(getBinaryFilename());
  806. tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
  807. #ifdef DISTRHO_OS_MAC
  808. if (tmpPath.endsWith("/MacOS"))
  809. {
  810. tmpPath.truncate(tmpPath.rfind('/'));
  811. if (tmpPath.endsWith("/Contents"))
  812. {
  813. tmpPath.truncate(tmpPath.rfind('/'));
  814. bundlePath = tmpPath;
  815. d_nextBundlePath = bundlePath.buffer();
  816. }
  817. }
  818. #else
  819. if (access(tmpPath + DISTRHO_OS_SEP_STR "resources", F_OK) == 0)
  820. {
  821. bundlePath = tmpPath;
  822. d_nextBundlePath = bundlePath.buffer();
  823. }
  824. #endif
  825. }
  826. #endif
  827. if (argc == 2 && std::strcmp(argv[1], "selftest") == 0)
  828. {
  829. #ifdef DPF_RUNTIME_TESTING
  830. return runSelfTests() ? 0 : 1;
  831. #else
  832. d_stderr2("Code was built without DPF_RUNTIME_TESTING macro enabled, selftest option is not available");
  833. return 1;
  834. #endif
  835. }
  836. #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
  837. /* the code below is based on
  838. * https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/
  839. */
  840. bool hasConsole = false;
  841. HANDLE consoleHandleOut, consoleHandleError;
  842. if (AttachConsole(ATTACH_PARENT_PROCESS))
  843. {
  844. // Redirect unbuffered STDOUT to the console
  845. consoleHandleOut = GetStdHandle(STD_OUTPUT_HANDLE);
  846. if (consoleHandleOut != INVALID_HANDLE_VALUE)
  847. {
  848. freopen("CONOUT$", "w", stdout);
  849. setvbuf(stdout, NULL, _IONBF, 0);
  850. }
  851. // Redirect unbuffered STDERR to the console
  852. consoleHandleError = GetStdHandle(STD_ERROR_HANDLE);
  853. if (consoleHandleError != INVALID_HANDLE_VALUE)
  854. {
  855. freopen("CONOUT$", "w", stderr);
  856. setvbuf(stderr, NULL, _IONBF, 0);
  857. }
  858. hasConsole = true;
  859. }
  860. #endif
  861. jack_status_t status = jack_status_t(0x0);
  862. jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status);
  863. if (client == nullptr)
  864. {
  865. String errorString;
  866. if (status & JackFailure)
  867. errorString += "Overall operation failed;\n";
  868. if (status & JackInvalidOption)
  869. errorString += "The operation contained an invalid or unsupported option;\n";
  870. if (status & JackNameNotUnique)
  871. errorString += "The desired client name was not unique;\n";
  872. if (status & JackServerStarted)
  873. errorString += "The JACK server was started as a result of this operation;\n";
  874. if (status & JackServerFailed)
  875. errorString += "Unable to connect to the JACK server;\n";
  876. if (status & JackServerError)
  877. errorString += "Communication error with the JACK server;\n";
  878. if (status & JackNoSuchClient)
  879. errorString += "Requested client does not exist;\n";
  880. if (status & JackLoadFailure)
  881. errorString += "Unable to load internal client;\n";
  882. if (status & JackInitFailure)
  883. errorString += "Unable to initialize client;\n";
  884. if (status & JackShmFailure)
  885. errorString += "Unable to access shared memory;\n";
  886. if (status & JackVersionError)
  887. errorString += "Client's protocol version does not match;\n";
  888. if (status & JackBackendError)
  889. errorString += "Backend Error;\n";
  890. if (status & JackClientZombie)
  891. errorString += "Client is being shutdown against its will;\n";
  892. if (errorString.isNotEmpty())
  893. {
  894. errorString[errorString.length()-2] = '.';
  895. d_stderr("Failed to create the JACK client, reason was:\n%s", errorString.buffer());
  896. }
  897. else
  898. d_stderr("Failed to create the JACK client, cannot continue!");
  899. #if defined(DISTRHO_OS_MAC)
  900. CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr,
  901. DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8);
  902. CFStringRef errorStringRef = CFStringCreateWithCString(nullptr,
  903. String("Failed to create JACK client, reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8);
  904. CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel,
  905. nullptr, nullptr, nullptr,
  906. errorTitleRef, errorStringRef,
  907. nullptr, nullptr, nullptr, nullptr);
  908. #elif defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
  909. // make sure message box is high-dpi aware
  910. if (const HMODULE user32 = LoadLibrary("user32.dll"))
  911. {
  912. typedef BOOL(WINAPI* SPDA)(void);
  913. #if defined(__GNUC__) && (__GNUC__ >= 9)
  914. # pragma GCC diagnostic push
  915. # pragma GCC diagnostic ignored "-Wcast-function-type"
  916. #endif
  917. const SPDA SetProcessDPIAware = (SPDA)GetProcAddress(user32, "SetProcessDPIAware");
  918. #if defined(__GNUC__) && (__GNUC__ >= 9)
  919. # pragma GCC diagnostic pop
  920. #endif
  921. if (SetProcessDPIAware)
  922. SetProcessDPIAware();
  923. FreeLibrary(user32);
  924. }
  925. const String win32error = "Failed to create JACK client, reason was:\n" + errorString;
  926. MessageBoxA(nullptr, win32error.buffer(), "", MB_ICONERROR);
  927. #endif
  928. return 1;
  929. }
  930. d_nextBufferSize = jackbridge_get_buffer_size(client);
  931. d_nextSampleRate = jackbridge_get_sample_rate(client);
  932. d_nextCanRequestParameterValueChanges = true;
  933. uintptr_t winId = 0;
  934. #if DISTRHO_PLUGIN_HAS_UI
  935. if (argc == 3 && std::strcmp(argv[1], "embed") == 0)
  936. winId = static_cast<uintptr_t>(std::atoll(argv[2]));
  937. #endif
  938. const PluginJack p(client, winId);
  939. #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
  940. /* the code below is based on
  941. * https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/
  942. */
  943. // Send "enter" to release application from the console
  944. // This is a hack, but if not used the console doesn't know the application has
  945. // returned. The "enter" key only sent if the console window is in focus.
  946. if (hasConsole && (GetConsoleWindow() == GetForegroundWindow() || SetFocus(GetConsoleWindow()) != nullptr))
  947. {
  948. INPUT ip;
  949. // Set up a generic keyboard event.
  950. ip.type = INPUT_KEYBOARD;
  951. ip.ki.wScan = 0; // hardware scan code for key
  952. ip.ki.time = 0;
  953. ip.ki.dwExtraInfo = 0;
  954. // Send the "Enter" key
  955. ip.ki.wVk = 0x0D; // virtual-key code for the "Enter" key
  956. ip.ki.dwFlags = 0; // 0 for key press
  957. SendInput(1, &ip, sizeof(INPUT));
  958. // Release the "Enter" key
  959. ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
  960. SendInput(1, &ip, sizeof(INPUT));
  961. }
  962. #endif
  963. return 0;
  964. }
  965. // -----------------------------------------------------------------------