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.

zynaddsubfx-synth.cpp 30KB

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