DISTRHO Plugin Framework
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.

438 lines
12KB

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