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.

CarlaPluginJSFX.cpp 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. /*
  2. * Carla JSFX Plugin
  3. * Copyright (C) 2021 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 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 GNU General Public License see the doc/GPL.txt file.
  16. */
  17. // TODO(jsfx) graphics section
  18. // TODO(jsfx) set the correct import path
  19. #include "CarlaPluginInternal.hpp"
  20. #include "CarlaEngine.hpp"
  21. #include "CarlaJsfxUtils.hpp"
  22. #include "CarlaBackendUtils.hpp"
  23. #include "CarlaScopeUtils.hpp"
  24. #include "CarlaUtils.hpp"
  25. #include "water/files/File.h"
  26. #include <algorithm>
  27. #include <string>
  28. #include <cstdio>
  29. #include <cstring>
  30. using water::CharPointer_UTF8;
  31. using water::File;
  32. using water::String;
  33. CARLA_BACKEND_START_NAMESPACE
  34. // -------------------------------------------------------------------------------------------------------------------
  35. // Fallback data
  36. static const ExternalMidiNote kExternalMidiNoteFallback = { -1, 0, 0 };
  37. // -------------------------------------------------------------------------------------------------------------------
  38. class CarlaPluginJSFX : public CarlaPlugin
  39. {
  40. public:
  41. CarlaPluginJSFX(CarlaEngine* const engine, const uint id) noexcept
  42. : CarlaPlugin(engine, id)
  43. {
  44. carla_debug("CarlaPluginJSFX::CarlaPluginJSFX(%p, %i)", engine, id);
  45. }
  46. ~CarlaPluginJSFX()
  47. {
  48. carla_debug("CarlaPluginJSFX::~CarlaPluginJSFX()");
  49. pData->singleMutex.lock();
  50. pData->masterMutex.lock();
  51. if (pData->client != nullptr && pData->client->isActive())
  52. pData->client->deactivate(true);
  53. if (pData->active)
  54. {
  55. deactivate();
  56. pData->active = false;
  57. }
  58. if (fEffect != nullptr)
  59. {
  60. delete fEffect;
  61. fEffect = nullptr;
  62. }
  63. if (fFileAPI != nullptr)
  64. {
  65. delete fFileAPI;
  66. fFileAPI = nullptr;
  67. }
  68. if (fPathLibrary != nullptr)
  69. {
  70. delete fPathLibrary;
  71. fPathLibrary = nullptr;
  72. }
  73. clearBuffers();
  74. }
  75. // -------------------------------------------------------------------
  76. // Information (base)
  77. PluginType getType() const noexcept override
  78. {
  79. return PLUGIN_JSFX;
  80. }
  81. // -------------------------------------------------------------------
  82. // Information (count)
  83. uint32_t getMidiInCount() const noexcept override
  84. {
  85. return 1;
  86. }
  87. uint32_t getMidiOutCount() const noexcept override
  88. {
  89. return 1;
  90. }
  91. uint32_t getParameterScalePointCount(const uint32_t parameterId) const noexcept override
  92. {
  93. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, 0);
  94. const JsusFx_Slider& slider = fEffect->sliders[pData->param.data[parameterId].rindex];
  95. return (uint32_t)slider.enumNames.size();
  96. }
  97. // -------------------------------------------------------------------
  98. // Information (current data)
  99. std::size_t getChunkData(void** dataPtr) noexcept override
  100. {
  101. CARLA_SAFE_ASSERT_RETURN(pData->options & PLUGIN_OPTION_USE_CHUNKS, 0);
  102. CARLA_SAFE_ASSERT_RETURN(dataPtr != nullptr, 0);
  103. JsusFxSerializationData fxData;
  104. CarlaJsusFxSerializer serializer(fxData);
  105. CARLA_SAFE_ASSERT_RETURN(fEffect->serialize(serializer, true), 0);
  106. fChunkText = serializer.convertDataToString(fxData);
  107. *dataPtr = (void*)fChunkText.toRawUTF8();
  108. return (std::size_t)fChunkText.getNumBytesAsUTF8();
  109. }
  110. // -------------------------------------------------------------------
  111. // Information (per-plugin data)
  112. uint getOptionsAvailable() const noexcept override
  113. {
  114. uint options = 0x0;
  115. options |= PLUGIN_OPTION_USE_CHUNKS;
  116. options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
  117. options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
  118. options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
  119. options |= PLUGIN_OPTION_SEND_PITCHBEND;
  120. options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
  121. options |= PLUGIN_OPTION_SEND_PROGRAM_CHANGES;
  122. options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
  123. return options;
  124. }
  125. float getParameterValue(const uint32_t parameterId) const noexcept override
  126. {
  127. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, 0.0f);
  128. const JsusFx_Slider& slider = fEffect->sliders[pData->param.data[parameterId].rindex];
  129. return slider.getValue();
  130. }
  131. bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
  132. {
  133. CARLA_SAFE_ASSERT_RETURN(fEffect != nullptr, false);
  134. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
  135. const JsusFx_Slider& slider = fEffect->sliders[pData->param.data[parameterId].rindex];
  136. std::snprintf(strBuf, STR_MAX, "%s", slider.desc);
  137. return true;
  138. }
  139. bool getParameterText(const uint32_t parameterId, char* const strBuf) noexcept override
  140. {
  141. CARLA_SAFE_ASSERT_RETURN(fEffect != nullptr, false);
  142. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
  143. const JsusFx_Slider& slider = fEffect->sliders[pData->param.data[parameterId].rindex];
  144. float value = getParameterValue(parameterId);
  145. int intValue = (int)value;
  146. if (intValue >= 0 && (size_t)intValue < slider.enumNames.size())
  147. std::snprintf(strBuf, STR_MAX, "%s", slider.enumNames[(size_t)intValue].c_str());
  148. else
  149. std::snprintf(strBuf, STR_MAX, "%.12g", value);
  150. return true;
  151. }
  152. float getParameterScalePointValue(const uint32_t parameterId, const uint32_t scalePointId) const noexcept override
  153. {
  154. CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), 0.0f);
  155. CARLA_SAFE_ASSERT_RETURN(scalePointId < getParameterScalePointCount(parameterId), 0.0f);
  156. return (float)scalePointId;
  157. }
  158. bool getParameterScalePointLabel(const uint32_t parameterId, const uint32_t scalePointId, char* const strBuf) const noexcept override
  159. {
  160. CARLA_SAFE_ASSERT_RETURN(parameterId < getParameterCount(), false);
  161. const JsusFx_Slider& slider = fEffect->sliders[pData->param.data[parameterId].rindex];
  162. CARLA_SAFE_ASSERT_RETURN(scalePointId < slider.enumNames.size(), false);
  163. std::snprintf(strBuf, STR_MAX, "%s", slider.enumNames[(size_t)scalePointId].c_str());
  164. return true;
  165. }
  166. bool getLabel(char* const strBuf) const noexcept override
  167. {
  168. std::strncpy(strBuf, fFilename.c_str(), STR_MAX);
  169. return true;
  170. }
  171. // -------------------------------------------------------------------
  172. // Set data (plugin-specific stuff)
  173. void setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept override
  174. {
  175. CARLA_SAFE_ASSERT_RETURN(fEffect != nullptr,);
  176. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
  177. int32_t sliderIndex = pData->param.data[parameterId].rindex;
  178. fEffect->moveSlider(sliderIndex, value);
  179. const JsusFx_Slider& slider = fEffect->sliders[sliderIndex];
  180. float newValue = slider.getValue();
  181. CarlaPlugin::setParameterValue(parameterId, newValue, sendGui, sendOsc, sendCallback);
  182. }
  183. void setParameterValueRT(const uint32_t parameterId, const float value, const bool sendCallbackLater) noexcept override
  184. {
  185. CARLA_SAFE_ASSERT_RETURN(fEffect != nullptr,);
  186. CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
  187. int32_t sliderIndex = pData->param.data[parameterId].rindex;
  188. fEffect->moveSlider(sliderIndex, value);
  189. const JsusFx_Slider& slider = fEffect->sliders[sliderIndex];
  190. float newValue = slider.getValue();
  191. CarlaPlugin::setParameterValueRT(parameterId, newValue, sendCallbackLater);
  192. }
  193. void setChunkData(const void* data, std::size_t dataSize) override
  194. {
  195. CARLA_SAFE_ASSERT_RETURN(pData->options & PLUGIN_OPTION_USE_CHUNKS,);
  196. water::String dataText(water::CharPointer_UTF8((const char*)data), dataSize);
  197. JsusFxSerializationData fxData;
  198. CARLA_SAFE_ASSERT_RETURN(CarlaJsusFxSerializer::convertStringToData(dataText, fxData),);
  199. CarlaJsusFxSerializer serializer(fxData);
  200. CARLA_SAFE_ASSERT_RETURN(fEffect->serialize(serializer, false),);
  201. }
  202. // -------------------------------------------------------------------
  203. // Plugin state
  204. void reload() override
  205. {
  206. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
  207. CARLA_SAFE_ASSERT_RETURN(fEffect != nullptr,);
  208. carla_debug("CarlaPluginJSFX::reload()");
  209. const EngineProcessMode processMode(pData->engine->getProccessMode());
  210. // Safely disable plugin for reload
  211. const ScopedDisabler sd(this);
  212. if (pData->active)
  213. deactivate();
  214. clearBuffers();
  215. // ---------------------------------------------------------------
  216. // TODO(jsfx) uncomment when implementing these features
  217. const int compileFlags = 0
  218. //| JsusFx::kCompileFlag_CompileGraphicsSection
  219. | JsusFx::kCompileFlag_CompileSerializeSection
  220. ;
  221. {
  222. const CarlaScopedLocale csl;
  223. if (!fEffect->compile(*fPathLibrary, fFilename, compileFlags))
  224. carla_stderr("Failed to compile JSFX");
  225. }
  226. // initialize the block size and sample rate
  227. // loading the chunk can invoke @slider which makes computations based on these
  228. fEffect->prepare(pData->engine->getSampleRate(), pData->engine->getBufferSize());
  229. // NOTE: count can be -1 in case of "none"
  230. uint32_t aIns = (fEffect->numInputs == -1) ? 0 : (uint32_t)fEffect->numInputs;
  231. uint32_t aOuts = (fEffect->numOutputs == -1) ? 0 : (uint32_t)fEffect->numOutputs;
  232. if (aIns > 0)
  233. {
  234. pData->audioIn.createNew(aIns);
  235. }
  236. if (aOuts > 0)
  237. {
  238. pData->audioOut.createNew(aOuts);
  239. }
  240. uint32_t params = 0;
  241. uint32_t mapOfParameterToSlider[JsusFx::kMaxSliders];
  242. for (uint32_t sliderIndex = 0; sliderIndex < JsusFx::kMaxSliders; ++sliderIndex)
  243. {
  244. JsusFx_Slider &slider = fEffect->sliders[sliderIndex];
  245. if (slider.exists)
  246. {
  247. mapOfParameterToSlider[params] = sliderIndex;
  248. ++params;
  249. }
  250. }
  251. if (params > 0)
  252. {
  253. pData->param.createNew(params, false);
  254. }
  255. const uint portNameSize(pData->engine->getMaxPortNameSize());
  256. CarlaString portName;
  257. // Audio Ins
  258. for (uint32_t j = 0; j < aIns; ++j)
  259. {
  260. portName.clear();
  261. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  262. {
  263. portName = pData->name;
  264. portName += ":";
  265. }
  266. if (j < fEffect->inputNames.size() && !fEffect->inputNames[j].empty())
  267. {
  268. portName += fEffect->inputNames[j].c_str();
  269. }
  270. else if (aIns > 1)
  271. {
  272. portName += "input_";
  273. portName += CarlaString(j+1);
  274. }
  275. else
  276. portName += "input";
  277. portName.truncate(portNameSize);
  278. pData->audioIn.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true, j);
  279. pData->audioIn.ports[j].rindex = j;
  280. }
  281. // Audio Outs
  282. for (uint32_t j = 0; j < aOuts; ++j)
  283. {
  284. portName.clear();
  285. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  286. {
  287. portName = pData->name;
  288. portName += ":";
  289. }
  290. if (j < fEffect->outputNames.size() && !fEffect->outputNames[j].empty())
  291. {
  292. portName += fEffect->outputNames[j].c_str();
  293. }
  294. else if (aOuts > 1)
  295. {
  296. portName += "output_";
  297. portName += CarlaString(j+1);
  298. }
  299. else
  300. portName += "output";
  301. portName.truncate(portNameSize);
  302. pData->audioOut.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, j);
  303. pData->audioOut.ports[j].rindex = j;
  304. }
  305. // Parameters
  306. for (uint32_t j = 0; j < params; ++j)
  307. {
  308. pData->param.data[j].type = PARAMETER_INPUT;
  309. pData->param.data[j].index = (int32_t)j;
  310. pData->param.data[j].rindex = (int32_t)mapOfParameterToSlider[j];
  311. const JsusFx_Slider &slider = fEffect->sliders[mapOfParameterToSlider[j]];
  312. float min = slider.min;
  313. float max = slider.max;
  314. float def = slider.def;
  315. float step = slider.inc;
  316. // only use values as integer if we have a proper range
  317. bool isEnum = slider.isEnum && min == 0.0f && max >= 0.0f &&
  318. max + 1.0f == (float)slider.enumNames.size();
  319. if (min > max)
  320. std::swap(min, max);
  321. if (def < min)
  322. def = min;
  323. else if (def > max)
  324. def = max;
  325. float stepSmall;
  326. float stepLarge;
  327. if (isEnum)
  328. {
  329. step = 1.0f;
  330. stepSmall = 1.0f;
  331. stepLarge = 10.0f;
  332. }
  333. else
  334. {
  335. stepSmall = step/10.0f;
  336. stepLarge = step*10.0f;
  337. }
  338. pData->param.data[j].hints |= PARAMETER_IS_ENABLED;
  339. if (isEnum)
  340. {
  341. pData->param.data[j].hints |= PARAMETER_IS_INTEGER;
  342. pData->param.data[j].hints |= PARAMETER_USES_SCALEPOINTS;
  343. pData->param.data[j].hints |= PARAMETER_USES_CUSTOM_TEXT;
  344. }
  345. else
  346. {
  347. pData->param.data[j].hints |= PARAMETER_CAN_BE_CV_CONTROLLED;
  348. }
  349. pData->param.ranges[j].min = min;
  350. pData->param.ranges[j].max = max;
  351. pData->param.ranges[j].def = def;
  352. pData->param.ranges[j].step = step;
  353. pData->param.ranges[j].stepSmall = stepSmall;
  354. pData->param.ranges[j].stepLarge = stepLarge;
  355. }
  356. //if (needsCtrlIn)
  357. {
  358. portName.clear();
  359. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  360. {
  361. portName = pData->name;
  362. portName += ":";
  363. }
  364. portName += "events-in";
  365. portName.truncate(portNameSize);
  366. pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0);
  367. }
  368. //if (needsCtrlOut)
  369. {
  370. portName.clear();
  371. if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
  372. {
  373. portName = pData->name;
  374. portName += ":";
  375. }
  376. portName += "events-out";
  377. portName.truncate(portNameSize);
  378. pData->event.portOut = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, false, 0);
  379. }
  380. }
  381. // -------------------------------------------------------------------
  382. // Plugin processing
  383. void activate() noexcept override
  384. {
  385. CARLA_SAFE_ASSERT_RETURN(fEffect,);
  386. const double sampleRate = pData->engine->getSampleRate();
  387. const uint32_t bufferSize = pData->engine->getBufferSize();
  388. fEffect->prepare((int)sampleRate, (int)bufferSize);
  389. fTransportValues = TransportValues();
  390. }
  391. void process(const float* const* const audioIn, float** const audioOut,
  392. const float* const* const, float**,
  393. const uint32_t frames) override
  394. {
  395. CARLA_SAFE_ASSERT_RETURN(fEffect,);
  396. // --------------------------------------------------------------------------------------------------------
  397. // Set TimeInfo
  398. const EngineTimeInfo timeInfo = pData->engine->getTimeInfo();
  399. const EngineTimeInfoBBT& bbt = timeInfo.bbt;
  400. fTransportValues.playbackState = timeInfo.playing ?
  401. JsusFx::kPlaybackState_Playing : JsusFx::kPlaybackState_Paused;
  402. fTransportValues.playbackPositionInSeconds = 1e-6*double(timeInfo.usecs);
  403. if (bbt.valid)
  404. {
  405. const double samplePos = double(timeInfo.frame);
  406. const double sampleRate = pData->engine->getSampleRate();
  407. fTransportValues.tempo = bbt.beatsPerMinute;
  408. fTransportValues.beatPosition = samplePos / (sampleRate * 60 / bbt.beatsPerMinute);
  409. fTransportValues.timeSignatureNumerator = int(bbt.beatsPerBar);
  410. fTransportValues.timeSignatureDenumerator = int(bbt.beatType);
  411. }
  412. fEffect->setTransportValues(
  413. fTransportValues.tempo,
  414. fTransportValues.playbackState,
  415. fTransportValues.playbackPositionInSeconds,
  416. fTransportValues.beatPosition,
  417. fTransportValues.timeSignatureNumerator,
  418. fTransportValues.timeSignatureDenumerator);
  419. // --------------------------------------------------------------------------------------------------------
  420. // Event Input and Processing
  421. if (pData->event.portIn != nullptr)
  422. {
  423. // ----------------------------------------------------------------------------------------------------
  424. // MIDI Input (External)
  425. if (pData->extNotes.mutex.tryLock())
  426. {
  427. for (RtLinkedList<ExternalMidiNote>::Itenerator it = pData->extNotes.data.begin2(); it.valid(); it.next())
  428. {
  429. const ExternalMidiNote& note(it.getValue(kExternalMidiNoteFallback));
  430. CARLA_SAFE_ASSERT_CONTINUE(note.channel >= 0 && note.channel < MAX_MIDI_CHANNELS);
  431. uint8_t midiEvent[3];
  432. midiEvent[0] = uint8_t((note.velo > 0 ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF) | (note.channel & MIDI_CHANNEL_BIT));
  433. midiEvent[1] = note.note;
  434. midiEvent[2] = note.velo;
  435. fEffect->addInputEvent(0, midiEvent, 3);
  436. }
  437. pData->extNotes.data.clear();
  438. pData->extNotes.mutex.unlock();
  439. } // End of MIDI Input (External)
  440. // ----------------------------------------------------------------------------------------------------
  441. // Event Input (System)
  442. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  443. bool allNotesOffSent = false;
  444. #endif
  445. for (uint32_t i=0, numEvents=pData->event.portIn->getEventCount(); i < numEvents; ++i)
  446. {
  447. EngineEvent& event(pData->event.portIn->getEvent(i));
  448. if (event.time >= frames)
  449. continue;
  450. switch (event.type)
  451. {
  452. case kEngineEventTypeNull:
  453. break;
  454. case kEngineEventTypeControl: {
  455. EngineControlEvent& ctrlEvent(event.ctrl);
  456. switch (ctrlEvent.type)
  457. {
  458. case kEngineControlEventTypeNull:
  459. break;
  460. case kEngineControlEventTypeParameter: {
  461. float value;
  462. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  463. // non-midi
  464. if (event.channel == kEngineEventNonMidiChannel)
  465. {
  466. const uint32_t k = ctrlEvent.param;
  467. CARLA_SAFE_ASSERT_CONTINUE(k < pData->param.count);
  468. ctrlEvent.handled = true;
  469. value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.normalizedValue);
  470. setParameterValueRT(k, value, true);
  471. continue;
  472. }
  473. // Control backend stuff
  474. if (event.channel == pData->ctrlChannel)
  475. {
  476. if (MIDI_IS_CONTROL_BREATH_CONTROLLER(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_DRYWET) != 0)
  477. {
  478. ctrlEvent.handled = true;
  479. value = ctrlEvent.normalizedValue;
  480. setDryWetRT(value, true);
  481. }
  482. else if (MIDI_IS_CONTROL_CHANNEL_VOLUME(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_VOLUME) != 0)
  483. {
  484. ctrlEvent.handled = true;
  485. value = ctrlEvent.normalizedValue*127.0f/100.0f;
  486. setVolumeRT(value, true);
  487. }
  488. else if (MIDI_IS_CONTROL_BALANCE(ctrlEvent.param) && (pData->hints & PLUGIN_CAN_BALANCE) != 0)
  489. {
  490. float left, right;
  491. value = ctrlEvent.normalizedValue/0.5f - 1.0f;
  492. if (value < 0.0f)
  493. {
  494. left = -1.0f;
  495. right = (value*2.0f)+1.0f;
  496. }
  497. else if (value > 0.0f)
  498. {
  499. left = (value*2.0f)-1.0f;
  500. right = 1.0f;
  501. }
  502. else
  503. {
  504. left = -1.0f;
  505. right = 1.0f;
  506. }
  507. ctrlEvent.handled = true;
  508. setBalanceLeftRT(left, true);
  509. setBalanceRightRT(right, true);
  510. }
  511. }
  512. #endif
  513. // Control plugin parameters
  514. uint32_t k;
  515. for (k=0; k < pData->param.count; ++k)
  516. {
  517. if (pData->param.data[k].midiChannel != event.channel)
  518. continue;
  519. if (pData->param.data[k].mappedControlIndex != ctrlEvent.param)
  520. continue;
  521. if (pData->param.data[k].type != PARAMETER_INPUT)
  522. continue;
  523. if ((pData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0)
  524. continue;
  525. ctrlEvent.handled = true;
  526. value = pData->param.getFinalUnnormalizedValue(k, ctrlEvent.normalizedValue);
  527. setParameterValueRT(k, value, true);
  528. }
  529. if ((pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) != 0 && ctrlEvent.param < MAX_MIDI_VALUE)
  530. {
  531. uint8_t midiData[3];
  532. midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
  533. midiData[1] = uint8_t(ctrlEvent.param);
  534. midiData[2] = uint8_t(ctrlEvent.normalizedValue*127.0f);
  535. fEffect->addInputEvent(static_cast<int>(event.time), midiData, 3);
  536. }
  537. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  538. if (! ctrlEvent.handled)
  539. checkForMidiLearn(event);
  540. #endif
  541. break;
  542. } // case kEngineControlEventTypeParameter
  543. case kEngineControlEventTypeMidiBank:
  544. if ((pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES) != 0)
  545. {
  546. uint8_t midiData[3];
  547. midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
  548. midiData[1] = MIDI_CONTROL_BANK_SELECT;
  549. midiData[2] = 0;
  550. fEffect->addInputEvent(static_cast<int>(event.time), midiData, 3);
  551. midiData[1] = MIDI_CONTROL_BANK_SELECT__LSB;
  552. midiData[2] = uint8_t(ctrlEvent.normalizedValue*127.0f);
  553. fEffect->addInputEvent(static_cast<int>(event.time), midiData, 3);
  554. }
  555. break;
  556. case kEngineControlEventTypeMidiProgram:
  557. if (event.channel == pData->ctrlChannel && (pData->options & PLUGIN_OPTION_MAP_PROGRAM_CHANGES) != 0)
  558. {
  559. if (ctrlEvent.param < pData->prog.count)
  560. {
  561. setProgramRT(ctrlEvent.param, true);
  562. }
  563. }
  564. else if ((pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES) != 0)
  565. {
  566. uint8_t midiData[3];
  567. midiData[0] = uint8_t(MIDI_STATUS_PROGRAM_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
  568. midiData[1] = uint8_t(ctrlEvent.normalizedValue*127.0f);
  569. fEffect->addInputEvent(static_cast<int>(event.time), midiData, 2);
  570. }
  571. break;
  572. case kEngineControlEventTypeAllSoundOff:
  573. if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
  574. {
  575. uint8_t midiData[3];
  576. midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
  577. midiData[1] = MIDI_CONTROL_ALL_SOUND_OFF;
  578. midiData[2] = 0;
  579. fEffect->addInputEvent(static_cast<int>(event.time), midiData, 3);
  580. }
  581. break;
  582. case kEngineControlEventTypeAllNotesOff:
  583. if (pData->options & PLUGIN_OPTION_SEND_ALL_SOUND_OFF)
  584. {
  585. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  586. if (event.channel == pData->ctrlChannel && ! allNotesOffSent)
  587. {
  588. allNotesOffSent = true;
  589. postponeRtAllNotesOff();
  590. }
  591. #endif
  592. uint8_t midiData[3];
  593. midiData[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (event.channel & MIDI_CHANNEL_BIT));
  594. midiData[1] = MIDI_CONTROL_ALL_NOTES_OFF;
  595. midiData[2] = 0;
  596. fEffect->addInputEvent(static_cast<int>(event.time), midiData, 3);
  597. }
  598. break;
  599. } // switch (ctrlEvent.type)
  600. break;
  601. } // case kEngineEventTypeControl
  602. case kEngineEventTypeMidi: {
  603. const EngineMidiEvent& midiEvent(event.midi);
  604. const uint8_t* const midiData(midiEvent.size > EngineMidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data);
  605. uint8_t status = uint8_t(MIDI_GET_STATUS_FROM_DATA(midiData));
  606. if ((status == MIDI_STATUS_NOTE_OFF || status == MIDI_STATUS_NOTE_ON) && (pData->options & PLUGIN_OPTION_SKIP_SENDING_NOTES))
  607. continue;
  608. if (status == MIDI_STATUS_CHANNEL_PRESSURE && (pData->options & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE) == 0)
  609. continue;
  610. if (status == MIDI_STATUS_CONTROL_CHANGE && (pData->options & PLUGIN_OPTION_SEND_CONTROL_CHANGES) == 0)
  611. continue;
  612. if (status == MIDI_STATUS_POLYPHONIC_AFTERTOUCH && (pData->options & PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH) == 0)
  613. continue;
  614. if (status == MIDI_STATUS_PITCH_WHEEL_CONTROL && (pData->options & PLUGIN_OPTION_SEND_PITCHBEND) == 0)
  615. continue;
  616. // Fix bad note-off
  617. if (status == MIDI_STATUS_NOTE_ON && midiData[2] == 0)
  618. status = MIDI_STATUS_NOTE_OFF;
  619. // put back channel in data
  620. uint8_t midiData2[midiEvent.size];
  621. midiData2[0] = uint8_t(status | (event.channel & MIDI_CHANNEL_BIT));
  622. std::memcpy(midiData2+1, midiData+1, static_cast<std::size_t>(midiEvent.size-1));
  623. fEffect->addInputEvent(static_cast<int>(event.time), midiData2, midiEvent.size);
  624. if (status == MIDI_STATUS_NOTE_ON)
  625. {
  626. pData->postponeNoteOnRtEvent(true, event.channel, midiData[1], midiData[2]);
  627. }
  628. else if (status == MIDI_STATUS_NOTE_OFF)
  629. {
  630. pData->postponeNoteOffRtEvent(true, event.channel, midiData[1]);
  631. }
  632. } break;
  633. } // switch (event.type)
  634. }
  635. pData->postRtEvents.trySplice();
  636. } // End of Event Input and Processing
  637. // --------------------------------------------------------------------------------------------------------
  638. // Plugin processing
  639. fEffect->process((const float**)audioIn, audioOut, (int)frames, fEffect->numInputs, fEffect->numOutputs);
  640. // End of Plugin processing (no events)
  641. // --------------------------------------------------------------------------------------------------------
  642. // MIDI Output
  643. if (pData->event.portOut != nullptr)
  644. {
  645. size_t iterPos = 0;
  646. int samplePosition;
  647. const uint8_t *data;
  648. int numBytes;
  649. while (fEffect->iterateOutputEvents(iterPos, samplePosition, data, numBytes))
  650. {
  651. CARLA_SAFE_ASSERT_BREAK(samplePosition >= 0);
  652. CARLA_SAFE_ASSERT_BREAK(samplePosition < static_cast<int>(frames));
  653. CARLA_SAFE_ASSERT_BREAK(numBytes > 0);
  654. CARLA_SAFE_ASSERT_CONTINUE(numBytes <= 0xff);
  655. if (! pData->event.portOut->writeMidiEvent(static_cast<uint32_t>(samplePosition),
  656. static_cast<uint8_t>(numBytes),
  657. data))
  658. break;
  659. }
  660. } // End of MIDI Output
  661. }
  662. // -------------------------------------------------------------------
  663. bool initJSFX(const CarlaPluginPtr plugin,
  664. const char* const filename, const char* name, const char* const label, const uint options)
  665. {
  666. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
  667. // ---------------------------------------------------------------
  668. // first checks
  669. if (pData->client != nullptr)
  670. {
  671. pData->engine->setLastError("Plugin client is already registered");
  672. return false;
  673. }
  674. if (filename == nullptr || filename[0] == '\0')
  675. {
  676. pData->engine->setLastError("null filename");
  677. return false;
  678. }
  679. // ---------------------------------------------------------------
  680. fPathLibrary = new CarlaJsusFxPathLibrary(String(CharPointer_UTF8(filename)));
  681. fFileAPI = new CarlaJsusFxFileAPI;
  682. fEffect = new CarlaJsusFx(*fPathLibrary);
  683. fEffect->fileAPI = fFileAPI;
  684. fFileAPI->init(fEffect->m_vm);
  685. fFilename.assign(filename);
  686. // ---------------------------------------------------------------
  687. // get info
  688. {
  689. const CarlaScopedLocale csl;
  690. if (!fEffect->readHeader(*fPathLibrary, filename))
  691. {
  692. pData->engine->setLastError("Cannot read the JSFX header");
  693. return false;
  694. }
  695. }
  696. // jsusfx lacks validity checks currently,
  697. // ensure we have at least the description (required)
  698. if (fEffect->desc[0] == '\0')
  699. {
  700. pData->engine->setLastError("The JSFX header is invalid");
  701. return false;
  702. }
  703. if (name != nullptr && name[0] != '\0')
  704. {
  705. pData->name = pData->engine->getUniquePluginName(name);
  706. }
  707. else
  708. {
  709. String baseName = File(filename).getFileNameWithoutExtension();
  710. pData->name = pData->engine->getUniquePluginName(baseName.toRawUTF8());
  711. }
  712. pData->filename = carla_strdup(filename);
  713. // ---------------------------------------------------------------
  714. // register client
  715. pData->client = pData->engine->addClient(plugin);
  716. if (pData->client == nullptr || ! pData->client->isOk())
  717. {
  718. pData->engine->setLastError("Failed to register plugin client");
  719. return false;
  720. }
  721. // ---------------------------------------------------------------
  722. // set options
  723. pData->options = 0x0;
  724. if (isPluginOptionEnabled(options, PLUGIN_OPTION_USE_CHUNKS))
  725. pData->options |= PLUGIN_OPTION_USE_CHUNKS;
  726. if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CONTROL_CHANGES))
  727. pData->options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
  728. if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CHANNEL_PRESSURE))
  729. pData->options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
  730. if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PITCHBEND))
  731. pData->options |= PLUGIN_OPTION_SEND_PITCHBEND;
  732. if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_ALL_SOUND_OFF))
  733. pData->options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
  734. if (isPluginOptionEnabled(options, PLUGIN_OPTION_MAP_PROGRAM_CHANGES))
  735. pData->options |= PLUGIN_OPTION_MAP_PROGRAM_CHANGES;
  736. if (isPluginOptionInverseEnabled(options, PLUGIN_OPTION_SKIP_SENDING_NOTES))
  737. pData->options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
  738. if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH))
  739. pData->options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
  740. return true;
  741. }
  742. private:
  743. CarlaJsusFxPathLibrary* fPathLibrary = nullptr;
  744. CarlaJsusFxFileAPI* fFileAPI = nullptr;
  745. CarlaJsusFx* fEffect = nullptr;
  746. std::string fFilename;
  747. water::String fChunkText;
  748. struct TransportValues
  749. {
  750. double tempo = 120;
  751. JsusFx::PlaybackState playbackState = JsusFx::kPlaybackState_Paused;
  752. double playbackPositionInSeconds = 0;
  753. double beatPosition = 0;
  754. int timeSignatureNumerator = 4;
  755. int timeSignatureDenumerator = 4;
  756. };
  757. TransportValues fTransportValues;
  758. };
  759. // -------------------------------------------------------------------------------------------------------------------
  760. CarlaPluginPtr CarlaPlugin::newJSFX(const Initializer& init)
  761. {
  762. carla_debug("CarlaPlugin::newJSFX({%p, \"%s\", \"%s\", \"%s\", " P_INT64 "})",
  763. init.engine, init.filename, init.name, init.label, init.uniqueId);
  764. JsusFx::init();
  765. std::shared_ptr<CarlaPluginJSFX> plugin(new CarlaPluginJSFX(init.engine, init.id));
  766. if (!plugin->initJSFX(plugin, init.filename, init.name, init.label, init.options))
  767. return nullptr;
  768. return plugin;
  769. }
  770. // -------------------------------------------------------------------------------------------------------------------
  771. CARLA_BACKEND_END_NAMESPACE