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.

1074 lines
32KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2012-2015 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. #include "CarlaNativeExtUI.hpp"
  18. #include "CarlaMIDI.h"
  19. #include "CarlaThread.hpp"
  20. #include "LinkedList.hpp"
  21. #include "CarlaMathUtils.hpp"
  22. #include "Misc/Master.h"
  23. #include "Misc/MiddleWare.h"
  24. #include "Misc/Part.h"
  25. #include "Misc/Util.h"
  26. #include <ctime>
  27. #include <set>
  28. #include <string>
  29. #include "juce_audio_basics/juce_audio_basics.h"
  30. using juce::roundToIntAccurate;
  31. using juce::FloatVectorOperations;
  32. using juce::ScopedPointer;
  33. // #define ZYN_MSG_ANYWHERE
  34. // -----------------------------------------------------------------------
  35. class ZynAddSubFxPrograms
  36. {
  37. public:
  38. ZynAddSubFxPrograms() noexcept
  39. : fInitiated(false),
  40. #ifdef CARLA_PROPER_CPP11_SUPPORT
  41. fRetProgram({0, 0, nullptr}),
  42. #endif
  43. fProgramCount(0),
  44. fPrograms(nullptr) {}
  45. ~ZynAddSubFxPrograms() noexcept
  46. {
  47. if (! fInitiated)
  48. return;
  49. for (uint32_t i=0; i<fProgramCount; ++i)
  50. delete fPrograms[i];
  51. delete[] fPrograms;
  52. }
  53. void initIfNeeded()
  54. {
  55. if (fInitiated)
  56. return;
  57. fInitiated = true;
  58. std::vector<const ProgramInfo*> programs;
  59. programs.push_back(new ProgramInfo(0, 0, "default", ""));
  60. Config config;
  61. config.init();
  62. SYNTH_T synth;
  63. Master master(synth, &config);
  64. // refresh banks
  65. master.bank.rescanforbanks();
  66. for (std::size_t i=0, size=master.bank.banks.size(); i<size; ++i)
  67. {
  68. const std::string dir(master.bank.banks[i].dir);
  69. if (dir.empty())
  70. continue;
  71. master.bank.loadbank(dir);
  72. for (uint ninstrument = 0; ninstrument < BANK_SIZE; ++ninstrument)
  73. {
  74. const Bank::ins_t& instrument(master.bank.ins[ninstrument]);
  75. if (instrument.name.empty() || instrument.name[0] == ' ')
  76. continue;
  77. programs.push_back(new ProgramInfo(i+1, ninstrument, instrument.name.c_str(), instrument.filename.c_str()));
  78. }
  79. }
  80. fPrograms = new const ProgramInfo*[programs.size()];
  81. for (const ProgramInfo* p : programs)
  82. fPrograms[fProgramCount++] = p;
  83. }
  84. uint32_t getNativeMidiProgramCount() const noexcept
  85. {
  86. return fProgramCount;
  87. }
  88. const NativeMidiProgram* getNativeMidiProgramInfo(const uint32_t index) const noexcept
  89. {
  90. if (index >= fProgramCount)
  91. return nullptr;
  92. const ProgramInfo* const pInfo(fPrograms[index]);
  93. CARLA_SAFE_ASSERT_RETURN(pInfo != nullptr, nullptr);
  94. fRetProgram.bank = pInfo->bank;
  95. fRetProgram.program = pInfo->prog;
  96. fRetProgram.name = pInfo->name;
  97. return &fRetProgram;
  98. }
  99. const char* getZynProgramFilename(const uint32_t bank, const uint32_t program) const noexcept
  100. {
  101. for (uint32_t i=0; i<fProgramCount; ++i)
  102. {
  103. const ProgramInfo* const pInfo(fPrograms[i]);
  104. if (pInfo->bank != bank)
  105. continue;
  106. if (pInfo->prog != program)
  107. continue;
  108. return pInfo->filename;
  109. }
  110. return nullptr;
  111. }
  112. private:
  113. struct ProgramInfo {
  114. uint32_t bank;
  115. uint32_t prog;
  116. const char* name;
  117. const char* filename;
  118. ProgramInfo(uint32_t b, uint32_t p, const char* n, const char* fn) noexcept
  119. : bank(b),
  120. prog(p),
  121. name(carla_strdup_safe(n)),
  122. filename(carla_strdup_safe(fn)) {}
  123. ~ProgramInfo() noexcept
  124. {
  125. if (name != nullptr)
  126. {
  127. delete[] name;
  128. name = nullptr;
  129. }
  130. if (filename != nullptr)
  131. {
  132. delete[] filename;
  133. filename = nullptr;
  134. }
  135. }
  136. #ifdef CARLA_PROPER_CPP11_SUPPORT
  137. ProgramInfo() = delete;
  138. ProgramInfo(ProgramInfo&) = delete;
  139. ProgramInfo(const ProgramInfo&) = delete;
  140. ProgramInfo& operator=(ProgramInfo&);
  141. ProgramInfo& operator=(const ProgramInfo&);
  142. #endif
  143. };
  144. bool fInitiated;
  145. mutable NativeMidiProgram fRetProgram;
  146. uint32_t fProgramCount;
  147. const ProgramInfo** fPrograms;
  148. CARLA_PREVENT_HEAP_ALLOCATION
  149. CARLA_DECLARE_NON_COPY_CLASS(ZynAddSubFxPrograms)
  150. };
  151. static ZynAddSubFxPrograms sPrograms;
  152. // -----------------------------------------------------------------------
  153. class MiddleWareThread : private CarlaThread
  154. {
  155. public:
  156. class ScopedStopper
  157. {
  158. public:
  159. ScopedStopper(MiddleWareThread& mwt) noexcept
  160. : wasRunning(mwt.isThreadRunning()),
  161. thread(mwt),
  162. middleWare(mwt.fMiddleWare)
  163. {
  164. if (wasRunning)
  165. thread.stop();
  166. }
  167. ~ScopedStopper() noexcept
  168. {
  169. if (wasRunning)
  170. thread.start(middleWare);
  171. }
  172. void updateMiddleWare(MiddleWare* const mw) noexcept
  173. {
  174. middleWare = mw;
  175. }
  176. private:
  177. const bool wasRunning;
  178. MiddleWareThread& thread;
  179. MiddleWare* middleWare;
  180. CARLA_PREVENT_HEAP_ALLOCATION
  181. CARLA_DECLARE_NON_COPY_CLASS(ScopedStopper)
  182. };
  183. MiddleWareThread()
  184. : CarlaThread("ZynMiddleWare"),
  185. fMiddleWare(nullptr) {}
  186. void start(MiddleWare* const mw) noexcept
  187. {
  188. fMiddleWare = mw;
  189. startThread();
  190. }
  191. void stop() noexcept
  192. {
  193. stopThread(1000);
  194. fMiddleWare = nullptr;
  195. }
  196. private:
  197. MiddleWare* fMiddleWare;
  198. void run() noexcept override
  199. {
  200. for (; ! shouldThreadExit();)
  201. {
  202. CARLA_SAFE_ASSERT_RETURN(fMiddleWare != nullptr,);
  203. try {
  204. fMiddleWare->tick();
  205. } CARLA_SAFE_EXCEPTION("ZynAddSubFX MiddleWare tick");
  206. carla_msleep(1);
  207. }
  208. }
  209. CARLA_DECLARE_NON_COPY_CLASS(MiddleWareThread)
  210. };
  211. // -----------------------------------------------------------------------
  212. class ZynAddSubFxPlugin : public NativePluginAndUiClass
  213. {
  214. public:
  215. enum Parameters {
  216. kParamPart01Enabled ,
  217. kParamPart16Enabled = kParamPart01Enabled + 15,
  218. kParamPart01Volume,
  219. kParamPart16Volume = kParamPart01Volume + 15,
  220. kParamPart01Panning,
  221. kParamPart16Panning = kParamPart01Panning + 15,
  222. kParamFilterCutoff, // Filter Frequency
  223. kParamFilterQ, // Filter Resonance
  224. kParamBandwidth, // Bandwidth
  225. kParamModAmp, // FM Gain
  226. kParamResCenter, // Resonance center frequency
  227. kParamResBandwidth, // Resonance bandwidth
  228. kParamCount
  229. };
  230. ZynAddSubFxPlugin(const NativeHostDescriptor* const host)
  231. : NativePluginAndUiClass(host, "zynaddsubfx-ui"),
  232. fMiddleWare(nullptr),
  233. fMaster(nullptr),
  234. fSynth(),
  235. fDefaultState(nullptr),
  236. fMutex(),
  237. fMiddleWareThread(new MiddleWareThread())
  238. {
  239. isPlugin = true;
  240. sPrograms.initIfNeeded();
  241. fConfig.init();
  242. // init parameters to default
  243. fParameters[kParamPart01Enabled] = 1.0f;
  244. for (int i=kParamPart16Enabled+1; --i>kParamPart01Enabled;)
  245. fParameters[i] = 0.0f;
  246. for (int i=kParamPart16Volume+1; --i>=kParamPart01Volume;)
  247. fParameters[i] = 100.0f;
  248. for (int i=kParamPart16Panning+1; --i>=kParamPart01Panning;)
  249. fParameters[i] = 64.0f;
  250. fParameters[kParamFilterCutoff] = 64.0f;
  251. fParameters[kParamFilterQ] = 64.0f;
  252. fParameters[kParamBandwidth] = 64.0f;
  253. fParameters[kParamModAmp] = 127.0f;
  254. fParameters[kParamResCenter] = 64.0f;
  255. fParameters[kParamResBandwidth] = 64.0f;
  256. fSynth.buffersize = static_cast<int>(getBufferSize());
  257. fSynth.samplerate = static_cast<uint>(getSampleRate());
  258. if (fSynth.buffersize > 32)
  259. fSynth.buffersize = 32;
  260. fSynth.alias();
  261. _initMaster();
  262. _setMasterParameters();
  263. fMaster->getalldata(&fDefaultState);
  264. fMiddleWareThread->start(fMiddleWare);
  265. }
  266. ~ZynAddSubFxPlugin() override
  267. {
  268. fMiddleWareThread->stop();
  269. _deleteMaster();
  270. std::free(fDefaultState);
  271. }
  272. protected:
  273. // -------------------------------------------------------------------
  274. // Plugin parameter calls
  275. uint32_t getParameterCount() const final
  276. {
  277. return kParamCount;
  278. }
  279. const NativeParameter* getParameterInfo(const uint32_t index) const override
  280. {
  281. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, nullptr);
  282. static NativeParameter param;
  283. int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
  284. param.name = nullptr;
  285. param.unit = nullptr;
  286. param.ranges.def = 64.0f;
  287. param.ranges.min = 0.0f;
  288. param.ranges.max = 127.0f;
  289. param.ranges.step = 1.0f;
  290. param.ranges.stepSmall = 1.0f;
  291. param.ranges.stepLarge = 20.0f;
  292. param.scalePointCount = 0;
  293. param.scalePoints = nullptr;
  294. if (index <= kParamPart16Enabled)
  295. {
  296. hints |= NATIVE_PARAMETER_IS_BOOLEAN;
  297. param.ranges.def = 0.0f;
  298. param.ranges.min = 0.0f;
  299. param.ranges.max = 1.0f;
  300. param.ranges.step = 1.0f;
  301. param.ranges.stepSmall = 1.0f;
  302. param.ranges.stepLarge = 1.0f;
  303. #define PARAM_PART_ENABLE_DESC(N) \
  304. case kParamPart01Enabled + N - 1: \
  305. param.name = "Part " #N " Enabled"; break;
  306. switch (index)
  307. {
  308. case kParamPart01Enabled:
  309. param.name = "Part 01 Enabled";
  310. param.ranges.def = 1.0f;
  311. break;
  312. PARAM_PART_ENABLE_DESC( 2)
  313. PARAM_PART_ENABLE_DESC( 3)
  314. PARAM_PART_ENABLE_DESC( 4)
  315. PARAM_PART_ENABLE_DESC( 5)
  316. PARAM_PART_ENABLE_DESC( 6)
  317. PARAM_PART_ENABLE_DESC( 7)
  318. PARAM_PART_ENABLE_DESC( 8)
  319. PARAM_PART_ENABLE_DESC( 9)
  320. PARAM_PART_ENABLE_DESC(10)
  321. PARAM_PART_ENABLE_DESC(11)
  322. PARAM_PART_ENABLE_DESC(12)
  323. PARAM_PART_ENABLE_DESC(13)
  324. PARAM_PART_ENABLE_DESC(14)
  325. PARAM_PART_ENABLE_DESC(15)
  326. PARAM_PART_ENABLE_DESC(16)
  327. }
  328. #undef PARAM_PART_ENABLE_DESC
  329. }
  330. else if (index <= kParamPart16Volume)
  331. {
  332. hints |= NATIVE_PARAMETER_IS_INTEGER;
  333. param.ranges.def = 100.0f;
  334. #define PARAM_PART_ENABLE_DESC(N) \
  335. case kParamPart01Volume + N - 1: \
  336. param.name = "Part " #N " Volume"; break;
  337. switch (index)
  338. {
  339. PARAM_PART_ENABLE_DESC( 1)
  340. PARAM_PART_ENABLE_DESC( 2)
  341. PARAM_PART_ENABLE_DESC( 3)
  342. PARAM_PART_ENABLE_DESC( 4)
  343. PARAM_PART_ENABLE_DESC( 5)
  344. PARAM_PART_ENABLE_DESC( 6)
  345. PARAM_PART_ENABLE_DESC( 7)
  346. PARAM_PART_ENABLE_DESC( 8)
  347. PARAM_PART_ENABLE_DESC( 9)
  348. PARAM_PART_ENABLE_DESC(10)
  349. PARAM_PART_ENABLE_DESC(11)
  350. PARAM_PART_ENABLE_DESC(12)
  351. PARAM_PART_ENABLE_DESC(13)
  352. PARAM_PART_ENABLE_DESC(14)
  353. PARAM_PART_ENABLE_DESC(15)
  354. PARAM_PART_ENABLE_DESC(16)
  355. }
  356. #undef PARAM_PART_ENABLE_DESC
  357. }
  358. else if (index <= kParamPart16Panning)
  359. {
  360. hints |= NATIVE_PARAMETER_IS_INTEGER;
  361. #define PARAM_PART_ENABLE_DESC(N) \
  362. case kParamPart01Panning + N - 1: \
  363. param.name = "Part " #N " Panning"; break;
  364. switch (index)
  365. {
  366. PARAM_PART_ENABLE_DESC( 1)
  367. PARAM_PART_ENABLE_DESC( 2)
  368. PARAM_PART_ENABLE_DESC( 3)
  369. PARAM_PART_ENABLE_DESC( 4)
  370. PARAM_PART_ENABLE_DESC( 5)
  371. PARAM_PART_ENABLE_DESC( 6)
  372. PARAM_PART_ENABLE_DESC( 7)
  373. PARAM_PART_ENABLE_DESC( 8)
  374. PARAM_PART_ENABLE_DESC( 9)
  375. PARAM_PART_ENABLE_DESC(10)
  376. PARAM_PART_ENABLE_DESC(11)
  377. PARAM_PART_ENABLE_DESC(12)
  378. PARAM_PART_ENABLE_DESC(13)
  379. PARAM_PART_ENABLE_DESC(14)
  380. PARAM_PART_ENABLE_DESC(15)
  381. PARAM_PART_ENABLE_DESC(16)
  382. }
  383. #undef PARAM_PART_ENABLE_DESC
  384. }
  385. else if (index <= kParamResBandwidth)
  386. {
  387. hints |= NATIVE_PARAMETER_IS_INTEGER;
  388. switch (index)
  389. {
  390. case kParamFilterCutoff:
  391. param.name = "Filter Cutoff";
  392. break;
  393. case kParamFilterQ:
  394. param.name = "Filter Q";
  395. break;
  396. case kParamBandwidth:
  397. param.name = "Bandwidth";
  398. break;
  399. case kParamModAmp:
  400. param.name = "FM Gain";
  401. param.ranges.def = 127.0f;
  402. break;
  403. case kParamResCenter:
  404. param.name = "Res Center Freq";
  405. break;
  406. case kParamResBandwidth:
  407. param.name = "Res Bandwidth";
  408. break;
  409. }
  410. }
  411. param.hints = static_cast<NativeParameterHints>(hints);
  412. return &param;
  413. }
  414. float getParameterValue(const uint32_t index) const final
  415. {
  416. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, 0.0f);
  417. return fParameters[index];
  418. }
  419. // -------------------------------------------------------------------
  420. // Plugin midi-program calls
  421. uint32_t getMidiProgramCount() const noexcept override
  422. {
  423. return sPrograms.getNativeMidiProgramCount();
  424. }
  425. const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const noexcept override
  426. {
  427. return sPrograms.getNativeMidiProgramInfo(index);
  428. }
  429. // -------------------------------------------------------------------
  430. // Plugin state calls
  431. void setParameterValue(const uint32_t index, const float value) final
  432. {
  433. CARLA_SAFE_ASSERT_RETURN(index < kParamCount,);
  434. if (index <= kParamPart16Enabled)
  435. {
  436. fParameters[index] = (value >= 0.5f) ? 1.0f : 0.0f;
  437. char msg[24];
  438. std::sprintf(msg, "/part%i/Penabled", index-kParamPart01Enabled);
  439. #ifdef ZYN_MSG_ANYWHERE
  440. fMiddleWare->messageAnywhere(msg, (value >= 0.5f) ? "T" : "F");
  441. #else
  442. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  443. fMiddleWare->activeUrl("");
  444. fMiddleWare->transmitMsg(msg, (value >= 0.5f) ? "T" : "F");
  445. #endif
  446. }
  447. else if (index <= kParamPart16Volume)
  448. {
  449. if (carla_isEqual(fParameters[index], value))
  450. return;
  451. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  452. char msg[24];
  453. std::sprintf(msg, "/part%i/Pvolume", index-kParamPart01Volume);
  454. #ifdef ZYN_MSG_ANYWHERE
  455. fMiddleWare->messageAnywhere(msg, "i", static_cast<int>(fParameters[index]));
  456. #else
  457. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  458. fMiddleWare->activeUrl("");
  459. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[index]));
  460. #endif
  461. }
  462. else if (index <= kParamPart16Panning)
  463. {
  464. if (carla_isEqual(fParameters[index], value))
  465. return;
  466. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  467. char msg[24];
  468. std::sprintf(msg, "/part%i/Ppanning", index-kParamPart01Panning);
  469. #ifdef ZYN_MSG_ANYWHERE
  470. fMiddleWare->messageAnywhere(msg, "i", static_cast<int>(fParameters[index]));
  471. #else
  472. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  473. fMiddleWare->activeUrl("");
  474. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[index]));
  475. #endif
  476. }
  477. else if (index <= kParamResBandwidth)
  478. {
  479. const MidiControllers zynControl(getZynControlFromIndex(index));
  480. CARLA_SAFE_ASSERT_RETURN(zynControl != C_NULL,);
  481. fParameters[index] = std::round(carla_fixedValue(0.0f, 127.0f, value));
  482. for (int npart=0; npart<NUM_MIDI_PARTS; ++npart)
  483. {
  484. if (fMaster->part[npart] != nullptr)
  485. fMaster->part[npart]->SetController(zynControl, static_cast<int>(value));
  486. }
  487. }
  488. }
  489. void setMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
  490. {
  491. CARLA_SAFE_ASSERT_RETURN(program < BANK_SIZE,);
  492. if (bank == 0)
  493. {
  494. // reset part to default
  495. setState(fDefaultState);
  496. return;
  497. }
  498. const char* const filename(sPrograms.getZynProgramFilename(bank, program));
  499. CARLA_SAFE_ASSERT_RETURN(filename != nullptr && filename[0] != '\0',);
  500. #ifdef ZYN_MSG_ANYWHERE
  501. fMiddleWare->messageAnywhere("/load-part", "is", channel, filename);
  502. #else
  503. fMiddleWare->transmitMsg("/load-part", "is", channel, filename);
  504. #endif
  505. }
  506. void setCustomData(const char* const key, const char* const value) override
  507. {
  508. CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
  509. CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
  510. /**/ if (std::strcmp(key, "CarlaAlternateFile1") == 0) // xmz
  511. {
  512. #ifdef ZYN_MSG_ANYWHERE
  513. fMiddleWare->messageAnywhere("/load_xmz", "s", value);
  514. #else
  515. fMiddleWare->transmitMsg("/load_xmz", "s", value);
  516. #endif
  517. }
  518. else if (std::strcmp(key, "CarlaAlternateFile2") == 0) // xiz
  519. {
  520. #ifdef ZYN_MSG_ANYWHERE
  521. fMiddleWare->messageAnywhere("/load_xiz", "is", 0, value);
  522. #else
  523. fMiddleWare->transmitMsg("/load_xiz", "is", 0, value);
  524. #endif
  525. }
  526. }
  527. // -------------------------------------------------------------------
  528. // Plugin process calls
  529. void process(float**, float** const outBuffer, const uint32_t frames,
  530. const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
  531. {
  532. if (! fMutex.tryLock())
  533. {
  534. if (! isOffline())
  535. {
  536. FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
  537. FloatVectorOperations::clear(outBuffer[1], static_cast<int>(frames));
  538. return;
  539. }
  540. fMutex.lock();
  541. }
  542. uint32_t framesOffset = 0;
  543. for (uint32_t i=0; i < midiEventCount; ++i)
  544. {
  545. const NativeMidiEvent* const midiEvent(&midiEvents[i]);
  546. if (midiEvent->time >= frames)
  547. continue;
  548. if (midiEvent->time > framesOffset)
  549. {
  550. fMaster->GetAudioOutSamples(midiEvent->time-framesOffset, fSynth.samplerate, outBuffer[0]+framesOffset,
  551. outBuffer[1]+framesOffset);
  552. framesOffset = midiEvent->time;
  553. }
  554. const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent->data);
  555. const char channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->data);
  556. if (MIDI_IS_STATUS_NOTE_OFF(status))
  557. {
  558. const char note = static_cast<char>(midiEvent->data[1]);
  559. fMaster->noteOff(channel, note);
  560. }
  561. else if (MIDI_IS_STATUS_NOTE_ON(status))
  562. {
  563. const char note = static_cast<char>(midiEvent->data[1]);
  564. const char velo = static_cast<char>(midiEvent->data[2]);
  565. fMaster->noteOn(channel, note, velo);
  566. }
  567. else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status))
  568. {
  569. const char note = static_cast<char>(midiEvent->data[1]);
  570. const char pressure = static_cast<char>(midiEvent->data[2]);
  571. fMaster->polyphonicAftertouch(channel, note, pressure);
  572. }
  573. else if (MIDI_IS_STATUS_CONTROL_CHANGE(status))
  574. {
  575. // skip controls which we map to parameters
  576. if (getIndexFromZynControl(midiEvent->data[1]) != kParamCount)
  577. continue;
  578. const int control = midiEvent->data[1];
  579. const int value = midiEvent->data[2];
  580. fMaster->setController(channel, control, value);
  581. }
  582. else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status))
  583. {
  584. const uint8_t lsb = midiEvent->data[1];
  585. const uint8_t msb = midiEvent->data[2];
  586. const int value = ((msb << 7) | lsb) - 8192;
  587. fMaster->setController(channel, C_pitchwheel, value);
  588. }
  589. }
  590. if (frames > framesOffset)
  591. fMaster->GetAudioOutSamples(frames-framesOffset, fSynth.samplerate, outBuffer[0]+framesOffset,
  592. outBuffer[1]+framesOffset);
  593. fMutex.unlock();
  594. }
  595. // -------------------------------------------------------------------
  596. // Plugin UI calls
  597. #ifdef HAVE_ZYN_UI_DEPS
  598. void uiShow(const bool show) override
  599. {
  600. if (show)
  601. {
  602. if (isPipeRunning())
  603. {
  604. const CarlaMutexLocker cml(getPipeLock());
  605. writeMessage("focus\n", 6);
  606. flushMessages();
  607. return;
  608. }
  609. carla_stdout("Trying to start UI using \"%s\"", getExtUiPath());
  610. CarlaExternalUI::setData(getExtUiPath(), fMiddleWare->getServerAddress(), getUiName());
  611. if (! CarlaExternalUI::startPipeServer(true))
  612. {
  613. uiClosed();
  614. hostUiUnavailable();
  615. }
  616. }
  617. else
  618. {
  619. CarlaExternalUI::stopPipeServer(2000);
  620. }
  621. }
  622. #endif
  623. // -------------------------------------------------------------------
  624. // Plugin state calls
  625. char* getState() const override
  626. {
  627. const MiddleWareThread::ScopedStopper mwss(*fMiddleWareThread);
  628. char* data = nullptr;
  629. fMaster->getalldata(&data);
  630. return data;
  631. }
  632. void setState(const char* const data) override
  633. {
  634. CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
  635. const MiddleWareThread::ScopedStopper mwss(*fMiddleWareThread);
  636. const CarlaMutexLocker cml(fMutex);
  637. fMaster->defaults();
  638. fMaster->putalldata(data);
  639. fMaster->applyparameters();
  640. fMaster->initialize_rt();
  641. fMiddleWare->updateResources(fMaster);
  642. _setMasterParameters();
  643. }
  644. // -------------------------------------------------------------------
  645. // Plugin dispatcher
  646. void bufferSizeChanged(const uint32_t bufferSize) final
  647. {
  648. MiddleWareThread::ScopedStopper mwss(*fMiddleWareThread);
  649. char* const state(getState());
  650. _deleteMaster();
  651. fSynth.buffersize = static_cast<int>(bufferSize);
  652. if (fSynth.buffersize > 32)
  653. fSynth.buffersize = 32;
  654. fSynth.alias();
  655. _initMaster();
  656. mwss.updateMiddleWare(fMiddleWare);
  657. setState(state);
  658. std::free(state);
  659. }
  660. void sampleRateChanged(const double sampleRate) final
  661. {
  662. MiddleWareThread::ScopedStopper mwss(*fMiddleWareThread);
  663. char* const state(getState());
  664. _deleteMaster();
  665. fSynth.samplerate = static_cast<uint>(sampleRate);
  666. fSynth.alias();
  667. _initMaster();
  668. mwss.updateMiddleWare(fMiddleWare);
  669. setState(state);
  670. std::free(state);
  671. }
  672. // -------------------------------------------------------------------
  673. private:
  674. MiddleWare* fMiddleWare;
  675. Master* fMaster;
  676. SYNTH_T fSynth;
  677. Config fConfig;
  678. char* fDefaultState;
  679. float fParameters[kParamCount];
  680. CarlaMutex fMutex;
  681. ScopedPointer<MiddleWareThread> fMiddleWareThread;
  682. static MidiControllers getZynControlFromIndex(const uint index)
  683. {
  684. switch (index)
  685. {
  686. case kParamFilterCutoff:
  687. return C_filtercutoff;
  688. case kParamFilterQ:
  689. return C_filterq;
  690. case kParamBandwidth:
  691. return C_bandwidth;
  692. case kParamModAmp:
  693. return C_fmamp;
  694. case kParamResCenter:
  695. return C_resonance_center;
  696. case kParamResBandwidth:
  697. return C_resonance_bandwidth;
  698. default:
  699. return C_NULL;
  700. }
  701. }
  702. static Parameters getIndexFromZynControl(const uint8_t control)
  703. {
  704. switch (control)
  705. {
  706. case C_filtercutoff:
  707. return kParamFilterCutoff;
  708. case C_filterq:
  709. return kParamFilterQ;
  710. case C_bandwidth:
  711. return kParamBandwidth;
  712. case C_fmamp:
  713. return kParamModAmp;
  714. case C_resonance_center:
  715. return kParamResCenter;
  716. case C_resonance_bandwidth:
  717. return kParamResBandwidth;
  718. default:
  719. return kParamCount;
  720. }
  721. }
  722. // -------------------------------------------------------------------
  723. void _initMaster()
  724. {
  725. fMiddleWare = new MiddleWare(std::move(fSynth), &fConfig);
  726. fMiddleWare->setUiCallback(__uiCallback, this);
  727. fMiddleWare->setIdleCallback(_idleCallback, this);
  728. _masterChangedCallback(fMiddleWare->spawnMaster());
  729. }
  730. void _setMasterParameters()
  731. {
  732. #ifndef ZYN_MSG_ANYWHERE
  733. fMiddleWare->transmitMsg("/echo", "ss", "OSC_URL", "");
  734. fMiddleWare->activeUrl("");
  735. #endif
  736. char msg[24];
  737. for (int i=kParamPart16Enabled+1; --i>=kParamPart01Enabled;)
  738. {
  739. std::sprintf(msg, "/part%i/Penabled", i-kParamPart01Enabled);
  740. #ifdef ZYN_MSG_ANYWHERE
  741. fMiddleWare->messageAnywhere(msg, (fParameters[i] >= 0.5f) ? "T" : "F");
  742. #else
  743. fMiddleWare->transmitMsg(msg, (fParameters[i] >= 0.5f) ? "T" : "F");
  744. #endif
  745. }
  746. for (int i=kParamPart16Volume+1; --i>=kParamPart01Volume;)
  747. {
  748. std::sprintf(msg, "/part%i/Pvolume", i-kParamPart01Volume);
  749. #ifdef ZYN_MSG_ANYWHERE
  750. fMiddleWare->messageAnywhere(msg, "i", static_cast<int>(fParameters[i]));
  751. #else
  752. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[i]));
  753. #endif
  754. }
  755. for (int i=kParamPart16Panning+1; --i>=kParamPart01Panning;)
  756. {
  757. std::sprintf(msg, "/part%i/Ppanning", i-kParamPart01Panning);
  758. #ifdef ZYN_MSG_ANYWHERE
  759. fMiddleWare->messageAnywhere(msg, "i", static_cast<int>(fParameters[i]));
  760. #else
  761. fMiddleWare->transmitMsg(msg, "i", static_cast<int>(fParameters[i]));
  762. #endif
  763. }
  764. for (int i=0; i<NUM_MIDI_PARTS; ++i)
  765. {
  766. fMaster->part[i]->SetController(C_filtercutoff, static_cast<int>(fParameters[kParamFilterCutoff]));
  767. fMaster->part[i]->SetController(C_filterq, static_cast<int>(fParameters[kParamFilterQ]));
  768. fMaster->part[i]->SetController(C_bandwidth, static_cast<int>(fParameters[kParamBandwidth]));
  769. fMaster->part[i]->SetController(C_fmamp, static_cast<int>(fParameters[kParamModAmp]));
  770. fMaster->part[i]->SetController(C_resonance_center, static_cast<int>(fParameters[kParamResCenter]));
  771. fMaster->part[i]->SetController(C_resonance_bandwidth, static_cast<int>(fParameters[kParamResBandwidth]));
  772. }
  773. }
  774. void _deleteMaster()
  775. {
  776. fMaster = nullptr;
  777. delete fMiddleWare;
  778. fMiddleWare = nullptr;
  779. }
  780. void _masterChangedCallback(Master* m)
  781. {
  782. fMaster = m;
  783. fMaster->setMasterChangedCallback(__masterChangedCallback, this);
  784. }
  785. static void __masterChangedCallback(void* ptr, Master* m)
  786. {
  787. ((ZynAddSubFxPlugin*)ptr)->_masterChangedCallback(m);
  788. }
  789. void _uiCallback(const char* const msg)
  790. {
  791. if (std::strncmp(msg, "/part", 5) != 0)
  792. return;
  793. const char* msgtmp = msg;
  794. msgtmp += 5;
  795. CARLA_SAFE_ASSERT_RETURN( msgtmp[0] >= '0' && msgtmp[0] <= '9',);
  796. CARLA_SAFE_ASSERT_RETURN((msgtmp[1] >= '0' && msgtmp[1] <= '9') || msgtmp[1] == '/',);
  797. char partnstr[3] = { '\0', '\0', '\0' };
  798. partnstr[0] = msgtmp[0];
  799. ++msgtmp;
  800. if (msgtmp[0] >= '0' && msgtmp[0] <= '9')
  801. {
  802. partnstr[1] = msgtmp[0];
  803. ++msgtmp;
  804. }
  805. const int partn = std::atoi(partnstr);
  806. ++msgtmp;
  807. /**/ if (std::strcmp(msgtmp, "Penabled") == 0)
  808. {
  809. const int index = kParamPart01Enabled+partn;
  810. const bool enbl = rtosc_argument(msg,0).T;
  811. fParameters[index] = enbl ? 1.0f : 0.0f;
  812. uiParameterChanged(kParamPart01Enabled+partn, enbl ? 1.0f : 0.0f);
  813. }
  814. else if (std::strcmp(msgtmp, "Pvolume") == 0)
  815. {
  816. const int index = kParamPart01Volume+partn;
  817. const int value = rtosc_argument(msg,0).i;
  818. fParameters[index] = value;
  819. uiParameterChanged(kParamPart01Volume+partn, value);
  820. }
  821. else if (std::strcmp(msgtmp, "Ppanning") == 0)
  822. {
  823. const int index = kParamPart01Panning+partn;
  824. const int value = rtosc_argument(msg,0).i;
  825. fParameters[index] = value;
  826. uiParameterChanged(kParamPart01Panning+partn, value);
  827. }
  828. }
  829. static void __uiCallback(void* ptr, const char* msg)
  830. {
  831. ((ZynAddSubFxPlugin*)ptr)->_uiCallback(msg);
  832. }
  833. static void _idleCallback(void* ptr)
  834. {
  835. ((ZynAddSubFxPlugin*)ptr)->hostGiveIdle();
  836. }
  837. // -------------------------------------------------------------------
  838. PluginClassEND(ZynAddSubFxPlugin)
  839. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ZynAddSubFxPlugin)
  840. };
  841. // -----------------------------------------------------------------------
  842. static const NativePluginDescriptor zynaddsubfxDesc = {
  843. /* category */ NATIVE_PLUGIN_CATEGORY_SYNTH,
  844. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_SYNTH
  845. #ifdef HAVE_ZYN_UI_DEPS
  846. |NATIVE_PLUGIN_HAS_UI
  847. #endif
  848. |NATIVE_PLUGIN_USES_MULTI_PROGS
  849. |NATIVE_PLUGIN_USES_STATE),
  850. /* supports */ static_cast<NativePluginSupports>(NATIVE_PLUGIN_SUPPORTS_CONTROL_CHANGES
  851. |NATIVE_PLUGIN_SUPPORTS_NOTE_AFTERTOUCH
  852. |NATIVE_PLUGIN_SUPPORTS_PITCHBEND
  853. |NATIVE_PLUGIN_SUPPORTS_ALL_SOUND_OFF),
  854. /* audioIns */ 0,
  855. /* audioOuts */ 2,
  856. /* midiIns */ 1,
  857. /* midiOuts */ 0,
  858. /* paramIns */ ZynAddSubFxPlugin::kParamCount,
  859. /* paramOuts */ 0,
  860. /* name */ "ZynAddSubFX",
  861. /* label */ "zynaddsubfx",
  862. /* maker */ "falkTX, Mark McCurry, Nasca Octavian Paul",
  863. /* copyright */ "GNU GPL v2+",
  864. PluginDescriptorFILL(ZynAddSubFxPlugin)
  865. };
  866. // -----------------------------------------------------------------------
  867. CARLA_EXPORT
  868. void carla_register_native_plugin_zynaddsubfx_synth();
  869. CARLA_EXPORT
  870. void carla_register_native_plugin_zynaddsubfx_synth()
  871. {
  872. carla_register_native_plugin(&zynaddsubfxDesc);
  873. }
  874. // -----------------------------------------------------------------------