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.

514 lines
25KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. #pragma once
  14. namespace juce
  15. {
  16. class ARAPlaybackRenderer;
  17. class ARAEditorRenderer;
  18. class ARAEditorView;
  19. class ARAInputStream;
  20. class ARAOutputStream;
  21. /** This class contains the customisation points for the JUCE provided ARA document controller
  22. implementation.
  23. Every ARA enabled plugin must provide its own document controller implementation. To do this,
  24. inherit from this class, and override its protected methods as needed. Then you need to
  25. implement a global function somewhere in your module called createARAFactory(). This function
  26. must return an ARAFactory* that will instantiate document controller objects using your
  27. specialisation. There are helper functions inside ARADocumentControllerSpecialisation, so the
  28. implementation of createARAFactory() can always be a simple one-liner. For example
  29. @code
  30. class MyDocumentController : public ARADocumentControllerSpecialisation
  31. {
  32. //...
  33. };
  34. const ARA::ARAFactory* JUCE_CALLTYPE createARAFactory()
  35. {
  36. return juce::ARADocumentControllerSpecialisation::createARAFactory<MyDocumentController>();
  37. }
  38. @endcode
  39. Most member functions have a default implementation so you can build up your required feature
  40. set gradually. The protected functions of this class fall in three distinct groups:
  41. - interactive editing and playback,
  42. - analysis features provided by the plugin and utilised by the host, and
  43. - maintaining the ARA model graph.
  44. On top of the pure virtual functions, you will probably want to override
  45. doCreatePlaybackRenderer() at the very least if you want your plugin to play any sound. This
  46. function belongs to the first group.
  47. If your plugin has analysis capabilities and wants to allow the host to access these, functions
  48. in the second group should be overridden.
  49. The default implementation of the ARA model object classes - i.e. ARADocument, ARAMusicalContext,
  50. ARARegionSequence, ARAAudioSource, ARAAudioModification, ARAPlaybackRegion - should be sufficient
  51. for maintaining a representation of the ARA model graph, hence overriding the model object
  52. creation functions e.g. doCreateMusicalContext() is considered an advanced use case. Hence you
  53. should be able to get a lot done without overriding functions in the third group.
  54. In order to react to the various ARA state changes you can override any of the ARA model object
  55. Listener functions that ARADocumentControllerSpecialisation inherits from. Such listener
  56. functions can be attached to one particular model objects instance, but the listener functions
  57. inside ARADocumentControllerSpecialisation will respond to the events of all instances of the
  58. model objects.
  59. @tags{ARA}
  60. */
  61. class ARADocumentControllerSpecialisation : public ARADocument::Listener,
  62. public ARAMusicalContext::Listener,
  63. public ARARegionSequence::Listener,
  64. public ARAAudioSource::Listener,
  65. public ARAAudioModification::Listener,
  66. public ARAPlaybackRegion::Listener
  67. {
  68. public:
  69. //==============================================================================
  70. /** Constructor. Used internally by the ARAFactory implementation.
  71. */
  72. ARADocumentControllerSpecialisation (const ARA::PlugIn::PlugInEntry* entry,
  73. const ARA::ARADocumentControllerHostInstance* instance);
  74. /** Destructor. */
  75. virtual ~ARADocumentControllerSpecialisation();
  76. /** Returns the underlying DocumentController object that references this specialisation.
  77. */
  78. ARA::PlugIn::DocumentController* getDocumentController() noexcept;
  79. /** Helper function for implementing the global createARAFactory() function.
  80. For example
  81. @code
  82. class MyDocumentController : public ARADocumentControllerSpecialisation
  83. {
  84. //...
  85. };
  86. const ARA::ARAFactory* JUCE_CALLTYPE createARAFactory()
  87. {
  88. return juce::ARADocumentControllerSpecialisation::createARAFactory<MyDocumentController>();
  89. }
  90. @endcode
  91. */
  92. template <typename SpecialisationType>
  93. static const ARA::ARAFactory* createARAFactory()
  94. {
  95. static_assert (std::is_base_of<ARADocumentControllerSpecialisation, SpecialisationType>::value,
  96. "DocumentController specialization types must inherit from ARADocumentControllerSpecialisation");
  97. return ARA::PlugIn::PlugInEntry::getPlugInEntry<FactoryConfig<SpecialisationType>>()->getFactory();
  98. }
  99. /** Returns a pointer to the ARADocumentControllerSpecialisation instance that is referenced
  100. by the provided DocumentController. You can use this function to access your specialisation
  101. from anywhere where you have access to ARA::PlugIn::DocumentController*.
  102. */
  103. template <typename Specialisation = ARADocumentControllerSpecialisation>
  104. static Specialisation* getSpecialisedDocumentController (ARA::PlugIn::DocumentController* dc)
  105. {
  106. return static_cast<Specialisation*> (getSpecialisedDocumentControllerImpl (dc));
  107. }
  108. /** Returns a pointer to the ARA document root maintained by this document controller. */
  109. template <typename DocumentType = ARADocument>
  110. DocumentType* getDocument()
  111. {
  112. return static_cast<DocumentType*> (getDocumentImpl());
  113. }
  114. protected:
  115. //==============================================================================
  116. /** Read an ARADocument archive from a juce::InputStream.
  117. @param input Data stream containing previously persisted data to be used when restoring the ARADocument
  118. @param filter A filter to be applied to the stream
  119. Return true if the operation is successful.
  120. @see ARADocumentControllerInterface::restoreObjectsFromArchive
  121. */
  122. virtual bool doRestoreObjectsFromStream (ARAInputStream& input, const ARARestoreObjectsFilter* filter) = 0;
  123. /** Write an ARADocument archive to a juce::OutputStream.
  124. @param output Data stream that should be used to write the persistent ARADocument data
  125. @param filter A filter to be applied to the stream
  126. Returns true if the operation is successful.
  127. @see ARADocumentControllerInterface::storeObjectsToArchive
  128. */
  129. virtual bool doStoreObjectsToStream (ARAOutputStream& output, const ARAStoreObjectsFilter* filter) = 0;
  130. //==============================================================================
  131. /** Override to return a custom subclass instance of ARAPlaybackRenderer. */
  132. virtual ARAPlaybackRenderer* doCreatePlaybackRenderer();
  133. /** Override to return a custom subclass instance of ARAEditorRenderer. */
  134. virtual ARAEditorRenderer* doCreateEditorRenderer();
  135. /** Override to return a custom subclass instance of ARAEditorView. */
  136. virtual ARAEditorView* doCreateEditorView();
  137. //==============================================================================
  138. // ARAAudioSource content access
  139. /** Override to implement isAudioSourceContentAvailable() for all your supported content types -
  140. the default implementation always returns false, preventing any calls to doGetAudioSourceContentGrade()
  141. and doCreateAudioSourceContentReader().
  142. This function's result is returned from
  143. ARA::PlugIn::DocumentControllerDelegate::doIsAudioSourceContentAvailable.
  144. */
  145. virtual bool doIsAudioSourceContentAvailable (const ARA::PlugIn::AudioSource* audioSource,
  146. ARA::ARAContentType type);
  147. /** Override to implement getAudioSourceContentGrade() for all your supported content types.
  148. This function's result is returned from
  149. ARA::PlugIn::DocumentControllerDelegate::doGetAudioSourceContentGrade.
  150. */
  151. virtual ARA::ARAContentGrade doGetAudioSourceContentGrade (const ARA::PlugIn::AudioSource* audioSource,
  152. ARA::ARAContentType type);
  153. /** Override to implement createAudioSourceContentReader() for all your supported content types,
  154. returning a custom subclass instance of ContentReader providing data of the requested type.
  155. This function's result is returned from
  156. ARA::PlugIn::DocumentControllerDelegate::doCreateAudioSourceContentReader.
  157. */
  158. virtual ARA::PlugIn::ContentReader* doCreateAudioSourceContentReader (ARA::PlugIn::AudioSource* audioSource,
  159. ARA::ARAContentType type,
  160. const ARA::ARAContentTimeRange* range);
  161. //==============================================================================
  162. // ARAAudioModification content access
  163. /** Override to implement isAudioModificationContentAvailable() for all your supported content types -
  164. the default implementation always returns false.
  165. For read-only data directly inherited from the underlying audio source you can just delegate the
  166. call to the audio source, but user-editable modification data must be specifically handled here.
  167. This function's result is returned from
  168. ARA::PlugIn::DocumentControllerDelegate::doIsAudioModificationContentAvailable.
  169. */
  170. virtual bool doIsAudioModificationContentAvailable (const ARA::PlugIn::AudioModification* audioModification,
  171. ARA::ARAContentType type);
  172. /** Override to implement getAudioModificationContentGrade() for all your supported content types.
  173. For read-only data directly inherited from the underlying audio source you can just delegate the
  174. call to the audio source, but user-editable modification data must be specifically handled here.
  175. This function's result is returned from
  176. ARA::PlugIn::DocumentControllerDelegate::doGetAudioModificationContentGrade.
  177. */
  178. virtual ARA::ARAContentGrade doGetAudioModificationContentGrade (const ARA::PlugIn::AudioModification* audioModification,
  179. ARA::ARAContentType type);
  180. /** Override to implement createAudioModificationContentReader() for all your supported content types,
  181. returning a custom subclass instance of ContentReader providing data of the requested \p type.
  182. For read-only data directly inherited from the underlying audio source you can just delegate the
  183. call to the audio source, but user-editable modification data must be specifically handled here.
  184. This function's result is returned from
  185. ARA::PlugIn::DocumentControllerDelegate::doCreateAudioModificationContentReader.
  186. */
  187. virtual ARA::PlugIn::ContentReader* doCreateAudioModificationContentReader (ARA::PlugIn::AudioModification* audioModification,
  188. ARA::ARAContentType type,
  189. const ARA::ARAContentTimeRange* range);
  190. //==============================================================================
  191. // ARAPlaybackRegion content access
  192. /** Override to implement isPlaybackRegionContentAvailable() for all your supported content types -
  193. the default implementation always returns false.
  194. Typically, this call can directly delegate to the underlying audio modification, since most
  195. plug-ins will apply their modification data to the playback region with a transformation that
  196. does not affect content availability.
  197. This function's result is returned from
  198. ARA::PlugIn::DocumentControllerDelegate::doIsPlaybackRegionContentAvailable.
  199. */
  200. virtual bool doIsPlaybackRegionContentAvailable (const ARA::PlugIn::PlaybackRegion* playbackRegion,
  201. ARA::ARAContentType type);
  202. /** Override to implement getPlaybackRegionContentGrade() for all your supported content types.
  203. Typically, this call can directly delegate to the underlying audio modification, since most
  204. plug-ins will apply their modification data to the playback region with a transformation that
  205. does not affect content grade.
  206. This function's result is returned from
  207. ARA::PlugIn::DocumentControllerDelegate::doGetPlaybackRegionContentGrade.
  208. */
  209. virtual ARA::ARAContentGrade doGetPlaybackRegionContentGrade (const ARA::PlugIn::PlaybackRegion* playbackRegion,
  210. ARA::ARAContentType type);
  211. /** Override to implement createPlaybackRegionContentReader() for all your supported content types,
  212. returning a custom subclass instance of ContentReader providing data of the requested type.
  213. This function's result is returned from
  214. ARA::PlugIn::DocumentControllerDelegate::doCreatePlaybackRegionContentReader.
  215. */
  216. virtual ARA::PlugIn::ContentReader* doCreatePlaybackRegionContentReader (ARA::PlugIn::PlaybackRegion* playbackRegion,
  217. ARA::ARAContentType type,
  218. const ARA::ARAContentTimeRange* range);
  219. //==============================================================================
  220. // ARAAudioSource analysis
  221. /** Override to implement isAudioSourceContentAnalysisIncomplete().
  222. This function's result is returned from
  223. ARA::PlugIn::DocumentControllerDelegate::doIsAudioSourceContentAnalysisIncomplete.
  224. */
  225. virtual bool doIsAudioSourceContentAnalysisIncomplete (const ARA::PlugIn::AudioSource* audioSource,
  226. ARA::ARAContentType type);
  227. /** Override to implement requestAudioSourceContentAnalysis().
  228. This function's called from
  229. ARA::PlugIn::DocumentControllerDelegate::doRequestAudioSourceContentAnalysis.
  230. */
  231. virtual void doRequestAudioSourceContentAnalysis (ARA::PlugIn::AudioSource* audioSource,
  232. std::vector<ARA::ARAContentType> const& contentTypes);
  233. //==============================================================================
  234. // Analysis Algorithm selection
  235. /** Override to implement getProcessingAlgorithmsCount().
  236. This function's result is returned from
  237. ARA::PlugIn::DocumentControllerDelegate::doGetProcessingAlgorithmsCount.
  238. */
  239. virtual ARA::ARAInt32 doGetProcessingAlgorithmsCount ();
  240. /** Override to implement getProcessingAlgorithmProperties().
  241. This function's result is returned from
  242. ARA::PlugIn::DocumentControllerDelegate::doGetProcessingAlgorithmProperties.
  243. */
  244. virtual const ARA::ARAProcessingAlgorithmProperties*
  245. doGetProcessingAlgorithmProperties (ARA::ARAInt32 algorithmIndex);
  246. /** Override to implement getProcessingAlgorithmForAudioSource().
  247. This function's result is returned from
  248. ARA::PlugIn::DocumentControllerDelegate::doGetProcessingAlgorithmForAudioSource.
  249. */
  250. virtual ARA::ARAInt32 doGetProcessingAlgorithmForAudioSource (const ARA::PlugIn::AudioSource* audioSource);
  251. /** Override to implement requestProcessingAlgorithmForAudioSource().
  252. This function's called from
  253. ARA::PlugIn::DocumentControllerDelegate::doRequestProcessingAlgorithmForAudioSource.
  254. */
  255. virtual void doRequestProcessingAlgorithmForAudioSource (ARA::PlugIn::AudioSource* audioSource, ARA::ARAInt32 algorithmIndex);
  256. //==============================================================================
  257. /** Override to return a custom subclass instance of ARADocument. */
  258. ARADocument* doCreateDocument();
  259. /** Override to return a custom subclass instance of ARAMusicalContext. */
  260. ARAMusicalContext* doCreateMusicalContext (ARADocument* document,
  261. ARA::ARAMusicalContextHostRef hostRef);
  262. /** Override to return a custom subclass instance of ARARegionSequence. */
  263. ARARegionSequence* doCreateRegionSequence (ARADocument* document,
  264. ARA::ARARegionSequenceHostRef hostRef);
  265. /** Override to return a custom subclass instance of ARAAudioSource. */
  266. ARAAudioSource* doCreateAudioSource (ARADocument* document,
  267. ARA::ARAAudioSourceHostRef hostRef);
  268. /** Override to return a custom subclass instance of ARAAudioModification. */
  269. ARAAudioModification* doCreateAudioModification (ARAAudioSource* audioSource,
  270. ARA::ARAAudioModificationHostRef hostRef,
  271. const ARAAudioModification* optionalModificationToClone);
  272. /** Override to return a custom subclass instance of ARAPlaybackRegion. */
  273. ARAPlaybackRegion* doCreatePlaybackRegion (ARAAudioModification* modification,
  274. ARA::ARAPlaybackRegionHostRef hostRef);
  275. private:
  276. //==============================================================================
  277. template <typename SpecialisationType>
  278. class FactoryConfig : public ARA::PlugIn::FactoryConfig
  279. {
  280. public:
  281. FactoryConfig() noexcept
  282. {
  283. const juce::String compatibleDocumentArchiveIDString = JucePlugin_ARACompatibleArchiveIDs;
  284. if (compatibleDocumentArchiveIDString.isNotEmpty())
  285. {
  286. compatibleDocumentArchiveIDStrings = juce::StringArray::fromLines (compatibleDocumentArchiveIDString);
  287. for (const auto& compatibleID : compatibleDocumentArchiveIDStrings)
  288. compatibleDocumentArchiveIDs.push_back (compatibleID.toRawUTF8());
  289. }
  290. // Update analyzeable content types
  291. static constexpr std::array<ARA::ARAContentType, 6> contentTypes {
  292. ARA::kARAContentTypeNotes,
  293. ARA::kARAContentTypeTempoEntries,
  294. ARA::kARAContentTypeBarSignatures,
  295. ARA::kARAContentTypeStaticTuning,
  296. ARA::kARAContentTypeKeySignatures,
  297. ARA::kARAContentTypeSheetChords
  298. };
  299. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6313)
  300. for (size_t i = 0; i < contentTypes.size(); ++i)
  301. if (JucePlugin_ARAContentTypes & (1 << i))
  302. analyzeableContentTypes.push_back (contentTypes[i]);
  303. JUCE_END_IGNORE_WARNINGS_MSVC
  304. // Update playback transformation flags
  305. static constexpr std::array<ARA::ARAPlaybackTransformationFlags, 4> playbackTransformationFlags {
  306. ARA::kARAPlaybackTransformationTimestretch,
  307. ARA::kARAPlaybackTransformationTimestretchReflectingTempo,
  308. ARA::kARAPlaybackTransformationContentBasedFadeAtTail,
  309. ARA::kARAPlaybackTransformationContentBasedFadeAtHead
  310. };
  311. supportedPlaybackTransformationFlags = 0;
  312. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6313)
  313. for (size_t i = 0; i < playbackTransformationFlags.size(); ++i)
  314. if (JucePlugin_ARATransformationFlags & (1 << i))
  315. supportedPlaybackTransformationFlags |= playbackTransformationFlags[i];
  316. JUCE_END_IGNORE_WARNINGS_MSVC
  317. }
  318. const char* getFactoryID() const noexcept override { return JucePlugin_ARAFactoryID; }
  319. const char* getPlugInName() const noexcept override { return JucePlugin_Name; }
  320. const char* getManufacturerName() const noexcept override { return JucePlugin_Manufacturer; }
  321. const char* getInformationURL() const noexcept override { return JucePlugin_ManufacturerWebsite; }
  322. const char* getVersion() const noexcept override { return JucePlugin_VersionString; }
  323. const char* getDocumentArchiveID() const noexcept override { return JucePlugin_ARADocumentArchiveID; }
  324. ARA::ARASize getCompatibleDocumentArchiveIDsCount() const noexcept override
  325. {
  326. return compatibleDocumentArchiveIDs.size();
  327. }
  328. const ARA::ARAPersistentID* getCompatibleDocumentArchiveIDs() const noexcept override
  329. {
  330. return compatibleDocumentArchiveIDs.empty() ? nullptr : compatibleDocumentArchiveIDs.data();
  331. }
  332. ARA::ARASize getAnalyzeableContentTypesCount() const noexcept override
  333. {
  334. return analyzeableContentTypes.size();
  335. }
  336. const ARA::ARAContentType* getAnalyzeableContentTypes() const noexcept override
  337. {
  338. return analyzeableContentTypes.empty() ? nullptr : analyzeableContentTypes.data();
  339. }
  340. ARA::ARAPlaybackTransformationFlags getSupportedPlaybackTransformationFlags() const noexcept override
  341. {
  342. return supportedPlaybackTransformationFlags;
  343. }
  344. ARA::PlugIn::DocumentController* createDocumentController (const ARA::PlugIn::PlugInEntry* entry,
  345. const ARA::ARADocumentControllerHostInstance* instance) const noexcept override
  346. {
  347. auto* spec = new SpecialisationType (entry, instance);
  348. return spec->getDocumentController();
  349. }
  350. void destroyDocumentController (ARA::PlugIn::DocumentController* controller) const noexcept override
  351. {
  352. delete getSpecialisedDocumentController (controller);
  353. }
  354. private:
  355. juce::StringArray compatibleDocumentArchiveIDStrings;
  356. std::vector<ARA::ARAPersistentID> compatibleDocumentArchiveIDs;
  357. std::vector<ARA::ARAContentType> analyzeableContentTypes;
  358. ARA::ARAPlaybackTransformationFlags supportedPlaybackTransformationFlags;
  359. };
  360. //==============================================================================
  361. static ARADocumentControllerSpecialisation* getSpecialisedDocumentControllerImpl (ARA::PlugIn::DocumentController*);
  362. ARADocument* getDocumentImpl();
  363. //==============================================================================
  364. class ARADocumentControllerImpl;
  365. std::unique_ptr<ARADocumentControllerImpl> documentController;
  366. };
  367. /** Used to read persisted ARA archives - see doRestoreObjectsFromStream() for details.
  368. @tags{ARA}
  369. */
  370. class ARAInputStream : public InputStream
  371. {
  372. public:
  373. explicit ARAInputStream (ARA::PlugIn::HostArchiveReader*);
  374. int64 getPosition() override { return position; }
  375. int64 getTotalLength() override { return size; }
  376. int read (void*, int) override;
  377. bool setPosition (int64) override;
  378. bool isExhausted() override;
  379. bool failed() const { return failure; }
  380. private:
  381. ARA::PlugIn::HostArchiveReader* archiveReader;
  382. int64 position = 0;
  383. int64 size;
  384. bool failure = false;
  385. };
  386. /** Used to write persistent ARA archives - see doStoreObjectsToStream() for details.
  387. @tags{ARA}
  388. */
  389. class ARAOutputStream : public OutputStream
  390. {
  391. public:
  392. explicit ARAOutputStream (ARA::PlugIn::HostArchiveWriter*);
  393. int64 getPosition() override { return position; }
  394. void flush() override {}
  395. bool write (const void*, size_t) override;
  396. bool setPosition (int64) override;
  397. private:
  398. ARA::PlugIn::HostArchiveWriter* archiveWriter;
  399. int64 position = 0;
  400. };
  401. } // namespace juce