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.

1406 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 || JUCE_LINUX)
  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 = (double) 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 final : 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 final : 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 final : 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 final : 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 final : 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 final : public juce::AudioPlayHead
  392. {
  393. Optional<PositionInfo> getPosition() const override
  394. {
  395. PositionInfo result;
  396. result.setTimeInSamples (timeInSamples.load());
  397. result.setIsPlaying (isPlaying.load());
  398. return result;
  399. }
  400. std::atomic<int64_t> timeInSamples { 0 };
  401. std::atomic<bool> isPlaying { false };
  402. };
  403. struct HostPlaybackController
  404. {
  405. virtual ~HostPlaybackController() = default;
  406. virtual void setPlaying (bool isPlaying) = 0;
  407. virtual void goToStart() = 0;
  408. virtual File getAudioSource() const = 0;
  409. virtual void setAudioSource (File audioSourceFile) = 0;
  410. virtual void clearAudioSource() = 0;
  411. };
  412. class AudioSourceComponent final : public Component,
  413. public FileDragAndDropTarget,
  414. public ChangeListener
  415. {
  416. public:
  417. explicit AudioSourceComponent (HostPlaybackController& controller, juce::ChangeBroadcaster& bc)
  418. : hostPlaybackController (controller),
  419. broadcaster (bc),
  420. waveformComponent (*this)
  421. {
  422. audioSourceLabel.setText ("You can drag and drop .wav files here", NotificationType::dontSendNotification);
  423. addAndMakeVisible (audioSourceLabel);
  424. addAndMakeVisible (waveformComponent);
  425. playButton.setButtonText ("Play / Pause");
  426. playButton.onClick = [this]
  427. {
  428. isPlaying = ! isPlaying;
  429. hostPlaybackController.setPlaying (isPlaying);
  430. };
  431. goToStartButton.setButtonText ("Go to start");
  432. goToStartButton.onClick = [this] { hostPlaybackController.goToStart(); };
  433. addAndMakeVisible (goToStartButton);
  434. addAndMakeVisible (playButton);
  435. broadcaster.addChangeListener (this);
  436. update();
  437. }
  438. ~AudioSourceComponent() override
  439. {
  440. broadcaster.removeChangeListener (this);
  441. }
  442. void changeListenerCallback (ChangeBroadcaster*) override
  443. {
  444. update();
  445. }
  446. void resized() override
  447. {
  448. auto localBounds = getLocalBounds();
  449. auto buttonsArea = localBounds.removeFromBottom (40).reduced (5);
  450. auto waveformArea = localBounds.removeFromBottom (150).reduced (5);
  451. juce::FlexBox fb;
  452. fb.justifyContent = juce::FlexBox::JustifyContent::center;
  453. fb.alignContent = juce::FlexBox::AlignContent::center;
  454. fb.items = { juce::FlexItem (goToStartButton).withMinWidth (100.0f).withMinHeight ((float) buttonsArea.getHeight()),
  455. juce::FlexItem (playButton).withMinWidth (100.0f).withMinHeight ((float) buttonsArea.getHeight()) };
  456. fb.performLayout (buttonsArea);
  457. waveformComponent.setBounds (waveformArea);
  458. audioSourceLabel.setBounds (localBounds);
  459. }
  460. bool isInterestedInFileDrag (const StringArray& files) override
  461. {
  462. if (files.size() != 1)
  463. return false;
  464. if (files.getReference (0).endsWithIgnoreCase (".wav"))
  465. return true;
  466. return false;
  467. }
  468. void update()
  469. {
  470. const auto currentAudioSource = hostPlaybackController.getAudioSource();
  471. if (currentAudioSource.existsAsFile())
  472. {
  473. waveformComponent.setSource (currentAudioSource);
  474. audioSourceLabel.setText (currentAudioSource.getFullPathName(),
  475. NotificationType::dontSendNotification);
  476. }
  477. else
  478. {
  479. waveformComponent.clearSource();
  480. audioSourceLabel.setText ("You can drag and drop .wav files here", NotificationType::dontSendNotification);
  481. }
  482. }
  483. void filesDropped (const StringArray& files, int, int) override
  484. {
  485. hostPlaybackController.setAudioSource (files.getReference (0));
  486. update();
  487. }
  488. private:
  489. class WaveformComponent final : public Component,
  490. public ChangeListener
  491. {
  492. public:
  493. WaveformComponent (AudioSourceComponent& p)
  494. : parent (p),
  495. thumbCache (7),
  496. audioThumb (128, formatManager, thumbCache)
  497. {
  498. setWantsKeyboardFocus (true);
  499. formatManager.registerBasicFormats();
  500. audioThumb.addChangeListener (this);
  501. }
  502. ~WaveformComponent() override
  503. {
  504. audioThumb.removeChangeListener (this);
  505. }
  506. void mouseDown (const MouseEvent&) override
  507. {
  508. isSelected = true;
  509. repaint();
  510. }
  511. void changeListenerCallback (ChangeBroadcaster*) override
  512. {
  513. repaint();
  514. }
  515. void paint (juce::Graphics& g) override
  516. {
  517. if (! isEmpty)
  518. {
  519. auto rect = getLocalBounds();
  520. const auto waveformColour = Colours::cadetblue;
  521. if (rect.getWidth() > 2)
  522. {
  523. g.setColour (isSelected ? juce::Colours::yellow : juce::Colours::black);
  524. g.drawRect (rect);
  525. rect.reduce (1, 1);
  526. g.setColour (waveformColour.darker (1.0f));
  527. g.fillRect (rect);
  528. }
  529. g.setColour (Colours::cadetblue);
  530. audioThumb.drawChannels (g, rect, 0.0, audioThumb.getTotalLength(), 1.0f);
  531. }
  532. }
  533. void setSource (const File& source)
  534. {
  535. isEmpty = false;
  536. audioThumb.setSource (new FileInputSource (source));
  537. }
  538. void clearSource()
  539. {
  540. isEmpty = true;
  541. isSelected = false;
  542. audioThumb.clear();
  543. }
  544. bool keyPressed (const KeyPress& key) override
  545. {
  546. if (isSelected && key == KeyPress::deleteKey)
  547. {
  548. parent.hostPlaybackController.clearAudioSource();
  549. return true;
  550. }
  551. return false;
  552. }
  553. private:
  554. AudioSourceComponent& parent;
  555. bool isEmpty = true;
  556. bool isSelected = false;
  557. AudioFormatManager formatManager;
  558. AudioThumbnailCache thumbCache;
  559. AudioThumbnail audioThumb;
  560. };
  561. HostPlaybackController& hostPlaybackController;
  562. juce::ChangeBroadcaster& broadcaster;
  563. Label audioSourceLabel;
  564. WaveformComponent waveformComponent;
  565. bool isPlaying { false };
  566. TextButton playButton, goToStartButton;
  567. };
  568. class ARAPluginInstanceWrapper final : public AudioPluginInstance
  569. {
  570. public:
  571. class ARATestHost final : public HostPlaybackController,
  572. public juce::ChangeBroadcaster
  573. {
  574. public:
  575. class Editor final : public AudioProcessorEditor
  576. {
  577. public:
  578. explicit Editor (ARATestHost& araTestHost)
  579. : AudioProcessorEditor (araTestHost.getAudioPluginInstance()),
  580. audioSourceComponent (araTestHost, araTestHost)
  581. {
  582. audioSourceComponent.update();
  583. addAndMakeVisible (audioSourceComponent);
  584. setSize (512, 220);
  585. }
  586. ~Editor() override { getAudioProcessor()->editorBeingDeleted (this); }
  587. void resized() override { audioSourceComponent.setBounds (getLocalBounds()); }
  588. private:
  589. AudioSourceComponent audioSourceComponent;
  590. };
  591. explicit ARATestHost (ARAPluginInstanceWrapper& instanceIn)
  592. : instance (instanceIn)
  593. {
  594. if (instance.inner->getPluginDescription().hasARAExtension)
  595. {
  596. instance.inner->setPlayHead (&playHead);
  597. createARAFactoryAsync (*instance.inner, [this] (ARAFactoryWrapper araFactory)
  598. {
  599. init (std::move (araFactory));
  600. });
  601. }
  602. }
  603. void init (ARAFactoryWrapper araFactory)
  604. {
  605. if (araFactory.get() != nullptr)
  606. {
  607. documentController = ARAHostDocumentController::create (std::move (araFactory),
  608. "AudioPluginHostDocument",
  609. std::make_unique<AudioAccessController>(),
  610. std::make_unique<ArchivingController>(),
  611. std::make_unique<ContentAccessController>(),
  612. std::make_unique<ModelUpdateController>(),
  613. std::make_unique<PlaybackController>());
  614. if (documentController != nullptr)
  615. {
  616. const auto allRoles = ARA::kARAPlaybackRendererRole | ARA::kARAEditorRendererRole | ARA::kARAEditorViewRole;
  617. const auto plugInExtensionInstance = documentController->bindDocumentToPluginInstance (*instance.inner,
  618. allRoles,
  619. allRoles);
  620. playbackRenderer = plugInExtensionInstance.getPlaybackRendererInterface();
  621. editorRenderer = plugInExtensionInstance.getEditorRendererInterface();
  622. synchronizeStateWithDocumentController();
  623. }
  624. else
  625. jassertfalse;
  626. }
  627. else
  628. jassertfalse;
  629. }
  630. void getStateInformation (juce::MemoryBlock& b)
  631. {
  632. std::lock_guard<std::mutex> configurationLock (instance.innerMutex);
  633. if (context != nullptr)
  634. context->getStateInformation (b);
  635. }
  636. void setStateInformation (const void* d, int s)
  637. {
  638. {
  639. std::lock_guard<std::mutex> lock { contextUpdateSourceMutex };
  640. contextUpdateSource = ContextUpdateSource { d, s };
  641. }
  642. synchronise();
  643. }
  644. ~ARATestHost() override { instance.inner->releaseResources(); }
  645. void afterProcessBlock (int numSamples)
  646. {
  647. const auto isPlayingNow = isPlaying.load();
  648. playHead.isPlaying.store (isPlayingNow);
  649. if (isPlayingNow)
  650. {
  651. const auto currentAudioSourceLength = audioSourceLength.load();
  652. const auto currentPlayHeadPosition = playHead.timeInSamples.load();
  653. // Rudimentary attempt to not seek beyond our sample data, assuming a fairly stable numSamples
  654. // value. We should gain control over calling the AudioProcessorGraph's processBlock() calls so
  655. // that we can do sample precise looping.
  656. if (currentAudioSourceLength - currentPlayHeadPosition < numSamples)
  657. playHead.timeInSamples.store (0);
  658. else
  659. playHead.timeInSamples.fetch_add (numSamples);
  660. }
  661. if (goToStartSignal.exchange (false))
  662. playHead.timeInSamples.store (0);
  663. }
  664. File getAudioSource() const override
  665. {
  666. std::lock_guard<std::mutex> lock { instance.innerMutex };
  667. if (context != nullptr)
  668. return context->audioFile;
  669. return {};
  670. }
  671. void setAudioSource (File audioSourceFile) override
  672. {
  673. if (audioSourceFile.existsAsFile())
  674. {
  675. {
  676. std::lock_guard<std::mutex> lock { contextUpdateSourceMutex };
  677. contextUpdateSource = ContextUpdateSource (std::move (audioSourceFile));
  678. }
  679. synchronise();
  680. }
  681. }
  682. void clearAudioSource() override
  683. {
  684. {
  685. std::lock_guard<std::mutex> lock { contextUpdateSourceMutex };
  686. contextUpdateSource = ContextUpdateSource (ContextUpdateSource::Type::reset);
  687. }
  688. synchronise();
  689. }
  690. void setPlaying (bool isPlayingIn) override { isPlaying.store (isPlayingIn); }
  691. void goToStart() override { goToStartSignal.store (true); }
  692. Editor* createEditor() { return new Editor (*this); }
  693. AudioPluginInstance& getAudioPluginInstance() { return instance; }
  694. private:
  695. /** Use this to put the plugin in an unprepared state for the duration of adding and removing PlaybackRegions
  696. to and from Renderers.
  697. */
  698. class ScopedPluginDeactivator
  699. {
  700. public:
  701. explicit ScopedPluginDeactivator (ARAPluginInstanceWrapper& inst) : instance (inst)
  702. {
  703. if (instance.prepareToPlayParams.isValid)
  704. instance.inner->releaseResources();
  705. }
  706. ~ScopedPluginDeactivator()
  707. {
  708. if (instance.prepareToPlayParams.isValid)
  709. instance.inner->prepareToPlay (instance.prepareToPlayParams.sampleRate,
  710. instance.prepareToPlayParams.samplesPerBlock);
  711. }
  712. private:
  713. ARAPluginInstanceWrapper& instance;
  714. JUCE_DECLARE_NON_COPYABLE (ScopedPluginDeactivator)
  715. };
  716. class ContextUpdateSource
  717. {
  718. public:
  719. enum class Type
  720. {
  721. empty,
  722. audioSourceFile,
  723. stateInformation,
  724. reset
  725. };
  726. ContextUpdateSource() = default;
  727. explicit ContextUpdateSource (const File& file)
  728. : type (Type::audioSourceFile),
  729. audioSourceFile (file)
  730. {
  731. }
  732. ContextUpdateSource (const void* d, int s)
  733. : type (Type::stateInformation),
  734. stateInformation (d, (size_t) s)
  735. {
  736. }
  737. ContextUpdateSource (Type t) : type (t)
  738. {
  739. jassert (t == Type::reset);
  740. }
  741. Type getType() const { return type; }
  742. const File& getAudioSourceFile() const
  743. {
  744. jassert (type == Type::audioSourceFile);
  745. return audioSourceFile;
  746. }
  747. const MemoryBlock& getStateInformation() const
  748. {
  749. jassert (type == Type::stateInformation);
  750. return stateInformation;
  751. }
  752. private:
  753. Type type = Type::empty;
  754. File audioSourceFile;
  755. MemoryBlock stateInformation;
  756. };
  757. void synchronise()
  758. {
  759. const SpinLock::ScopedLockType scope (instance.innerProcessBlockFlag);
  760. std::lock_guard<std::mutex> configurationLock (instance.innerMutex);
  761. synchronizeStateWithDocumentController();
  762. }
  763. void synchronizeStateWithDocumentController()
  764. {
  765. bool resetContext = false;
  766. auto newContext = [&]() -> std::unique_ptr<Context>
  767. {
  768. std::lock_guard<std::mutex> lock { contextUpdateSourceMutex };
  769. switch (contextUpdateSource.getType())
  770. {
  771. case ContextUpdateSource::Type::empty:
  772. return {};
  773. case ContextUpdateSource::Type::audioSourceFile:
  774. if (! (contextUpdateSource.getAudioSourceFile().existsAsFile()))
  775. return {};
  776. {
  777. const ARAEditGuard editGuard (documentController->getDocumentController());
  778. return std::make_unique<Context> (documentController->getDocumentController(),
  779. contextUpdateSource.getAudioSourceFile());
  780. }
  781. case ContextUpdateSource::Type::stateInformation:
  782. jassert (contextUpdateSource.getStateInformation().getSize() <= std::numeric_limits<int>::max());
  783. return Context::createFromStateInformation (documentController->getDocumentController(),
  784. contextUpdateSource.getStateInformation().getData(),
  785. (int) contextUpdateSource.getStateInformation().getSize());
  786. case ContextUpdateSource::Type::reset:
  787. resetContext = true;
  788. return {};
  789. }
  790. jassertfalse;
  791. return {};
  792. }();
  793. if (newContext != nullptr)
  794. {
  795. {
  796. ScopedPluginDeactivator deactivator (instance);
  797. context = std::move (newContext);
  798. audioSourceLength.store (context->fileAudioSource.getFormatReader().lengthInSamples);
  799. auto& region = context->playbackRegion.getPlaybackRegion();
  800. playbackRenderer.add (region);
  801. editorRenderer.add (region);
  802. }
  803. sendChangeMessage();
  804. }
  805. if (resetContext)
  806. {
  807. {
  808. ScopedPluginDeactivator deactivator (instance);
  809. context.reset();
  810. audioSourceLength.store (0);
  811. }
  812. sendChangeMessage();
  813. }
  814. }
  815. struct Context
  816. {
  817. Context (ARA::Host::DocumentController& dc, const File& audioFileIn)
  818. : audioFile (audioFileIn),
  819. musicalContext (dc),
  820. regionSequence (dc, musicalContext, "track 1"),
  821. fileAudioSource (dc, audioFile),
  822. audioModification (dc, fileAudioSource),
  823. playbackRegion (dc, regionSequence, audioModification, fileAudioSource)
  824. {
  825. }
  826. static std::unique_ptr<Context> createFromStateInformation (ARA::Host::DocumentController& dc, const void* d, int s)
  827. {
  828. if (auto xml = getXmlFromBinary (d, s))
  829. {
  830. if (xml->hasTagName (xmlRootTag))
  831. {
  832. File file { xml->getStringAttribute (xmlAudioFileAttrib) };
  833. if (file.existsAsFile())
  834. return std::make_unique<Context> (dc, std::move (file));
  835. }
  836. }
  837. return {};
  838. }
  839. void getStateInformation (juce::MemoryBlock& b)
  840. {
  841. XmlElement root { xmlRootTag };
  842. root.setAttribute (xmlAudioFileAttrib, audioFile.getFullPathName());
  843. copyXmlToBinary (root, b);
  844. }
  845. const static Identifier xmlRootTag;
  846. const static Identifier xmlAudioFileAttrib;
  847. File audioFile;
  848. MusicalContext musicalContext;
  849. RegionSequence regionSequence;
  850. FileAudioSource fileAudioSource;
  851. AudioModification audioModification;
  852. PlaybackRegion playbackRegion;
  853. };
  854. SimplePlayHead playHead;
  855. ARAPluginInstanceWrapper& instance;
  856. std::unique_ptr<ARAHostDocumentController> documentController;
  857. ARAHostModel::PlaybackRendererInterface playbackRenderer;
  858. ARAHostModel::EditorRendererInterface editorRenderer;
  859. std::unique_ptr<Context> context;
  860. mutable std::mutex contextUpdateSourceMutex;
  861. ContextUpdateSource contextUpdateSource;
  862. std::atomic<bool> isPlaying { false };
  863. std::atomic<bool> goToStartSignal { false };
  864. std::atomic<int64> audioSourceLength { 0 };
  865. };
  866. explicit ARAPluginInstanceWrapper (std::unique_ptr<AudioPluginInstance> innerIn)
  867. : inner (std::move (innerIn)), araHost (*this)
  868. {
  869. jassert (inner != nullptr);
  870. for (auto isInput : { true, false })
  871. matchBuses (isInput);
  872. setBusesLayout (inner->getBusesLayout());
  873. }
  874. //==============================================================================
  875. AudioProcessorEditor* createARAHostEditor() { return araHost.createEditor(); }
  876. //==============================================================================
  877. const String getName() const override
  878. {
  879. std::lock_guard<std::mutex> lock (innerMutex);
  880. return inner->getName();
  881. }
  882. StringArray getAlternateDisplayNames() const override
  883. {
  884. std::lock_guard<std::mutex> lock (innerMutex);
  885. return inner->getAlternateDisplayNames();
  886. }
  887. double getTailLengthSeconds() const override
  888. {
  889. std::lock_guard<std::mutex> lock (innerMutex);
  890. return inner->getTailLengthSeconds();
  891. }
  892. bool acceptsMidi() const override
  893. {
  894. std::lock_guard<std::mutex> lock (innerMutex);
  895. return inner->acceptsMidi();
  896. }
  897. bool producesMidi() const override
  898. {
  899. std::lock_guard<std::mutex> lock (innerMutex);
  900. return inner->producesMidi();
  901. }
  902. AudioProcessorEditor* createEditor() override
  903. {
  904. std::lock_guard<std::mutex> lock (innerMutex);
  905. return inner->createEditorIfNeeded();
  906. }
  907. bool hasEditor() const override
  908. {
  909. std::lock_guard<std::mutex> lock (innerMutex);
  910. return inner->hasEditor();
  911. }
  912. int getNumPrograms() override
  913. {
  914. std::lock_guard<std::mutex> lock (innerMutex);
  915. return inner->getNumPrograms();
  916. }
  917. int getCurrentProgram() override
  918. {
  919. std::lock_guard<std::mutex> lock (innerMutex);
  920. return inner->getCurrentProgram();
  921. }
  922. void setCurrentProgram (int i) override
  923. {
  924. std::lock_guard<std::mutex> lock (innerMutex);
  925. inner->setCurrentProgram (i);
  926. }
  927. const String getProgramName (int i) override
  928. {
  929. std::lock_guard<std::mutex> lock (innerMutex);
  930. return inner->getProgramName (i);
  931. }
  932. void changeProgramName (int i, const String& n) override
  933. {
  934. std::lock_guard<std::mutex> lock (innerMutex);
  935. inner->changeProgramName (i, n);
  936. }
  937. void getStateInformation (juce::MemoryBlock& b) override
  938. {
  939. XmlElement state ("ARAPluginInstanceWrapperState");
  940. {
  941. MemoryBlock m;
  942. araHost.getStateInformation (m);
  943. state.createNewChildElement ("host")->addTextElement (m.toBase64Encoding());
  944. }
  945. {
  946. std::lock_guard<std::mutex> lock (innerMutex);
  947. MemoryBlock m;
  948. inner->getStateInformation (m);
  949. state.createNewChildElement ("plugin")->addTextElement (m.toBase64Encoding());
  950. }
  951. copyXmlToBinary (state, b);
  952. }
  953. void setStateInformation (const void* d, int s) override
  954. {
  955. if (auto xml = getXmlFromBinary (d, s))
  956. {
  957. if (xml->hasTagName ("ARAPluginInstanceWrapperState"))
  958. {
  959. if (auto* hostState = xml->getChildByName ("host"))
  960. {
  961. MemoryBlock m;
  962. m.fromBase64Encoding (hostState->getAllSubText());
  963. jassert (m.getSize() <= std::numeric_limits<int>::max());
  964. araHost.setStateInformation (m.getData(), (int) m.getSize());
  965. }
  966. if (auto* pluginState = xml->getChildByName ("plugin"))
  967. {
  968. std::lock_guard<std::mutex> lock (innerMutex);
  969. MemoryBlock m;
  970. m.fromBase64Encoding (pluginState->getAllSubText());
  971. jassert (m.getSize() <= std::numeric_limits<int>::max());
  972. inner->setStateInformation (m.getData(), (int) m.getSize());
  973. }
  974. }
  975. }
  976. }
  977. void getCurrentProgramStateInformation (juce::MemoryBlock& b) override
  978. {
  979. std::lock_guard<std::mutex> lock (innerMutex);
  980. inner->getCurrentProgramStateInformation (b);
  981. }
  982. void setCurrentProgramStateInformation (const void* d, int s) override
  983. {
  984. std::lock_guard<std::mutex> lock (innerMutex);
  985. inner->setCurrentProgramStateInformation (d, s);
  986. }
  987. void prepareToPlay (double sr, int bs) override
  988. {
  989. std::lock_guard<std::mutex> lock (innerMutex);
  990. inner->setRateAndBufferSizeDetails (sr, bs);
  991. inner->prepareToPlay (sr, bs);
  992. prepareToPlayParams = { sr, bs };
  993. }
  994. void releaseResources() override { inner->releaseResources(); }
  995. void memoryWarningReceived() override { inner->memoryWarningReceived(); }
  996. void processBlock (AudioBuffer<float>& a, MidiBuffer& m) override
  997. {
  998. const SpinLock::ScopedTryLockType scope (innerProcessBlockFlag);
  999. if (! scope.isLocked())
  1000. return;
  1001. inner->processBlock (a, m);
  1002. araHost.afterProcessBlock (a.getNumSamples());
  1003. }
  1004. void processBlock (AudioBuffer<double>& a, MidiBuffer& m) override
  1005. {
  1006. const SpinLock::ScopedTryLockType scope (innerProcessBlockFlag);
  1007. if (! scope.isLocked())
  1008. return;
  1009. inner->processBlock (a, m);
  1010. araHost.afterProcessBlock (a.getNumSamples());
  1011. }
  1012. void processBlockBypassed (AudioBuffer<float>& a, MidiBuffer& m) override
  1013. {
  1014. const SpinLock::ScopedTryLockType scope (innerProcessBlockFlag);
  1015. if (! scope.isLocked())
  1016. return;
  1017. inner->processBlockBypassed (a, m);
  1018. araHost.afterProcessBlock (a.getNumSamples());
  1019. }
  1020. void processBlockBypassed (AudioBuffer<double>& a, MidiBuffer& m) override
  1021. {
  1022. const SpinLock::ScopedTryLockType scope (innerProcessBlockFlag);
  1023. if (! scope.isLocked())
  1024. return;
  1025. inner->processBlockBypassed (a, m);
  1026. araHost.afterProcessBlock (a.getNumSamples());
  1027. }
  1028. bool supportsDoublePrecisionProcessing() const override
  1029. {
  1030. std::lock_guard<std::mutex> lock (innerMutex);
  1031. return inner->supportsDoublePrecisionProcessing();
  1032. }
  1033. bool supportsMPE() const override
  1034. {
  1035. std::lock_guard<std::mutex> lock (innerMutex);
  1036. return inner->supportsMPE();
  1037. }
  1038. bool isMidiEffect() const override
  1039. {
  1040. std::lock_guard<std::mutex> lock (innerMutex);
  1041. return inner->isMidiEffect();
  1042. }
  1043. void reset() override
  1044. {
  1045. std::lock_guard<std::mutex> lock (innerMutex);
  1046. inner->reset();
  1047. }
  1048. void setNonRealtime (bool b) noexcept override
  1049. {
  1050. std::lock_guard<std::mutex> lock (innerMutex);
  1051. inner->setNonRealtime (b);
  1052. }
  1053. void refreshParameterList() override
  1054. {
  1055. std::lock_guard<std::mutex> lock (innerMutex);
  1056. inner->refreshParameterList();
  1057. }
  1058. void numChannelsChanged() override
  1059. {
  1060. std::lock_guard<std::mutex> lock (innerMutex);
  1061. inner->numChannelsChanged();
  1062. }
  1063. void numBusesChanged() override
  1064. {
  1065. std::lock_guard<std::mutex> lock (innerMutex);
  1066. inner->numBusesChanged();
  1067. }
  1068. void processorLayoutsChanged() override
  1069. {
  1070. std::lock_guard<std::mutex> lock (innerMutex);
  1071. inner->processorLayoutsChanged();
  1072. }
  1073. void setPlayHead (AudioPlayHead* p) override { ignoreUnused (p); }
  1074. void updateTrackProperties (const TrackProperties& p) override
  1075. {
  1076. std::lock_guard<std::mutex> lock (innerMutex);
  1077. inner->updateTrackProperties (p);
  1078. }
  1079. bool isBusesLayoutSupported (const BusesLayout& layout) const override
  1080. {
  1081. std::lock_guard<std::mutex> lock (innerMutex);
  1082. return inner->checkBusesLayoutSupported (layout);
  1083. }
  1084. bool canAddBus (bool) const override
  1085. {
  1086. std::lock_guard<std::mutex> lock (innerMutex);
  1087. return true;
  1088. }
  1089. bool canRemoveBus (bool) const override
  1090. {
  1091. std::lock_guard<std::mutex> lock (innerMutex);
  1092. return true;
  1093. }
  1094. //==============================================================================
  1095. void fillInPluginDescription (PluginDescription& description) const override
  1096. {
  1097. return inner->fillInPluginDescription (description);
  1098. }
  1099. private:
  1100. void matchBuses (bool isInput)
  1101. {
  1102. const auto inBuses = inner->getBusCount (isInput);
  1103. while (getBusCount (isInput) < inBuses)
  1104. addBus (isInput);
  1105. while (inBuses < getBusCount (isInput))
  1106. removeBus (isInput);
  1107. }
  1108. // Used for mutual exclusion between the audio and other threads
  1109. SpinLock innerProcessBlockFlag;
  1110. // Used for mutual exclusion on non-audio threads
  1111. mutable std::mutex innerMutex;
  1112. std::unique_ptr<AudioPluginInstance> inner;
  1113. ARATestHost araHost;
  1114. struct PrepareToPlayParams
  1115. {
  1116. PrepareToPlayParams() : isValid (false) {}
  1117. PrepareToPlayParams (double sampleRateIn, int samplesPerBlockIn)
  1118. : isValid (true), sampleRate (sampleRateIn), samplesPerBlock (samplesPerBlockIn)
  1119. {
  1120. }
  1121. bool isValid;
  1122. double sampleRate;
  1123. int samplesPerBlock;
  1124. };
  1125. PrepareToPlayParams prepareToPlayParams;
  1126. //==============================================================================
  1127. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ARAPluginInstanceWrapper)
  1128. };
  1129. #endif