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.

1016 lines
38KB

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