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.

2625 lines
90KB

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