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