The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

2603 lines
90KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. The code included in this file is provided under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  7. To use, copy, modify, and/or distribute this software for any purpose with or
  8. without fee is hereby granted provided that the above copyright notice and
  9. this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
  11. WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
  12. PURPOSE, ARE DISCLAIMED.
  13. ==============================================================================
  14. */
  15. /*******************************************************************************
  16. The block below describes the properties of this PIP. A PIP is a short snippet
  17. of code that can be read by the Projucer and used to generate a JUCE project.
  18. BEGIN_JUCE_PIP_METADATA
  19. name: SamplerPlugin
  20. version: 1.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: Sampler audio plugin.
  24. dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
  25. juce_audio_plugin_client, juce_audio_processors,
  26. juce_audio_utils, juce_core, juce_data_structures,
  27. juce_events, juce_graphics, juce_gui_basics, juce_gui_extra
  28. exporters: xcode_mac, vs2022
  29. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  30. type: AudioProcessor
  31. mainClass: SamplerAudioProcessor
  32. useLocalCopy: 1
  33. pluginCharacteristics: pluginIsSynth, pluginWantsMidiIn
  34. END_JUCE_PIP_METADATA
  35. *******************************************************************************/
  36. #pragma once
  37. #include "../Assets/DemoUtilities.h"
  38. #include <array>
  39. #include <atomic>
  40. #include <memory>
  41. #include <vector>
  42. #include <tuple>
  43. #include <iomanip>
  44. #include <sstream>
  45. #include <functional>
  46. #include <mutex>
  47. namespace IDs
  48. {
  49. #define DECLARE_ID(name) const juce::Identifier name (#name);
  50. DECLARE_ID (DATA_MODEL)
  51. DECLARE_ID (sampleReader)
  52. DECLARE_ID (centreFrequencyHz)
  53. DECLARE_ID (loopMode)
  54. DECLARE_ID (loopPointsSeconds)
  55. DECLARE_ID (MPE_SETTINGS)
  56. DECLARE_ID (synthVoices)
  57. DECLARE_ID (voiceStealingEnabled)
  58. DECLARE_ID (legacyModeEnabled)
  59. DECLARE_ID (mpeZoneLayout)
  60. DECLARE_ID (legacyFirstChannel)
  61. DECLARE_ID (legacyLastChannel)
  62. DECLARE_ID (legacyPitchbendRange)
  63. DECLARE_ID (VISIBLE_RANGE)
  64. DECLARE_ID (totalRange)
  65. DECLARE_ID (visibleRange)
  66. #undef DECLARE_ID
  67. } // namespace IDs
  68. enum class LoopMode
  69. {
  70. none,
  71. forward,
  72. pingpong
  73. };
  74. // We want to send type-erased commands to the audio thread, but we also
  75. // want those commands to contain move-only resources, so that we can
  76. // construct resources on the gui thread, and then transfer ownership
  77. // cheaply to the audio thread. We can't do this with std::function
  78. // because it enforces that functions are copy-constructible.
  79. // Therefore, we use a very simple templated type-eraser here.
  80. template <typename Proc>
  81. struct Command
  82. {
  83. virtual ~Command() noexcept = default;
  84. virtual void run (Proc& proc) = 0;
  85. };
  86. template <typename Proc, typename Func>
  87. class TemplateCommand : public Command<Proc>,
  88. private Func
  89. {
  90. public:
  91. template <typename FuncPrime>
  92. explicit TemplateCommand (FuncPrime&& funcPrime)
  93. : Func (std::forward<FuncPrime> (funcPrime))
  94. {}
  95. void run (Proc& proc) override { (*this) (proc); }
  96. };
  97. template <typename Proc>
  98. class CommandFifo final
  99. {
  100. public:
  101. explicit CommandFifo (int size)
  102. : buffer ((size_t) size),
  103. abstractFifo (size)
  104. {}
  105. CommandFifo()
  106. : CommandFifo (1024)
  107. {}
  108. template <typename Item>
  109. void push (Item&& item) noexcept
  110. {
  111. auto command = makeCommand (std::forward<Item> (item));
  112. abstractFifo.write (1).forEach ([&] (int index)
  113. {
  114. buffer[size_t (index)] = std::move (command);
  115. });
  116. }
  117. void call (Proc& proc) noexcept
  118. {
  119. abstractFifo.read (abstractFifo.getNumReady()).forEach ([&] (int index)
  120. {
  121. buffer[size_t (index)]->run (proc);
  122. });
  123. }
  124. private:
  125. template <typename Func>
  126. static std::unique_ptr<Command<Proc>> makeCommand (Func&& func)
  127. {
  128. using Decayed = std::decay_t<Func>;
  129. return std::make_unique<TemplateCommand<Proc, Decayed>> (std::forward<Func> (func));
  130. }
  131. std::vector<std::unique_ptr<Command<Proc>>> buffer;
  132. AbstractFifo abstractFifo;
  133. };
  134. //==============================================================================
  135. // Represents the constant parts of an audio sample: its name, sample rate,
  136. // length, and the audio sample data itself.
  137. // Samples might be pretty big, so we'll keep shared_ptrs to them most of the
  138. // time, to reduce duplication and copying.
  139. class Sample final
  140. {
  141. public:
  142. Sample (AudioFormatReader& source, double maxSampleLengthSecs)
  143. : sourceSampleRate (source.sampleRate),
  144. length (jmin (int (source.lengthInSamples),
  145. int (maxSampleLengthSecs * sourceSampleRate))),
  146. data (jmin (2, int (source.numChannels)), length + 4)
  147. {
  148. if (length == 0)
  149. throw std::runtime_error ("Unable to load sample");
  150. source.read (&data, 0, length + 4, 0, true, true);
  151. }
  152. double getSampleRate() const { return sourceSampleRate; }
  153. int getLength() const { return length; }
  154. const AudioBuffer<float>& getBuffer() const { return data; }
  155. private:
  156. double sourceSampleRate;
  157. int length;
  158. AudioBuffer<float> data;
  159. };
  160. //==============================================================================
  161. // A class which contains all the information related to sample-playback, such
  162. // as sample data, loop points, and loop kind.
  163. // It is expected that multiple sampler voices will maintain pointers to a
  164. // single instance of this class, to avoid redundant duplication of sample
  165. // data in memory.
  166. class MPESamplerSound final
  167. {
  168. public:
  169. void setSample (std::unique_ptr<Sample> value)
  170. {
  171. sample = move (value);
  172. setLoopPointsInSeconds (loopPoints);
  173. }
  174. Sample* getSample() const
  175. {
  176. return sample.get();
  177. }
  178. void setLoopPointsInSeconds (Range<double> value)
  179. {
  180. loopPoints = sample == nullptr ? value
  181. : Range<double> (0, sample->getLength() / sample->getSampleRate())
  182. .constrainRange (value);
  183. }
  184. Range<double> getLoopPointsInSeconds() const
  185. {
  186. return loopPoints;
  187. }
  188. void setCentreFrequencyInHz (double centre)
  189. {
  190. centreFrequencyInHz = centre;
  191. }
  192. double getCentreFrequencyInHz() const
  193. {
  194. return centreFrequencyInHz;
  195. }
  196. void setLoopMode (LoopMode type)
  197. {
  198. loopMode = type;
  199. }
  200. LoopMode getLoopMode() const
  201. {
  202. return loopMode;
  203. }
  204. private:
  205. std::unique_ptr<Sample> sample;
  206. double centreFrequencyInHz { 440.0 };
  207. Range<double> loopPoints;
  208. LoopMode loopMode { LoopMode::none };
  209. };
  210. //==============================================================================
  211. class MPESamplerVoice : public MPESynthesiserVoice
  212. {
  213. public:
  214. explicit MPESamplerVoice (std::shared_ptr<const MPESamplerSound> sound)
  215. : samplerSound (std::move (sound))
  216. {
  217. jassert (samplerSound != nullptr);
  218. }
  219. void noteStarted() override
  220. {
  221. jassert (currentlyPlayingNote.isValid());
  222. jassert (currentlyPlayingNote.keyState == MPENote::keyDown
  223. || currentlyPlayingNote.keyState == MPENote::keyDownAndSustained);
  224. level .setTargetValue (currentlyPlayingNote.noteOnVelocity.asUnsignedFloat());
  225. frequency.setTargetValue (currentlyPlayingNote.getFrequencyInHertz());
  226. auto loopPoints = samplerSound->getLoopPointsInSeconds();
  227. loopBegin.setTargetValue (loopPoints.getStart() * samplerSound->getSample()->getSampleRate());
  228. loopEnd .setTargetValue (loopPoints.getEnd() * samplerSound->getSample()->getSampleRate());
  229. for (auto smoothed : { &level, &frequency, &loopBegin, &loopEnd })
  230. smoothed->reset (currentSampleRate, smoothingLengthInSeconds);
  231. previousPressure = currentlyPlayingNote.pressure.asUnsignedFloat();
  232. currentSamplePos = 0.0;
  233. tailOff = 0.0;
  234. }
  235. void noteStopped (bool allowTailOff) override
  236. {
  237. jassert (currentlyPlayingNote.keyState == MPENote::off);
  238. if (allowTailOff && tailOff == 0.0)
  239. tailOff = 1.0;
  240. else
  241. stopNote();
  242. }
  243. void notePressureChanged() override
  244. {
  245. const auto currentPressure = static_cast<double> (currentlyPlayingNote.pressure.asUnsignedFloat());
  246. const auto deltaPressure = currentPressure - previousPressure;
  247. level.setTargetValue (jlimit (0.0, 1.0, level.getCurrentValue() + deltaPressure));
  248. previousPressure = currentPressure;
  249. }
  250. void notePitchbendChanged() override
  251. {
  252. frequency.setTargetValue (currentlyPlayingNote.getFrequencyInHertz());
  253. }
  254. void noteTimbreChanged() override {}
  255. void noteKeyStateChanged() override {}
  256. void renderNextBlock (AudioBuffer<float>& outputBuffer,
  257. int startSample,
  258. int numSamples) override
  259. {
  260. render (outputBuffer, startSample, numSamples);
  261. }
  262. void renderNextBlock (AudioBuffer<double>& outputBuffer,
  263. int startSample,
  264. int numSamples) override
  265. {
  266. render (outputBuffer, startSample, numSamples);
  267. }
  268. double getCurrentSamplePosition() const
  269. {
  270. return currentSamplePos;
  271. }
  272. private:
  273. template <typename Element>
  274. void render (AudioBuffer<Element>& outputBuffer, int startSample, int numSamples)
  275. {
  276. jassert (samplerSound->getSample() != nullptr);
  277. auto loopPoints = samplerSound->getLoopPointsInSeconds();
  278. loopBegin.setTargetValue (loopPoints.getStart() * samplerSound->getSample()->getSampleRate());
  279. loopEnd .setTargetValue (loopPoints.getEnd() * samplerSound->getSample()->getSampleRate());
  280. auto& data = samplerSound->getSample()->getBuffer();
  281. auto inL = data.getReadPointer (0);
  282. auto inR = data.getNumChannels() > 1 ? data.getReadPointer (1) : nullptr;
  283. auto outL = outputBuffer.getWritePointer (0, startSample);
  284. if (outL == nullptr)
  285. return;
  286. auto outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getWritePointer (1, startSample)
  287. : nullptr;
  288. size_t writePos = 0;
  289. while (--numSamples >= 0 && renderNextSample (inL, inR, outL, outR, writePos))
  290. writePos += 1;
  291. }
  292. template <typename Element>
  293. bool renderNextSample (const float* inL,
  294. const float* inR,
  295. Element* outL,
  296. Element* outR,
  297. size_t writePos)
  298. {
  299. auto currentLevel = level.getNextValue();
  300. auto currentFrequency = frequency.getNextValue();
  301. auto currentLoopBegin = loopBegin.getNextValue();
  302. auto currentLoopEnd = loopEnd.getNextValue();
  303. if (isTailingOff())
  304. {
  305. currentLevel *= tailOff;
  306. tailOff *= 0.9999;
  307. if (tailOff < 0.005)
  308. {
  309. stopNote();
  310. return false;
  311. }
  312. }
  313. auto pos = (int) currentSamplePos;
  314. auto nextPos = pos + 1;
  315. auto alpha = (Element) (currentSamplePos - pos);
  316. auto invAlpha = 1.0f - alpha;
  317. // just using a very simple linear interpolation here..
  318. auto l = static_cast<Element> (currentLevel * (inL[pos] * invAlpha + inL[nextPos] * alpha));
  319. auto r = static_cast<Element> ((inR != nullptr) ? currentLevel * (inR[pos] * invAlpha + inR[nextPos] * alpha)
  320. : l);
  321. if (outR != nullptr)
  322. {
  323. outL[writePos] += l;
  324. outR[writePos] += r;
  325. }
  326. else
  327. {
  328. outL[writePos] += (l + r) * 0.5f;
  329. }
  330. std::tie (currentSamplePos, currentDirection) = getNextState (currentFrequency,
  331. currentLoopBegin,
  332. currentLoopEnd);
  333. if (currentSamplePos > samplerSound->getSample()->getLength())
  334. {
  335. stopNote();
  336. return false;
  337. }
  338. return true;
  339. }
  340. double getSampleValue() const;
  341. bool isTailingOff() const
  342. {
  343. return tailOff != 0.0;
  344. }
  345. void stopNote()
  346. {
  347. clearCurrentNote();
  348. currentSamplePos = 0.0;
  349. }
  350. enum class Direction
  351. {
  352. forward,
  353. backward
  354. };
  355. std::tuple<double, Direction> getNextState (double freq,
  356. double begin,
  357. double end) const
  358. {
  359. auto nextPitchRatio = freq / samplerSound->getCentreFrequencyInHz();
  360. auto nextSamplePos = currentSamplePos;
  361. auto nextDirection = currentDirection;
  362. // Move the current sample pos in the correct direction
  363. switch (currentDirection)
  364. {
  365. case Direction::forward:
  366. nextSamplePos += nextPitchRatio;
  367. break;
  368. case Direction::backward:
  369. nextSamplePos -= nextPitchRatio;
  370. break;
  371. default:
  372. break;
  373. }
  374. // Update current sample position, taking loop mode into account
  375. // If the loop mode was changed while we were travelling backwards, deal
  376. // with it gracefully.
  377. if (nextDirection == Direction::backward && nextSamplePos < begin)
  378. {
  379. nextSamplePos = begin;
  380. nextDirection = Direction::forward;
  381. return std::tuple<double, Direction> (nextSamplePos, nextDirection);
  382. }
  383. if (samplerSound->getLoopMode() == LoopMode::none)
  384. return std::tuple<double, Direction> (nextSamplePos, nextDirection);
  385. if (nextDirection == Direction::forward && end < nextSamplePos && !isTailingOff())
  386. {
  387. if (samplerSound->getLoopMode() == LoopMode::forward)
  388. nextSamplePos = begin;
  389. else if (samplerSound->getLoopMode() == LoopMode::pingpong)
  390. {
  391. nextSamplePos = end;
  392. nextDirection = Direction::backward;
  393. }
  394. }
  395. return std::tuple<double, Direction> (nextSamplePos, nextDirection);
  396. }
  397. std::shared_ptr<const MPESamplerSound> samplerSound;
  398. SmoothedValue<double> level { 0 };
  399. SmoothedValue<double> frequency { 0 };
  400. SmoothedValue<double> loopBegin;
  401. SmoothedValue<double> loopEnd;
  402. double previousPressure { 0 };
  403. double currentSamplePos { 0 };
  404. double tailOff { 0 };
  405. Direction currentDirection { Direction::forward };
  406. double smoothingLengthInSeconds { 0.01 };
  407. };
  408. template <typename Contents>
  409. class ReferenceCountingAdapter : public ReferenceCountedObject
  410. {
  411. public:
  412. template <typename... Args>
  413. explicit ReferenceCountingAdapter (Args&&... args)
  414. : contents (std::forward<Args> (args)...)
  415. {}
  416. const Contents& get() const
  417. {
  418. return contents;
  419. }
  420. Contents& get()
  421. {
  422. return contents;
  423. }
  424. private:
  425. Contents contents;
  426. };
  427. template <typename Contents, typename... Args>
  428. std::unique_ptr<ReferenceCountingAdapter<Contents>>
  429. make_reference_counted (Args&&... args)
  430. {
  431. auto adapter = new ReferenceCountingAdapter<Contents> (std::forward<Args> (args)...);
  432. return std::unique_ptr<ReferenceCountingAdapter<Contents>> (adapter);
  433. }
  434. //==============================================================================
  435. inline std::unique_ptr<AudioFormatReader> makeAudioFormatReader (AudioFormatManager& manager,
  436. const void* sampleData,
  437. size_t dataSize)
  438. {
  439. return std::unique_ptr<AudioFormatReader> (manager.createReaderFor (std::make_unique<MemoryInputStream> (sampleData,
  440. dataSize,
  441. false)));
  442. }
  443. inline std::unique_ptr<AudioFormatReader> makeAudioFormatReader (AudioFormatManager& manager,
  444. const File& file)
  445. {
  446. return std::unique_ptr<AudioFormatReader> (manager.createReaderFor (file));
  447. }
  448. //==============================================================================
  449. class AudioFormatReaderFactory
  450. {
  451. public:
  452. AudioFormatReaderFactory() = default;
  453. AudioFormatReaderFactory (const AudioFormatReaderFactory&) = default;
  454. AudioFormatReaderFactory (AudioFormatReaderFactory&&) = default;
  455. AudioFormatReaderFactory& operator= (const AudioFormatReaderFactory&) = default;
  456. AudioFormatReaderFactory& operator= (AudioFormatReaderFactory&&) = default;
  457. virtual ~AudioFormatReaderFactory() noexcept = default;
  458. virtual std::unique_ptr<AudioFormatReader> make (AudioFormatManager&) const = 0;
  459. virtual std::unique_ptr<AudioFormatReaderFactory> clone() const = 0;
  460. };
  461. //==============================================================================
  462. class MemoryAudioFormatReaderFactory : public AudioFormatReaderFactory
  463. {
  464. public:
  465. MemoryAudioFormatReaderFactory (const void* sampleDataIn, size_t dataSizeIn)
  466. : sampleData (sampleDataIn),
  467. dataSize (dataSizeIn)
  468. {}
  469. std::unique_ptr<AudioFormatReader> make (AudioFormatManager& manager) const override
  470. {
  471. return makeAudioFormatReader (manager, sampleData, dataSize);
  472. }
  473. std::unique_ptr<AudioFormatReaderFactory> clone() const override
  474. {
  475. return std::unique_ptr<AudioFormatReaderFactory> (new MemoryAudioFormatReaderFactory (*this));
  476. }
  477. private:
  478. const void* sampleData;
  479. size_t dataSize;
  480. };
  481. //==============================================================================
  482. class FileAudioFormatReaderFactory : public AudioFormatReaderFactory
  483. {
  484. public:
  485. explicit FileAudioFormatReaderFactory (File fileIn)
  486. : file (std::move (fileIn))
  487. {}
  488. std::unique_ptr<AudioFormatReader> make (AudioFormatManager& manager) const override
  489. {
  490. return makeAudioFormatReader (manager, file);
  491. }
  492. std::unique_ptr<AudioFormatReaderFactory> clone() const override
  493. {
  494. return std::unique_ptr<AudioFormatReaderFactory> (new FileAudioFormatReaderFactory (*this));
  495. }
  496. private:
  497. File file;
  498. };
  499. namespace juce
  500. {
  501. template<>
  502. struct VariantConverter<LoopMode>
  503. {
  504. static LoopMode fromVar (const var& v)
  505. {
  506. return static_cast<LoopMode> (int (v));
  507. }
  508. static var toVar (LoopMode loopMode)
  509. {
  510. return static_cast<int> (loopMode);
  511. }
  512. };
  513. template <typename Wrapped>
  514. struct GenericVariantConverter
  515. {
  516. static Wrapped fromVar (const var& v)
  517. {
  518. auto cast = dynamic_cast<ReferenceCountingAdapter<Wrapped>*> (v.getObject());
  519. jassert (cast != nullptr);
  520. return cast->get();
  521. }
  522. static var toVar (Wrapped range)
  523. {
  524. return { make_reference_counted<Wrapped> (std::move (range)).release() };
  525. }
  526. };
  527. template <typename Numeric>
  528. struct VariantConverter<Range<Numeric>> : GenericVariantConverter<Range<Numeric>> {};
  529. template<>
  530. struct VariantConverter<MPEZoneLayout> : GenericVariantConverter<MPEZoneLayout> {};
  531. template<>
  532. struct VariantConverter<std::shared_ptr<AudioFormatReaderFactory>>
  533. : GenericVariantConverter<std::shared_ptr<AudioFormatReaderFactory>>
  534. {};
  535. } // namespace juce
  536. //==============================================================================
  537. class VisibleRangeDataModel : private ValueTree::Listener
  538. {
  539. public:
  540. class Listener
  541. {
  542. public:
  543. virtual ~Listener() noexcept = default;
  544. virtual void totalRangeChanged (Range<double>) {}
  545. virtual void visibleRangeChanged (Range<double>) {}
  546. };
  547. VisibleRangeDataModel()
  548. : VisibleRangeDataModel (ValueTree (IDs::VISIBLE_RANGE))
  549. {}
  550. explicit VisibleRangeDataModel (const ValueTree& vt)
  551. : valueTree (vt),
  552. totalRange (valueTree, IDs::totalRange, nullptr),
  553. visibleRange (valueTree, IDs::visibleRange, nullptr)
  554. {
  555. jassert (valueTree.hasType (IDs::VISIBLE_RANGE));
  556. valueTree.addListener (this);
  557. }
  558. VisibleRangeDataModel (const VisibleRangeDataModel& other)
  559. : VisibleRangeDataModel (other.valueTree)
  560. {}
  561. VisibleRangeDataModel& operator= (const VisibleRangeDataModel& other)
  562. {
  563. auto copy (other);
  564. swap (copy);
  565. return *this;
  566. }
  567. Range<double> getTotalRange() const
  568. {
  569. return totalRange;
  570. }
  571. void setTotalRange (Range<double> value, UndoManager* undoManager)
  572. {
  573. totalRange.setValue (value, undoManager);
  574. setVisibleRange (visibleRange, undoManager);
  575. }
  576. Range<double> getVisibleRange() const
  577. {
  578. return visibleRange;
  579. }
  580. void setVisibleRange (Range<double> value, UndoManager* undoManager)
  581. {
  582. visibleRange.setValue (totalRange.get().constrainRange (value), undoManager);
  583. }
  584. void addListener (Listener& listener)
  585. {
  586. listenerList.add (&listener);
  587. }
  588. void removeListener (Listener& listener)
  589. {
  590. listenerList.remove (&listener);
  591. }
  592. void swap (VisibleRangeDataModel& other) noexcept
  593. {
  594. using std::swap;
  595. swap (other.valueTree, valueTree);
  596. }
  597. private:
  598. void valueTreePropertyChanged (ValueTree&, const Identifier& property) override
  599. {
  600. if (property == IDs::totalRange)
  601. {
  602. totalRange.forceUpdateOfCachedValue();
  603. listenerList.call ([this] (Listener& l) { l.totalRangeChanged (totalRange); });
  604. }
  605. else if (property == IDs::visibleRange)
  606. {
  607. visibleRange.forceUpdateOfCachedValue();
  608. listenerList.call ([this] (Listener& l) { l.visibleRangeChanged (visibleRange); });
  609. }
  610. }
  611. void valueTreeChildAdded (ValueTree&, ValueTree&) override { jassertfalse; }
  612. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { jassertfalse; }
  613. void valueTreeChildOrderChanged (ValueTree&, int, int) override { jassertfalse; }
  614. void valueTreeParentChanged (ValueTree&) override { jassertfalse; }
  615. ValueTree valueTree;
  616. CachedValue<Range<double>> totalRange;
  617. CachedValue<Range<double>> visibleRange;
  618. ListenerList<Listener> listenerList;
  619. };
  620. //==============================================================================
  621. class MPESettingsDataModel : private ValueTree::Listener
  622. {
  623. public:
  624. class Listener
  625. {
  626. public:
  627. virtual ~Listener() noexcept = default;
  628. virtual void synthVoicesChanged (int) {}
  629. virtual void voiceStealingEnabledChanged (bool) {}
  630. virtual void legacyModeEnabledChanged (bool) {}
  631. virtual void mpeZoneLayoutChanged (const MPEZoneLayout&) {}
  632. virtual void legacyFirstChannelChanged (int) {}
  633. virtual void legacyLastChannelChanged (int) {}
  634. virtual void legacyPitchbendRangeChanged (int) {}
  635. };
  636. MPESettingsDataModel()
  637. : MPESettingsDataModel (ValueTree (IDs::MPE_SETTINGS))
  638. {}
  639. explicit MPESettingsDataModel (const ValueTree& vt)
  640. : valueTree (vt),
  641. synthVoices (valueTree, IDs::synthVoices, nullptr, 15),
  642. voiceStealingEnabled (valueTree, IDs::voiceStealingEnabled, nullptr, false),
  643. legacyModeEnabled (valueTree, IDs::legacyModeEnabled, nullptr, true),
  644. mpeZoneLayout (valueTree, IDs::mpeZoneLayout, nullptr, {}),
  645. legacyFirstChannel (valueTree, IDs::legacyFirstChannel, nullptr, 1),
  646. legacyLastChannel (valueTree, IDs::legacyLastChannel, nullptr, 15),
  647. legacyPitchbendRange (valueTree, IDs::legacyPitchbendRange, nullptr, 48)
  648. {
  649. jassert (valueTree.hasType (IDs::MPE_SETTINGS));
  650. valueTree.addListener (this);
  651. }
  652. MPESettingsDataModel (const MPESettingsDataModel& other)
  653. : MPESettingsDataModel (other.valueTree)
  654. {}
  655. MPESettingsDataModel& operator= (const MPESettingsDataModel& other)
  656. {
  657. auto copy (other);
  658. swap (copy);
  659. return *this;
  660. }
  661. int getSynthVoices() const
  662. {
  663. return synthVoices;
  664. }
  665. void setSynthVoices (int value, UndoManager* undoManager)
  666. {
  667. synthVoices.setValue (Range<int> (1, 20).clipValue (value), undoManager);
  668. }
  669. bool getVoiceStealingEnabled() const
  670. {
  671. return voiceStealingEnabled;
  672. }
  673. void setVoiceStealingEnabled (bool value, UndoManager* undoManager)
  674. {
  675. voiceStealingEnabled.setValue (value, undoManager);
  676. }
  677. bool getLegacyModeEnabled() const
  678. {
  679. return legacyModeEnabled;
  680. }
  681. void setLegacyModeEnabled (bool value, UndoManager* undoManager)
  682. {
  683. legacyModeEnabled.setValue (value, undoManager);
  684. }
  685. MPEZoneLayout getMPEZoneLayout() const
  686. {
  687. return mpeZoneLayout;
  688. }
  689. void setMPEZoneLayout (MPEZoneLayout value, UndoManager* undoManager)
  690. {
  691. mpeZoneLayout.setValue (value, undoManager);
  692. }
  693. int getLegacyFirstChannel() const
  694. {
  695. return legacyFirstChannel;
  696. }
  697. void setLegacyFirstChannel (int value, UndoManager* undoManager)
  698. {
  699. legacyFirstChannel.setValue (Range<int> (1, legacyLastChannel).clipValue (value), undoManager);
  700. }
  701. int getLegacyLastChannel() const
  702. {
  703. return legacyLastChannel;
  704. }
  705. void setLegacyLastChannel (int value, UndoManager* undoManager)
  706. {
  707. legacyLastChannel.setValue (Range<int> (legacyFirstChannel, 15).clipValue (value), undoManager);
  708. }
  709. int getLegacyPitchbendRange() const
  710. {
  711. return legacyPitchbendRange;
  712. }
  713. void setLegacyPitchbendRange (int value, UndoManager* undoManager)
  714. {
  715. legacyPitchbendRange.setValue (Range<int> (0, 95).clipValue (value), undoManager);
  716. }
  717. void addListener (Listener& listener)
  718. {
  719. listenerList.add (&listener);
  720. }
  721. void removeListener (Listener& listener)
  722. {
  723. listenerList.remove (&listener);
  724. }
  725. void swap (MPESettingsDataModel& other) noexcept
  726. {
  727. using std::swap;
  728. swap (other.valueTree, valueTree);
  729. }
  730. private:
  731. void valueTreePropertyChanged (ValueTree&, const Identifier& property) override
  732. {
  733. if (property == IDs::synthVoices)
  734. {
  735. synthVoices.forceUpdateOfCachedValue();
  736. listenerList.call ([this] (Listener& l) { l.synthVoicesChanged (synthVoices); });
  737. }
  738. else if (property == IDs::voiceStealingEnabled)
  739. {
  740. voiceStealingEnabled.forceUpdateOfCachedValue();
  741. listenerList.call ([this] (Listener& l) { l.voiceStealingEnabledChanged (voiceStealingEnabled); });
  742. }
  743. else if (property == IDs::legacyModeEnabled)
  744. {
  745. legacyModeEnabled.forceUpdateOfCachedValue();
  746. listenerList.call ([this] (Listener& l) { l.legacyModeEnabledChanged (legacyModeEnabled); });
  747. }
  748. else if (property == IDs::mpeZoneLayout)
  749. {
  750. mpeZoneLayout.forceUpdateOfCachedValue();
  751. listenerList.call ([this] (Listener& l) { l.mpeZoneLayoutChanged (mpeZoneLayout); });
  752. }
  753. else if (property == IDs::legacyFirstChannel)
  754. {
  755. legacyFirstChannel.forceUpdateOfCachedValue();
  756. listenerList.call ([this] (Listener& l) { l.legacyFirstChannelChanged (legacyFirstChannel); });
  757. }
  758. else if (property == IDs::legacyLastChannel)
  759. {
  760. legacyLastChannel.forceUpdateOfCachedValue();
  761. listenerList.call ([this] (Listener& l) { l.legacyLastChannelChanged (legacyLastChannel); });
  762. }
  763. else if (property == IDs::legacyPitchbendRange)
  764. {
  765. legacyPitchbendRange.forceUpdateOfCachedValue();
  766. listenerList.call ([this] (Listener& l) { l.legacyPitchbendRangeChanged (legacyPitchbendRange); });
  767. }
  768. }
  769. void valueTreeChildAdded (ValueTree&, ValueTree&) override { jassertfalse; }
  770. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { jassertfalse; }
  771. void valueTreeChildOrderChanged (ValueTree&, int, int) override { jassertfalse; }
  772. void valueTreeParentChanged (ValueTree&) override { jassertfalse; }
  773. ValueTree valueTree;
  774. CachedValue<int> synthVoices;
  775. CachedValue<bool> voiceStealingEnabled;
  776. CachedValue<bool> legacyModeEnabled;
  777. CachedValue<MPEZoneLayout> mpeZoneLayout;
  778. CachedValue<int> legacyFirstChannel;
  779. CachedValue<int> legacyLastChannel;
  780. CachedValue<int> legacyPitchbendRange;
  781. ListenerList<Listener> listenerList;
  782. };
  783. //==============================================================================
  784. class DataModel : private ValueTree::Listener
  785. {
  786. public:
  787. class Listener
  788. {
  789. public:
  790. virtual ~Listener() noexcept = default;
  791. virtual void sampleReaderChanged (std::shared_ptr<AudioFormatReaderFactory>) {}
  792. virtual void centreFrequencyHzChanged (double) {}
  793. virtual void loopModeChanged (LoopMode) {}
  794. virtual void loopPointsSecondsChanged (Range<double>) {}
  795. };
  796. explicit DataModel (AudioFormatManager& audioFormatManagerIn)
  797. : DataModel (audioFormatManagerIn, ValueTree (IDs::DATA_MODEL))
  798. {}
  799. DataModel (AudioFormatManager& audioFormatManagerIn, const ValueTree& vt)
  800. : audioFormatManager (&audioFormatManagerIn),
  801. valueTree (vt),
  802. sampleReader (valueTree, IDs::sampleReader, nullptr),
  803. centreFrequencyHz (valueTree, IDs::centreFrequencyHz, nullptr),
  804. loopMode (valueTree, IDs::loopMode, nullptr, LoopMode::none),
  805. loopPointsSeconds (valueTree, IDs::loopPointsSeconds, nullptr)
  806. {
  807. jassert (valueTree.hasType (IDs::DATA_MODEL));
  808. valueTree.addListener (this);
  809. }
  810. DataModel (const DataModel& other)
  811. : DataModel (*other.audioFormatManager, other.valueTree)
  812. {}
  813. DataModel& operator= (const DataModel& other)
  814. {
  815. auto copy (other);
  816. swap (copy);
  817. return *this;
  818. }
  819. std::unique_ptr<AudioFormatReader> getSampleReader() const
  820. {
  821. return sampleReader != nullptr ? sampleReader.get()->make (*audioFormatManager) : nullptr;
  822. }
  823. void setSampleReader (std::unique_ptr<AudioFormatReaderFactory> readerFactory,
  824. UndoManager* undoManager)
  825. {
  826. sampleReader.setValue (move (readerFactory), undoManager);
  827. setLoopPointsSeconds (Range<double> (0, getSampleLengthSeconds()).constrainRange (loopPointsSeconds),
  828. undoManager);
  829. }
  830. double getSampleLengthSeconds() const
  831. {
  832. if (auto r = getSampleReader())
  833. return (double) r->lengthInSamples / r->sampleRate;
  834. return 1.0;
  835. }
  836. double getCentreFrequencyHz() const
  837. {
  838. return centreFrequencyHz;
  839. }
  840. void setCentreFrequencyHz (double value, UndoManager* undoManager)
  841. {
  842. centreFrequencyHz.setValue (Range<double> (20, 20000).clipValue (value),
  843. undoManager);
  844. }
  845. LoopMode getLoopMode() const
  846. {
  847. return loopMode;
  848. }
  849. void setLoopMode (LoopMode value, UndoManager* undoManager)
  850. {
  851. loopMode.setValue (value, undoManager);
  852. }
  853. Range<double> getLoopPointsSeconds() const
  854. {
  855. return loopPointsSeconds;
  856. }
  857. void setLoopPointsSeconds (Range<double> value, UndoManager* undoManager)
  858. {
  859. loopPointsSeconds.setValue (Range<double> (0, getSampleLengthSeconds()).constrainRange (value),
  860. undoManager);
  861. }
  862. MPESettingsDataModel mpeSettings()
  863. {
  864. return MPESettingsDataModel (valueTree.getOrCreateChildWithName (IDs::MPE_SETTINGS, nullptr));
  865. }
  866. void addListener (Listener& listener)
  867. {
  868. listenerList.add (&listener);
  869. }
  870. void removeListener (Listener& listener)
  871. {
  872. listenerList.remove (&listener);
  873. }
  874. void swap (DataModel& other) noexcept
  875. {
  876. using std::swap;
  877. swap (other.valueTree, valueTree);
  878. }
  879. AudioFormatManager& getAudioFormatManager() const
  880. {
  881. return *audioFormatManager;
  882. }
  883. private:
  884. void valueTreePropertyChanged (ValueTree&, const Identifier& property) override
  885. {
  886. if (property == IDs::sampleReader)
  887. {
  888. sampleReader.forceUpdateOfCachedValue();
  889. listenerList.call ([this] (Listener& l) { l.sampleReaderChanged (sampleReader); });
  890. }
  891. else if (property == IDs::centreFrequencyHz)
  892. {
  893. centreFrequencyHz.forceUpdateOfCachedValue();
  894. listenerList.call ([this] (Listener& l) { l.centreFrequencyHzChanged (centreFrequencyHz); });
  895. }
  896. else if (property == IDs::loopMode)
  897. {
  898. loopMode.forceUpdateOfCachedValue();
  899. listenerList.call ([this] (Listener& l) { l.loopModeChanged (loopMode); });
  900. }
  901. else if (property == IDs::loopPointsSeconds)
  902. {
  903. loopPointsSeconds.forceUpdateOfCachedValue();
  904. listenerList.call ([this] (Listener& l) { l.loopPointsSecondsChanged (loopPointsSeconds); });
  905. }
  906. }
  907. void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
  908. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { jassertfalse; }
  909. void valueTreeChildOrderChanged (ValueTree&, int, int) override { jassertfalse; }
  910. void valueTreeParentChanged (ValueTree&) override { jassertfalse; }
  911. AudioFormatManager* audioFormatManager;
  912. ValueTree valueTree;
  913. CachedValue<std::shared_ptr<AudioFormatReaderFactory>> sampleReader;
  914. CachedValue<double> centreFrequencyHz;
  915. CachedValue<LoopMode> loopMode;
  916. CachedValue<Range<double>> loopPointsSeconds;
  917. ListenerList<Listener> listenerList;
  918. };
  919. namespace
  920. {
  921. void initialiseComboBoxWithConsecutiveIntegers (Component& owner,
  922. ComboBox& comboBox,
  923. Label& label,
  924. int firstValue,
  925. int numValues,
  926. int valueToSelect)
  927. {
  928. for (auto i = 0; i < numValues; ++i)
  929. comboBox.addItem (String (i + firstValue), i + 1);
  930. comboBox.setSelectedId (valueToSelect - firstValue + 1);
  931. label.attachToComponent (&comboBox, true);
  932. owner.addAndMakeVisible (comboBox);
  933. }
  934. constexpr int controlHeight = 24;
  935. constexpr int controlSeparation = 6;
  936. } // namespace
  937. //==============================================================================
  938. class MPELegacySettingsComponent final : public Component,
  939. private MPESettingsDataModel::Listener
  940. {
  941. public:
  942. explicit MPELegacySettingsComponent (const MPESettingsDataModel& model,
  943. UndoManager& um)
  944. : dataModel (model),
  945. undoManager (&um)
  946. {
  947. dataModel.addListener (*this);
  948. initialiseComboBoxWithConsecutiveIntegers (*this, legacyStartChannel, legacyStartChannelLabel, 1, 16, 1);
  949. initialiseComboBoxWithConsecutiveIntegers (*this, legacyEndChannel, legacyEndChannelLabel, 1, 16, 16);
  950. initialiseComboBoxWithConsecutiveIntegers (*this, legacyPitchbendRange, legacyPitchbendRangeLabel, 0, 96, 2);
  951. legacyStartChannel.onChange = [this]
  952. {
  953. if (isLegacyModeValid())
  954. {
  955. undoManager->beginNewTransaction();
  956. dataModel.setLegacyFirstChannel (getFirstChannel(), undoManager);
  957. }
  958. };
  959. legacyEndChannel.onChange = [this]
  960. {
  961. if (isLegacyModeValid())
  962. {
  963. undoManager->beginNewTransaction();
  964. dataModel.setLegacyLastChannel (getLastChannel(), undoManager);
  965. }
  966. };
  967. legacyPitchbendRange.onChange = [this]
  968. {
  969. if (isLegacyModeValid())
  970. {
  971. undoManager->beginNewTransaction();
  972. dataModel.setLegacyPitchbendRange (legacyPitchbendRange.getText().getIntValue(), undoManager);
  973. }
  974. };
  975. }
  976. int getMinHeight() const
  977. {
  978. return (controlHeight * 3) + (controlSeparation * 2);
  979. }
  980. private:
  981. void resized() override
  982. {
  983. Rectangle<int> r (proportionOfWidth (0.65f), 0, proportionOfWidth (0.25f), getHeight());
  984. for (auto& comboBox : { &legacyStartChannel, &legacyEndChannel, &legacyPitchbendRange })
  985. {
  986. comboBox->setBounds (r.removeFromTop (controlHeight));
  987. r.removeFromTop (controlSeparation);
  988. }
  989. }
  990. bool isLegacyModeValid() const
  991. {
  992. if (! areLegacyModeParametersValid())
  993. {
  994. handleInvalidLegacyModeParameters();
  995. return false;
  996. }
  997. return true;
  998. }
  999. void legacyFirstChannelChanged (int value) override
  1000. {
  1001. legacyStartChannel.setSelectedId (value, dontSendNotification);
  1002. }
  1003. void legacyLastChannelChanged (int value) override
  1004. {
  1005. legacyEndChannel.setSelectedId (value, dontSendNotification);
  1006. }
  1007. void legacyPitchbendRangeChanged (int value) override
  1008. {
  1009. legacyPitchbendRange.setSelectedId (value + 1, dontSendNotification);
  1010. }
  1011. int getFirstChannel() const
  1012. {
  1013. return legacyStartChannel.getText().getIntValue();
  1014. }
  1015. int getLastChannel() const
  1016. {
  1017. return legacyEndChannel.getText().getIntValue();
  1018. }
  1019. bool areLegacyModeParametersValid() const
  1020. {
  1021. return getFirstChannel() <= getLastChannel();
  1022. }
  1023. void handleInvalidLegacyModeParameters() const
  1024. {
  1025. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  1026. "Invalid legacy mode channel layout",
  1027. "Cannot set legacy mode start/end channel:\n"
  1028. "The end channel must not be less than the start channel!",
  1029. "Got it");
  1030. }
  1031. MPESettingsDataModel dataModel;
  1032. ComboBox legacyStartChannel, legacyEndChannel, legacyPitchbendRange;
  1033. Label legacyStartChannelLabel { {}, "First channel" },
  1034. legacyEndChannelLabel { {}, "Last channel" },
  1035. legacyPitchbendRangeLabel { {}, "Pitchbend range (semitones)" };
  1036. UndoManager* undoManager;
  1037. };
  1038. //==============================================================================
  1039. class MPENewSettingsComponent final : public Component,
  1040. private MPESettingsDataModel::Listener
  1041. {
  1042. public:
  1043. MPENewSettingsComponent (const MPESettingsDataModel& model,
  1044. UndoManager& um)
  1045. : dataModel (model),
  1046. undoManager (&um)
  1047. {
  1048. dataModel.addListener (*this);
  1049. addAndMakeVisible (isLowerZoneButton);
  1050. isLowerZoneButton.setToggleState (true, NotificationType::dontSendNotification);
  1051. initialiseComboBoxWithConsecutiveIntegers (*this, memberChannels, memberChannelsLabel, 0, 16, 15);
  1052. initialiseComboBoxWithConsecutiveIntegers (*this, masterPitchbendRange, masterPitchbendRangeLabel, 0, 96, 2);
  1053. initialiseComboBoxWithConsecutiveIntegers (*this, notePitchbendRange, notePitchbendRangeLabel, 0, 96, 48);
  1054. for (auto& button : { &setZoneButton, &clearAllZonesButton })
  1055. addAndMakeVisible (button);
  1056. setZoneButton.onClick = [this]
  1057. {
  1058. auto isLowerZone = isLowerZoneButton.getToggleState();
  1059. auto numMemberChannels = memberChannels.getText().getIntValue();
  1060. auto perNotePb = notePitchbendRange.getText().getIntValue();
  1061. auto masterPb = masterPitchbendRange.getText().getIntValue();
  1062. if (isLowerZone)
  1063. zoneLayout.setLowerZone (numMemberChannels, perNotePb, masterPb);
  1064. else
  1065. zoneLayout.setUpperZone (numMemberChannels, perNotePb, masterPb);
  1066. undoManager->beginNewTransaction();
  1067. dataModel.setMPEZoneLayout (zoneLayout, undoManager);
  1068. };
  1069. clearAllZonesButton.onClick = [this]
  1070. {
  1071. zoneLayout.clearAllZones();
  1072. undoManager->beginNewTransaction();
  1073. dataModel.setMPEZoneLayout (zoneLayout, undoManager);
  1074. };
  1075. }
  1076. int getMinHeight() const
  1077. {
  1078. return (controlHeight * 6) + (controlSeparation * 6);
  1079. }
  1080. private:
  1081. void resized() override
  1082. {
  1083. Rectangle<int> r (proportionOfWidth (0.65f), 0, proportionOfWidth (0.25f), getHeight());
  1084. isLowerZoneButton.setBounds (r.removeFromTop (controlHeight));
  1085. r.removeFromTop (controlSeparation);
  1086. for (auto& comboBox : { &memberChannels, &masterPitchbendRange, &notePitchbendRange })
  1087. {
  1088. comboBox->setBounds (r.removeFromTop (controlHeight));
  1089. r.removeFromTop (controlSeparation);
  1090. }
  1091. r.removeFromTop (controlSeparation);
  1092. auto buttonLeft = proportionOfWidth (0.5f);
  1093. setZoneButton.setBounds (r.removeFromTop (controlHeight).withLeft (buttonLeft));
  1094. r.removeFromTop (controlSeparation);
  1095. clearAllZonesButton.setBounds (r.removeFromTop (controlHeight).withLeft (buttonLeft));
  1096. }
  1097. void mpeZoneLayoutChanged (const MPEZoneLayout& value) override
  1098. {
  1099. zoneLayout = value;
  1100. }
  1101. MPESettingsDataModel dataModel;
  1102. MPEZoneLayout zoneLayout;
  1103. ComboBox memberChannels, masterPitchbendRange, notePitchbendRange;
  1104. ToggleButton isLowerZoneButton { "Lower zone" };
  1105. Label memberChannelsLabel { {}, "Nr. of member channels" },
  1106. masterPitchbendRangeLabel { {}, "Master pitchbend range (semitones)" },
  1107. notePitchbendRangeLabel { {}, "Note pitchbend range (semitones)" };
  1108. TextButton setZoneButton { "Set zone" },
  1109. clearAllZonesButton { "Clear all zones" };
  1110. UndoManager* undoManager;
  1111. };
  1112. //==============================================================================
  1113. class MPESettingsComponent final : public Component,
  1114. private MPESettingsDataModel::Listener
  1115. {
  1116. public:
  1117. MPESettingsComponent (const MPESettingsDataModel& model,
  1118. UndoManager& um)
  1119. : dataModel (model),
  1120. legacySettings (dataModel, um),
  1121. newSettings (dataModel, um),
  1122. undoManager (&um)
  1123. {
  1124. dataModel.addListener (*this);
  1125. addAndMakeVisible (newSettings);
  1126. addChildComponent (legacySettings);
  1127. initialiseComboBoxWithConsecutiveIntegers (*this, numberOfVoices, numberOfVoicesLabel, 1, 20, 15);
  1128. numberOfVoices.onChange = [this]
  1129. {
  1130. undoManager->beginNewTransaction();
  1131. dataModel.setSynthVoices (numberOfVoices.getText().getIntValue(), undoManager);
  1132. };
  1133. for (auto& button : { &legacyModeEnabledToggle, &voiceStealingEnabledToggle })
  1134. {
  1135. addAndMakeVisible (button);
  1136. }
  1137. legacyModeEnabledToggle.onClick = [this]
  1138. {
  1139. undoManager->beginNewTransaction();
  1140. dataModel.setLegacyModeEnabled (legacyModeEnabledToggle.getToggleState(), undoManager);
  1141. };
  1142. voiceStealingEnabledToggle.onClick = [this]
  1143. {
  1144. undoManager->beginNewTransaction();
  1145. dataModel.setVoiceStealingEnabled (voiceStealingEnabledToggle.getToggleState(), undoManager);
  1146. };
  1147. }
  1148. private:
  1149. void resized() override
  1150. {
  1151. auto topHeight = jmax (legacySettings.getMinHeight(), newSettings.getMinHeight());
  1152. auto r = getLocalBounds();
  1153. r.removeFromTop (15);
  1154. auto top = r.removeFromTop (topHeight);
  1155. legacySettings.setBounds (top);
  1156. newSettings.setBounds (top);
  1157. r.removeFromLeft (proportionOfWidth (0.65f));
  1158. r = r.removeFromLeft (proportionOfWidth (0.25f));
  1159. auto toggleLeft = proportionOfWidth (0.25f);
  1160. legacyModeEnabledToggle.setBounds (r.removeFromTop (controlHeight).withLeft (toggleLeft));
  1161. r.removeFromTop (controlSeparation);
  1162. voiceStealingEnabledToggle.setBounds (r.removeFromTop (controlHeight).withLeft (toggleLeft));
  1163. r.removeFromTop (controlSeparation);
  1164. numberOfVoices.setBounds (r.removeFromTop (controlHeight));
  1165. }
  1166. void legacyModeEnabledChanged (bool value) override
  1167. {
  1168. legacySettings.setVisible (value);
  1169. newSettings.setVisible (! value);
  1170. legacyModeEnabledToggle.setToggleState (value, dontSendNotification);
  1171. }
  1172. void voiceStealingEnabledChanged (bool value) override
  1173. {
  1174. voiceStealingEnabledToggle.setToggleState (value, dontSendNotification);
  1175. }
  1176. void synthVoicesChanged (int value) override
  1177. {
  1178. numberOfVoices.setSelectedId (value, dontSendNotification);
  1179. }
  1180. MPESettingsDataModel dataModel;
  1181. MPELegacySettingsComponent legacySettings;
  1182. MPENewSettingsComponent newSettings;
  1183. ToggleButton legacyModeEnabledToggle { "Enable Legacy Mode" },
  1184. voiceStealingEnabledToggle { "Enable synth voice stealing" };
  1185. ComboBox numberOfVoices;
  1186. Label numberOfVoicesLabel { {}, "Number of synth voices" };
  1187. UndoManager* undoManager;
  1188. };
  1189. //==============================================================================
  1190. class LoopPointMarker : public Component
  1191. {
  1192. public:
  1193. using MouseCallback = std::function<void (LoopPointMarker&, const MouseEvent&)>;
  1194. LoopPointMarker (String marker,
  1195. MouseCallback onMouseDownIn,
  1196. MouseCallback onMouseDragIn,
  1197. MouseCallback onMouseUpIn)
  1198. : text (std::move (marker)),
  1199. onMouseDown (std::move (onMouseDownIn)),
  1200. onMouseDrag (std::move (onMouseDragIn)),
  1201. onMouseUp (std::move (onMouseUpIn))
  1202. {
  1203. setMouseCursor (MouseCursor::LeftRightResizeCursor);
  1204. }
  1205. private:
  1206. void resized() override
  1207. {
  1208. auto height = 20;
  1209. auto triHeight = 6;
  1210. auto bounds = getLocalBounds();
  1211. Path newPath;
  1212. newPath.addRectangle (bounds.removeFromBottom (height));
  1213. newPath.startNewSubPath (bounds.getBottomLeft().toFloat());
  1214. newPath.lineTo (bounds.getBottomRight().toFloat());
  1215. Point<float> apex (static_cast<float> (bounds.getX() + (bounds.getWidth() / 2)),
  1216. static_cast<float> (bounds.getBottom() - triHeight));
  1217. newPath.lineTo (apex);
  1218. newPath.closeSubPath();
  1219. newPath.addLineSegment (Line<float> (apex, Point<float> (apex.getX(), 0)), 1);
  1220. path = newPath;
  1221. }
  1222. void paint (Graphics& g) override
  1223. {
  1224. g.setColour (Colours::deepskyblue);
  1225. g.fillPath (path);
  1226. auto height = 20;
  1227. g.setColour (Colours::white);
  1228. g.drawText (text, getLocalBounds().removeFromBottom (height), Justification::centred);
  1229. }
  1230. bool hitTest (int x, int y) override
  1231. {
  1232. return path.contains ((float) x, (float) y);
  1233. }
  1234. void mouseDown (const MouseEvent& e) override
  1235. {
  1236. onMouseDown (*this, e);
  1237. }
  1238. void mouseDrag (const MouseEvent& e) override
  1239. {
  1240. onMouseDrag (*this, e);
  1241. }
  1242. void mouseUp (const MouseEvent& e) override
  1243. {
  1244. onMouseUp (*this, e);
  1245. }
  1246. String text;
  1247. Path path;
  1248. MouseCallback onMouseDown;
  1249. MouseCallback onMouseDrag;
  1250. MouseCallback onMouseUp;
  1251. };
  1252. //==============================================================================
  1253. class Ruler : public Component,
  1254. private VisibleRangeDataModel::Listener
  1255. {
  1256. public:
  1257. explicit Ruler (const VisibleRangeDataModel& model)
  1258. : visibleRange (model)
  1259. {
  1260. visibleRange.addListener (*this);
  1261. setMouseCursor (MouseCursor::LeftRightResizeCursor);
  1262. }
  1263. private:
  1264. void paint (Graphics& g) override
  1265. {
  1266. auto minDivisionWidth = 50.0f;
  1267. auto maxDivisions = (float) getWidth() / minDivisionWidth;
  1268. auto lookFeel = dynamic_cast<LookAndFeel_V4*> (&getLookAndFeel());
  1269. auto bg = lookFeel->getCurrentColourScheme()
  1270. .getUIColour (LookAndFeel_V4::ColourScheme::UIColour::widgetBackground);
  1271. g.setGradientFill (ColourGradient (bg.brighter(),
  1272. 0,
  1273. 0,
  1274. bg.darker(),
  1275. 0,
  1276. (float) getHeight(),
  1277. false));
  1278. g.fillAll();
  1279. g.setColour (bg.brighter());
  1280. g.drawHorizontalLine (0, 0.0f, (float) getWidth());
  1281. g.setColour (bg.darker());
  1282. g.drawHorizontalLine (1, 0.0f, (float) getWidth());
  1283. g.setColour (Colours::lightgrey);
  1284. auto minLog = std::ceil (std::log10 (visibleRange.getVisibleRange().getLength() / maxDivisions));
  1285. auto precision = 2 + std::abs (minLog);
  1286. auto divisionMagnitude = std::pow (10, minLog);
  1287. auto startingDivision = std::ceil (visibleRange.getVisibleRange().getStart() / divisionMagnitude);
  1288. for (auto div = startingDivision; div * divisionMagnitude < visibleRange.getVisibleRange().getEnd(); ++div)
  1289. {
  1290. auto time = div * divisionMagnitude;
  1291. auto xPos = (time - visibleRange.getVisibleRange().getStart()) * getWidth()
  1292. / visibleRange.getVisibleRange().getLength();
  1293. std::ostringstream outStream;
  1294. outStream << std::setprecision (roundToInt (precision)) << time;
  1295. const auto bounds = Rectangle<int> (Point<int> (roundToInt (xPos) + 3, 0),
  1296. Point<int> (roundToInt (xPos + minDivisionWidth), getHeight()));
  1297. g.drawText (outStream.str(), bounds, Justification::centredLeft, false);
  1298. g.drawVerticalLine (roundToInt (xPos), 2.0f, (float) getHeight());
  1299. }
  1300. }
  1301. void mouseDown (const MouseEvent& e) override
  1302. {
  1303. visibleRangeOnMouseDown = visibleRange.getVisibleRange();
  1304. timeOnMouseDown = visibleRange.getVisibleRange().getStart()
  1305. + (visibleRange.getVisibleRange().getLength() * e.getMouseDownX()) / getWidth();
  1306. }
  1307. void mouseDrag (const MouseEvent& e) override
  1308. {
  1309. // Work out the scale of the new range
  1310. auto unitDistance = 100.0f;
  1311. auto scaleFactor = 1.0 / std::pow (2, (float) e.getDistanceFromDragStartY() / unitDistance);
  1312. // Now position it so that the mouse continues to point at the same
  1313. // place on the ruler.
  1314. auto visibleLength = std::max (0.12, visibleRangeOnMouseDown.getLength() * scaleFactor);
  1315. auto rangeBegin = timeOnMouseDown - visibleLength * e.x / getWidth();
  1316. const Range<double> range (rangeBegin, rangeBegin + visibleLength);
  1317. visibleRange.setVisibleRange (range, nullptr);
  1318. }
  1319. void visibleRangeChanged (Range<double>) override
  1320. {
  1321. repaint();
  1322. }
  1323. VisibleRangeDataModel visibleRange;
  1324. Range<double> visibleRangeOnMouseDown;
  1325. double timeOnMouseDown;
  1326. };
  1327. //==============================================================================
  1328. class LoopPointsOverlay : public Component,
  1329. private DataModel::Listener,
  1330. private VisibleRangeDataModel::Listener
  1331. {
  1332. public:
  1333. LoopPointsOverlay (const DataModel& dModel,
  1334. const VisibleRangeDataModel& vModel,
  1335. UndoManager& undoManagerIn)
  1336. : dataModel (dModel),
  1337. visibleRange (vModel),
  1338. beginMarker ("B",
  1339. [this] (LoopPointMarker& m, const MouseEvent& e) { this->loopPointMouseDown (m, e); },
  1340. [this] (LoopPointMarker& m, const MouseEvent& e) { this->loopPointDragged (m, e); },
  1341. [this] (LoopPointMarker& m, const MouseEvent& e) { this->loopPointMouseUp (m, e); }),
  1342. endMarker ("E",
  1343. [this] (LoopPointMarker& m, const MouseEvent& e) { this->loopPointMouseDown (m, e); },
  1344. [this] (LoopPointMarker& m, const MouseEvent& e) { this->loopPointDragged (m, e); },
  1345. [this] (LoopPointMarker& m, const MouseEvent& e) { this->loopPointMouseUp (m, e); }),
  1346. undoManager (&undoManagerIn)
  1347. {
  1348. dataModel .addListener (*this);
  1349. visibleRange.addListener (*this);
  1350. for (auto ptr : { &beginMarker, &endMarker })
  1351. addAndMakeVisible (ptr);
  1352. }
  1353. private:
  1354. void resized() override
  1355. {
  1356. positionLoopPointMarkers();
  1357. }
  1358. void loopPointMouseDown (LoopPointMarker&, const MouseEvent&)
  1359. {
  1360. loopPointsOnMouseDown = dataModel.getLoopPointsSeconds();
  1361. undoManager->beginNewTransaction();
  1362. }
  1363. void loopPointDragged (LoopPointMarker& marker, const MouseEvent& e)
  1364. {
  1365. auto x = xPositionToTime (e.getEventRelativeTo (this).position.x);
  1366. const Range<double> newLoopRange (&marker == &beginMarker ? x : loopPointsOnMouseDown.getStart(),
  1367. &marker == &endMarker ? x : loopPointsOnMouseDown.getEnd());
  1368. dataModel.setLoopPointsSeconds (newLoopRange, undoManager);
  1369. }
  1370. void loopPointMouseUp (LoopPointMarker& marker, const MouseEvent& e)
  1371. {
  1372. auto x = xPositionToTime (e.getEventRelativeTo (this).position.x);
  1373. const Range<double> newLoopRange (&marker == &beginMarker ? x : loopPointsOnMouseDown.getStart(),
  1374. &marker == &endMarker ? x : loopPointsOnMouseDown.getEnd());
  1375. dataModel.setLoopPointsSeconds (newLoopRange, undoManager);
  1376. }
  1377. void loopPointsSecondsChanged (Range<double>) override
  1378. {
  1379. positionLoopPointMarkers();
  1380. }
  1381. void visibleRangeChanged (Range<double>) override
  1382. {
  1383. positionLoopPointMarkers();
  1384. }
  1385. double timeToXPosition (double time) const
  1386. {
  1387. return (time - visibleRange.getVisibleRange().getStart()) * getWidth()
  1388. / visibleRange.getVisibleRange().getLength();
  1389. }
  1390. double xPositionToTime (double xPosition) const
  1391. {
  1392. return ((xPosition * visibleRange.getVisibleRange().getLength()) / getWidth())
  1393. + visibleRange.getVisibleRange().getStart();
  1394. }
  1395. void positionLoopPointMarkers()
  1396. {
  1397. auto halfMarkerWidth = 7;
  1398. for (auto tup : { std::make_tuple (&beginMarker, dataModel.getLoopPointsSeconds().getStart()),
  1399. std::make_tuple (&endMarker, dataModel.getLoopPointsSeconds().getEnd()) })
  1400. {
  1401. auto ptr = std::get<0> (tup);
  1402. auto time = std::get<1> (tup);
  1403. ptr->setSize (halfMarkerWidth * 2, getHeight());
  1404. ptr->setTopLeftPosition (roundToInt (timeToXPosition (time) - halfMarkerWidth), 0);
  1405. }
  1406. }
  1407. DataModel dataModel;
  1408. VisibleRangeDataModel visibleRange;
  1409. Range<double> loopPointsOnMouseDown;
  1410. LoopPointMarker beginMarker, endMarker;
  1411. UndoManager* undoManager;
  1412. };
  1413. //==============================================================================
  1414. class PlaybackPositionOverlay : public Component,
  1415. private Timer,
  1416. private VisibleRangeDataModel::Listener
  1417. {
  1418. public:
  1419. using Provider = std::function<std::vector<float>()>;
  1420. PlaybackPositionOverlay (const VisibleRangeDataModel& model,
  1421. Provider providerIn)
  1422. : visibleRange (model),
  1423. provider (std::move (providerIn))
  1424. {
  1425. visibleRange.addListener (*this);
  1426. startTimer (16);
  1427. }
  1428. private:
  1429. void paint (Graphics& g) override
  1430. {
  1431. g.setColour (Colours::red);
  1432. for (auto position : provider())
  1433. {
  1434. g.drawVerticalLine (roundToInt (timeToXPosition (position)), 0.0f, (float) getHeight());
  1435. }
  1436. }
  1437. void timerCallback() override
  1438. {
  1439. repaint();
  1440. }
  1441. void visibleRangeChanged (Range<double>) override
  1442. {
  1443. repaint();
  1444. }
  1445. double timeToXPosition (double time) const
  1446. {
  1447. return (time - visibleRange.getVisibleRange().getStart()) * getWidth()
  1448. / visibleRange.getVisibleRange().getLength();
  1449. }
  1450. VisibleRangeDataModel visibleRange;
  1451. Provider provider;
  1452. };
  1453. //==============================================================================
  1454. class WaveformView : public Component,
  1455. private ChangeListener,
  1456. private DataModel::Listener,
  1457. private VisibleRangeDataModel::Listener
  1458. {
  1459. public:
  1460. WaveformView (const DataModel& model,
  1461. const VisibleRangeDataModel& vr)
  1462. : dataModel (model),
  1463. visibleRange (vr),
  1464. thumbnailCache (4),
  1465. thumbnail (4, dataModel.getAudioFormatManager(), thumbnailCache)
  1466. {
  1467. dataModel .addListener (*this);
  1468. visibleRange.addListener (*this);
  1469. thumbnail .addChangeListener (this);
  1470. }
  1471. private:
  1472. void paint (Graphics& g) override
  1473. {
  1474. // Draw the waveforms
  1475. g.fillAll (Colours::black);
  1476. auto numChannels = thumbnail.getNumChannels();
  1477. if (numChannels == 0)
  1478. {
  1479. g.setColour (Colours::white);
  1480. g.drawFittedText ("No File Loaded", getLocalBounds(), Justification::centred, 1);
  1481. return;
  1482. }
  1483. auto bounds = getLocalBounds();
  1484. auto channelHeight = bounds.getHeight() / numChannels;
  1485. for (auto i = 0; i != numChannels; ++i)
  1486. {
  1487. drawChannel (g, i, bounds.removeFromTop (channelHeight));
  1488. }
  1489. }
  1490. void changeListenerCallback (ChangeBroadcaster* source) override
  1491. {
  1492. if (source == &thumbnail)
  1493. repaint();
  1494. }
  1495. void sampleReaderChanged (std::shared_ptr<AudioFormatReaderFactory> value) override
  1496. {
  1497. if (value != nullptr)
  1498. {
  1499. if (auto reader = value->make (dataModel.getAudioFormatManager()))
  1500. {
  1501. thumbnail.setReader (reader.release(), currentHashCode);
  1502. currentHashCode += 1;
  1503. return;
  1504. }
  1505. }
  1506. thumbnail.clear();
  1507. }
  1508. void visibleRangeChanged (Range<double>) override
  1509. {
  1510. repaint();
  1511. }
  1512. void drawChannel (Graphics& g, int channel, Rectangle<int> bounds)
  1513. {
  1514. g.setGradientFill (ColourGradient (Colours::lightblue,
  1515. bounds.getTopLeft().toFloat(),
  1516. Colours::darkgrey,
  1517. bounds.getBottomLeft().toFloat(),
  1518. false));
  1519. thumbnail.drawChannel (g,
  1520. bounds,
  1521. visibleRange.getVisibleRange().getStart(),
  1522. visibleRange.getVisibleRange().getEnd(),
  1523. channel,
  1524. 1.0f);
  1525. }
  1526. DataModel dataModel;
  1527. VisibleRangeDataModel visibleRange;
  1528. AudioThumbnailCache thumbnailCache;
  1529. AudioThumbnail thumbnail;
  1530. int64 currentHashCode = 0;
  1531. };
  1532. //==============================================================================
  1533. class WaveformEditor : public Component,
  1534. private DataModel::Listener
  1535. {
  1536. public:
  1537. WaveformEditor (const DataModel& model,
  1538. PlaybackPositionOverlay::Provider provider,
  1539. UndoManager& undoManager)
  1540. : dataModel (model),
  1541. waveformView (model, visibleRange),
  1542. playbackOverlay (visibleRange, move (provider)),
  1543. loopPoints (dataModel, visibleRange, undoManager),
  1544. ruler (visibleRange)
  1545. {
  1546. dataModel.addListener (*this);
  1547. addAndMakeVisible (waveformView);
  1548. addAndMakeVisible (playbackOverlay);
  1549. addChildComponent (loopPoints);
  1550. loopPoints.setAlwaysOnTop (true);
  1551. waveformView.toBack();
  1552. addAndMakeVisible (ruler);
  1553. }
  1554. private:
  1555. void resized() override
  1556. {
  1557. auto bounds = getLocalBounds();
  1558. ruler .setBounds (bounds.removeFromTop (25));
  1559. waveformView .setBounds (bounds);
  1560. playbackOverlay.setBounds (bounds);
  1561. loopPoints .setBounds (bounds);
  1562. }
  1563. void loopModeChanged (LoopMode value) override
  1564. {
  1565. loopPoints.setVisible (value != LoopMode::none);
  1566. }
  1567. void sampleReaderChanged (std::shared_ptr<AudioFormatReaderFactory>) override
  1568. {
  1569. auto lengthInSeconds = dataModel.getSampleLengthSeconds();
  1570. visibleRange.setTotalRange (Range<double> (0, lengthInSeconds), nullptr);
  1571. visibleRange.setVisibleRange (Range<double> (0, lengthInSeconds), nullptr);
  1572. }
  1573. DataModel dataModel;
  1574. VisibleRangeDataModel visibleRange;
  1575. WaveformView waveformView;
  1576. PlaybackPositionOverlay playbackOverlay;
  1577. LoopPointsOverlay loopPoints;
  1578. Ruler ruler;
  1579. };
  1580. //==============================================================================
  1581. class MainSamplerView : public Component,
  1582. private DataModel::Listener,
  1583. private ChangeListener
  1584. {
  1585. public:
  1586. MainSamplerView (const DataModel& model,
  1587. PlaybackPositionOverlay::Provider provider,
  1588. UndoManager& um)
  1589. : dataModel (model),
  1590. waveformEditor (dataModel, move (provider), um),
  1591. undoManager (um)
  1592. {
  1593. dataModel.addListener (*this);
  1594. addAndMakeVisible (waveformEditor);
  1595. addAndMakeVisible (loadNewSampleButton);
  1596. addAndMakeVisible (undoButton);
  1597. addAndMakeVisible (redoButton);
  1598. auto setReader = [this] (const FileChooser& fc)
  1599. {
  1600. const auto result = fc.getResult();
  1601. if (result != File())
  1602. {
  1603. undoManager.beginNewTransaction();
  1604. auto readerFactory = new FileAudioFormatReaderFactory (result);
  1605. dataModel.setSampleReader (std::unique_ptr<AudioFormatReaderFactory> (readerFactory),
  1606. &undoManager);
  1607. }
  1608. };
  1609. loadNewSampleButton.onClick = [this, setReader]
  1610. {
  1611. fileChooser.launchAsync (FileBrowserComponent::FileChooserFlags::openMode |
  1612. FileBrowserComponent::FileChooserFlags::canSelectFiles,
  1613. setReader);
  1614. };
  1615. addAndMakeVisible (centreFrequency);
  1616. centreFrequency.onValueChange = [this]
  1617. {
  1618. undoManager.beginNewTransaction();
  1619. dataModel.setCentreFrequencyHz (centreFrequency.getValue(),
  1620. centreFrequency.isMouseButtonDown() ? nullptr : &undoManager);
  1621. };
  1622. centreFrequency.setRange (20, 20000, 1);
  1623. centreFrequency.setSliderStyle (Slider::SliderStyle::IncDecButtons);
  1624. centreFrequency.setIncDecButtonsMode (Slider::IncDecButtonMode::incDecButtonsDraggable_Vertical);
  1625. auto radioGroupId = 1;
  1626. for (auto buttonPtr : { &loopKindNone, &loopKindForward, &loopKindPingpong })
  1627. {
  1628. addAndMakeVisible (buttonPtr);
  1629. buttonPtr->setRadioGroupId (radioGroupId, dontSendNotification);
  1630. buttonPtr->setClickingTogglesState (true);
  1631. }
  1632. loopKindNone.onClick = [this]
  1633. {
  1634. if (loopKindNone.getToggleState())
  1635. {
  1636. undoManager.beginNewTransaction();
  1637. dataModel.setLoopMode (LoopMode::none, &undoManager);
  1638. }
  1639. };
  1640. loopKindForward.onClick = [this]
  1641. {
  1642. if (loopKindForward.getToggleState())
  1643. {
  1644. undoManager.beginNewTransaction();
  1645. dataModel.setLoopMode (LoopMode::forward, &undoManager);
  1646. }
  1647. };
  1648. loopKindPingpong.onClick = [this]
  1649. {
  1650. if (loopKindPingpong.getToggleState())
  1651. {
  1652. undoManager.beginNewTransaction();
  1653. dataModel.setLoopMode (LoopMode::pingpong, &undoManager);
  1654. }
  1655. };
  1656. undoButton.onClick = [this] { undoManager.undo(); };
  1657. redoButton.onClick = [this] { undoManager.redo(); };
  1658. addAndMakeVisible (centreFrequencyLabel);
  1659. addAndMakeVisible (loopKindLabel);
  1660. changeListenerCallback (&undoManager);
  1661. undoManager.addChangeListener (this);
  1662. }
  1663. ~MainSamplerView() override
  1664. {
  1665. undoManager.removeChangeListener (this);
  1666. }
  1667. private:
  1668. void changeListenerCallback (ChangeBroadcaster* source) override
  1669. {
  1670. if (source == &undoManager)
  1671. {
  1672. undoButton.setEnabled (undoManager.canUndo());
  1673. redoButton.setEnabled (undoManager.canRedo());
  1674. }
  1675. }
  1676. void resized() override
  1677. {
  1678. auto bounds = getLocalBounds();
  1679. auto topBar = bounds.removeFromTop (50);
  1680. auto padding = 4;
  1681. loadNewSampleButton .setBounds (topBar.removeFromRight (100).reduced (padding));
  1682. redoButton .setBounds (topBar.removeFromRight (100).reduced (padding));
  1683. undoButton .setBounds (topBar.removeFromRight (100).reduced (padding));
  1684. centreFrequencyLabel.setBounds (topBar.removeFromLeft (100).reduced (padding));
  1685. centreFrequency .setBounds (topBar.removeFromLeft (100).reduced (padding));
  1686. auto bottomBar = bounds.removeFromBottom (50);
  1687. loopKindLabel .setBounds (bottomBar.removeFromLeft (100).reduced (padding));
  1688. loopKindNone .setBounds (bottomBar.removeFromLeft (80) .reduced (padding));
  1689. loopKindForward .setBounds (bottomBar.removeFromLeft (80) .reduced (padding));
  1690. loopKindPingpong.setBounds (bottomBar.removeFromLeft (80) .reduced (padding));
  1691. waveformEditor.setBounds (bounds);
  1692. }
  1693. void loopModeChanged (LoopMode value) override
  1694. {
  1695. switch (value)
  1696. {
  1697. case LoopMode::none:
  1698. loopKindNone.setToggleState (true, dontSendNotification);
  1699. break;
  1700. case LoopMode::forward:
  1701. loopKindForward.setToggleState (true, dontSendNotification);
  1702. break;
  1703. case LoopMode::pingpong:
  1704. loopKindPingpong.setToggleState (true, dontSendNotification);
  1705. break;
  1706. default:
  1707. break;
  1708. }
  1709. }
  1710. void centreFrequencyHzChanged (double value) override
  1711. {
  1712. centreFrequency.setValue (value, dontSendNotification);
  1713. }
  1714. DataModel dataModel;
  1715. WaveformEditor waveformEditor;
  1716. TextButton loadNewSampleButton { "Load New Sample" };
  1717. TextButton undoButton { "Undo" };
  1718. TextButton redoButton { "Redo" };
  1719. Slider centreFrequency;
  1720. TextButton loopKindNone { "None" },
  1721. loopKindForward { "Forward" },
  1722. loopKindPingpong { "Ping Pong" };
  1723. Label centreFrequencyLabel { {}, "Sample Centre Freq / Hz" },
  1724. loopKindLabel { {}, "Looping Mode" };
  1725. FileChooser fileChooser { "Select a file to load...", File(),
  1726. dataModel.getAudioFormatManager().getWildcardForAllFormats() };
  1727. UndoManager& undoManager;
  1728. };
  1729. //==============================================================================
  1730. struct ProcessorState
  1731. {
  1732. int synthVoices;
  1733. bool legacyModeEnabled;
  1734. Range<int> legacyChannels;
  1735. int legacyPitchbendRange;
  1736. bool voiceStealingEnabled;
  1737. MPEZoneLayout mpeZoneLayout;
  1738. std::unique_ptr<AudioFormatReaderFactory> readerFactory;
  1739. Range<double> loopPointsSeconds;
  1740. double centreFrequencyHz;
  1741. LoopMode loopMode;
  1742. };
  1743. //==============================================================================
  1744. class SamplerAudioProcessor : public AudioProcessor
  1745. {
  1746. public:
  1747. SamplerAudioProcessor()
  1748. : AudioProcessor (BusesProperties().withOutput ("Output", AudioChannelSet::stereo(), true))
  1749. {
  1750. if (auto inputStream = createAssetInputStream ("cello.wav"))
  1751. {
  1752. inputStream->readIntoMemoryBlock (mb);
  1753. readerFactory.reset (new MemoryAudioFormatReaderFactory (mb.getData(), mb.getSize()));
  1754. }
  1755. // Set up initial sample, which we load from a binary resource
  1756. AudioFormatManager manager;
  1757. manager.registerBasicFormats();
  1758. auto reader = readerFactory->make (manager);
  1759. jassert (reader != nullptr); // Failed to load resource!
  1760. auto sound = samplerSound;
  1761. auto sample = std::unique_ptr<Sample> (new Sample (*reader, 10.0));
  1762. auto lengthInSeconds = sample->getLength() / sample->getSampleRate();
  1763. sound->setLoopPointsInSeconds ({lengthInSeconds * 0.1, lengthInSeconds * 0.9 });
  1764. sound->setSample (move (sample));
  1765. // Start with the max number of voices
  1766. for (auto i = 0; i != maxVoices; ++i)
  1767. synthesiser.addVoice (new MPESamplerVoice (sound));
  1768. }
  1769. void prepareToPlay (double sampleRate, int) override
  1770. {
  1771. synthesiser.setCurrentPlaybackSampleRate (sampleRate);
  1772. }
  1773. void releaseResources() override {}
  1774. bool isBusesLayoutSupported (const BusesLayout& layouts) const override
  1775. {
  1776. return layouts.getMainOutputChannelSet() == AudioChannelSet::mono()
  1777. || layouts.getMainOutputChannelSet() == AudioChannelSet::stereo();
  1778. }
  1779. //==============================================================================
  1780. AudioProcessorEditor* createEditor() override
  1781. {
  1782. // This function will be called from the message thread. We lock the command
  1783. // queue to ensure that no messages are processed for the duration of this
  1784. // call.
  1785. SpinLock::ScopedLockType lock (commandQueueMutex);
  1786. ProcessorState state;
  1787. state.synthVoices = synthesiser.getNumVoices();
  1788. state.legacyModeEnabled = synthesiser.isLegacyModeEnabled();
  1789. state.legacyChannels = synthesiser.getLegacyModeChannelRange();
  1790. state.legacyPitchbendRange = synthesiser.getLegacyModePitchbendRange();
  1791. state.voiceStealingEnabled = synthesiser.isVoiceStealingEnabled();
  1792. state.mpeZoneLayout = synthesiser.getZoneLayout();
  1793. state.readerFactory = readerFactory == nullptr ? nullptr : readerFactory->clone();
  1794. auto sound = samplerSound;
  1795. state.loopPointsSeconds = sound->getLoopPointsInSeconds();
  1796. state.centreFrequencyHz = sound->getCentreFrequencyInHz();
  1797. state.loopMode = sound->getLoopMode();
  1798. return new SamplerAudioProcessorEditor (*this, std::move (state));
  1799. }
  1800. bool hasEditor() const override { return true; }
  1801. //==============================================================================
  1802. const String getName() const override { return "SamplerPlugin"; }
  1803. bool acceptsMidi() const override { return true; }
  1804. bool producesMidi() const override { return false; }
  1805. bool isMidiEffect() const override { return false; }
  1806. double getTailLengthSeconds() const override { return 0.0; }
  1807. //==============================================================================
  1808. int getNumPrograms() override { return 1; }
  1809. int getCurrentProgram() override { return 0; }
  1810. void setCurrentProgram (int) override {}
  1811. const String getProgramName (int) override { return "None"; }
  1812. void changeProgramName (int, const String&) override {}
  1813. //==============================================================================
  1814. void getStateInformation (MemoryBlock&) override {}
  1815. void setStateInformation (const void*, int) override {}
  1816. //==============================================================================
  1817. void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midi) override
  1818. {
  1819. process (buffer, midi);
  1820. }
  1821. void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midi) override
  1822. {
  1823. process (buffer, midi);
  1824. }
  1825. // These should be called from the GUI thread, and will block until the
  1826. // command buffer has enough room to accept a command.
  1827. void setSample (std::unique_ptr<AudioFormatReaderFactory> fact, AudioFormatManager& formatManager)
  1828. {
  1829. class SetSampleCommand
  1830. {
  1831. public:
  1832. SetSampleCommand (std::unique_ptr<AudioFormatReaderFactory> r,
  1833. std::unique_ptr<Sample> sampleIn,
  1834. std::vector<std::unique_ptr<MPESamplerVoice>> newVoicesIn)
  1835. : readerFactory (std::move (r)),
  1836. sample (std::move (sampleIn)),
  1837. newVoices (std::move (newVoicesIn))
  1838. {}
  1839. void operator() (SamplerAudioProcessor& proc)
  1840. {
  1841. proc.readerFactory = move (readerFactory);
  1842. auto sound = proc.samplerSound;
  1843. sound->setSample (std::move (sample));
  1844. auto numberOfVoices = proc.synthesiser.getNumVoices();
  1845. proc.synthesiser.clearVoices();
  1846. for (auto it = begin (newVoices); proc.synthesiser.getNumVoices() < numberOfVoices; ++it)
  1847. {
  1848. proc.synthesiser.addVoice (it->release());
  1849. }
  1850. }
  1851. private:
  1852. std::unique_ptr<AudioFormatReaderFactory> readerFactory;
  1853. std::unique_ptr<Sample> sample;
  1854. std::vector<std::unique_ptr<MPESamplerVoice>> newVoices;
  1855. };
  1856. // Note that all allocation happens here, on the main message thread. Then,
  1857. // we transfer ownership across to the audio thread.
  1858. auto loadedSamplerSound = samplerSound;
  1859. std::vector<std::unique_ptr<MPESamplerVoice>> newSamplerVoices;
  1860. newSamplerVoices.reserve (maxVoices);
  1861. for (auto i = 0; i != maxVoices; ++i)
  1862. newSamplerVoices.emplace_back (new MPESamplerVoice (loadedSamplerSound));
  1863. if (fact == nullptr)
  1864. {
  1865. commands.push (SetSampleCommand (move (fact),
  1866. nullptr,
  1867. move (newSamplerVoices)));
  1868. }
  1869. else if (auto reader = fact->make (formatManager))
  1870. {
  1871. commands.push (SetSampleCommand (move (fact),
  1872. std::unique_ptr<Sample> (new Sample (*reader, 10.0)),
  1873. move (newSamplerVoices)));
  1874. }
  1875. }
  1876. void setCentreFrequency (double centreFrequency)
  1877. {
  1878. commands.push ([centreFrequency] (SamplerAudioProcessor& proc)
  1879. {
  1880. auto loaded = proc.samplerSound;
  1881. if (loaded != nullptr)
  1882. loaded->setCentreFrequencyInHz (centreFrequency);
  1883. });
  1884. }
  1885. void setLoopMode (LoopMode loopMode)
  1886. {
  1887. commands.push ([loopMode] (SamplerAudioProcessor& proc)
  1888. {
  1889. auto loaded = proc.samplerSound;
  1890. if (loaded != nullptr)
  1891. loaded->setLoopMode (loopMode);
  1892. });
  1893. }
  1894. void setLoopPoints (Range<double> loopPoints)
  1895. {
  1896. commands.push ([loopPoints] (SamplerAudioProcessor& proc)
  1897. {
  1898. auto loaded = proc.samplerSound;
  1899. if (loaded != nullptr)
  1900. loaded->setLoopPointsInSeconds (loopPoints);
  1901. });
  1902. }
  1903. void setMPEZoneLayout (MPEZoneLayout layout)
  1904. {
  1905. commands.push ([layout] (SamplerAudioProcessor& proc)
  1906. {
  1907. // setZoneLayout will lock internally, so we don't care too much about
  1908. // ensuring that the layout doesn't get copied or destroyed on the
  1909. // audio thread. If the audio glitches while updating midi settings
  1910. // it doesn't matter too much.
  1911. proc.synthesiser.setZoneLayout (layout);
  1912. });
  1913. }
  1914. void setLegacyModeEnabled (int pitchbendRange, Range<int> channelRange)
  1915. {
  1916. commands.push ([pitchbendRange, channelRange] (SamplerAudioProcessor& proc)
  1917. {
  1918. proc.synthesiser.enableLegacyMode (pitchbendRange, channelRange);
  1919. });
  1920. }
  1921. void setVoiceStealingEnabled (bool voiceStealingEnabled)
  1922. {
  1923. commands.push ([voiceStealingEnabled] (SamplerAudioProcessor& proc)
  1924. {
  1925. proc.synthesiser.setVoiceStealingEnabled (voiceStealingEnabled);
  1926. });
  1927. }
  1928. void setNumberOfVoices (int numberOfVoices)
  1929. {
  1930. // We don't want to call 'new' on the audio thread. Normally, we'd
  1931. // construct things here, on the GUI thread, and then move them into the
  1932. // command lambda. Unfortunately, C++11 doesn't have extended lambda
  1933. // capture, so we use a custom struct instead.
  1934. class SetNumVoicesCommand
  1935. {
  1936. public:
  1937. SetNumVoicesCommand (std::vector<std::unique_ptr<MPESamplerVoice>> newVoicesIn)
  1938. : newVoices (std::move (newVoicesIn))
  1939. {}
  1940. void operator() (SamplerAudioProcessor& proc)
  1941. {
  1942. if ((int) newVoices.size() < proc.synthesiser.getNumVoices())
  1943. proc.synthesiser.reduceNumVoices (int (newVoices.size()));
  1944. else
  1945. for (auto it = begin (newVoices); (size_t) proc.synthesiser.getNumVoices() < newVoices.size(); ++it)
  1946. proc.synthesiser.addVoice (it->release());
  1947. }
  1948. private:
  1949. std::vector<std::unique_ptr<MPESamplerVoice>> newVoices;
  1950. };
  1951. numberOfVoices = std::min ((int) maxVoices, numberOfVoices);
  1952. auto loadedSamplerSound = samplerSound;
  1953. std::vector<std::unique_ptr<MPESamplerVoice>> newSamplerVoices;
  1954. newSamplerVoices.reserve ((size_t) numberOfVoices);
  1955. for (auto i = 0; i != numberOfVoices; ++i)
  1956. newSamplerVoices.emplace_back (new MPESamplerVoice (loadedSamplerSound));
  1957. commands.push (SetNumVoicesCommand (move (newSamplerVoices)));
  1958. }
  1959. // These accessors are just for an 'overview' and won't give the exact
  1960. // state of the audio engine at a particular point in time.
  1961. // If you call getNumVoices(), get the result '10', and then call
  1962. // getPlaybackPosiiton(9), there's a chance the audio engine will have
  1963. // been updated to remove some voices in the meantime, so the returned
  1964. // value won't correspond to an existing voice.
  1965. int getNumVoices() const { return synthesiser.getNumVoices(); }
  1966. float getPlaybackPosition (int voice) const { return playbackPositions.at ((size_t) voice); }
  1967. private:
  1968. //==============================================================================
  1969. class SamplerAudioProcessorEditor : public AudioProcessorEditor,
  1970. public FileDragAndDropTarget,
  1971. private DataModel::Listener,
  1972. private MPESettingsDataModel::Listener
  1973. {
  1974. public:
  1975. SamplerAudioProcessorEditor (SamplerAudioProcessor& p, ProcessorState state)
  1976. : AudioProcessorEditor (&p),
  1977. samplerAudioProcessor (p),
  1978. mainSamplerView (dataModel,
  1979. [&p]
  1980. {
  1981. std::vector<float> ret;
  1982. auto voices = p.getNumVoices();
  1983. ret.reserve ((size_t) voices);
  1984. for (auto i = 0; i != voices; ++i)
  1985. ret.emplace_back (p.getPlaybackPosition (i));
  1986. return ret;
  1987. },
  1988. undoManager)
  1989. {
  1990. dataModel.addListener (*this);
  1991. mpeSettings.addListener (*this);
  1992. formatManager.registerBasicFormats();
  1993. addAndMakeVisible (tabbedComponent);
  1994. auto lookFeel = dynamic_cast<LookAndFeel_V4*> (&getLookAndFeel());
  1995. auto bg = lookFeel->getCurrentColourScheme()
  1996. .getUIColour (LookAndFeel_V4::ColourScheme::UIColour::widgetBackground);
  1997. tabbedComponent.addTab ("Sample Editor", bg, &mainSamplerView, false);
  1998. tabbedComponent.addTab ("MPE Settings", bg, &settingsComponent, false);
  1999. mpeSettings.setSynthVoices (state.synthVoices, nullptr);
  2000. mpeSettings.setLegacyModeEnabled (state.legacyModeEnabled, nullptr);
  2001. mpeSettings.setLegacyFirstChannel (state.legacyChannels.getStart(), nullptr);
  2002. mpeSettings.setLegacyLastChannel (state.legacyChannels.getEnd(), nullptr);
  2003. mpeSettings.setLegacyPitchbendRange (state.legacyPitchbendRange, nullptr);
  2004. mpeSettings.setVoiceStealingEnabled (state.voiceStealingEnabled, nullptr);
  2005. mpeSettings.setMPEZoneLayout (state.mpeZoneLayout, nullptr);
  2006. dataModel.setSampleReader (move (state.readerFactory), nullptr);
  2007. dataModel.setLoopPointsSeconds (state.loopPointsSeconds, nullptr);
  2008. dataModel.setCentreFrequencyHz (state.centreFrequencyHz, nullptr);
  2009. dataModel.setLoopMode (state.loopMode, nullptr);
  2010. // Make sure that before the constructor has finished, you've set the
  2011. // editor's size to whatever you need it to be.
  2012. setResizable (true, true);
  2013. setResizeLimits (640, 480, 2560, 1440);
  2014. setSize (640, 480);
  2015. }
  2016. private:
  2017. void resized() override
  2018. {
  2019. tabbedComponent.setBounds (getLocalBounds());
  2020. }
  2021. bool keyPressed (const KeyPress& key) override
  2022. {
  2023. if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
  2024. {
  2025. undoManager.undo();
  2026. return true;
  2027. }
  2028. if (key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
  2029. {
  2030. undoManager.redo();
  2031. return true;
  2032. }
  2033. return Component::keyPressed (key);
  2034. }
  2035. bool isInterestedInFileDrag (const StringArray& files) override
  2036. {
  2037. WildcardFileFilter filter (formatManager.getWildcardForAllFormats(), {}, "Known Audio Formats");
  2038. return files.size() == 1 && filter.isFileSuitable (files[0]);
  2039. }
  2040. void filesDropped (const StringArray& files, int, int) override
  2041. {
  2042. jassert (files.size() == 1);
  2043. undoManager.beginNewTransaction();
  2044. auto r = new FileAudioFormatReaderFactory (files[0]);
  2045. dataModel.setSampleReader (std::unique_ptr<AudioFormatReaderFactory> (r),
  2046. &undoManager);
  2047. }
  2048. void sampleReaderChanged (std::shared_ptr<AudioFormatReaderFactory> value) override
  2049. {
  2050. samplerAudioProcessor.setSample (value == nullptr ? nullptr : value->clone(),
  2051. dataModel.getAudioFormatManager());
  2052. }
  2053. void centreFrequencyHzChanged (double value) override
  2054. {
  2055. samplerAudioProcessor.setCentreFrequency (value);
  2056. }
  2057. void loopPointsSecondsChanged (Range<double> value) override
  2058. {
  2059. samplerAudioProcessor.setLoopPoints (value);
  2060. }
  2061. void loopModeChanged (LoopMode value) override
  2062. {
  2063. samplerAudioProcessor.setLoopMode (value);
  2064. }
  2065. void synthVoicesChanged (int value) override
  2066. {
  2067. samplerAudioProcessor.setNumberOfVoices (value);
  2068. }
  2069. void voiceStealingEnabledChanged (bool value) override
  2070. {
  2071. samplerAudioProcessor.setVoiceStealingEnabled (value);
  2072. }
  2073. void legacyModeEnabledChanged (bool value) override
  2074. {
  2075. if (value)
  2076. setProcessorLegacyMode();
  2077. else
  2078. setProcessorMPEMode();
  2079. }
  2080. void mpeZoneLayoutChanged (const MPEZoneLayout&) override
  2081. {
  2082. setProcessorMPEMode();
  2083. }
  2084. void legacyFirstChannelChanged (int) override
  2085. {
  2086. setProcessorLegacyMode();
  2087. }
  2088. void legacyLastChannelChanged (int) override
  2089. {
  2090. setProcessorLegacyMode();
  2091. }
  2092. void legacyPitchbendRangeChanged (int) override
  2093. {
  2094. setProcessorLegacyMode();
  2095. }
  2096. void setProcessorLegacyMode()
  2097. {
  2098. samplerAudioProcessor.setLegacyModeEnabled (mpeSettings.getLegacyPitchbendRange(),
  2099. Range<int> (mpeSettings.getLegacyFirstChannel(),
  2100. mpeSettings.getLegacyLastChannel()));
  2101. }
  2102. void setProcessorMPEMode()
  2103. {
  2104. samplerAudioProcessor.setMPEZoneLayout (mpeSettings.getMPEZoneLayout());
  2105. }
  2106. SamplerAudioProcessor& samplerAudioProcessor;
  2107. AudioFormatManager formatManager;
  2108. DataModel dataModel { formatManager };
  2109. UndoManager undoManager;
  2110. MPESettingsDataModel mpeSettings { dataModel.mpeSettings() };
  2111. TabbedComponent tabbedComponent { TabbedButtonBar::Orientation::TabsAtTop };
  2112. MPESettingsComponent settingsComponent { dataModel.mpeSettings(), undoManager };
  2113. MainSamplerView mainSamplerView;
  2114. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SamplerAudioProcessorEditor)
  2115. };
  2116. //==============================================================================
  2117. template <typename Element>
  2118. void process (AudioBuffer<Element>& buffer, MidiBuffer& midiMessages)
  2119. {
  2120. // Try to acquire a lock on the command queue.
  2121. // If we were successful, we pop all pending commands off the queue and
  2122. // apply them to the processor.
  2123. // If we weren't able to acquire the lock, it's because someone called
  2124. // createEditor, which requires that the processor data model stays in
  2125. // a valid state for the duration of the call.
  2126. const GenericScopedTryLock<SpinLock> lock (commandQueueMutex);
  2127. if (lock.isLocked())
  2128. commands.call (*this);
  2129. synthesiser.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
  2130. auto loadedSamplerSound = samplerSound;
  2131. if (loadedSamplerSound->getSample() == nullptr)
  2132. return;
  2133. auto numVoices = synthesiser.getNumVoices();
  2134. // Update the current playback positions
  2135. for (auto i = 0; i < maxVoices; ++i)
  2136. {
  2137. auto* voicePtr = dynamic_cast<MPESamplerVoice*> (synthesiser.getVoice (i));
  2138. if (i < numVoices && voicePtr != nullptr)
  2139. playbackPositions[(size_t) i] = static_cast<float> (voicePtr->getCurrentSamplePosition() / loadedSamplerSound->getSample()->getSampleRate());
  2140. else
  2141. playbackPositions[(size_t) i] = 0.0f;
  2142. }
  2143. }
  2144. CommandFifo<SamplerAudioProcessor> commands;
  2145. MemoryBlock mb;
  2146. std::unique_ptr<AudioFormatReaderFactory> readerFactory;
  2147. std::shared_ptr<MPESamplerSound> samplerSound = std::make_shared<MPESamplerSound>();
  2148. MPESynthesiser synthesiser;
  2149. // This mutex is used to ensure we don't modify the processor state during
  2150. // a call to createEditor, which would cause the UI to become desynched
  2151. // with the real state of the processor.
  2152. SpinLock commandQueueMutex;
  2153. enum { maxVoices = 20 };
  2154. // This is used for visualising the current playback position of each voice.
  2155. std::array<std::atomic<float>, maxVoices> playbackPositions;
  2156. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SamplerAudioProcessor)
  2157. };