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.

367 lines
10KB

  1. /*
  2. * DISTHRO Plugin Toolkit (DPT)
  3. * Copyright (C) 2012 Filipe Coelho <falktx@gmail.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or 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 license see the GPL.txt file
  16. */
  17. #ifdef DISTRHO_PLUGIN_TARGET_JACK
  18. #include "DistrhoDefines.h"
  19. #if ! DISTRHO_PLUGIN_HAS_UI
  20. # error Standalone JACK mode requires UI
  21. #endif
  22. #include "DistrhoPluginInternal.h"
  23. #include "DistrhoUIInternal.h"
  24. #include <jack/jack.h>
  25. #include <jack/midiport.h>
  26. #include <jack/transport.h>
  27. #include <QtCore/QSettings>
  28. #include <QtGui/QApplication>
  29. #include <QtGui/QMainWindow>
  30. #include <QtGui/QMessageBox>
  31. // -------------------------------------------------
  32. START_NAMESPACE_DISTRHO
  33. class PluginJack : public QMainWindow
  34. {
  35. public:
  36. PluginJack(jack_client_t* client_)
  37. : QMainWindow(nullptr),
  38. widget(this),
  39. settings("DISTRHO", DISTRHO_PLUGIN_NAME),
  40. ui(this, widget.winId(), setParameterCallback, setStateCallback, nullptr, uiNoteCallback, uiResizeCallback),
  41. client(client_)
  42. {
  43. setCentralWidget(&widget);
  44. setFixedSize(ui.getWidth(), ui.getHeight());
  45. setWindowTitle(DISTRHO_PLUGIN_NAME);
  46. if (DISTRHO_PLUGIN_NUM_INPUTS > 0)
  47. {
  48. portsIn = new jack_port_t* [DISTRHO_PLUGIN_NUM_INPUTS];
  49. for (int i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++)
  50. portsIn[i] = jack_port_register(client, d_string("Audio Input ") + d_string(i+1), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  51. }
  52. else
  53. portsIn = nullptr;
  54. if (DISTRHO_PLUGIN_NUM_OUTPUTS > 0)
  55. {
  56. portsOut = new jack_port_t* [DISTRHO_PLUGIN_NUM_OUTPUTS];
  57. for (int i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++)
  58. portsOut[i] = jack_port_register(client, d_string("Audio Output ") + d_string(i+1), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  59. }
  60. else
  61. portsOut = nullptr;
  62. #if DISTRHO_PLUGIN_IS_SYNTH
  63. portMidi = jack_port_register(client, "Midi Input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
  64. #endif
  65. jack_set_process_callback(client, jackProcessCallback, this);
  66. uiTimer = startTimer(30);
  67. // load settings
  68. restoreGeometry(settings.value("Global/Geometry", QByteArray()).toByteArray());
  69. for (uint32_t i=0; i < plugin.parameterCount(); i++)
  70. {
  71. bool ok;
  72. float value = settings.value(QString("Parameters/%1").arg(i)).toFloat(&ok);
  73. if (ok)
  74. {
  75. plugin.setParameterValue(i, value);
  76. ui.parameterChanged(i, value);
  77. }
  78. }
  79. #if DISTRHO_PLUGIN_WANT_STATE
  80. for (uint32_t i=0; i < plugin.stateCount(); i++)
  81. {
  82. const char* key = plugin.stateKey(i);
  83. QString stringValue(settings.value(key).toString());
  84. if (! stringValue.isEmpty())
  85. ui.stateChanged(key, stringValue.toUtf8().constData());
  86. }
  87. #endif
  88. }
  89. ~PluginJack()
  90. {
  91. // save settings
  92. settings.setValue("Global/Geometry", saveGeometry());
  93. if (uiTimer)
  94. killTimer(uiTimer);
  95. if (portsIn)
  96. delete[] portsIn;
  97. if (portsOut)
  98. delete[] portsOut;
  99. }
  100. // ---------------------------------------------
  101. protected:
  102. void setParameterValue(uint32_t index, float value)
  103. {
  104. plugin.setParameterValue(index, value);
  105. settings.setValue(QString("Parameters/%1").arg(index), value);
  106. }
  107. #if DISTRHO_PLUGIN_WANT_STATE
  108. void setState(const char* key, const char* value)
  109. {
  110. plugin.setState(key, value);
  111. settings.setValue(key, value);
  112. }
  113. #endif
  114. #if DISTRHO_PLUGIN_IS_SYNTH
  115. void uiNote(bool onOff, uint8_t channel, uint8_t note, uint8_t velocity)
  116. {
  117. // TODO
  118. }
  119. #endif
  120. void uiResize(unsigned int width, unsigned int height)
  121. {
  122. widget.setFixedSize(width, height);
  123. setFixedSize(width, height);
  124. }
  125. int jackProcess(jack_nframes_t nframes)
  126. {
  127. if (nframes <= 1)
  128. return 1;
  129. // Check for updated bufferSize
  130. if (nframes != d_lastBufferSize)
  131. {
  132. d_lastBufferSize = nframes;
  133. plugin.setBufferSize(nframes, true);
  134. }
  135. const float* ins[DISTRHO_PLUGIN_NUM_INPUTS];
  136. float* outs[DISTRHO_PLUGIN_NUM_OUTPUTS];
  137. for (int i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; i++)
  138. ins[i] = (float*)jack_port_get_buffer(portsIn[i], nframes);
  139. for (int i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; i++)
  140. outs[i] = (float*)jack_port_get_buffer(portsOut[i], nframes);
  141. #if DISTRHO_PLUGIN_IS_SYNTH
  142. uint32_t midiEventCount = 0;
  143. void* mIn = jack_port_get_buffer(portMidi, nframes);
  144. // TODO
  145. plugin.run(ins, outs, nframes, midiEventCount, midiEvents);
  146. #else
  147. plugin.run(ins, outs, nframes, 0, nullptr);
  148. #endif
  149. return 0;
  150. }
  151. // ---------------------------------------------
  152. void closeEvent(QCloseEvent* event)
  153. {
  154. QMainWindow::closeEvent(event);
  155. qApp->quit();
  156. }
  157. void timerEvent(QTimerEvent* event)
  158. {
  159. if (event->timerId() == uiTimer)
  160. ui.idle();
  161. QMainWindow::timerEvent(event);
  162. }
  163. // ---------------------------------------------
  164. private:
  165. // Qt4 stuff
  166. int uiTimer;
  167. QWidget widget;
  168. QSettings settings;
  169. PluginInternal plugin;
  170. UIInternal ui;
  171. jack_client_t* const client;
  172. jack_port_t** portsIn;
  173. jack_port_t** portsOut;
  174. #if DISTRHO_PLUGIN_IS_SYNTH
  175. jack_port_t* portMidi;
  176. MidiEvent midiEvents[MAX_MIDI_EVENTS];
  177. #endif
  178. // ---------------------------------------------
  179. // Callbacks
  180. static void setParameterCallback(void* ptr, uint32_t index, float value)
  181. {
  182. PluginJack* _this_ = (PluginJack*)ptr;
  183. assert(_this_);
  184. _this_->setParameterValue(index, value);
  185. }
  186. static void setStateCallback(void* ptr, const char* key, const char* value)
  187. {
  188. #if DISTRHO_PLUGIN_WANT_STATE
  189. PluginJack* _this_ = (PluginJack*)ptr;
  190. assert(_this_);
  191. _this_->setState(key, value);
  192. #else
  193. Q_UNUSED(ptr);
  194. Q_UNUSED(key);
  195. Q_UNUSED(value);
  196. #endif
  197. }
  198. static void uiNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity)
  199. {
  200. #if DISTRHO_PLUGIN_IS_SYNTH
  201. PluginJack* _this_ = (PluginJack*)ptr;
  202. assert(_this_);
  203. _this_->uiNote(onOff, channel, note, velocity);
  204. #else
  205. Q_UNUSED(ptr);
  206. Q_UNUSED(onOff);
  207. Q_UNUSED(channel);
  208. Q_UNUSED(note);
  209. Q_UNUSED(velocity);
  210. #endif
  211. }
  212. static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height)
  213. {
  214. PluginJack* _this_ = (PluginJack*)ptr;
  215. assert(_this_);
  216. _this_->uiResize(width, height);
  217. }
  218. static int jackProcessCallback(jack_nframes_t nframes, void* arg)
  219. {
  220. PluginJack* _this_ = (PluginJack*)arg;
  221. assert(_this_);
  222. return _this_->jackProcess(nframes);
  223. }
  224. };
  225. // -------------------------------------------------
  226. END_NAMESPACE_DISTRHO
  227. // -------------------------------------------------
  228. std::string jack_status_get_error_string(const jack_status_t& status)
  229. {
  230. std::string errorString;
  231. if (status & JackFailure)
  232. errorString += "Overall operation failed;\n";
  233. if (status & JackInvalidOption)
  234. errorString += "The operation contained an invalid or unsupported option;\n";
  235. if (status & JackNameNotUnique)
  236. errorString += "The desired client name was not unique;\n";
  237. if (status & JackServerStarted)
  238. errorString += "The JACK server was started as a result of this operation;\n";
  239. if (status & JackServerFailed)
  240. errorString += "Unable to connect to the JACK server;\n";
  241. if (status & JackServerError)
  242. errorString += "Communication error with the JACK server;\n";
  243. if (status & JackNoSuchClient)
  244. errorString += "Requested client does not exist;\n";
  245. if (status & JackLoadFailure)
  246. errorString += "Unable to load internal client;\n";
  247. if (status & JackInitFailure)
  248. errorString += "Unable to initialize client;\n";
  249. if (status & JackShmFailure)
  250. errorString += "Unable to access shared memory;\n";
  251. if (status & JackVersionError)
  252. errorString += "Client's protocol version does not match;\n";
  253. if (status & JackBackendError)
  254. errorString += "Backend Error;\n";
  255. if (status & JackClientZombie)
  256. errorString += "Client is being shutdown against its will;\n";
  257. if (errorString.size() > 0)
  258. errorString.replace(errorString.size()-2, 2, ".");
  259. return errorString;
  260. }
  261. int main(int argc, char* argv[])
  262. {
  263. USE_NAMESPACE_DISTRHO
  264. QApplication app(argc, argv, true);
  265. jack_status_t status;
  266. jack_client_t* client = jack_client_open(DISTRHO_PLUGIN_NAME, JackNullOption, &status);
  267. if (! client)
  268. {
  269. std::string errorString(jack_status_get_error_string(status));
  270. QMessageBox::critical(nullptr, app.translate(DISTRHO_PLUGIN_NAME, "Error"),
  271. app.translate(DISTRHO_PLUGIN_NAME,
  272. "Could not connect to JACK, possible reasons:\n"
  273. "%1").arg(QString::fromStdString(errorString)));
  274. return 1;
  275. }
  276. d_lastBufferSize = jack_get_buffer_size(client);
  277. d_lastSampleRate = jack_get_sample_rate(client);
  278. setLastUiSampleRate(d_lastSampleRate);
  279. PluginJack plugin(client);
  280. plugin.show();
  281. jack_activate(client);
  282. int ret = app.exec();
  283. jack_deactivate(client);
  284. jack_client_close(client);
  285. return ret;
  286. }
  287. // -------------------------------------------------
  288. #endif // DISTRHO_PLUGIN_TARGET_JACK