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.

1405 lines
47KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. #include <juce_core/system/juce_TargetPlatform.h>
  20. #if JUCE_PLUGINHOST_ARA && (JUCE_MAC || JUCE_WINDOWS)
  21. #include <JuceHeader.h>
  22. #include <ARA_API/ARAInterface.h>
  23. #include <ARA_Library/Dispatch/ARAHostDispatch.h>
  24. class FileAudioSource
  25. {
  26. auto getAudioSourceProperties() const
  27. {
  28. auto properties = ARAHostModel::AudioSource::getEmptyProperties();
  29. properties.name = formatReader->getFile().getFullPathName().toRawUTF8();
  30. properties.persistentID = formatReader->getFile().getFullPathName().toRawUTF8();
  31. properties.sampleCount = formatReader->lengthInSamples;
  32. properties.sampleRate = formatReader->sampleRate;
  33. properties.channelCount = (int) formatReader->numChannels;
  34. properties.merits64BitSamples = false;
  35. return properties;
  36. }
  37. public:
  38. FileAudioSource (ARA::Host::DocumentController& dc, const juce::File& file)
  39. : formatReader ([&file]
  40. {
  41. auto result = rawToUniquePtr (WavAudioFormat().createMemoryMappedReader (file));
  42. result->mapEntireFile();
  43. return result;
  44. }()),
  45. audioSource (Converter::toHostRef (this), dc, getAudioSourceProperties())
  46. {
  47. audioSource.enableAudioSourceSamplesAccess (true);
  48. }
  49. bool readAudioSamples (float* const* buffers, int64 startSample, int64 numSamples)
  50. {
  51. // TODO: the ARA interface defines numSamples as int64. We should do multiple reads if necessary with the reader.
  52. if (numSamples > std::numeric_limits<int>::max())
  53. return false;
  54. return formatReader->read (buffers, (int) formatReader->numChannels, startSample, (int) (numSamples));
  55. }
  56. bool readAudioSamples (double* const* buffers, int64 startSample, int64 numSamples)
  57. {
  58. ignoreUnused (buffers, startSample, numSamples);
  59. return false;
  60. }
  61. MemoryMappedAudioFormatReader& getFormatReader() const { return *formatReader; }
  62. auto getPluginRef() const { return audioSource.getPluginRef(); }
  63. auto& getSource() { return audioSource; }
  64. using Converter = ARAHostModel::ConversionFunctions<FileAudioSource*, ARA::ARAAudioSourceHostRef>;
  65. private:
  66. std::unique_ptr<MemoryMappedAudioFormatReader> formatReader;
  67. ARAHostModel::AudioSource audioSource;
  68. };
  69. //==============================================================================
  70. class MusicalContext
  71. {
  72. auto getMusicalContextProperties() const
  73. {
  74. auto properties = ARAHostModel::MusicalContext::getEmptyProperties();
  75. properties.name = "MusicalContext";
  76. properties.orderIndex = 0;
  77. properties.color = nullptr;
  78. return properties;
  79. }
  80. public:
  81. MusicalContext (ARA::Host::DocumentController& dc)
  82. : context (Converter::toHostRef (this), dc, getMusicalContextProperties())
  83. {
  84. }
  85. auto getPluginRef() const { return context.getPluginRef(); }
  86. private:
  87. using Converter = ARAHostModel::ConversionFunctions<MusicalContext*, ARA::ARAMusicalContextHostRef>;
  88. ARAHostModel::MusicalContext context;
  89. };
  90. //==============================================================================
  91. class RegionSequence
  92. {
  93. auto getRegionSequenceProperties() const
  94. {
  95. auto properties = ARAHostModel::RegionSequence::getEmptyProperties();
  96. properties.name = name.toRawUTF8();
  97. properties.orderIndex = 0;
  98. properties.musicalContextRef = context.getPluginRef();
  99. properties.color = nullptr;
  100. return properties;
  101. }
  102. public:
  103. RegionSequence (ARA::Host::DocumentController& dc, MusicalContext& contextIn, String nameIn)
  104. : context (contextIn),
  105. name (std::move (nameIn)),
  106. sequence (Converter::toHostRef (this), dc, getRegionSequenceProperties())
  107. {
  108. }
  109. auto& getMusicalContext() const { return context; }
  110. auto getPluginRef() const { return sequence.getPluginRef(); }
  111. private:
  112. using Converter = ARAHostModel::ConversionFunctions<RegionSequence*, ARA::ARARegionSequenceHostRef>;
  113. MusicalContext& context;
  114. String name;
  115. ARAHostModel::RegionSequence sequence;
  116. };
  117. class AudioModification
  118. {
  119. auto getProperties() const
  120. {
  121. auto properties = ARAHostModel::AudioModification::getEmptyProperties();
  122. properties.persistentID = "x";
  123. return properties;
  124. }
  125. public:
  126. AudioModification (ARA::Host::DocumentController& dc, FileAudioSource& source)
  127. : modification (Converter::toHostRef (this), dc, source.getSource(), getProperties())
  128. {
  129. }
  130. auto& getModification() { return modification; }
  131. private:
  132. using Converter = ARAHostModel::ConversionFunctions<AudioModification*, ARA::ARAAudioModificationHostRef>;
  133. ARAHostModel::AudioModification modification;
  134. };
  135. //==============================================================================
  136. class PlaybackRegion
  137. {
  138. auto getPlaybackRegionProperties() const
  139. {
  140. auto properties = ARAHostModel::PlaybackRegion::getEmptyProperties();
  141. properties.transformationFlags = ARA::kARAPlaybackTransformationNoChanges;
  142. properties.startInModificationTime = 0.0;
  143. const auto& formatReader = audioSource.getFormatReader();
  144. properties.durationInModificationTime = formatReader.lengthInSamples / formatReader.sampleRate;
  145. properties.startInPlaybackTime = 0.0;
  146. properties.durationInPlaybackTime = properties.durationInModificationTime;
  147. properties.musicalContextRef = sequence.getMusicalContext().getPluginRef();
  148. properties.regionSequenceRef = sequence.getPluginRef();
  149. properties.name = nullptr;
  150. properties.color = nullptr;
  151. return properties;
  152. }
  153. public:
  154. PlaybackRegion (ARA::Host::DocumentController& dc,
  155. RegionSequence& s,
  156. AudioModification& m,
  157. FileAudioSource& source)
  158. : sequence (s),
  159. audioSource (source),
  160. region (Converter::toHostRef (this), dc, m.getModification(), getPlaybackRegionProperties())
  161. {
  162. jassert (source.getPluginRef() == m.getModification().getAudioSource().getPluginRef());
  163. }
  164. auto& getPlaybackRegion() { return region; }
  165. private:
  166. using Converter = ARAHostModel::ConversionFunctions<PlaybackRegion*, ARA::ARAPlaybackRegionHostRef>;
  167. RegionSequence& sequence;
  168. FileAudioSource& audioSource;
  169. ARAHostModel::PlaybackRegion region;
  170. };
  171. //==============================================================================
  172. class AudioAccessController : public ARA::Host::AudioAccessControllerInterface
  173. {
  174. public:
  175. ARA::ARAAudioReaderHostRef createAudioReaderForSource (ARA::ARAAudioSourceHostRef audioSourceHostRef,
  176. bool use64BitSamples) noexcept override
  177. {
  178. auto audioReader = std::make_unique<AudioReader> (audioSourceHostRef, use64BitSamples);
  179. auto audioReaderHostRef = Converter::toHostRef (audioReader.get());
  180. auto* readerPtr = audioReader.get();
  181. audioReaders.emplace (readerPtr, std::move (audioReader));
  182. return audioReaderHostRef;
  183. }
  184. bool readAudioSamples (ARA::ARAAudioReaderHostRef readerRef,
  185. ARA::ARASamplePosition samplePosition,
  186. ARA::ARASampleCount samplesPerChannel,
  187. void* const* buffers) noexcept override
  188. {
  189. const auto use64BitSamples = Converter::fromHostRef (readerRef)->use64Bit;
  190. auto* audioSource = FileAudioSource::Converter::fromHostRef (Converter::fromHostRef (readerRef)->sourceHostRef);
  191. if (use64BitSamples)
  192. return audioSource->readAudioSamples (
  193. reinterpret_cast<double* const*> (buffers), samplePosition, samplesPerChannel);
  194. return audioSource->readAudioSamples (
  195. reinterpret_cast<float* const*> (buffers), samplePosition, samplesPerChannel);
  196. }
  197. void destroyAudioReader (ARA::ARAAudioReaderHostRef readerRef) noexcept override
  198. {
  199. audioReaders.erase (Converter::fromHostRef (readerRef));
  200. }
  201. private:
  202. struct AudioReader
  203. {
  204. AudioReader (ARA::ARAAudioSourceHostRef source, bool use64BitSamples)
  205. : sourceHostRef (source), use64Bit (use64BitSamples)
  206. {
  207. }
  208. ARA::ARAAudioSourceHostRef sourceHostRef;
  209. bool use64Bit;
  210. };
  211. using Converter = ARAHostModel::ConversionFunctions<AudioReader*, ARA::ARAAudioReaderHostRef>;
  212. std::map<AudioReader*, std::unique_ptr<AudioReader>> audioReaders;
  213. };
  214. class ArchivingController : public ARA::Host::ArchivingControllerInterface
  215. {
  216. public:
  217. using ReaderConverter = ARAHostModel::ConversionFunctions<MemoryBlock*, ARA::ARAArchiveReaderHostRef>;
  218. using WriterConverter = ARAHostModel::ConversionFunctions<MemoryOutputStream*, ARA::ARAArchiveWriterHostRef>;
  219. ARA::ARASize getArchiveSize (ARA::ARAArchiveReaderHostRef archiveReaderHostRef) noexcept override
  220. {
  221. return (ARA::ARASize) ReaderConverter::fromHostRef (archiveReaderHostRef)->getSize();
  222. }
  223. bool readBytesFromArchive (ARA::ARAArchiveReaderHostRef archiveReaderHostRef,
  224. ARA::ARASize position,
  225. ARA::ARASize length,
  226. ARA::ARAByte* buffer) noexcept override
  227. {
  228. auto* archiveReader = ReaderConverter::fromHostRef (archiveReaderHostRef);
  229. if ((position + length) <= archiveReader->getSize())
  230. {
  231. std::memcpy (buffer, addBytesToPointer (archiveReader->getData(), position), length);
  232. return true;
  233. }
  234. return false;
  235. }
  236. bool writeBytesToArchive (ARA::ARAArchiveWriterHostRef archiveWriterHostRef,
  237. ARA::ARASize position,
  238. ARA::ARASize length,
  239. const ARA::ARAByte* buffer) noexcept override
  240. {
  241. auto* archiveWriter = WriterConverter::fromHostRef (archiveWriterHostRef);
  242. if (archiveWriter->setPosition ((int64) position) && archiveWriter->write (buffer, length))
  243. return true;
  244. return false;
  245. }
  246. void notifyDocumentArchivingProgress (float value) noexcept override { ignoreUnused (value); }
  247. void notifyDocumentUnarchivingProgress (float value) noexcept override { ignoreUnused (value); }
  248. ARA::ARAPersistentID getDocumentArchiveID (ARA::ARAArchiveReaderHostRef archiveReaderHostRef) noexcept override
  249. {
  250. ignoreUnused (archiveReaderHostRef);
  251. return nullptr;
  252. }
  253. };
  254. class ContentAccessController : public ARA::Host::ContentAccessControllerInterface
  255. {
  256. public:
  257. using Converter = ARAHostModel::ConversionFunctions<ARA::ARAContentType, ARA::ARAContentReaderHostRef>;
  258. bool isMusicalContextContentAvailable (ARA::ARAMusicalContextHostRef musicalContextHostRef,
  259. ARA::ARAContentType type) noexcept override
  260. {
  261. ignoreUnused (musicalContextHostRef);
  262. return (type == ARA::kARAContentTypeTempoEntries || type == ARA::kARAContentTypeBarSignatures);
  263. }
  264. ARA::ARAContentGrade getMusicalContextContentGrade (ARA::ARAMusicalContextHostRef musicalContextHostRef,
  265. ARA::ARAContentType type) noexcept override
  266. {
  267. ignoreUnused (musicalContextHostRef, type);
  268. return ARA::kARAContentGradeInitial;
  269. }
  270. ARA::ARAContentReaderHostRef
  271. createMusicalContextContentReader (ARA::ARAMusicalContextHostRef musicalContextHostRef,
  272. ARA::ARAContentType type,
  273. const ARA::ARAContentTimeRange* range) noexcept override
  274. {
  275. ignoreUnused (musicalContextHostRef, range);
  276. return Converter::toHostRef (type);
  277. }
  278. bool isAudioSourceContentAvailable (ARA::ARAAudioSourceHostRef audioSourceHostRef,
  279. ARA::ARAContentType type) noexcept override
  280. {
  281. ignoreUnused (audioSourceHostRef, type);
  282. return false;
  283. }
  284. ARA::ARAContentGrade getAudioSourceContentGrade (ARA::ARAAudioSourceHostRef audioSourceHostRef,
  285. ARA::ARAContentType type) noexcept override
  286. {
  287. ignoreUnused (audioSourceHostRef, type);
  288. return 0;
  289. }
  290. ARA::ARAContentReaderHostRef
  291. createAudioSourceContentReader (ARA::ARAAudioSourceHostRef audioSourceHostRef,
  292. ARA::ARAContentType type,
  293. const ARA::ARAContentTimeRange* range) noexcept override
  294. {
  295. ignoreUnused (audioSourceHostRef, type, range);
  296. return nullptr;
  297. }
  298. ARA::ARAInt32 getContentReaderEventCount (ARA::ARAContentReaderHostRef contentReaderHostRef) noexcept override
  299. {
  300. const auto contentType = Converter::fromHostRef (contentReaderHostRef);
  301. if (contentType == ARA::kARAContentTypeTempoEntries || contentType == ARA::kARAContentTypeBarSignatures)
  302. return 2;
  303. return 0;
  304. }
  305. const void* getContentReaderDataForEvent (ARA::ARAContentReaderHostRef contentReaderHostRef,
  306. ARA::ARAInt32 eventIndex) noexcept override
  307. {
  308. if (Converter::fromHostRef (contentReaderHostRef) == ARA::kARAContentTypeTempoEntries)
  309. {
  310. if (eventIndex == 0)
  311. {
  312. tempoEntry.timePosition = 0.0;
  313. tempoEntry.quarterPosition = 0.0;
  314. }
  315. else if (eventIndex == 1)
  316. {
  317. tempoEntry.timePosition = 2.0;
  318. tempoEntry.quarterPosition = 4.0;
  319. }
  320. return &tempoEntry;
  321. }
  322. else if (Converter::fromHostRef (contentReaderHostRef) == ARA::kARAContentTypeBarSignatures)
  323. {
  324. if (eventIndex == 0)
  325. {
  326. barSignature.position = 0.0;
  327. barSignature.numerator = 4;
  328. barSignature.denominator = 4;
  329. }
  330. if (eventIndex == 1)
  331. {
  332. barSignature.position = 1.0;
  333. barSignature.numerator = 4;
  334. barSignature.denominator = 4;
  335. }
  336. return &barSignature;
  337. }
  338. jassertfalse;
  339. return nullptr;
  340. }
  341. void destroyContentReader (ARA::ARAContentReaderHostRef contentReaderHostRef) noexcept override
  342. {
  343. ignoreUnused (contentReaderHostRef);
  344. }
  345. ARA::ARAContentTempoEntry tempoEntry;
  346. ARA::ARAContentBarSignature barSignature;
  347. };
  348. class ModelUpdateController : public ARA::Host::ModelUpdateControllerInterface
  349. {
  350. public:
  351. void notifyAudioSourceAnalysisProgress (ARA::ARAAudioSourceHostRef audioSourceHostRef,
  352. ARA::ARAAnalysisProgressState state,
  353. float value) noexcept override
  354. {
  355. ignoreUnused (audioSourceHostRef, state, value);
  356. }
  357. void notifyAudioSourceContentChanged (ARA::ARAAudioSourceHostRef audioSourceHostRef,
  358. const ARA::ARAContentTimeRange* range,
  359. ARA::ContentUpdateScopes scopeFlags) noexcept override
  360. {
  361. ignoreUnused (audioSourceHostRef, range, scopeFlags);
  362. }
  363. void notifyAudioModificationContentChanged (ARA::ARAAudioModificationHostRef audioModificationHostRef,
  364. const ARA::ARAContentTimeRange* range,
  365. ARA::ContentUpdateScopes scopeFlags) noexcept override
  366. {
  367. ignoreUnused (audioModificationHostRef, range, scopeFlags);
  368. }
  369. void notifyPlaybackRegionContentChanged (ARA::ARAPlaybackRegionHostRef playbackRegionHostRef,
  370. const ARA::ARAContentTimeRange* range,
  371. ARA::ContentUpdateScopes scopeFlags) noexcept override
  372. {
  373. ignoreUnused (playbackRegionHostRef, range, scopeFlags);
  374. }
  375. };
  376. class PlaybackController : public ARA::Host::PlaybackControllerInterface
  377. {
  378. public:
  379. void requestStartPlayback() noexcept override {}
  380. void requestStopPlayback() noexcept override {}
  381. void requestSetPlaybackPosition (ARA::ARATimePosition timePosition) noexcept override
  382. {
  383. ignoreUnused (timePosition);
  384. }
  385. void requestSetCycleRange (ARA::ARATimePosition startTime, ARA::ARATimeDuration duration) noexcept override
  386. {
  387. ignoreUnused (startTime, duration);
  388. }
  389. void requestEnableCycle (bool enable) noexcept override { ignoreUnused (enable); }
  390. };
  391. struct SimplePlayHead : public juce::AudioPlayHead
  392. {
  393. bool getCurrentPosition (CurrentPositionInfo& result) override
  394. {
  395. result.timeInSamples = timeInSamples.load();
  396. result.isPlaying = isPlaying.load();
  397. return true;
  398. }
  399. std::atomic<int64_t> timeInSamples { 0 };
  400. std::atomic<bool> isPlaying { false };
  401. };
  402. struct HostPlaybackController
  403. {
  404. virtual ~HostPlaybackController() = default;
  405. virtual void setPlaying (bool isPlaying) = 0;
  406. virtual void goToStart() = 0;
  407. virtual File getAudioSource() const = 0;
  408. virtual void setAudioSource (File audioSourceFile) = 0;
  409. virtual void clearAudioSource() = 0;
  410. };
  411. class AudioSourceComponent : public Component,
  412. public FileDragAndDropTarget,
  413. public ChangeListener
  414. {
  415. public:
  416. explicit AudioSourceComponent (HostPlaybackController& controller, juce::ChangeBroadcaster& bc)
  417. : hostPlaybackController (controller),
  418. broadcaster (bc),
  419. waveformComponent (*this)
  420. {
  421. audioSourceLabel.setText ("You can drag and drop .wav files here", NotificationType::dontSendNotification);
  422. addAndMakeVisible (audioSourceLabel);
  423. addAndMakeVisible (waveformComponent);
  424. playButton.setButtonText ("Play / Pause");
  425. playButton.onClick = [this]
  426. {
  427. isPlaying = ! isPlaying;
  428. hostPlaybackController.setPlaying (isPlaying);
  429. };
  430. goToStartButton.setButtonText ("Go to start");
  431. goToStartButton.onClick = [this] { hostPlaybackController.goToStart(); };
  432. addAndMakeVisible (goToStartButton);
  433. addAndMakeVisible (playButton);
  434. broadcaster.addChangeListener (this);
  435. update();
  436. }
  437. ~AudioSourceComponent() override
  438. {
  439. broadcaster.removeChangeListener (this);
  440. }
  441. void changeListenerCallback (ChangeBroadcaster*) override
  442. {
  443. update();
  444. }
  445. void resized() override
  446. {
  447. auto localBounds = getLocalBounds();
  448. auto buttonsArea = localBounds.removeFromBottom (40).reduced (5);
  449. auto waveformArea = localBounds.removeFromBottom (150).reduced (5);
  450. juce::FlexBox fb;
  451. fb.justifyContent = juce::FlexBox::JustifyContent::center;
  452. fb.alignContent = juce::FlexBox::AlignContent::center;
  453. fb.items = { juce::FlexItem (goToStartButton).withMinWidth (100.0f).withMinHeight ((float) buttonsArea.getHeight()),
  454. juce::FlexItem (playButton).withMinWidth (100.0f).withMinHeight ((float) buttonsArea.getHeight()) };
  455. fb.performLayout (buttonsArea);
  456. waveformComponent.setBounds (waveformArea);
  457. audioSourceLabel.setBounds (localBounds);
  458. }
  459. bool isInterestedInFileDrag (const StringArray& files) override
  460. {
  461. if (files.size() != 1)
  462. return false;
  463. if (files.getReference (0).endsWithIgnoreCase (".wav"))
  464. return true;
  465. return false;
  466. }
  467. void update()
  468. {
  469. const auto currentAudioSource = hostPlaybackController.getAudioSource();
  470. if (currentAudioSource.existsAsFile())
  471. {
  472. waveformComponent.setSource (currentAudioSource);
  473. audioSourceLabel.setText (currentAudioSource.getFullPathName(),
  474. NotificationType::dontSendNotification);
  475. }
  476. else
  477. {
  478. waveformComponent.clearSource();
  479. audioSourceLabel.setText ("You can drag and drop .wav files here", NotificationType::dontSendNotification);
  480. }
  481. }
  482. void filesDropped (const StringArray& files, int, int) override
  483. {
  484. hostPlaybackController.setAudioSource (files.getReference (0));
  485. update();
  486. }
  487. private:
  488. class WaveformComponent : public Component,
  489. public ChangeListener
  490. {
  491. public:
  492. WaveformComponent (AudioSourceComponent& p)
  493. : parent (p),
  494. thumbCache (7),
  495. audioThumb (128, formatManager, thumbCache)
  496. {
  497. setWantsKeyboardFocus (true);
  498. formatManager.registerBasicFormats();
  499. audioThumb.addChangeListener (this);
  500. }
  501. ~WaveformComponent() override
  502. {
  503. audioThumb.removeChangeListener (this);
  504. }
  505. void mouseDown (const MouseEvent&) override
  506. {
  507. isSelected = true;
  508. repaint();
  509. }
  510. void changeListenerCallback (ChangeBroadcaster*) override
  511. {
  512. repaint();
  513. }
  514. void paint (juce::Graphics& g) override
  515. {
  516. if (! isEmpty)
  517. {
  518. auto rect = getLocalBounds();
  519. const auto waveformColour = Colours::cadetblue;
  520. if (rect.getWidth() > 2)
  521. {
  522. g.setColour (isSelected ? juce::Colours::yellow : juce::Colours::black);
  523. g.drawRect (rect);
  524. rect.reduce (1, 1);
  525. g.setColour (waveformColour.darker (1.0f));
  526. g.fillRect (rect);
  527. }
  528. g.setColour (Colours::cadetblue);
  529. audioThumb.drawChannels (g, rect, 0.0, audioThumb.getTotalLength(), 1.0f);
  530. }
  531. }
  532. void setSource (const File& source)
  533. {
  534. isEmpty = false;
  535. audioThumb.setSource (new FileInputSource (source));
  536. }
  537. void clearSource()
  538. {
  539. isEmpty = true;
  540. isSelected = false;
  541. audioThumb.clear();
  542. }
  543. bool keyPressed (const KeyPress& key) override
  544. {
  545. if (isSelected && key == KeyPress::deleteKey)
  546. {
  547. parent.hostPlaybackController.clearAudioSource();
  548. return true;
  549. }
  550. return false;
  551. }
  552. private:
  553. AudioSourceComponent& parent;
  554. bool isEmpty = true;
  555. bool isSelected = false;
  556. AudioFormatManager formatManager;
  557. AudioThumbnailCache thumbCache;
  558. AudioThumbnail audioThumb;
  559. };
  560. HostPlaybackController& hostPlaybackController;
  561. juce::ChangeBroadcaster& broadcaster;
  562. Label audioSourceLabel;
  563. WaveformComponent waveformComponent;
  564. bool isPlaying { false };
  565. TextButton playButton, goToStartButton;
  566. };
  567. class ARAPluginInstanceWrapper : public AudioPluginInstance
  568. {
  569. public:
  570. class ARATestHost : public HostPlaybackController,
  571. public juce::ChangeBroadcaster
  572. {
  573. public:
  574. class Editor : public AudioProcessorEditor
  575. {
  576. public:
  577. explicit Editor (ARATestHost& araTestHost)
  578. : AudioProcessorEditor (araTestHost.getAudioPluginInstance()),
  579. audioSourceComponent (araTestHost, araTestHost)
  580. {
  581. audioSourceComponent.update();
  582. addAndMakeVisible (audioSourceComponent);
  583. setSize (512, 220);
  584. }
  585. ~Editor() override { getAudioProcessor()->editorBeingDeleted (this); }
  586. void resized() override { audioSourceComponent.setBounds (getLocalBounds()); }
  587. private:
  588. AudioSourceComponent audioSourceComponent;
  589. };
  590. explicit ARATestHost (ARAPluginInstanceWrapper& instanceIn)
  591. : instance (instanceIn)
  592. {
  593. if (instance.inner->getPluginDescription().hasARAExtension)
  594. {
  595. instance.inner->setPlayHead (&playHead);
  596. createARAFactoryAsync (*instance.inner, [this] (ARAFactoryWrapper araFactory)
  597. {
  598. init (std::move (araFactory));
  599. });
  600. }
  601. }
  602. void init (ARAFactoryWrapper araFactory)
  603. {
  604. if (araFactory.get() != nullptr)
  605. {
  606. documentController = ARAHostDocumentController::create (std::move (araFactory),
  607. "AudioPluginHostDocument",
  608. std::make_unique<AudioAccessController>(),
  609. std::make_unique<ArchivingController>(),
  610. std::make_unique<ContentAccessController>(),
  611. std::make_unique<ModelUpdateController>(),
  612. std::make_unique<PlaybackController>());
  613. if (documentController != nullptr)
  614. {
  615. const auto allRoles = ARA::kARAPlaybackRendererRole | ARA::kARAEditorRendererRole | ARA::kARAEditorViewRole;
  616. const auto plugInExtensionInstance = documentController->bindDocumentToPluginInstance (*instance.inner,
  617. allRoles,
  618. allRoles);
  619. playbackRenderer = plugInExtensionInstance.getPlaybackRendererInterface();
  620. editorRenderer = plugInExtensionInstance.getEditorRendererInterface();
  621. synchronizeStateWithDocumentController();
  622. }
  623. else
  624. jassertfalse;
  625. }
  626. else
  627. jassertfalse;
  628. }
  629. void getStateInformation (juce::MemoryBlock& b)
  630. {
  631. std::lock_guard<std::mutex> configurationLock (instance.innerMutex);
  632. if (context != nullptr)
  633. context->getStateInformation (b);
  634. }
  635. void setStateInformation (const void* d, int s)
  636. {
  637. {
  638. std::lock_guard<std::mutex> lock { contextUpdateSourceMutex };
  639. contextUpdateSource = ContextUpdateSource { d, s };
  640. }
  641. synchronise();
  642. }
  643. ~ARATestHost() override { instance.inner->releaseResources(); }
  644. void afterProcessBlock (int numSamples)
  645. {
  646. const auto isPlayingNow = isPlaying.load();
  647. playHead.isPlaying.store (isPlayingNow);
  648. if (isPlayingNow)
  649. {
  650. const auto currentAudioSourceLength = audioSourceLength.load();
  651. const auto currentPlayHeadPosition = playHead.timeInSamples.load();
  652. // Rudimentary attempt to not seek beyond our sample data, assuming a fairly stable numSamples
  653. // value. We should gain control over calling the AudioProcessorGraph's processBlock() calls so
  654. // that we can do sample precise looping.
  655. if (currentAudioSourceLength - currentPlayHeadPosition < numSamples)
  656. playHead.timeInSamples.store (0);
  657. else
  658. playHead.timeInSamples.fetch_add (numSamples);
  659. }
  660. if (goToStartSignal.exchange (false))
  661. playHead.timeInSamples.store (0);
  662. }
  663. File getAudioSource() const override
  664. {
  665. std::lock_guard<std::mutex> lock { instance.innerMutex };
  666. if (context != nullptr)
  667. return context->audioFile;
  668. return {};
  669. }
  670. void setAudioSource (File audioSourceFile) override
  671. {
  672. if (audioSourceFile.existsAsFile())
  673. {
  674. {
  675. std::lock_guard<std::mutex> lock { contextUpdateSourceMutex };
  676. contextUpdateSource = ContextUpdateSource (std::move (audioSourceFile));
  677. }
  678. synchronise();
  679. }
  680. }
  681. void clearAudioSource() override
  682. {
  683. {
  684. std::lock_guard<std::mutex> lock { contextUpdateSourceMutex };
  685. contextUpdateSource = ContextUpdateSource (ContextUpdateSource::Type::reset);
  686. }
  687. synchronise();
  688. }
  689. void setPlaying (bool isPlayingIn) override { isPlaying.store (isPlayingIn); }
  690. void goToStart() override { goToStartSignal.store (true); }
  691. Editor* createEditor() { return new Editor (*this); }
  692. AudioPluginInstance& getAudioPluginInstance() { return instance; }
  693. private:
  694. /** Use this to put the plugin in an unprepared state for the duration of adding and removing PlaybackRegions
  695. to and from Renderers.
  696. */
  697. class ScopedPluginDeactivator
  698. {
  699. public:
  700. explicit ScopedPluginDeactivator (ARAPluginInstanceWrapper& inst) : instance (inst)
  701. {
  702. if (instance.prepareToPlayParams.isValid)
  703. instance.inner->releaseResources();
  704. }
  705. ~ScopedPluginDeactivator()
  706. {
  707. if (instance.prepareToPlayParams.isValid)
  708. instance.inner->prepareToPlay (instance.prepareToPlayParams.sampleRate,
  709. instance.prepareToPlayParams.samplesPerBlock);
  710. }
  711. private:
  712. ARAPluginInstanceWrapper& instance;
  713. JUCE_DECLARE_NON_COPYABLE (ScopedPluginDeactivator)
  714. };
  715. class ContextUpdateSource
  716. {
  717. public:
  718. enum class Type
  719. {
  720. empty,
  721. audioSourceFile,
  722. stateInformation,
  723. reset
  724. };
  725. ContextUpdateSource() = default;
  726. explicit ContextUpdateSource (const File& file)
  727. : type (Type::audioSourceFile),
  728. audioSourceFile (file)
  729. {
  730. }
  731. ContextUpdateSource (const void* d, int s)
  732. : type (Type::stateInformation),
  733. stateInformation (d, (size_t) s)
  734. {
  735. }
  736. ContextUpdateSource (Type t) : type (t)
  737. {
  738. jassert (t == Type::reset);
  739. }
  740. Type getType() const { return type; }
  741. const File& getAudioSourceFile() const
  742. {
  743. jassert (type == Type::audioSourceFile);
  744. return audioSourceFile;
  745. }
  746. const MemoryBlock& getStateInformation() const
  747. {
  748. jassert (type == Type::stateInformation);
  749. return stateInformation;
  750. }
  751. private:
  752. Type type = Type::empty;
  753. File audioSourceFile;
  754. MemoryBlock stateInformation;
  755. };
  756. void synchronise()
  757. {
  758. const SpinLock::ScopedLockType scope (instance.innerProcessBlockFlag);
  759. std::lock_guard<std::mutex> configurationLock (instance.innerMutex);
  760. synchronizeStateWithDocumentController();
  761. }
  762. void synchronizeStateWithDocumentController()
  763. {
  764. bool resetContext = false;
  765. auto newContext = [&]() -> std::unique_ptr<Context>
  766. {
  767. std::lock_guard<std::mutex> lock { contextUpdateSourceMutex };
  768. switch (contextUpdateSource.getType())
  769. {
  770. case ContextUpdateSource::Type::empty:
  771. return {};
  772. case ContextUpdateSource::Type::audioSourceFile:
  773. if (! (contextUpdateSource.getAudioSourceFile().existsAsFile()))
  774. return {};
  775. {
  776. const ARAEditGuard editGuard (documentController->getDocumentController());
  777. return std::make_unique<Context> (documentController->getDocumentController(),
  778. contextUpdateSource.getAudioSourceFile());
  779. }
  780. case ContextUpdateSource::Type::stateInformation:
  781. jassert (contextUpdateSource.getStateInformation().getSize() <= std::numeric_limits<int>::max());
  782. return Context::createFromStateInformation (documentController->getDocumentController(),
  783. contextUpdateSource.getStateInformation().getData(),
  784. (int) contextUpdateSource.getStateInformation().getSize());
  785. case ContextUpdateSource::Type::reset:
  786. resetContext = true;
  787. return {};
  788. }
  789. jassertfalse;
  790. return {};
  791. }();
  792. if (newContext != nullptr)
  793. {
  794. {
  795. ScopedPluginDeactivator deactivator (instance);
  796. context = std::move (newContext);
  797. audioSourceLength.store (context->fileAudioSource.getFormatReader().lengthInSamples);
  798. auto& region = context->playbackRegion.getPlaybackRegion();
  799. playbackRenderer.add (region);
  800. editorRenderer.add (region);
  801. }
  802. sendChangeMessage();
  803. }
  804. if (resetContext)
  805. {
  806. {
  807. ScopedPluginDeactivator deactivator (instance);
  808. context.reset();
  809. audioSourceLength.store (0);
  810. }
  811. sendChangeMessage();
  812. }
  813. }
  814. struct Context
  815. {
  816. Context (ARA::Host::DocumentController& dc, const File& audioFileIn)
  817. : audioFile (audioFileIn),
  818. musicalContext (dc),
  819. regionSequence (dc, musicalContext, "track 1"),
  820. fileAudioSource (dc, audioFile),
  821. audioModification (dc, fileAudioSource),
  822. playbackRegion (dc, regionSequence, audioModification, fileAudioSource)
  823. {
  824. }
  825. static std::unique_ptr<Context> createFromStateInformation (ARA::Host::DocumentController& dc, const void* d, int s)
  826. {
  827. if (auto xml = getXmlFromBinary (d, s))
  828. {
  829. if (xml->hasTagName (xmlRootTag))
  830. {
  831. File file { xml->getStringAttribute (xmlAudioFileAttrib) };
  832. if (file.existsAsFile())
  833. return std::make_unique<Context> (dc, std::move (file));
  834. }
  835. }
  836. return {};
  837. }
  838. void getStateInformation (juce::MemoryBlock& b)
  839. {
  840. XmlElement root { xmlRootTag };
  841. root.setAttribute (xmlAudioFileAttrib, audioFile.getFullPathName());
  842. copyXmlToBinary (root, b);
  843. }
  844. const static Identifier xmlRootTag;
  845. const static Identifier xmlAudioFileAttrib;
  846. File audioFile;
  847. MusicalContext musicalContext;
  848. RegionSequence regionSequence;
  849. FileAudioSource fileAudioSource;
  850. AudioModification audioModification;
  851. PlaybackRegion playbackRegion;
  852. };
  853. SimplePlayHead playHead;
  854. ARAPluginInstanceWrapper& instance;
  855. std::unique_ptr<ARAHostDocumentController> documentController;
  856. ARAHostModel::PlaybackRendererInterface playbackRenderer;
  857. ARAHostModel::EditorRendererInterface editorRenderer;
  858. std::unique_ptr<Context> context;
  859. mutable std::mutex contextUpdateSourceMutex;
  860. ContextUpdateSource contextUpdateSource;
  861. std::atomic<bool> isPlaying { false };
  862. std::atomic<bool> goToStartSignal { false };
  863. std::atomic<int64> audioSourceLength { 0 };
  864. };
  865. explicit ARAPluginInstanceWrapper (std::unique_ptr<AudioPluginInstance> innerIn)
  866. : inner (std::move (innerIn)), araHost (*this)
  867. {
  868. jassert (inner != nullptr);
  869. for (auto isInput : { true, false })
  870. matchBuses (isInput);
  871. setBusesLayout (inner->getBusesLayout());
  872. }
  873. //==============================================================================
  874. AudioProcessorEditor* createARAHostEditor() { return araHost.createEditor(); }
  875. //==============================================================================
  876. const String getName() const override
  877. {
  878. std::lock_guard<std::mutex> lock (innerMutex);
  879. return inner->getName();
  880. }
  881. StringArray getAlternateDisplayNames() const override
  882. {
  883. std::lock_guard<std::mutex> lock (innerMutex);
  884. return inner->getAlternateDisplayNames();
  885. }
  886. double getTailLengthSeconds() const override
  887. {
  888. std::lock_guard<std::mutex> lock (innerMutex);
  889. return inner->getTailLengthSeconds();
  890. }
  891. bool acceptsMidi() const override
  892. {
  893. std::lock_guard<std::mutex> lock (innerMutex);
  894. return inner->acceptsMidi();
  895. }
  896. bool producesMidi() const override
  897. {
  898. std::lock_guard<std::mutex> lock (innerMutex);
  899. return inner->producesMidi();
  900. }
  901. AudioProcessorEditor* createEditor() override
  902. {
  903. std::lock_guard<std::mutex> lock (innerMutex);
  904. return inner->createEditorIfNeeded();
  905. }
  906. bool hasEditor() const override
  907. {
  908. std::lock_guard<std::mutex> lock (innerMutex);
  909. return inner->hasEditor();
  910. }
  911. int getNumPrograms() override
  912. {
  913. std::lock_guard<std::mutex> lock (innerMutex);
  914. return inner->getNumPrograms();
  915. }
  916. int getCurrentProgram() override
  917. {
  918. std::lock_guard<std::mutex> lock (innerMutex);
  919. return inner->getCurrentProgram();
  920. }
  921. void setCurrentProgram (int i) override
  922. {
  923. std::lock_guard<std::mutex> lock (innerMutex);
  924. inner->setCurrentProgram (i);
  925. }
  926. const String getProgramName (int i) override
  927. {
  928. std::lock_guard<std::mutex> lock (innerMutex);
  929. return inner->getProgramName (i);
  930. }
  931. void changeProgramName (int i, const String& n) override
  932. {
  933. std::lock_guard<std::mutex> lock (innerMutex);
  934. inner->changeProgramName (i, n);
  935. }
  936. void getStateInformation (juce::MemoryBlock& b) override
  937. {
  938. XmlElement state ("ARAPluginInstanceWrapperState");
  939. {
  940. MemoryBlock m;
  941. araHost.getStateInformation (m);
  942. state.createNewChildElement ("host")->addTextElement (m.toBase64Encoding());
  943. }
  944. {
  945. std::lock_guard<std::mutex> lock (innerMutex);
  946. MemoryBlock m;
  947. inner->getStateInformation (m);
  948. state.createNewChildElement ("plugin")->addTextElement (m.toBase64Encoding());
  949. }
  950. copyXmlToBinary (state, b);
  951. }
  952. void setStateInformation (const void* d, int s) override
  953. {
  954. if (auto xml = getXmlFromBinary (d, s))
  955. {
  956. if (xml->hasTagName ("ARAPluginInstanceWrapperState"))
  957. {
  958. if (auto* hostState = xml->getChildByName ("host"))
  959. {
  960. MemoryBlock m;
  961. m.fromBase64Encoding (hostState->getAllSubText());
  962. jassert (m.getSize() <= std::numeric_limits<int>::max());
  963. araHost.setStateInformation (m.getData(), (int) m.getSize());
  964. }
  965. if (auto* pluginState = xml->getChildByName ("plugin"))
  966. {
  967. std::lock_guard<std::mutex> lock (innerMutex);
  968. MemoryBlock m;
  969. m.fromBase64Encoding (pluginState->getAllSubText());
  970. jassert (m.getSize() <= std::numeric_limits<int>::max());
  971. inner->setStateInformation (m.getData(), (int) m.getSize());
  972. }
  973. }
  974. }
  975. }
  976. void getCurrentProgramStateInformation (juce::MemoryBlock& b) override
  977. {
  978. std::lock_guard<std::mutex> lock (innerMutex);
  979. inner->getCurrentProgramStateInformation (b);
  980. }
  981. void setCurrentProgramStateInformation (const void* d, int s) override
  982. {
  983. std::lock_guard<std::mutex> lock (innerMutex);
  984. inner->setCurrentProgramStateInformation (d, s);
  985. }
  986. void prepareToPlay (double sr, int bs) override
  987. {
  988. std::lock_guard<std::mutex> lock (innerMutex);
  989. inner->setRateAndBufferSizeDetails (sr, bs);
  990. inner->prepareToPlay (sr, bs);
  991. prepareToPlayParams = { sr, bs };
  992. }
  993. void releaseResources() override { inner->releaseResources(); }
  994. void memoryWarningReceived() override { inner->memoryWarningReceived(); }
  995. void processBlock (AudioBuffer<float>& a, MidiBuffer& m) override
  996. {
  997. const SpinLock::ScopedTryLockType scope (innerProcessBlockFlag);
  998. if (! scope.isLocked())
  999. return;
  1000. inner->processBlock (a, m);
  1001. araHost.afterProcessBlock (a.getNumSamples());
  1002. }
  1003. void processBlock (AudioBuffer<double>& a, MidiBuffer& m) override
  1004. {
  1005. const SpinLock::ScopedTryLockType scope (innerProcessBlockFlag);
  1006. if (! scope.isLocked())
  1007. return;
  1008. inner->processBlock (a, m);
  1009. araHost.afterProcessBlock (a.getNumSamples());
  1010. }
  1011. void processBlockBypassed (AudioBuffer<float>& a, MidiBuffer& m) override
  1012. {
  1013. const SpinLock::ScopedTryLockType scope (innerProcessBlockFlag);
  1014. if (! scope.isLocked())
  1015. return;
  1016. inner->processBlockBypassed (a, m);
  1017. araHost.afterProcessBlock (a.getNumSamples());
  1018. }
  1019. void processBlockBypassed (AudioBuffer<double>& a, MidiBuffer& m) override
  1020. {
  1021. const SpinLock::ScopedTryLockType scope (innerProcessBlockFlag);
  1022. if (! scope.isLocked())
  1023. return;
  1024. inner->processBlockBypassed (a, m);
  1025. araHost.afterProcessBlock (a.getNumSamples());
  1026. }
  1027. bool supportsDoublePrecisionProcessing() const override
  1028. {
  1029. std::lock_guard<std::mutex> lock (innerMutex);
  1030. return inner->supportsDoublePrecisionProcessing();
  1031. }
  1032. bool supportsMPE() const override
  1033. {
  1034. std::lock_guard<std::mutex> lock (innerMutex);
  1035. return inner->supportsMPE();
  1036. }
  1037. bool isMidiEffect() const override
  1038. {
  1039. std::lock_guard<std::mutex> lock (innerMutex);
  1040. return inner->isMidiEffect();
  1041. }
  1042. void reset() override
  1043. {
  1044. std::lock_guard<std::mutex> lock (innerMutex);
  1045. inner->reset();
  1046. }
  1047. void setNonRealtime (bool b) noexcept override
  1048. {
  1049. std::lock_guard<std::mutex> lock (innerMutex);
  1050. inner->setNonRealtime (b);
  1051. }
  1052. void refreshParameterList() override
  1053. {
  1054. std::lock_guard<std::mutex> lock (innerMutex);
  1055. inner->refreshParameterList();
  1056. }
  1057. void numChannelsChanged() override
  1058. {
  1059. std::lock_guard<std::mutex> lock (innerMutex);
  1060. inner->numChannelsChanged();
  1061. }
  1062. void numBusesChanged() override
  1063. {
  1064. std::lock_guard<std::mutex> lock (innerMutex);
  1065. inner->numBusesChanged();
  1066. }
  1067. void processorLayoutsChanged() override
  1068. {
  1069. std::lock_guard<std::mutex> lock (innerMutex);
  1070. inner->processorLayoutsChanged();
  1071. }
  1072. void setPlayHead (AudioPlayHead* p) override { ignoreUnused (p); }
  1073. void updateTrackProperties (const TrackProperties& p) override
  1074. {
  1075. std::lock_guard<std::mutex> lock (innerMutex);
  1076. inner->updateTrackProperties (p);
  1077. }
  1078. bool isBusesLayoutSupported (const BusesLayout& layout) const override
  1079. {
  1080. std::lock_guard<std::mutex> lock (innerMutex);
  1081. return inner->checkBusesLayoutSupported (layout);
  1082. }
  1083. bool canAddBus (bool) const override
  1084. {
  1085. std::lock_guard<std::mutex> lock (innerMutex);
  1086. return true;
  1087. }
  1088. bool canRemoveBus (bool) const override
  1089. {
  1090. std::lock_guard<std::mutex> lock (innerMutex);
  1091. return true;
  1092. }
  1093. //==============================================================================
  1094. void fillInPluginDescription (PluginDescription& description) const override
  1095. {
  1096. return inner->fillInPluginDescription (description);
  1097. }
  1098. private:
  1099. void matchBuses (bool isInput)
  1100. {
  1101. const auto inBuses = inner->getBusCount (isInput);
  1102. while (getBusCount (isInput) < inBuses)
  1103. addBus (isInput);
  1104. while (inBuses < getBusCount (isInput))
  1105. removeBus (isInput);
  1106. }
  1107. // Used for mutual exclusion between the audio and other threads
  1108. SpinLock innerProcessBlockFlag;
  1109. // Used for mutual exclusion on non-audio threads
  1110. mutable std::mutex innerMutex;
  1111. std::unique_ptr<AudioPluginInstance> inner;
  1112. ARATestHost araHost;
  1113. struct PrepareToPlayParams
  1114. {
  1115. PrepareToPlayParams() : isValid (false) {}
  1116. PrepareToPlayParams (double sampleRateIn, int samplesPerBlockIn)
  1117. : isValid (true), sampleRate (sampleRateIn), samplesPerBlock (samplesPerBlockIn)
  1118. {
  1119. }
  1120. bool isValid;
  1121. double sampleRate;
  1122. int samplesPerBlock;
  1123. };
  1124. PrepareToPlayParams prepareToPlayParams;
  1125. //==============================================================================
  1126. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAPluginInstanceWrapper)
  1127. };
  1128. #endif