Audio plugin host https://kx.studio/carla
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.

531 lines
14KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2016 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 DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI
  18. # undef DISTRHO_PLUGIN_HAS_UI
  19. # define DISTRHO_PLUGIN_HAS_UI 0
  20. #endif
  21. #if DISTRHO_PLUGIN_HAS_UI
  22. # include "DistrhoUIInternal.hpp"
  23. #else
  24. # include "../extra/Sleep.hpp"
  25. #endif
  26. #include "jack/jack.h"
  27. #include "jack/midiport.h"
  28. #include "jack/transport.h"
  29. #ifndef DISTRHO_OS_WINDOWS
  30. # include <signal.h>
  31. #endif
  32. // -----------------------------------------------------------------------
  33. START_NAMESPACE_DISTRHO
  34. #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_STATE
  35. static const setStateFunc setStateCallback = nullptr;
  36. #endif
  37. // -----------------------------------------------------------------------
  38. static volatile bool gCloseSignalReceived = false;
  39. #ifdef DISTRHO_OS_WINDOWS
  40. static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept
  41. {
  42. if (dwCtrlType == CTRL_C_EVENT)
  43. {
  44. gCloseSignalReceived = true;
  45. return TRUE;
  46. }
  47. return FALSE;
  48. }
  49. static void initSignalHandler()
  50. {
  51. SetConsoleCtrlHandler(winSignalHandler, TRUE);
  52. }
  53. #else
  54. static void closeSignalHandler(int) noexcept
  55. {
  56. gCloseSignalReceived = true;
  57. }
  58. static void initSignalHandler()
  59. {
  60. struct sigaction sint;
  61. struct sigaction sterm;
  62. sint.sa_handler = closeSignalHandler;
  63. sint.sa_flags = SA_RESTART;
  64. sint.sa_restorer = nullptr;
  65. sigemptyset(&sint.sa_mask);
  66. sigaction(SIGINT, &sint, nullptr);
  67. sterm.sa_handler = closeSignalHandler;
  68. sterm.sa_flags = SA_RESTART;
  69. sterm.sa_restorer = nullptr;
  70. sigemptyset(&sterm.sa_mask);
  71. sigaction(SIGTERM, &sterm, nullptr);
  72. }
  73. #endif
  74. // -----------------------------------------------------------------------
  75. #if DISTRHO_PLUGIN_HAS_UI
  76. class PluginJack : public IdleCallback
  77. #else
  78. class PluginJack
  79. #endif
  80. {
  81. public:
  82. PluginJack(jack_client_t* const client)
  83. : fPlugin(),
  84. #if DISTRHO_PLUGIN_HAS_UI
  85. fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, fPlugin.getInstancePointer()),
  86. #endif
  87. fClient(client)
  88. {
  89. char strBuf[0xff+1];
  90. strBuf[0xff] = '\0';
  91. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  92. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  93. {
  94. std::snprintf(strBuf, 0xff, "in%i", i+1);
  95. fPortAudioIns[i] = jack_port_register(fClient, strBuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  96. }
  97. #endif
  98. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  99. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  100. {
  101. std::snprintf(strBuf, 0xff, "out%i", i+1);
  102. fPortAudioOuts[i] = jack_port_register(fClient, strBuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  103. }
  104. #endif
  105. #if DISTRHO_PLUGIN_IS_SYNTH
  106. fPortMidiIn = jack_port_register(fClient, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
  107. #endif
  108. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  109. if (fPlugin.getProgramCount() > 0)
  110. {
  111. fPlugin.loadProgram(0);
  112. # if DISTRHO_PLUGIN_HAS_UI
  113. fUI.programLoaded(0);
  114. # endif
  115. }
  116. #endif
  117. if (const uint32_t count = fPlugin.getParameterCount())
  118. {
  119. fLastOutputValues = new float[count];
  120. for (uint32_t i=0; i < count; ++i)
  121. {
  122. if (fPlugin.isParameterOutput(i))
  123. {
  124. fLastOutputValues[i] = fPlugin.getParameterValue(i);
  125. }
  126. else
  127. {
  128. fLastOutputValues[i] = 0.0f;
  129. # if DISTRHO_PLUGIN_HAS_UI
  130. fUI.parameterChanged(i, fPlugin.getParameterValue(i));
  131. # endif
  132. }
  133. }
  134. }
  135. else
  136. {
  137. fLastOutputValues = nullptr;
  138. }
  139. jack_set_buffer_size_callback(fClient, jackBufferSizeCallback, this);
  140. jack_set_sample_rate_callback(fClient, jackSampleRateCallback, this);
  141. jack_set_process_callback(fClient, jackProcessCallback, this);
  142. jack_on_shutdown(fClient, jackShutdownCallback, this);
  143. fPlugin.activate();
  144. jack_activate(fClient);
  145. #if DISTRHO_PLUGIN_HAS_UI
  146. if (const char* const name = jack_get_client_name(fClient))
  147. fUI.setWindowTitle(name);
  148. else
  149. fUI.setWindowTitle(fPlugin.getName());
  150. fUI.exec(this);
  151. #else
  152. while (! gCloseSignalReceived)
  153. d_sleep(1);
  154. #endif
  155. }
  156. ~PluginJack()
  157. {
  158. if (fClient != nullptr)
  159. jack_deactivate(fClient);
  160. if (fLastOutputValues != nullptr)
  161. {
  162. delete[] fLastOutputValues;
  163. fLastOutputValues = nullptr;
  164. }
  165. fPlugin.deactivate();
  166. if (fClient == nullptr)
  167. return;
  168. #if DISTRHO_PLUGIN_IS_SYNTH
  169. jack_port_unregister(fClient, fPortMidiIn);
  170. fPortMidiIn = nullptr;
  171. #endif
  172. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  173. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  174. {
  175. jack_port_unregister(fClient, fPortAudioIns[i]);
  176. fPortAudioIns[i] = nullptr;
  177. }
  178. #endif
  179. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  180. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  181. {
  182. jack_port_unregister(fClient, fPortAudioOuts[i]);
  183. fPortAudioOuts[i] = nullptr;
  184. }
  185. #endif
  186. jack_client_close(fClient);
  187. }
  188. // -------------------------------------------------------------------
  189. protected:
  190. #if DISTRHO_PLUGIN_HAS_UI
  191. void idleCallback() override
  192. {
  193. if (gCloseSignalReceived)
  194. return fUI.quit();
  195. float value;
  196. for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
  197. {
  198. if (! fPlugin.isParameterOutput(i))
  199. continue;
  200. value = fPlugin.getParameterValue(i);
  201. if (fLastOutputValues[i] == value)
  202. continue;
  203. fLastOutputValues[i] = value;
  204. fUI.parameterChanged(i, value);
  205. }
  206. fUI.exec_idle();
  207. }
  208. #endif
  209. void jackBufferSize(const jack_nframes_t nframes)
  210. {
  211. fPlugin.setBufferSize(nframes, true);
  212. }
  213. void jackSampleRate(const jack_nframes_t nframes)
  214. {
  215. fPlugin.setSampleRate(nframes, true);
  216. }
  217. void jackProcess(const jack_nframes_t nframes)
  218. {
  219. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  220. const float* audioIns[DISTRHO_PLUGIN_NUM_INPUTS];
  221. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  222. audioIns[i] = (const float*)jack_port_get_buffer(fPortAudioIns[i], nframes);
  223. #else
  224. static const float** audioIns = nullptr;
  225. #endif
  226. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  227. float* audioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
  228. for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  229. audioOuts[i] = (float*)jack_port_get_buffer(fPortAudioOuts[i], nframes);
  230. #else
  231. static float** audioOuts = nullptr;
  232. #endif
  233. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  234. jack_position_t pos;
  235. fTimePosition.playing = (jack_transport_query(fClient, &pos) == JackTransportRolling);
  236. if (pos.unique_1 == pos.unique_2)
  237. {
  238. fTimePosition.frame = pos.frame;
  239. if (pos.valid & JackTransportBBT)
  240. {
  241. fTimePosition.bbt.valid = true;
  242. fTimePosition.bbt.bar = pos.bar;
  243. fTimePosition.bbt.beat = pos.beat;
  244. fTimePosition.bbt.tick = pos.tick;
  245. fTimePosition.bbt.barStartTick = pos.bar_start_tick;
  246. fTimePosition.bbt.beatsPerBar = pos.beats_per_bar;
  247. fTimePosition.bbt.beatType = pos.beat_type;
  248. fTimePosition.bbt.ticksPerBeat = pos.ticks_per_beat;
  249. fTimePosition.bbt.beatsPerMinute = pos.beats_per_minute;
  250. }
  251. else
  252. fTimePosition.bbt.valid = false;
  253. }
  254. else
  255. {
  256. fTimePosition.bbt.valid = false;
  257. fTimePosition.frame = 0;
  258. }
  259. fPlugin.setTimePosition(fTimePosition);
  260. #endif
  261. #if DISTRHO_PLUGIN_IS_SYNTH
  262. void* const midiBuf = jack_port_get_buffer(fPortMidiIn, nframes);
  263. if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf))
  264. {
  265. uint32_t midiEventCount = 0;
  266. MidiEvent midiEvents[eventCount];
  267. jack_midi_event_t jevent;
  268. for (uint32_t i=0; i < eventCount; ++i)
  269. {
  270. if (jack_midi_event_get(&jevent, midiBuf, i) != 0)
  271. break;
  272. MidiEvent& midiEvent(midiEvents[midiEventCount++]);
  273. midiEvent.frame = jevent.time;
  274. midiEvent.size = jevent.size;
  275. if (midiEvent.size > MidiEvent::kDataSize)
  276. midiEvent.dataExt = jevent.buffer;
  277. else
  278. std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size);
  279. }
  280. fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount);
  281. }
  282. else
  283. {
  284. fPlugin.run(audioIns, audioOuts, nframes, nullptr, 0);
  285. }
  286. #else
  287. fPlugin.run(audioIns, audioOuts, nframes);
  288. #endif
  289. }
  290. void jackShutdown()
  291. {
  292. d_stderr("jack has shutdown, quitting now...");
  293. fClient = nullptr;
  294. #if DISTRHO_PLUGIN_HAS_UI
  295. fUI.quit();
  296. #endif
  297. }
  298. // -------------------------------------------------------------------
  299. void setParameterValue(const uint32_t index, const float value)
  300. {
  301. fPlugin.setParameterValue(index, value);
  302. }
  303. #if DISTRHO_PLUGIN_WANT_STATE
  304. void setState(const char* const key, const char* const value)
  305. {
  306. fPlugin.setState(key, value);
  307. }
  308. #endif
  309. #if DISTRHO_PLUGIN_HAS_UI
  310. void setSize(const uint width, const uint height)
  311. {
  312. fUI.setWindowSize(width, height);
  313. }
  314. #endif
  315. // -------------------------------------------------------------------
  316. private:
  317. PluginExporter fPlugin;
  318. #if DISTRHO_PLUGIN_HAS_UI
  319. UIExporter fUI;
  320. #endif
  321. jack_client_t* fClient;
  322. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  323. jack_port_t* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS];
  324. #endif
  325. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  326. jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
  327. #endif
  328. #if DISTRHO_PLUGIN_IS_SYNTH
  329. jack_port_t* fPortMidiIn;
  330. #endif
  331. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  332. TimePosition fTimePosition;
  333. #endif
  334. // Temporary data
  335. float* fLastOutputValues;
  336. // -------------------------------------------------------------------
  337. // Callbacks
  338. #define uiPtr ((PluginJack*)ptr)
  339. static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr)
  340. {
  341. uiPtr->jackBufferSize(nframes);
  342. return 0;
  343. }
  344. static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr)
  345. {
  346. uiPtr->jackSampleRate(nframes);
  347. return 0;
  348. }
  349. static int jackProcessCallback(jack_nframes_t nframes, void* ptr)
  350. {
  351. uiPtr->jackProcess(nframes);
  352. return 0;
  353. }
  354. static void jackShutdownCallback(void* ptr)
  355. {
  356. uiPtr->jackShutdown();
  357. }
  358. static void setParameterValueCallback(void* ptr, uint32_t index, float value)
  359. {
  360. uiPtr->setParameterValue(index, value);
  361. }
  362. #if DISTRHO_PLUGIN_WANT_STATE
  363. static void setStateCallback(void* ptr, const char* key, const char* value)
  364. {
  365. uiPtr->setState(key, value);
  366. }
  367. #endif
  368. #if DISTRHO_PLUGIN_HAS_UI
  369. static void setSizeCallback(void* ptr, uint width, uint height)
  370. {
  371. uiPtr->setSize(width, height);
  372. }
  373. #endif
  374. #undef uiPtr
  375. };
  376. END_NAMESPACE_DISTRHO
  377. // -----------------------------------------------------------------------
  378. int main()
  379. {
  380. USE_NAMESPACE_DISTRHO;
  381. jack_status_t status = jack_status_t(0x0);
  382. jack_client_t* client = jack_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status);
  383. if (client == nullptr)
  384. {
  385. String errorString;
  386. if (status & JackFailure)
  387. errorString += "Overall operation failed;\n";
  388. if (status & JackInvalidOption)
  389. errorString += "The operation contained an invalid or unsupported option;\n";
  390. if (status & JackNameNotUnique)
  391. errorString += "The desired client name was not unique;\n";
  392. if (status & JackServerStarted)
  393. errorString += "The JACK server was started as a result of this operation;\n";
  394. if (status & JackServerFailed)
  395. errorString += "Unable to connect to the JACK server;\n";
  396. if (status & JackServerError)
  397. errorString += "Communication error with the JACK server;\n";
  398. if (status & JackNoSuchClient)
  399. errorString += "Requested client does not exist;\n";
  400. if (status & JackLoadFailure)
  401. errorString += "Unable to load internal client;\n";
  402. if (status & JackInitFailure)
  403. errorString += "Unable to initialize client;\n";
  404. if (status & JackShmFailure)
  405. errorString += "Unable to access shared memory;\n";
  406. if (status & JackVersionError)
  407. errorString += "Client's protocol version does not match;\n";
  408. if (status & JackBackendError)
  409. errorString += "Backend Error;\n";
  410. if (status & JackClientZombie)
  411. errorString += "Client is being shutdown against its will;\n";
  412. if (errorString.isNotEmpty())
  413. {
  414. errorString[errorString.length()-2] = '.';
  415. d_stderr("Failed to create jack client, reason was:\n%s", errorString.buffer());
  416. }
  417. else
  418. d_stderr("Failed to create jack client, cannot continue!");
  419. return 1;
  420. }
  421. USE_NAMESPACE_DISTRHO;
  422. initSignalHandler();
  423. d_lastBufferSize = jack_get_buffer_size(client);
  424. d_lastSampleRate = jack_get_sample_rate(client);
  425. #if DISTRHO_PLUGIN_HAS_UI
  426. d_lastUiSampleRate = d_lastSampleRate;
  427. #endif
  428. const PluginJack p(client);
  429. return 0;
  430. }
  431. // -----------------------------------------------------------------------