From 2af4ab7d8bb504d81c43278fe476275cfb76dc90 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:59:55 +0100 Subject: [PATCH 01/42] Revert "Temporarily disable and remove AUv3 and ARA IPC drafts for upcoming 2.3 release" This reverts commit fb6f7c27ad9a690929d6bd6bc53490b93f8d2529. --- CMakeLists.txt | 3 - IPC/ARAIPC.h | 92 ++ IPC/ARAIPCAudioUnit_v3.h | 107 +++ IPC/ARAIPCAudioUnit_v3.mm | 396 ++++++++ IPC/ARAIPCCFEncoding.cpp | 330 +++++++ IPC/ARAIPCCFEncoding.h | 95 ++ IPC/ARAIPCConnection.cpp | 559 +++++++++++ IPC/ARAIPCConnection.h | 316 +++++++ IPC/ARAIPCEncoding.h | 1449 +++++++++++++++++++++++++++++ IPC/ARAIPCMessage.h | 129 +++ IPC/ARAIPCProxyHost.cpp | 1343 +++++++++++++++++++++++++++ IPC/ARAIPCProxyHost.h | 77 ++ IPC/ARAIPCProxyPlugIn.cpp | 1848 +++++++++++++++++++++++++++++++++++++ IPC/ARAIPCProxyPlugIn.h | 93 ++ 14 files changed, 6834 insertions(+), 3 deletions(-) create mode 100644 IPC/ARAIPC.h create mode 100644 IPC/ARAIPCAudioUnit_v3.h create mode 100644 IPC/ARAIPCAudioUnit_v3.mm create mode 100644 IPC/ARAIPCCFEncoding.cpp create mode 100644 IPC/ARAIPCCFEncoding.h create mode 100644 IPC/ARAIPCConnection.cpp create mode 100644 IPC/ARAIPCConnection.h create mode 100644 IPC/ARAIPCEncoding.h create mode 100644 IPC/ARAIPCMessage.h create mode 100644 IPC/ARAIPCProxyHost.cpp create mode 100644 IPC/ARAIPCProxyHost.h create mode 100644 IPC/ARAIPCProxyPlugIn.cpp create mode 100644 IPC/ARAIPCProxyPlugIn.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ca4729d..59d00ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,8 +167,6 @@ configure_ARA_Library_target(ARA_PlugIn_Library) # ====================== -if(FALSE) - add_library(ARA_IPC_Library ${ARA_LIBRARY_TARGET_TYPE} "${CMAKE_CURRENT_SOURCE_DIR}/IPC/ARAIPC.h" "${CMAKE_CURRENT_SOURCE_DIR}/IPC/ARAIPCEncoding.h" @@ -199,4 +197,3 @@ target_link_libraries(ARA_IPC_Library PRIVATE ) configure_ARA_Library_target(ARA_IPC_Library) -endif() diff --git a/IPC/ARAIPC.h b/IPC/ARAIPC.h new file mode 100644 index 0000000..ff56194 --- /dev/null +++ b/IPC/ARAIPC.h @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPC.h +//! C-compatible declarations of externally visible IPC types +//! Typically, this file is not included directly - either ARAIPCProxyHost.h +//! ARAIPCProxyPlugIn.h will be used instead. +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef ARAIPC_h +#define ARAIPC_h + +#include "ARA_API/ARAInterface.h" + + +//! @addtogroup ARA_Library_IPC +//! @{ + +//! switch to bypass all IPC code +#if !defined (ARA_ENABLE_IPC) + #if defined (__APPLE__) || defined (_WIN32) + #define ARA_ENABLE_IPC 1 + #else + #define ARA_ENABLE_IPC 0 + #endif +#endif + +#if ARA_ENABLE_IPC + + +#if defined(__cplusplus) +namespace ARA { +namespace IPC { +extern "C" { + +#if !defined (ARA_MAP_IPC_REF) + #define ARA_MAP_IPC_REF(IPCClassType, FirstIPCRefType, ...) \ + static inline ARA::ToRefConversionHelper toIPCRef (const IPCClassType* ptr) noexcept { return ARA::ToRefConversionHelper { ptr }; } \ + template , IPCRefType>::value, bool>::type = true> \ + static inline DesiredIPCClassType* fromIPCRef (IPCRefType ref) noexcept { IPCClassType* object { ARA::FromRefConversionHelper (ref) }; return static_cast (object); } +#endif + + //! helper define to properly insert ARA::IPC namespace into C compatible headers + #define ARA_IPC_NAMESPACE ARA::IPC:: +#else + #define ARA_IPC_NAMESPACE +#endif + + +//! IPC reference markup type identifier. \br\br +//! Examples: \br +//! ::ARAARAIPCConnectionRef \br +//! ::ARAIPCMessageChannelRef \br +//! ::ARAIPCPlugInInstanceRef \br +#define ARA_IPC_REF(IPCRefType) struct IPCRefType##MarkupType * IPCRefType + + +//! C-compatible wrapper of ARA IPC Connection +typedef ARA_IPC_REF(ARAIPCConnectionRef); + + +//! C-compatible wrapper of ARA IPC MessageChannel +typedef ARA_IPC_REF(ARAIPCMessageChannelRef); + + +//! to keep the IPC decoupled from the companion API in use, the IPC code uses +//! an opaque encapsulation to represent a companion API plug-in instance +typedef ARA_IPC_REF(ARAIPCPlugInInstanceRef); + + +#if defined(__cplusplus) +} // extern "C" +} // namespace IPC +} // namespace ARA +#endif + +#endif // ARA_ENABLE_IPC + +//! @} ARA_Library_IPC + +#endif // ARAIPC_h diff --git a/IPC/ARAIPCAudioUnit_v3.h b/IPC/ARAIPCAudioUnit_v3.h new file mode 100644 index 0000000..831ea63 --- /dev/null +++ b/IPC/ARAIPCAudioUnit_v3.h @@ -0,0 +1,107 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCAudioUnit_v3.h +//! Implementation of ARA IPC message sending through AUMessageChannel +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#import "ARA_API/ARAAudioUnit_v3.h" + + +//! @addtogroup ARA_Library_IPC +//! @{ + + +//! using ARA IPC for Audio Units requires to compile for macOS 13 or higher +#if defined(__MAC_13_0) + #define ARA_AUDIOUNITV3_IPC_IS_AVAILABLE 1 +#else + #define ARA_AUDIOUNITV3_IPC_IS_AVAILABLE 0 +#endif + +#if ARA_AUDIOUNITV3_IPC_IS_AVAILABLE + + +#import "ARA_Library/IPC/ARAIPC.h" + + +#if defined(__cplusplus) +namespace ARA { +namespace IPC { +extern "C" { +#endif + + +API_AVAILABLE_BEGIN(macos(13.0), ios(16.0)) + +//! optional delegate that can be provided in ARAIPCAUProxyPlugInInitialize() +typedef void (*ARAMainThreadWaitForMessageDelegate) (void * _Nullable delegateUserData); + +//! host side: initialize proxy plug-in component and its internal the message channels +//! will return nullptr if the Audio Unit does not implement [AUAudioUnit messageChannelFor:] +//! for the required ARA message channels +//! the provided audioUnit is only used to establish the channels, it can be closed again +//! after the call if not needed otherwise +//! must be balanced with ARAIPCAUProxyPlugInUninitialize() +//! the optional delegate will be called periodically when the IPC needs to block the main thread, +//! with the opaque user data pointer being passed back into the call +ARAIPCConnectionRef _Nullable ARA_CALL ARAIPCAUProxyPlugInInitialize(AUAudioUnit * _Nonnull audioUnit, + ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, + void * _Nullable delegateUserData); + +//! allows the host to let the plug-in perform ARA IPC on the main thread when otherwise +//! blocking it for an extended period of time +void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCConnectionRef _Nonnull proxyRef); + +//! host side: Audio Unit specialization of ARAIPCProxyPlugInBindToDocumentController() +//! must be balanced with ARAIPCProxyPlugInCleanupBinding() when the given audioUnit is destroyed +const ARAPlugInExtensionInstance * _Nonnull ARA_CALL ARAIPCAUProxyPlugInBindToDocumentController(AUAudioUnit * _Nonnull audioUnit, + ARADocumentControllerRef _Nonnull documentControllerRef, + ARAPlugInInstanceRoleFlags knownRoles, + ARAPlugInInstanceRoleFlags assignedRoles); + +//! host side: uninitialize the proxy component set up in ARAIPCAUProxyPlugInInitialize() +void ARA_CALL ARAIPCAUProxyPlugInUninitialize(ARAIPCConnectionRef _Nonnull proxyRef); + + + +//! plug-in side:implementation for AUMessageChannel -init... +//! will initialize the proxy on demand - make sure to add all factories via ARAIPCProxyHostAddFactory() +//! before the first call to this function and to call ARAIPCAUProxyHostUninitialize() if the call was made +ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageChannel(NSObject * _Nonnull audioUnitChannel, + bool isMainThreadChannel); + +//! plug-in side: implementation for AUMessageChannel -callAudioUnit: +NSDictionary * _Nonnull ARA_CALL ARAIPCAUProxyHostCommandHandler(ARAIPCMessageChannelRef _Nonnull messageChannelRef, NSDictionary * _Nonnull message); + +//! plug-in side:implementation for AUMessageChannel -dealloc +void ARA_CALL ARAIPCAUProxyHostUninitializeMessageChannel(ARAIPCMessageChannelRef _Nonnull messageChannelRef); + +//! plug-in side: static cleanup upon shutdown +void ARA_CALL ARAIPCAUProxyHostUninitialize(void); + + +API_AVAILABLE_END + + +#if defined(__cplusplus) +} // extern "C" +} // namespace IPC +} // namespace ARA +#endif + + +//! @} ARA_Library_IPC + +#endif // ARA_AUDIOUNITV3_IPC_IS_AVAILABLE diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm new file mode 100644 index 0000000..f39b008 --- /dev/null +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -0,0 +1,396 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCAudioUnit_v3.m +//! Implementation of ARA IPC message sending through AUMessageChannel +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#import "ARAIPCAudioUnit_v3.h" + + +#if ARA_AUDIOUNITV3_IPC_IS_AVAILABLE + + +#import "ARA_Library/Debug/ARADebug.h" +#import "ARA_Library/IPC/ARAIPCEncoding.h" +#import "ARA_Library/IPC/ARAIPCCFEncoding.h" +#import "ARA_Library/IPC/ARAIPCConnection.h" +#import "ARA_Library/IPC/ARAIPCProxyHost.h" +#import "ARA_Library/IPC/ARAIPCProxyPlugIn.h" +#if !ARA_ENABLE_IPC + #error "configuration mismatch: enabling ARA_AUDIOUNITV3_IPC_IS_AVAILABLE requires enabling ARA_ENABLE_IPC too" +#endif + +#include + + +// JUCE hotfix: because JUCE directly includes the .cpp/.mm files from this SDK instead of properly +// compiling the ARA_IPC_Library, these switches allow for skipping the host- or the +// plug-in side of the code, depending on which side is being compiled. +#if !defined(ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY) + #define ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY 0 +#endif +#if !defined(ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY) + #define ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY 0 +#endif + + +namespace ARA { +namespace IPC { + + +_Pragma ("GCC diagnostic push") +_Pragma ("GCC diagnostic ignored \"-Wold-style-cast\"") // __bridge casts can only be done old-style +_Pragma ("GCC diagnostic ignored \"-Wnullability-completeness\"") // \todo add proper nullability annotation + + +API_AVAILABLE_BEGIN(macos(13.0), ios(16.0)) + + +// key for transaction locking through the IPC channel +constexpr NSString * _messageIDKey { @"msgID" }; + + +// connection implementation for both proxy implementations +class AUConnection : public Connection +{ +public: + using Connection::Connection; + + MessageEncoder * createEncoder () override + { + return new CFMessageEncoder {}; + } + + bool receiverEndianessMatches () override + { + // \todo shouldn't the AUMessageChannel provide this information? + return true; + } + + void performPendingMainThreadTasks () + { + getMainThreadDispatcher ()->processPendingMessageIfNeeded (); + } +}; + + +// message channel base class for both proxy implementations +class AudioUnitMessageChannel : public MessageChannel +{ +public: + AudioUnitMessageChannel (NSObject * _Nonnull audioUnitChannel) + : _audioUnitChannel { audioUnitChannel } + { +#if !__has_feature(objc_arc) + [_audioUnitChannel retain]; +#endif + } + +#if !__has_feature(objc_arc) + ~AudioUnitMessageChannel () override + { + [_audioUnitChannel release]; + } +#endif + +public: + void routeReceivedMessage (NSDictionary * _Nonnull message) + { + ARA_INTERNAL_ASSERT (![NSThread isMainThread]); + const MessageID messageID { [(NSNumber *) [message objectForKey:_messageIDKey] intValue] }; + const auto decoder { new CFMessageDecoder { (__bridge CFDictionaryRef) message } }; + getMessageDispatcher ()->routeReceivedMessage (messageID, decoder); + } + + void sendMessage (MessageID messageID, MessageEncoder * encoder) override + { + const auto dictionary { static_cast (encoder)->copyDictionary () }; +#if !__has_feature(objc_arc) + auto message { (__bridge NSMutableDictionary *) dictionary }; +#else + auto message { (__bridge_transfer NSMutableDictionary *) dictionary }; +#endif + [message setObject:[NSNumber numberWithInt: messageID] forKey:_messageIDKey]; + const auto reply { _sendMessage (message) }; + ARA_INTERNAL_ASSERT ([reply count] == 0); +#if !__has_feature(objc_arc) + CFRelease (dictionary); +#endif + } + +protected: + virtual NSDictionary * _sendMessage (NSDictionary * message) = 0; + +protected: + NSObject * __strong _Nonnull _audioUnitChannel; +}; + + +// plug-in side: proxy host message channel specialization +#if !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY +class ProxyHostMessageChannel : public AudioUnitMessageChannel +{ +public: + using AudioUnitMessageChannel::AudioUnitMessageChannel; + +protected: + NSDictionary * _sendMessage (NSDictionary * message) override + { + if (const auto callHostBlock { _audioUnitChannel.callHostBlock }) + { + NSDictionary * reply { callHostBlock (message) }; + return reply; + } + else + { + ARA_INTERNAL_ASSERT (false && "trying to send IPC message while host has not set callHostBlock"); + return nil; + } + } +}; +#endif // !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY + + +// host side: proxy plug-in message channel specialization +#if !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY +class ProxyPlugInMessageChannel : public AudioUnitMessageChannel +{ +public: + ProxyPlugInMessageChannel (NSObject * _Nonnull audioUnitChannel) + : AudioUnitMessageChannel { audioUnitChannel } + { + _audioUnitChannel.callHostBlock = + ^NSDictionary * _Nullable (NSDictionary * _Nonnull message) + { + routeReceivedMessage (message); + return [NSDictionary dictionary]; // \todo it would yield better performance if the callHostBlock would allow nil as return value + }; + + // \todo macOS as of 14.3.1 does not forward the above assignment of callHostBlock to the + // remote side until the first message is sent. However, if another channel is assigned + // its (same) callHostBlock, then the pending callHostBlock for the first channel is + // lost for some reason. We need to send an empty dummy message to work around this bug, + // which them must be filtered in ARAIPCAUProxyHostCommandHandler(). + [audioUnitChannel callAudioUnit:[NSDictionary dictionary]]; + } + + ~ProxyPlugInMessageChannel () override + { + _audioUnitChannel.callHostBlock = nil; + } + +protected: + NSDictionary * _sendMessage (NSDictionary * message) override + { + const auto reply { [_audioUnitChannel callAudioUnit:message] }; + return reply; + } +}; +#endif // !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY + +// host side: proxy plug-in implementation +#if !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY +class AUProxyPlugIn : public ProxyPlugIn, public AUConnection +{ +public: + static AUProxyPlugIn* createWithAudioUnit (AUAudioUnit * _Nonnull audioUnit, + ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, + void * _Nullable delegateUserData) + { + // AUAudioUnits created before macOS 13 will not know about this API yet + if (![audioUnit respondsToSelector:@selector(messageChannelFor:)]) + return nullptr; + + auto mainChannel { [audioUnit messageChannelFor:ARA_AUDIOUNIT_MAIN_THREAD_MESSAGES_UTI] }; + if (!mainChannel) + return nullptr; + + auto otherChannel { [audioUnit messageChannelFor:ARA_AUDIOUNIT_OTHER_THREADS_MESSAGES_UTI] }; + if (!otherChannel) + return nullptr; + + return new AUProxyPlugIn { static_cast*> (mainChannel), + static_cast*> (otherChannel), + audioUnit, waitForMessageDelegate, delegateUserData }; + } + + ~AUProxyPlugIn () override + { +#if !__has_feature(objc_arc) + [_initAU release]; +#endif + } + +private: + AUProxyPlugIn (NSObject * _Nonnull mainChannel, + NSObject * _Nonnull otherChannel, + AUAudioUnit * _Nonnull initAU, + ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, + void * _Nullable delegateUserData) + : ProxyPlugIn { this }, + AUConnection { waitForMessageDelegate, delegateUserData }, + _initAU { initAU } + { + setMainThreadChannel (new ProxyPlugInMessageChannel { mainChannel }); + setOtherThreadsChannel (new ProxyPlugInMessageChannel { otherChannel }); + setMessageHandler (this); +#if !__has_feature(objc_arc) + [_initAU retain]; +#endif + } + +private: + const AUAudioUnit * __strong _initAU; // workaround for macOS 14: keep the AU that vends the message channels alive, otherwise the channels will eventually stop working + // \todo once this is fixed in macOS, we only need to store this on older macOS versions +}; +#endif // !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY + + +#if defined (__GNUC__) + _Pragma ("GCC diagnostic push") + _Pragma ("GCC diagnostic ignored \"-Wunguarded-availability\"") +#endif + +ARA_MAP_IPC_REF (AudioUnitMessageChannel, ARAIPCMessageChannelRef) +ARA_MAP_IPC_REF (AUConnection, ARAIPCConnectionRef) + +#if defined (__GNUC__) + _Pragma ("GCC diagnostic pop") +#endif + + +extern "C" { + + +// host side: proxy plug-in C adapter +#if !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY + +ARAIPCConnectionRef ARA_CALL ARAIPCAUProxyPlugInInitialize (AUAudioUnit * _Nonnull audioUnit, + ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, + void * _Nullable delegateUserData) +{ + return toIPCRef (AUProxyPlugIn::createWithAudioUnit (audioUnit, waitForMessageDelegate, delegateUserData)); +} + +void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCConnectionRef _Nonnull proxyRef) +{ + fromIPCRef (proxyRef)->performPendingMainThreadTasks (); +} + +const ARAPlugInExtensionInstance * _Nonnull ARA_CALL ARAIPCAUProxyPlugInBindToDocumentController (AUAudioUnit * _Nonnull audioUnit, + ARADocumentControllerRef _Nonnull documentControllerRef, + ARAPlugInInstanceRoleFlags knownRoles, + ARAPlugInInstanceRoleFlags assignedRoles) +{ + static_assert (sizeof (AUAudioUnit *) == sizeof (NSUInteger), "opaque ref type size mismatch"); + auto remoteInstance { static_cast ([audioUnit valueForKey:@"araRemoteInstanceRef"]) }; + auto remoteInstanceRef { reinterpret_cast ([remoteInstance unsignedIntegerValue]) }; + const auto plugInExtensionInstance { ARAIPCProxyPlugInBindToDocumentController (remoteInstanceRef, documentControllerRef, knownRoles, assignedRoles) }; + return plugInExtensionInstance; +} + +void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCConnectionRef _Nonnull proxyRef) +{ + delete fromIPCRef (proxyRef); +} + +#endif // !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY + + +// plug-in side: proxy host C adapter +#if !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY + +class AUProxyHost : public ProxyHost, public AUConnection +{ +public: + AUProxyHost () + : ProxyHost { this }, + AUConnection { nullptr, nullptr } + { + ARAIPCProxyHostSetBindingHandler (handleBinding); + setMessageHandler (this); + } + +private: + static const ARAPlugInExtensionInstance * ARA_CALL handleBinding (ARAIPCPlugInInstanceRef plugInInstanceRef, + ARADocumentControllerRef controllerRef, + ARAPlugInInstanceRoleFlags knownRoles, ARAPlugInInstanceRoleFlags assignedRoles) + { + auto audioUnit { (__bridge AUAudioUnit *) plugInInstanceRef }; + return [audioUnit bindToDocumentController:controllerRef withRoles:assignedRoles knownRoles:knownRoles]; + } +}; + +AUProxyHost* _proxyHost; + + +ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageChannel (NSObject * _Nonnull audioUnitChannel, + bool isMainThreadChannel) +{ + // \todo the connection currently stores the creation thread as main thread for Windows compatibility, + // so we need to make sure the proxy is created on the main thread + auto createProxyIfNeeded { + ^void () + { + if (!_proxyHost) + _proxyHost = new AUProxyHost {}; + }}; + + if ([NSThread isMainThread]) + createProxyIfNeeded (); + else + dispatch_sync (dispatch_get_main_queue (), createProxyIfNeeded); + + auto result { new ProxyHostMessageChannel { audioUnitChannel } }; + if (isMainThreadChannel) + _proxyHost->setMainThreadChannel (result); + else + _proxyHost->setOtherThreadsChannel (result); + + return toIPCRef (result); +} + +NSDictionary * _Nonnull ARA_CALL ARAIPCAUProxyHostCommandHandler (ARAIPCMessageChannelRef _Nonnull messageChannelRef, NSDictionary * _Nonnull message) +{ + if ([message count]) // \todo filter dummy message sent in ProxyPlugInMessageChannel(), see there + static_cast (fromIPCRef (messageChannelRef))->routeReceivedMessage (message); + return [NSDictionary dictionary]; // \todo it would yield better performance if -callAudioUnit: would allow nil as return value +} + +void ARA_CALL ARAIPCAUProxyHostUninitializeMessageChannel (ARAIPCMessageChannelRef _Nonnull messageChannelRef) +{ + delete fromIPCRef (messageChannelRef); +} + +void ARA_CALL ARAIPCAUProxyHostUninitialize (void) +{ + if (_proxyHost) + delete _proxyHost; +} + +#endif // !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY + +API_AVAILABLE_END + + +_Pragma ("GCC diagnostic pop") + + +} // extern "C" +} // namespace IPC +} // namespace ARA + + +#endif // ARA_AUDIOUNITV3_IPC_IS_AVAILABLE diff --git a/IPC/ARAIPCCFEncoding.cpp b/IPC/ARAIPCCFEncoding.cpp new file mode 100644 index 0000000..baa8152 --- /dev/null +++ b/IPC/ARAIPCCFEncoding.cpp @@ -0,0 +1,330 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCCFEncoding.cpp +//! Implementation of ARAIPCMessageEn-/Decoder backed by CF(Mutable)Dictionary +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#include "ARAIPCCFEncoding.h" + + +#if ARA_ENABLE_IPC && defined (__APPLE__) + + +#include "ARA_Library/Debug/ARADebug.h" + +#include +#include +#include + + +namespace ARA { +namespace IPC { + + +#if defined (__GNUC__) + _Pragma ("GCC diagnostic push") + _Pragma ("GCC diagnostic ignored \"-Wold-style-cast\"") +#endif + + +// helper class to deal with CoreFoundation reference counting +class _CFReleaser +{ +public: + explicit _CFReleaser (CFStringRef ref) : _ref { ref } {} + _CFReleaser (const _CFReleaser& other) { _ref = (CFStringRef) CFRetain (other._ref); } + _CFReleaser (_CFReleaser&& other) { _ref = other._ref; other._ref = CFStringRef {}; } + ~_CFReleaser () { CFRelease (_ref); } + operator CFStringRef () { return _ref; } +private: + CFStringRef _ref; +}; + + +// wrap key value into CFString (no reference count transferred to caller) +CFStringRef _getEncodedKey (MessageArgumentKey argKey) +{ + // \todo All plist formats available for CFPropertyListCreateData () in createEncodedMessage () need CFString keys. + // Once we switch to the more modern (NS)XPC API we shall be able to use CFNumber keys directly... + static std::map cache; + auto existingEntry { cache.find (argKey) }; + if (existingEntry != cache.end ()) + return existingEntry->second; + return cache.emplace (argKey, CFStringCreateWithCString (kCFAllocatorDefault, std::to_string (argKey).c_str (), kCFStringEncodingUTF8)).first->second; +} + + + +CFMessageEncoder::CFMessageEncoder () +: _dictionary { CFDictionaryCreateMutable (kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) } +{} + +CFMessageEncoder::~CFMessageEncoder () +{ + CFRelease (_dictionary); +} + +void CFMessageEncoder::appendInt32 (MessageArgumentKey argKey, int32_t argValue) +{ + auto argObject { CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &argValue) }; + CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject); + CFRelease (argObject); +} + +void CFMessageEncoder::appendInt64 (MessageArgumentKey argKey, int64_t argValue) +{ + auto argObject { CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &argValue) }; + CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject); + CFRelease (argObject); +} + +void CFMessageEncoder::appendSize (MessageArgumentKey argKey, size_t argValue) +{ + static_assert (sizeof (SInt64) == sizeof (size_t), "integer type needs adjustment for this compiler setup"); + + auto argObject { CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &argValue) }; + CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject); + CFRelease (argObject); +} + +void CFMessageEncoder::appendFloat (MessageArgumentKey argKey, float argValue) +{ + auto argObject { CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &argValue) }; + CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject); + CFRelease (argObject); +} + +void CFMessageEncoder::appendDouble (MessageArgumentKey argKey, double argValue) +{ + auto argObject { CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &argValue) }; + CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject); + CFRelease (argObject); +} + +void CFMessageEncoder::appendString (MessageArgumentKey argKey, const char * argValue) +{ + auto argObject { CFStringCreateWithCString (kCFAllocatorDefault, argValue, kCFStringEncodingUTF8) }; + CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject); + CFRelease (argObject); +} + +void CFMessageEncoder::appendBytes (MessageArgumentKey argKey, const uint8_t * argValue, size_t argSize, bool copy) +{ + CFDataRef argObject; + if (copy) + argObject = CFDataCreate (kCFAllocatorDefault, argValue, (CFIndex) argSize); + else + argObject = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, argValue, (CFIndex) argSize, kCFAllocatorNull); + CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject); + CFRelease (argObject); +} + +MessageEncoder* CFMessageEncoder::appendSubMessage (MessageArgumentKey argKey) +{ + auto argObject = new CFMessageEncoder {}; + CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject->_dictionary); + return argObject; +} + +__attribute__((cf_returns_retained)) CFMutableDictionaryRef CFMessageEncoder::copyDictionary () const +{ + CFRetain (_dictionary); + return _dictionary; +} + +__attribute__((cf_returns_retained)) CFDataRef CFMessageEncoder::createMessageEncoderData () const +{ + if (CFDictionaryGetCount (_dictionary) == 0) + return nullptr; + return CFPropertyListCreateData (kCFAllocatorDefault, _dictionary, kCFPropertyListBinaryFormat_v1_0, 0, nullptr); +} + + +CFMessageDecoder::CFMessageDecoder (CFDictionaryRef dictionary) +: CFMessageDecoder::CFMessageDecoder (dictionary, true) +{} + +CFMessageDecoder::CFMessageDecoder (CFDictionaryRef dictionary, bool retain) +: _dictionary { dictionary } +{ + if (dictionary && retain) + CFRetain (dictionary); +} + +CFMessageDecoder::~CFMessageDecoder () +{ + if (_dictionary) + CFRelease (_dictionary); +} + +bool CFMessageDecoder::readInt32 (MessageArgumentKey argKey, int32_t* argValue) const +{ + CFNumberRef number {}; + if (_dictionary) + number = (CFNumberRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)); + if (!number) + { + *argValue = 0; + return false; + } + ARA_INTERNAL_ASSERT (CFGetTypeID (number) == CFNumberGetTypeID ()); + CFNumberGetValue (number, kCFNumberSInt32Type, argValue); + return true; +} + +bool CFMessageDecoder::readInt64 (MessageArgumentKey argKey, int64_t* argValue) const +{ + CFNumberRef number {}; + if (_dictionary) + number = (CFNumberRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)); + if (!number) + { + *argValue = 0; + return false; + } + ARA_INTERNAL_ASSERT (CFGetTypeID (number) == CFNumberGetTypeID ()); + CFNumberGetValue (number, kCFNumberSInt64Type, argValue); + return true; +} + +bool CFMessageDecoder::readSize (MessageArgumentKey argKey, size_t* argValue) const +{ + static_assert (sizeof (SInt64) == sizeof (size_t), "integer type needs adjustment for this compiler setup"); + + CFNumberRef number {}; + if (_dictionary) + number = (CFNumberRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)); + if (!number) + { + *argValue = 0; + return false; + } + ARA_INTERNAL_ASSERT (CFGetTypeID (number) == CFNumberGetTypeID ()); + CFNumberGetValue (number, kCFNumberSInt64Type, argValue); + return true; +} + +bool CFMessageDecoder::readFloat (MessageArgumentKey argKey, float* argValue) const +{ + CFNumberRef number {}; + if (_dictionary) + number = (CFNumberRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)); + if (!number) + { + *argValue = 0.0f; + return false; + } + ARA_INTERNAL_ASSERT (CFGetTypeID (number) == CFNumberGetTypeID ()); + CFNumberGetValue (number, kCFNumberFloatType, argValue); + return true; +} + +bool CFMessageDecoder::readDouble (MessageArgumentKey argKey, double* argValue) const +{ + CFNumberRef number {}; + if (_dictionary) + number = (CFNumberRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)); + if (!number) + { + *argValue = 0.0; + return false; + } + ARA_INTERNAL_ASSERT (CFGetTypeID (number) == CFNumberGetTypeID ()); + CFNumberGetValue (number, kCFNumberDoubleType, argValue); + return true; +} + +bool CFMessageDecoder::readString (MessageArgumentKey argKey, const char ** argValue) const +{ + CFStringRef string {}; + if (_dictionary) + string = (CFStringRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)); + if (!string) + { + *argValue = nullptr; + return false; + } + ARA_INTERNAL_ASSERT (string && (CFGetTypeID (string) == CFStringGetTypeID ())); + *argValue = CFStringGetCStringPtr (string, kCFStringEncodingUTF8); + if (!*argValue) // CFStringGetCStringPtr() may fail e.g. with chord names like "G/D" + { + const auto length { CFStringGetLength (string) }; + std::string temp; // \todo does not work: { static_cast (length), char { 0 } }; + temp.assign ( static_cast (length) , char { 0 } ); + CFIndex ARA_MAYBE_UNUSED_VAR (count) { CFStringGetBytes (string, CFRangeMake (0, length), kCFStringEncodingUTF8, 0, false, (UInt8*)(&temp[0]), length, nullptr) }; + ARA_INTERNAL_ASSERT (count == length); + static std::set strings; // \todo static cache of "undecodeable" strings requires single-threaded use - maybe make iVar instead? + strings.insert (temp); + *argValue = strings.find (temp)->c_str (); + } + return true; +} + +bool CFMessageDecoder::readBytesSize (MessageArgumentKey argKey, size_t* argSize) const +{ + CFDataRef bytes {}; + if (_dictionary) + bytes = (CFDataRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)); + if (!bytes) + { + *argSize = 0; + return false; + } + ARA_INTERNAL_ASSERT (bytes && (CFGetTypeID (bytes) == CFDataGetTypeID ())); + *argSize = (size_t) CFDataGetLength (bytes); + return true; +} + +void CFMessageDecoder::readBytes (MessageArgumentKey argKey, uint8_t* argValue) const +{ + ARA_INTERNAL_ASSERT (_dictionary); + auto bytes { (CFDataRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)) }; + ARA_INTERNAL_ASSERT (bytes && (CFGetTypeID (bytes) == CFDataGetTypeID ())); + const auto length { CFDataGetLength (bytes) }; + CFDataGetBytes (bytes, CFRangeMake (0, length), argValue); +} + +MessageDecoder* CFMessageDecoder::readSubMessage (MessageArgumentKey argKey) const +{ + auto dictionary { (CFDictionaryRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)) }; + ARA_INTERNAL_ASSERT (!dictionary || (CFGetTypeID (dictionary) == CFDictionaryGetTypeID ())); + if (dictionary == nullptr) + return nullptr; + return new CFMessageDecoder { dictionary }; +} + +bool CFMessageDecoder::hasDataForKey (MessageArgumentKey argKey) const +{ + return CFDictionaryContainsKey (_dictionary, _getEncodedKey (argKey)); +} + +CFMessageDecoder* CFMessageDecoder::createWithMessageData (CFDataRef messageData) +{ + if (CFDataGetLength (messageData) == 0) + return nullptr; + + auto dictionary { (CFDictionaryRef) CFPropertyListCreateWithData (kCFAllocatorDefault, messageData, kCFPropertyListImmutable, nullptr, nullptr) }; + ARA_INTERNAL_ASSERT (dictionary && (CFGetTypeID (dictionary) == CFDictionaryGetTypeID ())); + return new CFMessageDecoder { dictionary, false }; +} + +#if defined (__APPLE__) + _Pragma ("GCC diagnostic pop") +#endif + +} // namespace IPC +} // namespace ARA + +#endif // ARA_ENABLE_IPC && defined (__APPLE__) diff --git a/IPC/ARAIPCCFEncoding.h b/IPC/ARAIPCCFEncoding.h new file mode 100644 index 0000000..701e767 --- /dev/null +++ b/IPC/ARAIPCCFEncoding.h @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCCFEncoding.h +//! Implementation of ARAIPCMessageEn-/Decoder backed by CF(Mutable)Dictionary +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef ARAIPCCFEncoding_h +#define ARAIPCCFEncoding_h + +#include "ARA_Library/IPC/ARAIPCMessage.h" + +#if ARA_ENABLE_IPC + + +#include + + +//! @addtogroup ARA_Library_IPC +//! @{ + + +namespace ARA { +namespace IPC { + + +class CFMessageEncoder : public MessageEncoder +{ +public: + CFMessageEncoder (); + ~CFMessageEncoder () override; + + void appendInt32 (MessageArgumentKey argKey, int32_t argValue) override; + void appendInt64 (MessageArgumentKey argKey, int64_t argValue) override; + void appendSize (MessageArgumentKey argKey, size_t argValue) override; + void appendFloat (MessageArgumentKey argKey, float argValue) override; + void appendDouble (MessageArgumentKey argKey, double argValue) override; + void appendString (MessageArgumentKey argKey, const char * argValue) override; + void appendBytes (MessageArgumentKey argKey, const uint8_t * argValue, size_t argSize, bool copy) override; + MessageEncoder* appendSubMessage (MessageArgumentKey argKey) override; + + __attribute__((cf_returns_retained)) CFMutableDictionaryRef copyDictionary () const; + __attribute__((cf_returns_retained)) CFDataRef createMessageEncoderData () const; + +private: + CFMutableDictionaryRef const _dictionary; +}; + + +class CFMessageDecoder : public MessageDecoder +{ +public: + explicit CFMessageDecoder (CFDictionaryRef dictionary); + ~CFMessageDecoder () override; + + bool readInt32 (MessageArgumentKey argKey, int32_t* argValue) const override; + bool readInt64 (MessageArgumentKey argKey, int64_t* argValue) const override; + bool readSize (MessageArgumentKey argKey, size_t* argValue) const override; + bool readFloat (MessageArgumentKey argKey, float* argValue) const override; + bool readDouble (MessageArgumentKey argKey, double* argValue) const override; + bool readString (MessageArgumentKey argKey, const char ** argValue) const override; + bool readBytesSize (MessageArgumentKey argKey, size_t* argSize) const override; + void readBytes (MessageArgumentKey argKey, uint8_t* argValue) const override; + MessageDecoder* readSubMessage (MessageArgumentKey argKey) const override; + bool hasDataForKey (MessageArgumentKey argKey) const override; + + static CFMessageDecoder* createWithMessageData (CFDataRef messageData); + +private: + CFMessageDecoder (CFDictionaryRef dictionary, bool retain); + +private: + CFDictionaryRef const _dictionary; +}; + + +} // namespace IPC +} // namespace ARA + +//! @} ARA_Library_IPC + +#endif // ARA_ENABLE_IPC + +#endif // ARAIPCCFEncoding_h diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp new file mode 100644 index 0000000..80157a1 --- /dev/null +++ b/IPC/ARAIPCConnection.cpp @@ -0,0 +1,559 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCConnection.cpp +//! Connection to another process to pass messages to/receive messages from it +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#include "ARAIPCConnection.h" + + +#if ARA_ENABLE_IPC + + +#include "ARA_Library/Debug/ARADebug.h" +#include "ARA_Library/IPC/ARAIPCEncoding.h" + +#include +#if __cplusplus >= 202002L + #include + #include +#endif + +#if defined (__APPLE__) + #include +#endif + + +// ARA IPC design overview +// +// ARA API calls can be stacked multiple times. +// For example, when the plug-in has completed analyzing an audio source, it will +// inform the host about the updated content information the next time the host +// calls notifyModelUpdates() by invoking notifyAudioSourceContentChanged(). +// From that method, the host now queries isAudioSourceContentAvailable() for the +// relevant content types and calls createAudioSourceContentReader() etc. to read +// the relevant content data. +// Such a stack of multiple messages is referred to as a "transaction" here. +// +// However, many IPC APIs including Audio Unit message channels cannot be stacked, +// i.e. each message has to completely be processed before the next one can be sent. +// Therefore, each ARA API calls needs to be split into two IPC messages: +// the actual call message that does not return any result just yet, and a matching +// reply message that returns the result (even if it is void, because the caller +// needs to know when the call has completed). +// After sending the actual call message, the sender loops while listening for +// incoming messages, which might either be a stacked callback that is processed +// accordingly, or the reply message which ends the loop. +// +// In this loop, extra efforts might be necessary to handle the IPC threading: +// When e.g. using Audio Unit AUMessageChannel, Grand Central Dispatch will deliver +// the incoming IPC messages on some undefined thread. In such cases, a dispatch +// from the receiving thread to the target thread is necessary, implemented via +// a condition variable that the receive thread awakes when a message comes in. +// Instead of looping, a sending thread will then wait on this condition for the reply. +// +// While most ARA communication is happening on the main thread, there are +// several calls that may be made from other threads. This poses several challenges +// when tunnelling everything through a single(threaded) IPC channel. +// +// First of all, there needs to be some locking mechanism around actually accessing +// the IPC. This adds a dependency between previously independent threads, so both +// hosts and plug-ins will need to carefully evaluate their existing ARA code to check +// for potential deadlocks or priority inversion caused by this. +// To reduce the potential impact of this, the implementation uses two IPC channels +// in parallel: one for all main thread communication (which can work lockless) and +// another one that is used for all other threads. +// +// Further, calls that are using IPC are no longer realtime safe. This means that calls +// like getPlaybackRegionHeadAndTailTime(), which could previously be executed on render +// threads, now need to be moved to other threads. Accordingly, hosts need to cache data +// like the head and tail times from the main thread and update them there whenever +// notifyPlaybackRegionContentChanged() is received. +// +// Another challenge when injecting IPC into the ARA communication is that for each +// message that comes in, an appropriate thread has to be selected to process it. +// When a new transaction is initiated, the implementation checks whether this is happening +// on the main thread or on any other thread, and chooses the appropriate channel accordingly. +// On the receiving side, calls coming in on the main thread channel are forwarded to the main +// thread (unless the receive code already runs there), and for the other threads the code is +// executed directly on the receive thread. +// +// Replies or callbacks are routed back to the originating thread in the sender. This is done +// by adding a token when sending a message that identifies the sending thread, and replies and +// callbacks pass this token back to allow for proper dispatching from the receive thread to the +// thread that initiated the transaction. +// Since the actual ARA code is agnostic to IPC being used, the receiving side uses +// thread local storage to make the sender's thread token available for all stacked calls +// that the ARA code might make in response to the message. +// +// Note that there is a crucial difference between the dispatch of a new transaction and +// the dispatch of any follow-up messages in the transaction: the initial message is dispatched +// to a thread that is potentially executing other code as well in some form of run loop, +// whereas the follow-ups need to dispatch to a thread that is currently blocking inside ARA code. + + +namespace ARA { +namespace IPC { + + +// keys to store the threading information in the IPC messages +constexpr MessageArgumentKey sendThreadKey { -1 }; +constexpr MessageArgumentKey receiveThreadKey { -2 }; + + +#if defined (_WIN32) + #define readThreadRef readInt32 + #define appendThreadRef appendInt32 +#else + #define readThreadRef readSize + #define appendThreadRef appendSize +#endif + + +#if __cplusplus < 201703L +constexpr MessageDispatcher::ThreadRef MessageDispatcher::_invalidThread; +constexpr intptr_t MainThreadMessageDispatcher::_noPendingMessageDecoder; +#endif + + +MessageDispatcher::MessageDispatcher (Connection* connection, MessageChannel* messageChannel) +: _connection { connection }, + _messageChannel { messageChannel } +{ + static_assert (sizeof (std::thread::id) == sizeof (ThreadRef), "the current implementation relies on a specific thread ID size"); +// unfortunately at least in clang std::thread::id's c'tor isn't constexpr +// static_assert (std::thread::id {} == *reinterpret_cast(&_invalidThread), "the current implementation relies on invalid thread IDs being 0"); + ARA_INTERNAL_ASSERT (std::thread::id {} == *reinterpret_cast(&_invalidThread)); + + _messageChannel->setMessageDispatcher (this); +} + +MessageDispatcher::~MessageDispatcher () +{ + delete _messageChannel; +} + +MessageDispatcher::ThreadRef MessageDispatcher::_getCurrentThread () +{ + const auto thisThread { std::this_thread::get_id () }; + const auto result { *reinterpret_cast (&thisThread) }; + ARA_INTERNAL_ASSERT (result != _invalidThread); + return result; +} + +void MessageDispatcher::_sendMessage (MessageID messageID, MessageEncoder* encoder) +{ + if (isReply (messageID)) + ARA_IPC_LOG ("replies to message on thread %p", _getCurrentThread ()); + else + ARA_IPC_LOG ("sends message with ID %i on thread %p", messageID, _getCurrentThread ()); + + _messageChannel->sendMessage (messageID, encoder); + + delete encoder; +} + +void MessageDispatcher::_handleReply (const MessageDecoder* decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) +{ + ARA_IPC_LOG ("handles received reply on thread %p", _getCurrentThread ()); + if (replyHandler) + (replyHandler) (decoder, replyHandlerUserData); + else + ARA_INTERNAL_ASSERT (!decoder || !decoder->hasDataForKey (0)); // replies should be empty when not handled (i.e. void functions) + delete decoder; +} + +MessageEncoder* MessageDispatcher::_handleReceivedMessage (MessageID messageID, const MessageDecoder* decoder) +{ + ARA_INTERNAL_ASSERT (!isReply (messageID)); + + ARA_IPC_LOG ("handles received message with ID %i on thread %p", messageID, _getCurrentThread ()); + auto replyEncoder { _connection->createEncoder () }; + _connection->getMessageHandler ()->handleReceivedMessage (messageID, decoder, replyEncoder); + + delete decoder; + + return replyEncoder; +} + + +void MainThreadMessageDispatcher::sendMessage (MessageID messageID, MessageEncoder* encoder, + Connection::ReplyHandler replyHandler, void* replyHandlerUserData) +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + const auto previousPendingReplyHandler { _pendingReplyHandler }; + const PendingReplyHandler pendingReplyHandler { replyHandler, replyHandlerUserData, previousPendingReplyHandler }; + _pendingReplyHandler = &pendingReplyHandler; + + _sendMessage (messageID, encoder); + + while (previousPendingReplyHandler != _pendingReplyHandler) + { + if (getConnection ()->waitForMessageOnCreationThread ()) + processPendingMessageIfNeeded (); + } +} + +void MainThreadMessageDispatcher::processPendingMessageIfNeeded () +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + const auto pendingMessageDecoder { _pendingMessageDecoder.exchange (reinterpret_cast (_noPendingMessageDecoder), std::memory_order_acquire) }; + if (pendingMessageDecoder != reinterpret_cast (_noPendingMessageDecoder)) + { + const auto pendingMessageID { _pendingMessageID }; + if (isReply (pendingMessageID)) + { + ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); + _handleReply (pendingMessageDecoder, _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); + _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; + } + else + { + auto replyEncoder { _handleReceivedMessage (pendingMessageID, pendingMessageDecoder) }; + _sendMessage (0, replyEncoder); + } + } +} + +void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) +{ + _pendingMessageID = messageID; + _pendingMessageDecoder.store (decoder, std::memory_order_release); + getConnection ()->signalMesssageReceived (); + + if (getConnection ()->wasCreatedOnCurrentThread ()) + { + processPendingMessageIfNeeded (); + } + else + { + if (isReply (messageID)) + ARA_IPC_LOG ("dispatches received reply from thread %p to creation thread", _getCurrentThread ()); + else + ARA_IPC_LOG ("dispatches received message with ID %i from thread %p to creation thread", messageID, _getCurrentThread ()); + getConnection ()->dispatchToCreationThread (std::bind (&MainThreadMessageDispatcher::processPendingMessageIfNeeded, this)); + } +} + + +// actually "static" members of OtherThreadsMessageDispatcher, but for some reason C++ doesn't allow this... +thread_local OtherThreadsMessageDispatcher::ThreadRef _remoteTargetThread { 0 }; +thread_local const OtherThreadsMessageDispatcher::PendingReplyHandler* _pendingReplyHandler { nullptr }; + +void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, MessageEncoder* encoder, + Connection::ReplyHandler replyHandler, void* replyHandlerUserData) +{ + const auto currentThread { _getCurrentThread () }; + encoder->appendThreadRef (sendThreadKey, currentThread); + if (_remoteTargetThread != _invalidThread) + encoder->appendThreadRef (receiveThreadKey, _remoteTargetThread); + + _sendLock.lock (); + _sendMessage (messageID, encoder); + _sendLock.unlock (); + + if (getConnection ()->wasCreatedOnCurrentThread ()) + { + const auto previousPendingReplyHandler { _pendingReplyHandler }; + const PendingReplyHandler pendingReplyHandler { replyHandler, replyHandlerUserData, previousPendingReplyHandler }; + _pendingReplyHandler = &pendingReplyHandler; + do + { + getConnection ()->waitForMessageOnCreationThread (); + } while (_pendingReplyHandler != previousPendingReplyHandler); + } + else + { + while (true) + { + std::unique_lock lock { _routeLock }; + _routeReceiveCondition.wait (lock, [this, ¤tThread] + { return _getRoutedMessageForThread (currentThread) != nullptr; }); + RoutedMessage* message = _getRoutedMessageForThread (currentThread); + const auto receivedMessageID { message->_messageID }; + const auto receivedDecoder { message->_decoder }; + message->_targetThread = _invalidThread; + lock.unlock (); + + if (isReply (receivedMessageID)) + { + _handleReply (receivedDecoder, replyHandler, replyHandlerUserData); // will also delete receivedDecoder + break; + } + else + { + _processReceivedMessage (receivedMessageID, receivedDecoder); // will also delete receivedDecoder + } + } + } +} + +OtherThreadsMessageDispatcher::RoutedMessage* OtherThreadsMessageDispatcher::_getRoutedMessageForThread (ThreadRef thread) +{ + for (auto& message : _routedMessages) + { + if (message._targetThread == thread) + return &message; + } + return nullptr; +} + +void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) +{ + ThreadRef targetThread; + if (decoder->readThreadRef (receiveThreadKey, &targetThread)) + { + ARA_INTERNAL_ASSERT (targetThread != _invalidThread); + if (targetThread == _getCurrentThread ()) + { + if (isReply (messageID)) + { + ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); + _handleReply (decoder, _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); + _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; + } + else + { + _processReceivedMessage (messageID, decoder); + } + } + else + { + if (isReply (messageID)) + ARA_IPC_LOG ("dispatches received reply from thread %p to sending thread %p", _getCurrentThread (), targetThread); + else + ARA_IPC_LOG ("dispatches received message with ID %i from thread %p to sending thread %p", messageID, _getCurrentThread (), targetThread); + + _routeLock.lock (); + RoutedMessage* message { _getRoutedMessageForThread (_invalidThread) }; + if (message == nullptr) + { + _routedMessages.push_back ({}); + message = &_routedMessages.back (); + } + message->_messageID = messageID; + message->_decoder = decoder; + message->_targetThread = targetThread; + _routeReceiveCondition.notify_all (); + _routeLock.unlock (); + } + } + else + { + ARA_INTERNAL_ASSERT (!isReply (messageID)); + _processReceivedMessage (messageID, decoder); + } +} + +void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID, const MessageDecoder* decoder) +{ + const auto previousRemoteTargetThread { _remoteTargetThread }; + ThreadRef remoteTargetThread; + const auto success { decoder->readThreadRef (sendThreadKey, &remoteTargetThread) }; + ARA_INTERNAL_ASSERT (success); + _remoteTargetThread = remoteTargetThread; + + auto replyEncoder { _handleReceivedMessage (messageID, decoder) }; + + replyEncoder->appendThreadRef (receiveThreadKey, remoteTargetThread); + + _sendLock.lock (); + _sendMessage (0, replyEncoder); + _sendLock.unlock (); + + _remoteTargetThread = previousRemoteTargetThread; +} + + +#if defined (_WIN32) + +// from https://devblogs.microsoft.com/oldnewthing/20141015-00/?p=43843 +BOOL ConvertToRealHandle(HANDLE h, + BOOL bInheritHandle, + HANDLE *phConverted) +{ + return DuplicateHandle(GetCurrentProcess(), h, + GetCurrentProcess(), phConverted, + 0, bInheritHandle, DUPLICATE_SAME_ACCESS); +} + +HANDLE _GetRealCurrentThread () +{ + HANDLE currentThread {}; + auto success { ConvertToRealHandle (::GetCurrentThread (), FALSE, ¤tThread) }; + ARA_INTERNAL_ASSERT (success); + return currentThread; +} + +void APCRouteNewTransactionFunc (ULONG_PTR parameter) +{ + auto funcPtr { reinterpret_cast (parameter) }; + (*funcPtr) (); + delete funcPtr; +} + +#elif defined (__APPLE__) + +void Connection::performRunloopSource (void* info) +{ + auto connection { reinterpret_cast (info) }; + connection->_mutex.lock (); + while (!connection->_queue.empty ()) + { + auto func { connection->_queue.front () }; + connection->_queue.pop (); + connection->_mutex.unlock (); + + func (); + + connection->_mutex.lock (); + } + connection->_mutex.unlock (); +} + +#endif + + +Connection::Connection (WaitForMessageDelegate waitForMessageDelegate, void* delegateUserData) +: _waitForMessageDelegate { waitForMessageDelegate }, + _delegateUserData { delegateUserData }, +#if __cplusplus >= 202002L + _waitForMessageSemaphore { new std::binary_semaphore { 0 } }, +#elif defined (_WIN32) + _waitForMessageSemaphore { ::CreateSemaphore (nullptr, 0, LONG_MAX, nullptr) }, +#elif defined (__APPLE__) + _waitForMessageSemaphore { dispatch_semaphore_create (0) }, +#else + #error "IPC not yet implemented for this platform" +#endif + _creationThreadID { std::this_thread::get_id () }, +#if defined (_WIN32) + _creationThreadHandle { _GetRealCurrentThread () } +{} +#elif defined (__APPLE__) + _creationThreadRunLoop { CFRunLoopGetCurrent () } +{ + CFRunLoopSourceContext context { 0, this, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, performRunloopSource }; + _runloopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 0, &context); + CFRunLoopAddSource (_creationThreadRunLoop, _runloopSource, kCFRunLoopCommonModes); +} +#endif + +Connection::~Connection () +{ +#if defined (__APPLE__) + CFRunLoopRemoveSource (_creationThreadRunLoop, _runloopSource, kCFRunLoopCommonModes); + CFRelease (_runloopSource); +#endif + delete _otherThreadsDispatcher; + delete _mainThreadDispatcher; +#if __cplusplus >= 202002L + delete static_cast (_waitForMessageSemaphore); +#elif defined (_WIN32) + ::CloseHandle (_waitForMessageSemaphore); +#elif defined (__APPLE__) + dispatch_release (static_cast (_waitForMessageSemaphore)); +#else + #error "IPC not yet implemented for this platform" +#endif +} + +void Connection::setMainThreadChannel (MessageChannel* messageChannel) +{ + ARA_INTERNAL_ASSERT (_mainThreadDispatcher == nullptr); + _mainThreadDispatcher = new MainThreadMessageDispatcher { this, messageChannel }; +} + +void Connection::setOtherThreadsChannel (MessageChannel* messageChannel) +{ + ARA_INTERNAL_ASSERT (_otherThreadsDispatcher == nullptr); + _otherThreadsDispatcher = new OtherThreadsMessageDispatcher { this, messageChannel }; +} + +void Connection::setMessageHandler (MessageHandler* messageHandler) +{ + ARA_INTERNAL_ASSERT (_messageHandler == nullptr); + _messageHandler = messageHandler; +} + +void Connection::sendMessage (MessageID messageID, MessageEncoder* encoder, ReplyHandler replyHandler, void* replyHandlerUserData) +{ + ARA_INTERNAL_ASSERT ((_mainThreadDispatcher != nullptr) && (_otherThreadsDispatcher != nullptr) && (_messageHandler != nullptr)); + if (wasCreatedOnCurrentThread ()) + _mainThreadDispatcher->sendMessage (messageID, encoder, replyHandler, replyHandlerUserData); + else + _otherThreadsDispatcher->sendMessage (messageID, encoder, replyHandler, replyHandlerUserData); +} + +void Connection::dispatchToCreationThread (DispatchableFunction func) +{ +#if defined (_WIN32) + auto funcPtr { new DispatchableFunction { func } }; + const auto result { ::QueueUserAPC (APCRouteNewTransactionFunc, _creationThreadHandle, reinterpret_cast (funcPtr)) }; + ARA_INTERNAL_ASSERT (result != 0); +#elif defined (__APPLE__) + _mutex.lock (); + _queue.emplace (std::move (func)); + _mutex.unlock (); + CFRunLoopSourceSignal (_runloopSource); + CFRunLoopWakeUp (_creationThreadRunLoop); +#endif +} + +bool Connection::waitForMessageOnCreationThread () +{ + ARA_INTERNAL_ASSERT (wasCreatedOnCurrentThread ()); + + constexpr ARATimeDuration timeout { 0.010 }; + bool didReceiveMessage; +#if __cplusplus >= 202002L + didReceiveMessage = static_cast (_waitForMessageSemaphore)->try_acquire_for (std::chrono::duration { timeout }); +#elif defined (_WIN32) + didReceiveMessage = (::WaitForSingleObject (_waitForMessageSemaphore, static_cast (timeout * 1000.0 + 0.5)) == WAIT_OBJECT_0); +#elif defined (__APPLE__) + const auto deadline { dispatch_time (DISPATCH_TIME_NOW, static_cast (10e9 * timeout + 0.5)) }; + didReceiveMessage = (dispatch_semaphore_wait (static_cast (_waitForMessageSemaphore), deadline) == 0); +#else + #error "IPC not yet implemented for this platform" +#endif + if (didReceiveMessage) + return true; + + if (_waitForMessageDelegate) + _waitForMessageDelegate (_delegateUserData); + + return false; +} + +void Connection::signalMesssageReceived () +{ +#if __cplusplus >= 202002L + static_cast (_waitForMessageSemaphore)->release (); +#elif defined (_WIN32) + ::ReleaseSemaphore (_waitForMessageSemaphore, 1, nullptr); +#elif defined (__APPLE__) + dispatch_semaphore_signal (static_cast (_waitForMessageSemaphore)); +#else + #error "IPC not yet implemented for this platform" +#endif +} + + +} // namespace IPC +} // namespace ARA + +#endif // ARA_ENABLE_IPC diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h new file mode 100644 index 0000000..36137c0 --- /dev/null +++ b/IPC/ARAIPCConnection.h @@ -0,0 +1,316 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCConnection.h +//! Connection to another process to pass messages to/receive messages from it +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef ARAIPCConnection_h +#define ARAIPCConnection_h + +#include "ARA_Library/IPC/ARAIPCMessage.h" +#include "ARA_Library/Debug/ARADebug.h" + +#if ARA_ENABLE_IPC + + +#if defined (_WIN32) + #include +#elif defined (__APPLE__) + #include +#endif + +#include +#include +#include +#include +#include + + +//! @addtogroup ARA_Library_IPC +//! @{ + +namespace ARA { +namespace IPC { + + +class MessageChannel; +class MessageDispatcher; +class MainThreadMessageDispatcher; +class OtherThreadsMessageDispatcher; + + +#if ARA_ENABLE_DEBUG_OUTPUT && 0 + #define ARA_IPC_LOG(...) ARA_LOG ("ARA IPC " __VA_ARGS__) +#else + #define ARA_IPC_LOG(...) ((void) 0) +#endif + + +//! delegate interface for processing messages received through an IPC connection +class MessageHandler +{ +public: + virtual ~MessageHandler () = default; + + //! IPC connections will call this method for incoming messages after + //! after filtering replies and routing them to the correct thread. + virtual void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, + MessageEncoder* const replyEncoder) = 0; +}; + + +//! IPC message channel: primitive for sending and receiving messages +//! @{ +class MessageChannel +{ +public: + virtual ~MessageChannel () = default; + + //! implemented by subclasses to perform the actual message (or reply) sending + virtual void sendMessage (MessageID messageID, MessageEncoder* encoder) = 0; + + void setMessageDispatcher (MessageDispatcher* messageDispatcher) + { + ARA_INTERNAL_ASSERT (_messageDispatcher == nullptr); + _messageDispatcher = messageDispatcher; + } + + MessageDispatcher* getMessageDispatcher () const { return _messageDispatcher; } + +private: + MessageDispatcher* _messageDispatcher {}; +}; +//! @} + + +//! IPC connection: gateway for sending and receiving messages, +//! utilizing potentially multiple MessageChannel instances +//! @{ +class Connection +{ +public: + using WaitForMessageDelegate = void (*) (void* delegateUserData); + explicit Connection (WaitForMessageDelegate waitForMessageDelegate, void* delegateUserData); + virtual ~Connection (); + + //! set the message channel for all main thread communication + //! Must be done before sending or receiving the first message on any channel. + //! The connection takes ownership of the channel and deletes them upon teardown. + void setMainThreadChannel (MessageChannel* messageChannel); + + MainThreadMessageDispatcher* getMainThreadDispatcher () const { return _mainThreadDispatcher; } + + //! set the message channel for all non-main thread communication + //! Must be done before sending or receiving the first message on any channel. + //! The connection takes ownership of the channel and deletes them upon teardown. + void setOtherThreadsChannel (MessageChannel* messageChannel); + + OtherThreadsMessageDispatcher* getOtherThreadsDispatcher () const { return _otherThreadsDispatcher; } + + //! set the message handler for all communication on all threads + //! Must be done before sending or receiving the first message on any channel. + //! The connection does not take ownership of the message handler. + void setMessageHandler (MessageHandler* messageHandler); + + MessageHandler* getMessageHandler () const { return _messageHandler; } + + //! Reply Handler: a function passed to sendMessage () that is called to process the reply to a message + //! decoder will be nullptr if incoming message was empty + using ReplyHandler = void (ARA_CALL *) (const MessageDecoder* decoder, void* userData); + + //! send an encoded messages to the receiving process + //! The encoder will be deleted after sending the message. + //! If an empty reply ("void") is expected, the replyHandler should be nullptr. + //! This method can be called from any thread, concurrent calls will be serialized. + //! The calling thread will be blocked until the receiver has processed the message and + //! returned a (potentially empty) reply, which will be forwarded to the replyHandler. + void sendMessage (MessageID messageID, MessageEncoder* encoder, + ReplyHandler replyHandler, void* replyHandlerUserData); + + //! implemented by subclasses: generate an encoder to encode a new message, + //! later passed to sendMessage(), which will destroy the encoder after sending + virtual MessageEncoder* createEncoder () = 0; + + //! implemented by subclasses: indicate byte order mismatch between sending + //! and receiving machine + virtual bool receiverEndianessMatches () = 0; + + bool wasCreatedOnCurrentThread () const { return std::this_thread::get_id () == _creationThreadID; } + + //! if IPC messages that need to be processed on the creation thread are received on some + //! other thread due to the design of the underlying IPC APIs, then this dispatch allows them + //! to forward it to the creation thread + using DispatchableFunction = std::function; + void dispatchToCreationThread (DispatchableFunction func); + + //! when a message dispatcher blocks the creation thread for some time, it needs to periodically + //! call this method to let other main thread tasks execute cooperatively + //! returns true if a message was received, false otherwise + bool waitForMessageOnCreationThread (); + + //! message dispatcher need to call this when routing a message to the creation thread + //! in order to wake it up + void signalMesssageReceived (); + +#if defined (__APPLE__) + static void performRunloopSource (void* info); +#endif + +private: + const WaitForMessageDelegate _waitForMessageDelegate; + void* const _delegateUserData; + void* const _waitForMessageSemaphore; // concrete type is platform-dependent + MainThreadMessageDispatcher* _mainThreadDispatcher {}; + OtherThreadsMessageDispatcher* _otherThreadsDispatcher {}; + MessageHandler* _messageHandler {}; + std::thread::id const _creationThreadID; +#if defined (_WIN32) + HANDLE const _creationThreadHandle; +#elif defined (__APPLE__) + CFRunLoopRef const _creationThreadRunLoop; + CFRunLoopSourceRef _runloopSource; + std::queue _queue; // \todo instead of locking, use a lockless concurrent queue, + std::recursive_mutex _mutex; // eg this one: https://github.com/hogliux/farbot +#else + #error "not yet implemented on this platform" +#endif +}; +//! @} + + +//! IPC message dispatcher: handles threading restrictions for MessageChannel access +//! @{ + +//! abstract base class +class MessageDispatcher +{ +public: // needs to be public for thread-local variables (which cannot be class members) +#if defined (_WIN32) + using ThreadRef = int32_t; +#elif defined (__APPLE__) + using ThreadRef = size_t; +#else + #error "not yet implemented on this platform" +#endif + static constexpr ThreadRef _invalidThread { 0 }; + +public: + //! the dispatcher takes ownership of the channel and will delete it upon teardown + explicit MessageDispatcher (Connection* connection, MessageChannel* messageChannel); + virtual ~MessageDispatcher (); + + Connection* getConnection () const { return _connection; } + MessageChannel* getMessageChannel () const { return _messageChannel; } + + //! send an encoded messages to the receiving process + //! The encoder will be deleted after sending the message. + //! If an empty reply ("void") is expected, the replyHandler should be nullptr. + //! This method can be called from any thread, concurrent calls will be serialized. + //! The calling thread will be blocked until the receiver has processed the message and + //! returned a (potentially empty) reply, which will be forwarded to the replyHandler. + virtual void sendMessage (MessageID messageID, MessageEncoder* encoder, + Connection::ReplyHandler replyHandler, void* replyHandlerUserData) = 0; + + //! route an incoming message to the correct target thread + //! takes ownership of the decoder and will eventually delete it + virtual void routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) = 0; + +// protected: this does not work with thread_local... + struct PendingReplyHandler + { + Connection::ReplyHandler _replyHandler; + void* _replyHandlerUserData; + const PendingReplyHandler* _prevPendingReplyHandler; + }; + + static bool isReply (MessageID messageID) { return messageID == 0; } + +protected: + void _sendMessage (MessageID messageID, MessageEncoder* encoder); + bool _waitForMessage (); + void _handleReply (const MessageDecoder* decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData); + MessageEncoder* _handleReceivedMessage (MessageID messageID, const MessageDecoder* decoder); + + static ThreadRef _getCurrentThread (); + +private: + Connection* const _connection; + MessageChannel* const _messageChannel; +}; + +//! single-threaded variant for main thread communication only +class MainThreadMessageDispatcher : public MessageDispatcher +{ +public: + using MessageDispatcher::MessageDispatcher; + + void sendMessage (MessageID messageID, MessageEncoder* encoder, + Connection::ReplyHandler replyHandler, void* replyHandlerUserData) override; + + void routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) override; + + void processPendingMessageIfNeeded (); + +private: +// \todo this is not allowed for some reason, so we must cast at every use of _noPendingMessageDecoder... +// static constexpr auto _noPendingMessageDecoder { reinterpret_cast (static_cast (-1)) }; + static constexpr auto _noPendingMessageDecoder { static_cast (-1) }; + MessageID _pendingMessageID { 0 }; // read/write _pendingMessageDecoder with proper barrier before/after reading/writing this + std::atomic _pendingMessageDecoder { reinterpret_cast (_noPendingMessageDecoder) }; + + const PendingReplyHandler* _pendingReplyHandler { nullptr }; +}; + +//! multi-threaded variant for all non-main thread communication +class OtherThreadsMessageDispatcher : public MessageDispatcher +{ +public: + using MessageDispatcher::MessageDispatcher; + + void sendMessage (MessageID messageID, MessageEncoder* encoder, + Connection::ReplyHandler replyHandler, void* replyHandlerUserData) override; + + void routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) override; + +private: + struct RoutedMessage + { + MessageID _messageID { 0 }; + const MessageDecoder* _decoder { nullptr }; + ThreadRef _targetThread { _invalidThread }; + }; + RoutedMessage* _getRoutedMessageForThread (ThreadRef thread); + + void _processReceivedMessage (MessageID messageID, const MessageDecoder* decoder); + +private: + // incoming data is stored in _routedMessages by the receive handler for the + // sending threads waiting to pick it up (signalled via _routeReceiveCondition) + std::condition_variable _routeReceiveCondition; + std::vector _routedMessages { 12 }; // we shouldn't use more than a handful of threads concurrently for the IPC + std::mutex _sendLock; + std::mutex _routeLock; +}; + + +} // namespace IPC +} // namespace ARA + +//! @} ARA_Library_IPC + + +#endif // ARA_ENABLE_IPC + +#endif // ARAIPCConnection_h diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h new file mode 100644 index 0000000..8862469 --- /dev/null +++ b/IPC/ARAIPCEncoding.h @@ -0,0 +1,1449 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCEncoding.h +//! Implementation helpers shared by both the ARA IPC proxy host and plug-in +//! Typically, this file is not included directly - either ARAIPCProxyHost.h +//! ARAIPCProxyPlugIn.h will be used instead. +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef ARAIPCEncoding_h +#define ARAIPCEncoding_h + +#include "ARA_Library/IPC/ARAIPCConnection.h" + +#if ARA_ENABLE_IPC + + +#include "ARA_Library/Debug/ARADebug.h" +#include "ARA_Library/Dispatch/ARAContentReader.h" +#include "ARA_Library/Utilities/ARAChannelFormat.h" + +#include +#include +#include +#include +#include +#include +#include + + +//! @addtogroup ARA_Library_IPC +//! @{ + +namespace ARA { +namespace IPC { + + +//------------------------------------------------------------------------------ +// wrapper factories to efficiently handle sending and receiving raw bytes +//------------------------------------------------------------------------------ + +// a read function based on a ptr+size pair or std::vector +// it returns pointer to read bytes from and byte count via reference +// copy should be set to false if the bytes are guaranteed to remain valid +// until the message has been sent - this is the case for all blocking sends, +// but not for non-blocking sends and depends on context for replies +class BytesEncoder : public std::function +{ +public: + BytesEncoder (const uint8_t* const bytes, const size_t size, const bool copy) + : std::function { + [bytes, size, copy] (const uint8_t*& bytesPtr, size_t& bytesSize, bool& bytesCopy) -> void + { + bytesPtr = bytes; + bytesSize = size; + bytesCopy = copy; + } } + {} + BytesEncoder (const std::vector& bytes, const bool copy) + : BytesEncoder { bytes.data (), bytes.size (), copy } + {} +}; + +// a write function based on a ptr+size pair or std::vector +// resizes to the desired byte count and returns pointer to write bytes to +class BytesDecoder : public std::function +{ +public: + BytesDecoder (uint8_t* const bytes, size_t& size) + : std::function { + [bytes, &size] (size_t& bytesSize) -> uint8_t* + { + if (bytesSize > size) + bytesSize = size; // if there is more data then we can take, clip + else + size = bytesSize; // otherwise store size + return bytes; + } } + {} + BytesDecoder (std::vector& bytes) + : std::function { + [&bytes] (size_t& size) -> uint8_t* + { + bytes.resize (size); + return bytes.data (); + } } + {} +}; + + +//------------------------------------------------------------------------------ +// wrapper factories to efficiently handle sending and receiving arrays +//------------------------------------------------------------------------------ + + +template +struct ArrayArgument +{ + static_assert (sizeof (ElementT) > sizeof (ARAByte), "byte-sized arrays should be sent as raw bytes"); + ElementT* elements; + size_t count; +}; + + +//------------------------------------------------------------------------------ +// various private helpers +//------------------------------------------------------------------------------ + +// private helper template to detect ARA ref types +template +struct _IsRefType +{ + static constexpr bool value { false }; +}; +#define ARA_IPC_SPECIALIZE_FOR_REF_TYPE(Type) \ +template<> \ +struct _IsRefType \ +{ \ + static constexpr bool value { true }; \ +}; +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAMusicalContextRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARARegionSequenceRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAAudioSourceRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAAudioModificationRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAPlaybackRegionRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAContentReaderRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARADocumentControllerRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAPlaybackRendererRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAEditorRendererRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAEditorViewRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAPlugInExtensionRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAMusicalContextHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARARegionSequenceHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAAudioSourceHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAAudioModificationHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAPlaybackRegionHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAContentReaderHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAAudioAccessControllerHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAAudioReaderHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAArchivingControllerHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAArchiveReaderHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAArchiveWriterHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAContentAccessControllerHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAModelUpdateControllerHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAPlaybackControllerHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCConnectionRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCMessageChannelRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCPlugInInstanceRef) +#undef ARA_IPC_SPECIALIZE_FOR_REF_TYPE + + +// helper template to identify pointers to ARA structs in message arguments +template +struct _IsStructPointerArg +{ + struct _False + { + static constexpr bool value { false }; + }; + struct _True + { + static constexpr bool value { true }; + }; + using type = typename std::conditional::value && + std::is_pointer::value && !std::is_same::value, _True, _False>::type; +}; + + +//------------------------------------------------------------------------------ +// private primitive wrappers for MessageEn-/Decoder C API +//------------------------------------------------------------------------------ + + +// primitives for appending an argument to a message +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const int32_t argValue) +{ + encoder->appendInt32 (argKey, argValue); +} +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const int64_t argValue) +{ + encoder->appendInt64 (argKey, argValue); +} +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const size_t argValue) +{ + encoder->appendSize (argKey, argValue); +} +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const float argValue) +{ + encoder->appendFloat (argKey, argValue); +} +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const double argValue) +{ + encoder->appendDouble (argKey, argValue); +} +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const char* const argValue) +{ + encoder->appendString (argKey, argValue); +} +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const BytesEncoder& argValue) +{ + const uint8_t* bytes; + size_t size; + bool copy; + argValue (bytes, size, copy); + encoder->appendBytes (argKey, bytes, size, copy); +} + +// primitives for reading an (optional) argument from a message +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, int32_t& argValue) +{ + return decoder->readInt32 (argKey, &argValue); +} +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, int64_t& argValue) +{ + return decoder->readInt64 (argKey, &argValue); +} +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, size_t& argValue) +{ + return decoder->readSize (argKey, &argValue); +} +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, float& argValue) +{ + return decoder->readFloat (argKey, &argValue); +} +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, double& argValue) +{ + return decoder->readDouble (argKey, &argValue); +} +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, const char*& argValue) +{ + return decoder->readString (argKey, &argValue); +} +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, BytesDecoder& argValue) +{ + size_t receivedSize; + const auto found { decoder->readBytesSize (argKey, &receivedSize) }; + auto availableSize { receivedSize }; + const auto bytes { argValue (availableSize) }; + if (!found) + return false; + if (availableSize < receivedSize) + return false; + decoder->readBytes (argKey, bytes); + return true; +} + + +//------------------------------------------------------------------------------ +// overloads of the IPCMessageEn-/Decoder primitives for types that can be +// directly mapped to a primitive type +//------------------------------------------------------------------------------ + + +// templated overloads of the IPCMessageEn-/Decoder primitives for ARA (host) ref types, +// which are stored as size_t +template::value, bool>::type = true> +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const T argValue) +{ + encoder->appendSize (argKey, reinterpret_cast (argValue)); +} +template::value, bool>::type = true> +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, T& argValue) +{ + // \todo is there a safe/proper way across all compilers for this cast to avoid the copy? +// return decoder.readSize (argKey, *reinterpret_cast (&argValue)); + size_t tmp; + const auto success { decoder->readSize (argKey, &tmp) }; + argValue = reinterpret_cast (tmp); + return success; +} + +/* instead of using ARA_IPC_ENCODE_EMBEDDED_BYTES below, we could instead allow + sending arrays of ARABytes via this overload (seems simpler but less efficient): +// to read and write arrays of ARAByte (not raw bytes but e.g. ARAKeySignatureIntervalUsage), +// we use int32_t to keep the IPCMessageEn-/Decoder API small +inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const ARAByte argValue) +{ + encoder->appendInt32 (argKey, static_cast (argValue)); +} +inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, ARAByte& argValue) +{ + int32_t tmp; + const auto result { decoder->readInt32 (argKey, tmp) }; + ARA_INTERNAL_ASSERT ((0 <= tmp) && (tmp <= static_cast (std::numeric_limits::max ()))); + argValue = static_cast (tmp); + return result; +} +*/ + + +//------------------------------------------------------------------------------ +// private helper templates to en-/decode ARA API values +// The mapping is 1:1 except for ARA (host)refs which are encoded as size_t, and aggregate types +// (i.e. ARA structs or std::vector<> of types other than ARAByte), which are expressed as sub-messages. +// En- and Decoding use the same implementation technique: +// To support using compound types (arrays, structs) both as indexed sub-message for call arguments +// when sending as well as as root message for replies, there's a templated _ValueEn-/Decoder struct +// for each type which provides an encode&append/read&decode call that extracts a sub-message if +// needed and then performs the en/decode via a separate plain en-/decoding call only available in +// compound types. The latter call will be used directly for compound data type replies. +// In order not to have to spell out _ValueEn-/Decoder<> explicitly, overloaded wrapper function +// templates _encodeAndAppend() and _readAndDecode() are provided. +//------------------------------------------------------------------------------ + + +// declarations of wrapper functions to implicitly deduce _ValueEn-/Decoder<> - +// they are defined further down below, after all specializations are defined +template +inline void _encodeAndAppend (MessageEncoder* encoder, const MessageArgumentKey argKey, const ValueT& argValue); +template +inline bool _readAndDecode (ValueT& result, const MessageDecoder* decoder, const MessageArgumentKey argKey); + + +// primary templates for basic types (numbers, strings, (host)refs and raw bytes) +template +struct _ValueEncoder +{ + static inline void encodeAndAppend (MessageEncoder* encoder, const MessageArgumentKey argKey, const ValueT& argValue) + { + _appendToMessage (encoder, argKey, argValue); + } +}; +template +struct _ValueDecoder +{ + static inline bool readAndDecode (ValueT& result, const MessageDecoder* decoder, const MessageArgumentKey argKey) + { + return _readFromMessage (decoder, argKey, result); + } +}; + + +// common base classes for en-/decoding compound types (arrays, structs) via nested messages, +// providing the generic encode&append/read&decode calls +template +struct _CompoundValueEncoderBase +{ + static inline void encodeAndAppend (MessageEncoder* encoder, const MessageArgumentKey argKey, const ValueT& argValue) + { + auto subEncoder { encoder->appendSubMessage (argKey) }; + ARA_INTERNAL_ASSERT (subEncoder != nullptr); + _ValueEncoder::encode (subEncoder, argValue); + delete subEncoder; + } +}; +template +struct _CompoundValueDecoderBase +{ + static inline bool readAndDecode (ValueT& result, const MessageDecoder* decoder, const MessageArgumentKey argKey) + { + auto subDecoder { decoder->readSubMessage (argKey) }; + if (subDecoder == nullptr) + return false; + const auto success { _ValueDecoder::decode (result, subDecoder) }; + delete subDecoder; + return success; + } +}; + + +// specialization for encoding arrays (variable or fixed size) +template +struct _ValueEncoder> : public _CompoundValueEncoderBase> +{ + static inline void encode (MessageEncoder* encoder, const ArrayArgument& value) + { + ARA_INTERNAL_ASSERT (value.count <= static_cast (std::numeric_limits::max ())); + const auto count { static_cast (value.count) }; + _encodeAndAppend (encoder, 0, count); + for (auto i { 0 }; i < count; ++i) + _encodeAndAppend (encoder, i + 1, value.elements[static_cast (i)]); + } +}; + +// specialization for decoding fixed-size arrays +template +struct _ValueDecoder> : public _CompoundValueDecoderBase> +{ + static inline bool decode (ArrayArgument& result, const MessageDecoder* decoder) + { + bool success { true }; + MessageArgumentKey count; + success &= _readAndDecode (count, decoder, 0); + success &= (count == static_cast (result.count)); + if (count > static_cast (result.count)) + count = static_cast (result.count); + + for (auto i { 0 }; i < count; ++i) + success &= _readAndDecode (result.elements[static_cast (i)], decoder, i + 1); + return success; + } +}; + +// specialization for decoding variable arrays +template +struct _ValueDecoder> : public _CompoundValueDecoderBase> +{ + static inline bool decode (std::vector& result, const MessageDecoder* decoder) + { + bool success { true }; + MessageArgumentKey count; + success &= _readAndDecode (count, decoder, 0); + result.resize (static_cast (count)); + for (auto i { 0 }; i < count; ++i) + success &= _readAndDecode (result[static_cast (i)], decoder, i + 1); + return success; + } +}; + + +// specializations for en/decoding each ARA struct + +#define ARA_IPC_BEGIN_ENCODE(StructT) \ +template<> struct _ValueEncoder : public _CompoundValueEncoderBase \ +{ /* specialization for given struct */ \ + using StructType = StructT; \ + static inline void encode (MessageEncoder* encoder, const StructType& value) \ + { +#define ARA_IPC_ENCODE_MEMBER(member) \ + _encodeAndAppend (encoder, offsetof (StructType, member), value.member); +#define ARA_IPC_ENCODE_EMBEDDED_BYTES(member) \ + const BytesEncoder tmp_##member { reinterpret_cast (value.member), sizeof (value.member), true }; \ + _encodeAndAppend (encoder, offsetof (StructType, member), tmp_##member); +#define ARA_IPC_ENCODE_EMBEDDED_ARRAY(member) \ + const ArrayArgument::type> tmp_##member { value.member, std::extent::value }; \ + _encodeAndAppend (encoder, offsetof (StructType, member), tmp_##member); +#define ARA_IPC_ENCODE_VARIABLE_ARRAY(member, count) \ + if ((value.count > 0) && (value.member != nullptr)) { \ + const ArrayArgument::type> tmp_##member { value.member, value.count }; \ + _encodeAndAppend (encoder, offsetof (StructType, member), tmp_##member); \ + } +#define ARA_IPC_ENCODE_OPTIONAL_MEMBER(member) \ + if (value.member != nullptr) \ + ARA_IPC_ENCODE_MEMBER (member) +#define ARA_IPC_HAS_ADDENDUM_MEMBER(member) \ + /* \todo ARA_IMPLEMENTS_FIELD decorates the type with the ARA:: namespace, */ \ + /* this conflicts with decltype's result - this copied version drops the ARA:: */ \ + (value.structSize > offsetof (std::remove_reference::type, member)) +#define ARA_IPC_ENCODE_ADDENDUM_MEMBER(member) \ + if (ARA_IPC_HAS_ADDENDUM_MEMBER (member)) \ + ARA_IPC_ENCODE_MEMBER (member) +#define ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_MEMBER(member) \ + if (ARA_IPC_HAS_ADDENDUM_MEMBER (member)) \ + ARA_IPC_ENCODE_OPTIONAL_MEMBER (member) +#define ARA_IPC_ENCODE_OPTIONAL_STRUCT_PTR(member) \ + if (value.member != nullptr) \ + _encodeAndAppend (encoder, offsetof (StructType, member), *value.member); +#define ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_STRUCT_PTR(member) \ + if (ARA_IPC_HAS_ADDENDUM_MEMBER (member)) \ + ARA_IPC_ENCODE_OPTIONAL_STRUCT_PTR (member) +#define ARA_IPC_END_ENCODE \ + } \ +}; + + +#define ARA_IPC_BEGIN_DECODE(StructT) \ +template<> struct _ValueDecoder : public _CompoundValueDecoderBase \ +{ /* specialization for given struct */ \ + using StructType = StructT; \ + static inline bool decode (StructType& result, const MessageDecoder* decoder) \ + { \ + bool success { true }; +#define ARA_IPC_BEGIN_DECODE_SIZED(StructT) \ + ARA_IPC_BEGIN_DECODE (StructT) \ + result.structSize = k##StructT##MinSize; +#define ARA_IPC_DECODE_MEMBER(member) \ + success &= _readAndDecode (result.member, decoder, offsetof (StructType, member)); \ + ARA_INTERNAL_ASSERT (success); +#define ARA_IPC_DECODE_EMBEDDED_BYTES(member) \ + auto resultSize_##member { sizeof (result.member) }; \ + BytesDecoder tmp_##member { reinterpret_cast (result.member), resultSize_##member }; \ + success &= _readAndDecode (tmp_##member, decoder, offsetof (StructType, member)); \ + success &= (resultSize_##member == sizeof (result.member)); \ + ARA_INTERNAL_ASSERT (success); +#define ARA_IPC_DECODE_EMBEDDED_ARRAY(member) \ + ArrayArgument::type> tmp_##member { result.member, std::extent::value }; \ + success &= _readAndDecode (tmp_##member, decoder, offsetof (StructType, member)); \ + ARA_INTERNAL_ASSERT (success); +#define ARA_IPC_DECODE_VARIABLE_ARRAY(member, count, updateCount) \ + /* \todo the outer struct contains a pointer to the inner struct, so we need some */ \ + /* thread-safe place to store it - is there a better way to achieve this? */ \ + thread_local std::vector::type>::type> tmp_##member; \ + if (_readAndDecode (tmp_##member, decoder, offsetof (StructType, member))) { \ + result.member = tmp_##member.data (); \ + if (updateCount) { result.count = tmp_##member.size (); } \ + } else { \ + result.member = nullptr; \ + if (updateCount) { result.count = 0; } \ + } +#define ARA_IPC_DECODE_OPTIONAL_MEMBER(member) \ + if (!_readAndDecode (result.member, decoder, offsetof (StructType, member))) \ + result.member = nullptr; +#define ARA_IPC_UPDATE_STRUCT_SIZE_FOR_OPTIONAL(member) \ + /* \todo ARA_IMPLEMENTED_STRUCT_SIZE decorates the type with the ARA:: namespace, */ \ + /* conflicting with the local alias StructType - this copy simply drops the ARA:: */ \ + constexpr auto size { offsetof (StructType, member) + sizeof (static_cast (nullptr)->member) }; \ + result.structSize = std::max (result.structSize, size); +#define ARA_IPC_DECODE_ADDENDUM_MEMBER(member) \ + if (_readAndDecode (result.member, decoder, offsetof (StructType, member))) { \ + ARA_IPC_UPDATE_STRUCT_SIZE_FOR_OPTIONAL (member); \ + } +#define ARA_IPC_DECODE_OPTIONAL_ADDENDUM_MEMBER(member) \ + ARA_IPC_DECODE_ADDENDUM_MEMBER (member) \ + else { \ + result.member = nullptr; \ + } +#define ARA_IPC_DECODE_OPTIONAL_STRUCT_PTR(member, OPTIONAL_UPDATE_MACRO) \ + auto subDecoder_##member { decoder->readSubMessage (offsetof (StructType, member)) }; \ + if (subDecoder_##member != nullptr) { \ + /* \todo the outer struct contains a pointer to the inner struct, so we need some */\ + /* thread-safe place to store it - is there a better way to achieve this? */\ + thread_local std::remove_const::type>::type cache; \ + success &= _ValueDecoder::decode (cache, subDecoder_##member); \ + ARA_INTERNAL_ASSERT (success); \ + result.member = &cache; \ + { OPTIONAL_UPDATE_MACRO } \ + delete subDecoder_##member; \ + } \ + else { \ + result.member = nullptr; \ + } +#define ARA_IPC_DECODE_OPTIONAL_ADDENDUM_STRUCT_PTR(member) \ + ARA_IPC_DECODE_OPTIONAL_STRUCT_PTR (member, ARA_IPC_UPDATE_STRUCT_SIZE_FOR_OPTIONAL (member)) +#define ARA_IPC_END_DECODE \ + return success; \ + } \ +}; + + +ARA_IPC_BEGIN_ENCODE (ARAColor) + ARA_IPC_ENCODE_MEMBER (r) + ARA_IPC_ENCODE_MEMBER (g) + ARA_IPC_ENCODE_MEMBER (b) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAColor) + ARA_IPC_DECODE_MEMBER (r) + ARA_IPC_DECODE_MEMBER (g) + ARA_IPC_DECODE_MEMBER (b) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARADocumentProperties) + ARA_IPC_ENCODE_OPTIONAL_MEMBER (name) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARADocumentProperties) + ARA_IPC_DECODE_OPTIONAL_MEMBER (name) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAMusicalContextProperties) + ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_MEMBER (name) + ARA_IPC_ENCODE_ADDENDUM_MEMBER (orderIndex) + ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_STRUCT_PTR (color) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARAMusicalContextProperties) + ARA_IPC_DECODE_OPTIONAL_ADDENDUM_MEMBER (name) + ARA_IPC_DECODE_ADDENDUM_MEMBER (orderIndex) + ARA_IPC_DECODE_OPTIONAL_ADDENDUM_STRUCT_PTR (color) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARARegionSequenceProperties) + ARA_IPC_ENCODE_OPTIONAL_MEMBER (name) + ARA_IPC_ENCODE_MEMBER (orderIndex) + ARA_IPC_ENCODE_MEMBER (musicalContextRef) + ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_STRUCT_PTR (color) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARARegionSequenceProperties) + ARA_IPC_DECODE_OPTIONAL_MEMBER (name) + ARA_IPC_DECODE_MEMBER (orderIndex) + ARA_IPC_DECODE_MEMBER (musicalContextRef) + ARA_IPC_DECODE_OPTIONAL_ADDENDUM_STRUCT_PTR (color) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAAudioSourceProperties) + ARA_IPC_ENCODE_OPTIONAL_MEMBER (name) + ARA_IPC_ENCODE_MEMBER (persistentID) + ARA_IPC_ENCODE_MEMBER (sampleCount) + ARA_IPC_ENCODE_MEMBER (sampleRate) + ARA_IPC_ENCODE_MEMBER (channelCount) + ARA_IPC_ENCODE_MEMBER (merits64BitSamples) + ARA_IPC_ENCODE_ADDENDUM_MEMBER (channelArrangementDataType) + if (ARA_IPC_HAS_ADDENDUM_MEMBER (channelArrangement)) + { + // \todo potential endianess conversion is missing here if needed - but this is not known here, + // it must be performed in the calling code. + // this means the caller would need to copy the incoming data for mutation - + // maybe it would be better to move receiverEndianessMatches () from the sending side to + // the receiving side? We need to review how such a change would affect audio readers etc. + const auto size { ChannelFormat::getChannelArrangementDataSize (value.channelCount, value.channelArrangementDataType, value.channelArrangement) }; + if (size > 0) + { + const BytesEncoder tmp_channelArrangement { reinterpret_cast (value.channelArrangement), size, true }; + _encodeAndAppend (encoder, offsetof (ARAAudioSourceProperties, channelArrangement), tmp_channelArrangement); + } + } +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARAAudioSourceProperties) + ARA_IPC_DECODE_OPTIONAL_MEMBER (name) + ARA_IPC_DECODE_MEMBER (persistentID) + ARA_IPC_DECODE_MEMBER (sampleCount) + ARA_IPC_DECODE_MEMBER (sampleRate) + ARA_IPC_DECODE_MEMBER (channelCount) + ARA_IPC_DECODE_MEMBER (merits64BitSamples) + ARA_IPC_DECODE_ADDENDUM_MEMBER (channelArrangementDataType) + if (result.structSize > offsetof (ARAAudioSourceProperties, channelArrangementDataType)) + { + ARA_IPC_UPDATE_STRUCT_SIZE_FOR_OPTIONAL (channelArrangement); + + if (result.channelArrangementDataType == kARAChannelArrangementUndefined) + { + result.channelArrangement = nullptr; + } + else + { + /* \todo the outer struct contains a pointer to the inner struct, so we need some */ + /* thread-safe place to store it - is there a better way to achieve this? */ + thread_local ARAByte cache[2016UL]; // some arbitrary space that can be conveniently allocated + auto resultSize_channelArrangement { sizeof (cache) }; + BytesDecoder tmp_channelArrangement { cache, resultSize_channelArrangement }; + if (_readAndDecode (tmp_channelArrangement, decoder, offsetof (ARAAudioSourceProperties, channelArrangement)) && + (resultSize_channelArrangement < sizeof (cache))) + { + result.channelArrangement = &cache; + } + else + { + result.channelArrangementDataType = kARAChannelArrangementUndefined; + result.channelArrangement = nullptr; + success = false; + } + } + } +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAAudioModificationProperties) + ARA_IPC_ENCODE_OPTIONAL_MEMBER (name) + ARA_IPC_ENCODE_MEMBER (persistentID) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARAAudioModificationProperties) + ARA_IPC_DECODE_OPTIONAL_MEMBER (name) + ARA_IPC_DECODE_MEMBER (persistentID) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAPlaybackRegionProperties) + ARA_IPC_ENCODE_MEMBER (transformationFlags) + ARA_IPC_ENCODE_MEMBER (startInModificationTime) + ARA_IPC_ENCODE_MEMBER (durationInModificationTime) + ARA_IPC_ENCODE_MEMBER (startInPlaybackTime) + ARA_IPC_ENCODE_MEMBER (durationInPlaybackTime) + if (!ARA_IPC_HAS_ADDENDUM_MEMBER (regionSequenceRef)) + ARA_IPC_ENCODE_MEMBER (musicalContextRef) + ARA_IPC_ENCODE_ADDENDUM_MEMBER (regionSequenceRef) + ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_MEMBER (name) + ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_STRUCT_PTR (color) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARAPlaybackRegionProperties) + ARA_IPC_DECODE_MEMBER (transformationFlags) + ARA_IPC_DECODE_MEMBER (startInModificationTime) + ARA_IPC_DECODE_MEMBER (durationInModificationTime) + ARA_IPC_DECODE_MEMBER (startInPlaybackTime) + ARA_IPC_DECODE_MEMBER (durationInPlaybackTime) + ARA_IPC_DECODE_OPTIONAL_MEMBER (musicalContextRef) + ARA_IPC_DECODE_ADDENDUM_MEMBER (regionSequenceRef) + ARA_IPC_DECODE_OPTIONAL_ADDENDUM_MEMBER (name) + ARA_IPC_DECODE_OPTIONAL_ADDENDUM_STRUCT_PTR (color) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAContentTimeRange) + ARA_IPC_ENCODE_MEMBER (start) + ARA_IPC_ENCODE_MEMBER (duration) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAContentTimeRange) + ARA_IPC_DECODE_MEMBER (start) + ARA_IPC_DECODE_MEMBER (duration) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAContentTempoEntry) + ARA_IPC_ENCODE_MEMBER (timePosition) + ARA_IPC_ENCODE_MEMBER (quarterPosition) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAContentTempoEntry) + ARA_IPC_DECODE_MEMBER (timePosition) + ARA_IPC_DECODE_MEMBER (quarterPosition) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAContentBarSignature) + ARA_IPC_ENCODE_MEMBER (numerator) + ARA_IPC_ENCODE_MEMBER (denominator) + ARA_IPC_ENCODE_MEMBER (position) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAContentBarSignature) + ARA_IPC_DECODE_MEMBER (numerator) + ARA_IPC_DECODE_MEMBER (denominator) + ARA_IPC_DECODE_MEMBER (position) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAContentNote) + ARA_IPC_ENCODE_MEMBER (frequency) + ARA_IPC_ENCODE_MEMBER (pitchNumber) + ARA_IPC_ENCODE_MEMBER (volume) + ARA_IPC_ENCODE_MEMBER (startPosition) + ARA_IPC_ENCODE_MEMBER (attackDuration) + ARA_IPC_ENCODE_MEMBER (noteDuration) + ARA_IPC_ENCODE_MEMBER (signalDuration) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAContentNote) + ARA_IPC_DECODE_MEMBER (frequency) + ARA_IPC_DECODE_MEMBER (pitchNumber) + ARA_IPC_DECODE_MEMBER (volume) + ARA_IPC_DECODE_MEMBER (startPosition) + ARA_IPC_DECODE_MEMBER (attackDuration) + ARA_IPC_DECODE_MEMBER (noteDuration) + ARA_IPC_DECODE_MEMBER (signalDuration) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAContentTuning) + ARA_IPC_ENCODE_MEMBER (concertPitchFrequency) + ARA_IPC_ENCODE_MEMBER (root) + ARA_IPC_ENCODE_EMBEDDED_ARRAY (tunings) + ARA_IPC_ENCODE_OPTIONAL_MEMBER (name) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAContentTuning) + ARA_IPC_DECODE_MEMBER (concertPitchFrequency) + ARA_IPC_DECODE_MEMBER (root) + ARA_IPC_DECODE_EMBEDDED_ARRAY (tunings) + ARA_IPC_DECODE_OPTIONAL_MEMBER (name) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAContentKeySignature) + ARA_IPC_ENCODE_MEMBER (root) + ARA_IPC_ENCODE_EMBEDDED_BYTES (intervals) + ARA_IPC_ENCODE_OPTIONAL_MEMBER (name) + ARA_IPC_ENCODE_MEMBER (position) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAContentKeySignature) + ARA_IPC_DECODE_MEMBER (root) + ARA_IPC_DECODE_EMBEDDED_BYTES (intervals) + ARA_IPC_DECODE_OPTIONAL_MEMBER (name) + ARA_IPC_DECODE_MEMBER (position) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAContentChord) + ARA_IPC_ENCODE_MEMBER (root) + ARA_IPC_ENCODE_MEMBER (bass) + ARA_IPC_ENCODE_EMBEDDED_BYTES (intervals) + ARA_IPC_ENCODE_OPTIONAL_MEMBER (name) + ARA_IPC_ENCODE_MEMBER (position) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAContentChord) + ARA_IPC_DECODE_MEMBER (root) + ARA_IPC_DECODE_MEMBER (bass) + ARA_IPC_DECODE_EMBEDDED_BYTES (intervals) + ARA_IPC_DECODE_OPTIONAL_MEMBER (name) + ARA_IPC_DECODE_MEMBER (position) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARARestoreObjectsFilter) + ARA_IPC_ENCODE_MEMBER (documentData) + ARA_IPC_ENCODE_VARIABLE_ARRAY (audioSourceArchiveIDs, audioSourceIDsCount) + ARA_IPC_ENCODE_VARIABLE_ARRAY (audioSourceCurrentIDs, audioSourceIDsCount) + ARA_IPC_ENCODE_VARIABLE_ARRAY (audioModificationArchiveIDs, audioModificationIDsCount) + ARA_IPC_ENCODE_VARIABLE_ARRAY (audioModificationCurrentIDs, audioModificationIDsCount) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARARestoreObjectsFilter) + ARA_IPC_DECODE_MEMBER (documentData) + ARA_IPC_DECODE_VARIABLE_ARRAY (audioSourceArchiveIDs, audioSourceIDsCount, true) + ARA_IPC_DECODE_VARIABLE_ARRAY (audioSourceCurrentIDs, audioSourceIDsCount, false) + ARA_IPC_DECODE_VARIABLE_ARRAY (audioModificationArchiveIDs, audioModificationIDsCount, true) + ARA_IPC_DECODE_VARIABLE_ARRAY (audioModificationCurrentIDs, audioModificationIDsCount, false) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAStoreObjectsFilter) + ARA_IPC_ENCODE_MEMBER (documentData) + ARA_IPC_ENCODE_VARIABLE_ARRAY (audioSourceRefs, audioSourceRefsCount) + ARA_IPC_ENCODE_VARIABLE_ARRAY (audioModificationRefs, audioModificationRefsCount) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARAStoreObjectsFilter) + ARA_IPC_DECODE_MEMBER (documentData) + ARA_IPC_DECODE_VARIABLE_ARRAY (audioSourceRefs, audioSourceRefsCount, true) + ARA_IPC_DECODE_VARIABLE_ARRAY (audioModificationRefs, audioModificationRefsCount, true) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAProcessingAlgorithmProperties) + ARA_IPC_ENCODE_MEMBER (persistentID) + ARA_IPC_ENCODE_MEMBER (name) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARAProcessingAlgorithmProperties) + ARA_IPC_DECODE_MEMBER (persistentID) + ARA_IPC_DECODE_MEMBER (name) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAViewSelection) + ARA_IPC_ENCODE_VARIABLE_ARRAY (playbackRegionRefs, playbackRegionRefsCount) + ARA_IPC_ENCODE_VARIABLE_ARRAY (regionSequenceRefs, regionSequenceRefsCount) + ARA_IPC_ENCODE_OPTIONAL_STRUCT_PTR (timeRange) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARAViewSelection) + ARA_IPC_DECODE_VARIABLE_ARRAY (playbackRegionRefs, playbackRegionRefsCount, true) + ARA_IPC_DECODE_VARIABLE_ARRAY (regionSequenceRefs, regionSequenceRefsCount, true) + ARA_IPC_DECODE_OPTIONAL_STRUCT_PTR (timeRange, ) +ARA_IPC_END_DECODE + +ARA_IPC_BEGIN_ENCODE (ARAFactory) + ARA_IPC_ENCODE_MEMBER (lowestSupportedApiGeneration) + ARA_IPC_ENCODE_MEMBER (highestSupportedApiGeneration) + ARA_IPC_ENCODE_MEMBER (factoryID) + ARA_IPC_ENCODE_MEMBER (plugInName) + ARA_IPC_ENCODE_MEMBER (manufacturerName) + ARA_IPC_ENCODE_MEMBER (informationURL) + ARA_IPC_ENCODE_MEMBER (version) + ARA_IPC_ENCODE_MEMBER (documentArchiveID) + ARA_IPC_ENCODE_VARIABLE_ARRAY (compatibleDocumentArchiveIDs, compatibleDocumentArchiveIDsCount) + ARA_IPC_ENCODE_VARIABLE_ARRAY (analyzeableContentTypes, analyzeableContentTypesCount) + ARA_IPC_ENCODE_MEMBER (supportedPlaybackTransformationFlags) + ARA_IPC_ENCODE_ADDENDUM_MEMBER (supportsStoringAudioFileChunks) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE_SIZED (ARAFactory) + ARA_IPC_DECODE_MEMBER (lowestSupportedApiGeneration) + ARA_IPC_DECODE_MEMBER (highestSupportedApiGeneration) + ARA_IPC_DECODE_MEMBER (factoryID) + result.initializeARAWithConfiguration = nullptr; + result.uninitializeARA = nullptr; + ARA_IPC_DECODE_MEMBER (plugInName) + ARA_IPC_DECODE_MEMBER (manufacturerName) + ARA_IPC_DECODE_MEMBER (informationURL) + ARA_IPC_DECODE_MEMBER (version) + result.createDocumentControllerWithDocument = nullptr; + ARA_IPC_DECODE_MEMBER (documentArchiveID) + ARA_IPC_DECODE_VARIABLE_ARRAY (compatibleDocumentArchiveIDs, compatibleDocumentArchiveIDsCount, true) + ARA_IPC_DECODE_VARIABLE_ARRAY (analyzeableContentTypes, analyzeableContentTypesCount, true) + ARA_IPC_DECODE_MEMBER (supportedPlaybackTransformationFlags) + ARA_IPC_DECODE_ADDENDUM_MEMBER (supportsStoringAudioFileChunks) +ARA_IPC_END_DECODE + + +// ARADocumentControllerInterface::storeAudioSourceToAudioFileChunk() must return the documentArchiveID and the +// openAutomatically flag in addition to the return value, we need a special struct to encode this through IPC. +struct StoreAudioSourceToAudioFileChunkReply +{ + ARABool result; + ARAPersistentID documentArchiveID; + ARABool openAutomatically; +}; +ARA_IPC_BEGIN_ENCODE (StoreAudioSourceToAudioFileChunkReply) + ARA_IPC_ENCODE_MEMBER (result) + ARA_IPC_ENCODE_MEMBER (documentArchiveID) + ARA_IPC_ENCODE_MEMBER (openAutomatically) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (StoreAudioSourceToAudioFileChunkReply) + ARA_IPC_DECODE_MEMBER (result) + ARA_IPC_DECODE_MEMBER (documentArchiveID) + ARA_IPC_DECODE_MEMBER (openAutomatically) +ARA_IPC_END_DECODE + +// ARADocumentControllerInterface::getPlaybackRegionHeadAndTailTime() must return both head- and tailtime. +struct GetPlaybackRegionHeadAndTailTimeReply +{ + ARATimeDuration headTime; + ARATimeDuration tailTime; +}; +ARA_IPC_BEGIN_ENCODE (GetPlaybackRegionHeadAndTailTimeReply) + ARA_IPC_ENCODE_MEMBER (headTime) + ARA_IPC_ENCODE_MEMBER (tailTime) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (GetPlaybackRegionHeadAndTailTimeReply) + ARA_IPC_DECODE_MEMBER (headTime) + ARA_IPC_DECODE_MEMBER (tailTime) +ARA_IPC_END_DECODE + + +#undef ARA_IPC_BEGIN_ENCODE +#undef ARA_IPC_ENCODE_MEMBER +#undef ARA_IPC_ENCODE_EMBEDDED_BYES +#undef ARA_IPC_ENCODE_EMBEDDED_ARRAY +#undef ARA_IPC_ENCODE_VARIABLE_ARRAY +#undef ARA_IPC_ENCODE_OPTIONAL_MEMBER +#undef ARA_IPC_HAS_ADDENDUM_MEMBER +#undef ARA_IPC_ENCODE_ADDENDUM_MEMBER +#undef ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_MEMBER +#undef ARA_IPC_ENCODE_OPTIONAL_STRUCT_PTR +#undef ARA_IPC_ENCODE_OPTIONAL_ADDENDUM_STRUCT_PTR +#undef ARA_IPC_END_ENCODE + +#undef ARA_IPC_BEGIN_DECODE +#undef ARA_IPC_BEGIN_DECODE_SIZED +#undef ARA_IPC_DECODE_MEMBER +#undef ARA_IPC_DECODE_EMBEDDED_BYTES +#undef ARA_IPC_DECODE_EMBEDDED_ARRAY +#undef ARA_IPC_DECODE_VARIABLE_ARRAY +#undef ARA_IPC_DECODE_OPTIONAL_MEMBER +#undef ARA_IPC_UPDATE_STRUCT_SIZE_FOR_OPTIONAL +#undef ARA_IPC_DECODE_ADDENDUM_MEMBER +#undef ARA_IPC_DECODE_OPTIONAL_ADDENDUM_MEMBER +#undef ARA_IPC_DECODE_OPTIONAL_STRUCT_PTR +#undef ARA_IPC_DECODE_OPTIONAL_ADDENDUM_STRUCT_PTR +#undef ARA_IPC_END_DECODE + + +// actual definitions of wrapper functions to implicitly deduce _ValueEn-/Decoder<> - +// they are forward-declared above, before _ValueEn-/Decoder<> are defined +template +inline void _encodeAndAppend (MessageEncoder* encoder, const MessageArgumentKey argKey, const ValueT& argValue) +{ + return _ValueEncoder::encodeAndAppend (encoder, argKey, argValue); +} +template +inline bool _readAndDecode (ValueT& result, const MessageDecoder* decoder, const MessageArgumentKey argKey) +{ + return _ValueDecoder::readAndDecode (result, decoder, argKey); +} + + +//------------------------------------------------------------------------------ + +// private helper for decodeArguments() to deal with optional arguments +template +struct _IsOptionalArgument +{ + static constexpr bool value { false }; +}; +template +struct _IsOptionalArgument> +{ + static constexpr bool value { true }; +}; + +// private helpers for encodeArguments() and decodeArguments() to deal with the variable arguments one at a time +inline void _encodeArgumentsHelper (MessageEncoder* /*encoder*/, const MessageArgumentKey /*argKey*/) +{ +} +template::type::value, bool>::type = true> +inline void _encodeArgumentsHelper (MessageEncoder* encoder, const MessageArgumentKey argKey, const ArgT& argValue, const MoreArgs &... moreArgs) +{ + _encodeAndAppend (encoder, argKey, argValue); + _encodeArgumentsHelper (encoder, argKey + 1, moreArgs...); +} +template::type::value, bool>::type = true> +inline void _encodeArgumentsHelper (MessageEncoder* encoder, const MessageArgumentKey argKey, const ArgT& argValue, const MoreArgs &... moreArgs) +{ + if (argValue != nullptr) + _encodeAndAppend (encoder, argKey, *argValue); + _encodeArgumentsHelper (encoder, argKey + 1, moreArgs...); +} +template +inline void _encodeArgumentsHelper (MessageEncoder* encoder, const MessageArgumentKey argKey, const std::nullptr_t& /*argValue*/, const MoreArgs &... moreArgs) +{ + _encodeArgumentsHelper (encoder, argKey + 1, moreArgs...); +} + +inline void _decodeArgumentsHelper (const MessageDecoder* /*decoder*/, const MessageArgumentKey /*argKey*/) +{ +} +template::value, bool>::type = true> +inline void _decodeArgumentsHelper (const MessageDecoder* decoder, const MessageArgumentKey argKey, ArgT& argValue, MoreArgs &... moreArgs) +{ + _readAndDecode (argValue, decoder, argKey); + _decodeArgumentsHelper (decoder, argKey + 1, moreArgs...); +} +template::value, bool>::type = true> +inline void _decodeArgumentsHelper (const MessageDecoder* decoder, const MessageArgumentKey argKey, ArgT& argValue, MoreArgs &... moreArgs) +{ + argValue.second = _readAndDecode (argValue.first, decoder, argKey); + _decodeArgumentsHelper (decoder, argKey + 1, moreArgs...); +} + +// private helpers for ARA_IPC_HOST_METHOD_ID and ARA_IPC_PLUGIN_METHOD_ID +template +constexpr MessageID _getHostInterfaceID (); +template<> +constexpr MessageID _getHostInterfaceID () { return 0; } +template<> +constexpr MessageID _getHostInterfaceID () { return 1; } +template<> +constexpr MessageID _getHostInterfaceID () { return 2; } +template<> +constexpr MessageID _getHostInterfaceID () { return 3; } +template<> +constexpr MessageID _getHostInterfaceID () { return 4; } + +template +constexpr MessageID _getPlugInInterfaceID (); +template<> +constexpr MessageID _getPlugInInterfaceID () { return 0; } +template<> +constexpr MessageID _getPlugInInterfaceID () { return 1; } +template<> +constexpr MessageID _getPlugInInterfaceID () { return 2; } +template<> +constexpr MessageID _getPlugInInterfaceID () { return 3; } + + +//------------------------------------------------------------------------------ +// actual client API +//------------------------------------------------------------------------------ + + +// helper class wrapping MessageID to prevent implicit conversions to/from +// MessageID, so that they can be identified reliably in call signatures +// this is important e.g. for the various forms of RemoteCaller::remoteCall (). +class MethodID +{ +private: + constexpr MethodID (MessageID messageID) : _id { messageID } {} + +public: + template + static inline constexpr MethodID createWithARAInterfaceIDAndOffset () + { + static_assert (offset > 0, "offset 0 is never a valid function pointer"); + static_assert ((interfaceID < 8), "currently using only 3 bits for interface ID"); +#if defined (__i386__) || defined (_M_IX86) + static_assert ((sizeof (void*) == 4), "compiler settings imply 32 bit pointers"); + static_assert (((offset & 0x3FFFFFF4) == offset), "offset is misaligned or too large"); + return (offset << 1) + interfaceID; // lower 2 bits of offset are 0 due to alignment, must shift 1 bit to store interface ID +#else + static_assert ((sizeof (void*) == 8), "assuming 64 bit pointers per default"); + static_assert (((offset & 0x7FFFFFF8) == offset), "offset is misaligned or too large"); + return offset + interfaceID; // lower 3 bits of offset are 0 due to alignment, can be used to store interface ID +#endif + } + + template + static inline constexpr MethodID createWithARAGlobalMessageID () + { + static_assert (!isCustomMessageID (messageID), "must not be in custom message range"); + return messageID; + } + + template + static inline constexpr MethodID createWithCustomMessageID () + { + static_assert (isCustomMessageID (messageID), "must not be in reserved message range"); + return messageID; + } + + static inline constexpr bool isCustomMessageID (MessageID messageID) + { + return (messageID < kReservedMessageIDRangeStart) || (kReservedMessageIDRangeEnd <= messageID); + } + + template + static inline constexpr bool isMessageToHostInterface (MessageID messageID) + { + return _getHostInterfaceID () == (messageID % 8); + } + + template + static inline constexpr bool isMessageToPlugInInterface (MessageID messageID) + { + return _getPlugInInterfaceID () == (messageID % 8); + } + + constexpr MessageID getMessageID () const { return _id; } + +private: + const MessageID _id; +}; + +inline bool operator== (const MessageID messageID, const MethodID methodID) +{ + return (messageID == methodID.getMessageID ()); +} +inline bool operator== (const MethodID methodID, const MessageID messageID) +{ + return (messageID == methodID.getMessageID ()); +} +inline bool operator!= (const MessageID messageID, const MethodID methodID) +{ + return (messageID != methodID.getMessageID ()); +} +inline bool operator!= (const MethodID methodID, const MessageID messageID) +{ + return (messageID != methodID.getMessageID ()); +} + + +// create a MethodID for a given host-side or plug-in-side ARA method +#define ARA_IPC_HOST_METHOD_ID(StructT, member) MethodID::createWithARAInterfaceIDAndOffset <_getHostInterfaceID (), offsetof (StructT, member)> () +#define ARA_IPC_PLUGIN_METHOD_ID(StructT, member) MethodID::createWithARAInterfaceIDAndOffset <_getPlugInInterfaceID (), offsetof (StructT, member)> () + + +// "global" messages for startup and teardown that are not calculated based on interface structs +constexpr auto kGetFactoriesCountMethodID { MethodID::createWithARAGlobalMessageID<1> () }; +constexpr auto kGetFactoryMethodID { MethodID::createWithARAGlobalMessageID<2> () }; +constexpr auto kInitializeARAMethodID { MethodID::createWithARAGlobalMessageID<3> () }; +constexpr auto kCreateDocumentControllerMethodID { MethodID::createWithARAGlobalMessageID<4> () }; +constexpr auto kBindToDocumentControllerMethodID { MethodID::createWithARAGlobalMessageID<5> () }; +constexpr auto kCleanupBindingMethodID { MethodID::createWithARAGlobalMessageID<6> () }; +constexpr auto kUninitializeARAMethodID { MethodID::createWithARAGlobalMessageID<7> () }; + + +// for debugging: translate IDs of messages sent by the host back to human-readable text +inline const char* decodeHostMessageID (const MessageID messageID) +{ + switch (messageID) + { + #define ARA_IPC_GLOBAL_MESSAGE_CASE(methodID) \ + case methodID.getMessageID (): return #methodID; + ARA_IPC_GLOBAL_MESSAGE_CASE (kGetFactoriesCountMethodID); + ARA_IPC_GLOBAL_MESSAGE_CASE (kGetFactoryMethodID); + ARA_IPC_GLOBAL_MESSAGE_CASE (kInitializeARAMethodID); + ARA_IPC_GLOBAL_MESSAGE_CASE (kCreateDocumentControllerMethodID); + ARA_IPC_GLOBAL_MESSAGE_CASE (kBindToDocumentControllerMethodID); + ARA_IPC_GLOBAL_MESSAGE_CASE (kCleanupBindingMethodID); + ARA_IPC_GLOBAL_MESSAGE_CASE (kUninitializeARAMethodID); +#undef ARA_IPC_GLOBAL_MESSAGE_CASE + +#define ARA_IPC_PLUGIN_INTERFACE_CASE(StructT, member) \ + case ARA_IPC_PLUGIN_METHOD_ID (StructT, member).getMessageID (): return #StructT "::" #member; + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyDocumentController) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getFactory) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, beginEditing) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, endEditing) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, notifyModelUpdates) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, beginRestoringDocumentFromArchive) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, endRestoringDocumentFromArchive) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, storeDocumentToArchive) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateDocumentProperties) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createMusicalContext) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateMusicalContextProperties) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateMusicalContextContent) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyMusicalContext) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createAudioSource) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateAudioSourceProperties) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateAudioSourceContent) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, enableAudioSourceSamplesAccess) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyAudioSource) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createAudioModification) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, cloneAudioModification) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateAudioModificationProperties) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyAudioModification) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createPlaybackRegion) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updatePlaybackRegionProperties) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyPlaybackRegion) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isAudioSourceContentAvailable) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, requestAudioSourceContentAnalysis) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getAudioSourceContentGrade) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createAudioSourceContentReader) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isAudioModificationContentAvailable) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getAudioModificationContentGrade) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createAudioModificationContentReader) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isPlaybackRegionContentAvailable) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getPlaybackRegionContentGrade) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createPlaybackRegionContentReader) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getContentReaderEventCount) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getContentReaderDataForEvent) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyContentReader) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createRegionSequence) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateRegionSequenceProperties) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyRegionSequence) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, restoreObjectsFromArchive) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, storeObjectsToArchive) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmsCount) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmProperties) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isLicensedForCapabilities) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARAPlaybackRendererInterface, addPlaybackRegion) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARAPlaybackRendererInterface, removePlaybackRegion) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorRendererInterface, addPlaybackRegion) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorRendererInterface, removePlaybackRegion) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorRendererInterface, addRegionSequence) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorRendererInterface, removeRegionSequence) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorViewInterface, notifySelection) + ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorViewInterface, notifyHideRegionSequences) +#undef ARA_IPC_PLUGIN_INTERFACE_CASE + } + ARA_INTERNAL_ASSERT (false); + return "unknown message ID"; +} + +// for debugging: translate IDs of messages sent by the host back to human-readable text +inline const char* decodePlugInMessageID (const MessageID messageID) +{ + switch (messageID) + { +#define ARA_IPC_HOST_INTERFACE_CASE(StructT, member) \ + case ARA_IPC_HOST_METHOD_ID (StructT, member).getMessageID (): return #StructT "::" #member; + ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, createAudioReaderForSource) + ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, readAudioSamples) + ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, destroyAudioReader) + ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, getArchiveSize) + ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, readBytesFromArchive) + ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, writeBytesToArchive) + ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, notifyDocumentArchivingProgress) + ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress) + ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, getDocumentArchiveID) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, isMusicalContextContentAvailable) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, getMusicalContextContentGrade) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, createMusicalContextContentReader) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, isAudioSourceContentAvailable) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, getAudioSourceContentGrade) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, createAudioSourceContentReader) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, getContentReaderEventCount) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, getContentReaderDataForEvent) + ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, destroyContentReader) + ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress) + ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged) + ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged) + ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged) + ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyDocumentDataChanged) + ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestStartPlayback) + ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestStopPlayback) + ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestSetPlaybackPosition) + ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestSetCycleRange) + ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestEnableCycle) +#undef ARA_IPC_HOST_INTERFACE_CASE + } + ARA_INTERNAL_ASSERT (false); + return "unknown message ID"; +} + + +// caller side: create a message with the specified arguments +template +inline void encodeArguments (MessageEncoder* encoder, const Args &... args) +{ + _encodeArgumentsHelper (encoder, 0, args...); +} + +// caller side: decode the received reply to a sent message +template::value || !std::is_pod::value, bool>::type = true> +inline bool decodeReply (RetT& result, const MessageDecoder* decoder) +{ + return _readAndDecode (result, decoder, 0); +} +template::value && std::is_pod::value, bool>::type = true> +inline bool decodeReply (RetT& result, const MessageDecoder* decoder) +{ + return _ValueDecoder::decode (result, decoder); +} + + +// callee side: decode the arguments of a received message +template +inline void decodeArguments (const MessageDecoder* decoder, Args &... args) +{ + ARA_INTERNAL_ASSERT (decoder != nullptr); + _decodeArgumentsHelper (decoder, 0, args...); +} + +// callee side: wrapper for optional method arguments: first is the argument value, second if it was present +template +using OptionalArgument = typename std::pair; + +// callee side: encode the reply to a received message +template::value || !std::is_pod::value, bool>::type = true> +inline void encodeReply (MessageEncoder* encoder, const ValueT& value) +{ + ARA_INTERNAL_ASSERT (encoder != nullptr); + _encodeAndAppend (encoder, 0, value); +} +template::value && std::is_pod::value, bool>::type = true> +inline void encodeReply (MessageEncoder* encoder, const ValueT& value) +{ + ARA_INTERNAL_ASSERT (encoder != nullptr); + _ValueEncoder::encode (encoder, value); +} + + +//------------------------------------------------------------------------------ +// support for content readers +//------------------------------------------------------------------------------ + +inline void encodeContentEvent (MessageEncoder* encoder, const ARAContentType type, const void* eventData) +{ + switch (type) + { + case kARAContentTypeNotes: encodeReply (encoder, *static_cast (eventData)); break; + case kARAContentTypeTempoEntries: encodeReply (encoder, *static_cast (eventData)); break; + case kARAContentTypeBarSignatures: encodeReply (encoder, *static_cast (eventData)); break; + case kARAContentTypeStaticTuning: encodeReply (encoder, *static_cast (eventData)); break; + case kARAContentTypeKeySignatures: encodeReply (encoder, *static_cast (eventData)); break; + case kARAContentTypeSheetChords: encodeReply (encoder, *static_cast (eventData)); break; + default: ARA_INTERNAL_ASSERT (false && "content type not implemented yet"); break; + } +} + +class ContentEventDecoder +{ +public: + ContentEventDecoder (ARAContentType type) + : _decoderFunction { _getDecoderFunctionForContentType (type) }, + _contentString { _findStringMember (type) } + {} + + const void* decode (const MessageDecoder* decoder) + { + (this->*_decoderFunction) (decoder); + return &_eventStorage; + } + +private: + using _DecoderFunction = void (ContentEventDecoder::*) (const MessageDecoder*); + + template + void _decodeContentEvent (const MessageDecoder* decoder) + { + decodeReply (*reinterpret_cast (&this->_eventStorage), decoder); + if (this->_contentString != nullptr) + { + this->_stringStorage.assign (*this->_contentString); + *this->_contentString = this->_stringStorage.c_str (); + } + } + + static inline +#if __cplusplus >= 201402L + constexpr +#endif + _DecoderFunction _getDecoderFunctionForContentType (const ARAContentType type) + { + switch (type) + { + case kARAContentTypeNotes: return &ContentEventDecoder::_decodeContentEvent::DataType>; + case kARAContentTypeTempoEntries: return &ContentEventDecoder::_decodeContentEvent::DataType>; + case kARAContentTypeBarSignatures: return &ContentEventDecoder::_decodeContentEvent::DataType>; + case kARAContentTypeStaticTuning: return &ContentEventDecoder::_decodeContentEvent::DataType>; + case kARAContentTypeKeySignatures: return &ContentEventDecoder::_decodeContentEvent::DataType>; + case kARAContentTypeSheetChords: return &ContentEventDecoder::_decodeContentEvent::DataType>; + default: ARA_INTERNAL_ASSERT (false); return nullptr; + } + } + + static inline +#if __cplusplus >= 201402L + constexpr +#endif + size_t _getStringMemberOffsetForContentType (const ARAContentType type) + { + switch (type) + { + case kARAContentTypeStaticTuning: return offsetof (ARAContentTuning, name); + case kARAContentTypeKeySignatures: return offsetof (ARAContentKeySignature, name); + case kARAContentTypeSheetChords: return offsetof (ARAContentChord, name); + default: return 0; + } + } + + inline ARAUtf8String* _findStringMember (const ARAContentType type) + { + const auto offset { ContentEventDecoder::_getStringMemberOffsetForContentType (type) }; + if (offset == 0) + return nullptr; + return reinterpret_cast (reinterpret_cast (&this->_eventStorage) + offset); + } + +private: + _DecoderFunction const _decoderFunction; // instead of performing the switch (type) for each event, + // we select a templated member function in the c'tor + union + { + ARAContentTempoEntry _tempoEntry; + ARAContentBarSignature _barSignature; + ARAContentNote _note; + ARAContentTuning _tuning; + ARAContentKeySignature _keySignature; + ARAContentChord _chord; + } _eventStorage {}; + + ARAUtf8String* _contentString {}; + std::string _stringStorage {}; + + ARA_DISABLE_COPY_AND_MOVE (ContentEventDecoder) +}; + +//------------------------------------------------------------------------------ +// implementation helpers +//------------------------------------------------------------------------------ + +// helper base class to create and send messages, decoding reply if applicable +// It's possible to specify a CustomDecodeFunction as reply type to access an un-decoded reply +// if needed (e.g. to deal with memory ownership issue). +class RemoteCaller +{ +public: + using CustomDecodeFunction = std::function; + + explicit RemoteCaller (Connection* connection) noexcept + : _connection { connection } + {} + + template + void remoteCall (const MethodID methodID, const Args &... args) const + { + auto encoder { _connection->createEncoder () }; + encodeArguments (encoder, args...); + _connection->sendMessage (methodID.getMessageID (), encoder, nullptr, nullptr); + } + + template + void remoteCall (RetT& result, const MethodID methodID, const Args &... args) const + { + auto encoder { _connection->createEncoder () }; + encodeArguments (encoder, args...); + const auto replyHandler { [] (const MessageDecoder* decoder, void* userData) -> void + { + ARA_INTERNAL_ASSERT (decoder); + decodeReply (*reinterpret_cast (userData), decoder); + } }; + _connection->sendMessage (methodID.getMessageID (), encoder, replyHandler, &result); + } + + template + void remoteCall (CustomDecodeFunction& decodeFunction, const MethodID methodID, const Args &... args) const + { + auto encoder { _connection->createEncoder () }; + encodeArguments (encoder, args...); + const auto replyHandler { [] (const MessageDecoder* decoder, void* userData) -> void + { + ARA_INTERNAL_ASSERT (decoder); + (*reinterpret_cast (userData)) (decoder); + } }; + _connection->sendMessage (methodID.getMessageID (), encoder, replyHandler, &decodeFunction); + } + + bool receiverEndianessMatches () const { return _connection->receiverEndianessMatches (); } + + Connection* getConnection () const { return _connection; } + +private: + Connection* const _connection; +}; + + +} // namespace IPC +} // namespace ARA + +//! @} ARA_Library_IPC + + +#endif // ARA_ENABLE_IPC + +#endif // ARAIPCEncoding_h diff --git a/IPC/ARAIPCMessage.h b/IPC/ARAIPCMessage.h new file mode 100644 index 0000000..1503010 --- /dev/null +++ b/IPC/ARAIPCMessage.h @@ -0,0 +1,129 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCMessage.h +//! Abstractions shared by both the ARA IPC proxy host and plug-in +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef ARAIPCMessage_h +#define ARAIPCMessage_h + +#include "ARA_Library/IPC/ARAIPC.h" + +#if ARA_ENABLE_IPC + + +//! @addtogroup ARA_Library_IPC +//! @{ + +namespace ARA { +namespace IPC { + + +//! ID type for messages +typedef ARAInt32 MessageID; + +//! the implementation reserves this range of IDs, any custom messages must be >= kMessageIDRangeStart and < kMessageIDRangeEnd +constexpr MessageID kReservedMessageIDRangeStart { 1 }; +constexpr MessageID kReservedMessageIDRangeEnd { 8*16*16 - 1 }; + + +//! key type for message dictionaries - negative keys are reserved for the implementation +typedef ARAInt32 MessageArgumentKey; + + +//! Message Encoder +//! @{ +class MessageEncoder +{ +public: + virtual ~MessageEncoder () = default; + + //! number types + //! The "size" variant will also be used for the pointer-sized ARA (host) refs. + //@{ + virtual void appendInt32 (MessageArgumentKey argKey, int32_t argValue) = 0; + virtual void appendInt64 (MessageArgumentKey argKey, int64_t argValue) = 0; + virtual void appendSize (MessageArgumentKey argKey, size_t argValue) = 0; + virtual void appendFloat (MessageArgumentKey argKey, float argValue) = 0; + virtual void appendDouble (MessageArgumentKey argKey, double argValue) = 0; + //@} + + //! UTF8-encoded C strings + virtual void appendString (MessageArgumentKey argKey, const char* argValue) = 0; + + //! raw bytes + //! As optimization, disable copying if the memory containing the bytes stays + //! alive&unchanged until the message has been sent. + virtual void appendBytes (MessageArgumentKey argKey, const uint8_t* argValue, size_t argSize, bool copy) = 0; + + //! sub-messages to encode compound types + //! The caller is responsible for deleting the returned sub-encoder after use. + virtual MessageEncoder* appendSubMessage (MessageArgumentKey argKey) = 0; +}; +//! @} + + +//! Message Decoder +//! @{ +class MessageDecoder +{ +public: + virtual ~MessageDecoder () = default; + + //! number types + //! The "size" variant will also be used for the pointer-sized ARA (host) refs. + //! Will return false and set argValue to 0 if key not found. + //@{ + virtual bool readInt32 (MessageArgumentKey argKey, int32_t* argValue) const = 0; + virtual bool readInt64 (MessageArgumentKey argKey, int64_t* argValue) const = 0; + virtual bool readSize (MessageArgumentKey argKey, size_t* argValue) const = 0; + virtual bool readFloat (MessageArgumentKey argKey, float* argValue) const = 0; + virtual bool readDouble (MessageArgumentKey argKey, double* argValue) const = 0; + //@} + + //! UTF8-encoded C strings + //! Will return false and set argValue to NULL if key not found. + //! Note that received string data is only valid as long as the message that + //! provided them is alive. + virtual bool readString (MessageArgumentKey argKey, const char ** argValue) const = 0; + + //! raw bytes + //! first query size, then provide a buffer large enough to copy the bytes to. + //! readBytesSize () will return false and set argSize to 0 if key not found. + //@{ + virtual bool readBytesSize (MessageArgumentKey argKey, size_t* argSize) const = 0; + virtual void readBytes (MessageArgumentKey argKey, uint8_t* argValue) const = 0; + //@} + + //! sub-messages to decode compound types + //! returns nullptr if key not found or if the value for the key is not representing a sub-message + //! The caller is responsible for deleting the encoder after use. + virtual MessageDecoder* readSubMessage (MessageArgumentKey argKey) const = 0; + + //! test whether a given argument is present in the message + virtual bool hasDataForKey (MessageArgumentKey argKey) const = 0; +}; +//! @} + + +} // namespace IPC +} // namespace ARA + +//! @} ARA_Library_IPC + + +#endif // ARA_ENABLE_IPC + +#endif // ARAIPCMessage_h diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp new file mode 100644 index 0000000..20fdcae --- /dev/null +++ b/IPC/ARAIPCProxyHost.cpp @@ -0,0 +1,1343 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCProxyHost.cpp +//! implementation of host-side ARA IPC proxy host +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#include "ARAIPCProxyHost.h" + + +#if ARA_ENABLE_IPC + +#include "ARA_Library/IPC/ARAIPCEncoding.h" +#include "ARA_Library/Dispatch/ARAHostDispatch.h" +#include "ARA_Library/Dispatch/ARAPlugInDispatch.h" +#include "ARA_Library/Utilities/ARAStdVectorUtilities.h" + +#include +#include +#include + + +#if ARA_SUPPORT_VERSION_1 + #error "The ARA IPC proxy host implementation does not support ARA 1." +#endif + + +namespace ARA { +namespace IPC { +namespace ProxyHostImpl { + +class AudioAccessController; +class ArchivingController; +class ContentAccessController; +class ModelUpdateController; +class PlaybackController; +class DocumentController; + + +/*******************************************************************************/ + +struct RemoteAudioSource +{ + ARAAudioSourceHostRef mainHostRef; + ARAAudioSourceRef plugInRef; + ARAChannelCount channelCount; +}; +ARA_MAP_REF (RemoteAudioSource, ARAAudioSourceRef) +ARA_MAP_HOST_REF (RemoteAudioSource, ARAAudioSourceHostRef) + +struct RemoteAudioReader +{ + RemoteAudioSource* audioSource; + ARAAudioReaderHostRef mainHostRef; + size_t sampleSize; + void (*swapFunction) (void* buffer, ARASampleCount sampleCount); +}; +ARA_MAP_HOST_REF (RemoteAudioReader, ARAAudioReaderHostRef) + +struct RemoteContentReader +{ + ARAContentReaderRef plugInRef; + ARAContentType contentType; +}; +ARA_MAP_REF (RemoteContentReader, ARAContentReaderRef) + +struct RemoteHostContentReader +{ + RemoteHostContentReader (ARAContentReaderHostRef hostRef, ARAContentType type) + : remoteHostRef { hostRef }, decoder { type } {} + + ARAContentReaderHostRef remoteHostRef; + ContentEventDecoder decoder; +}; +ARA_MAP_HOST_REF (RemoteHostContentReader, ARAContentReaderHostRef) + + +/*******************************************************************************/ +//! Implementation of AudioAccessControllerInterface that channels all calls through IPC +class AudioAccessController : public Host::AudioAccessControllerInterface, protected RemoteCaller +{ +public: + AudioAccessController (Connection* connection, ARAAudioAccessControllerHostRef remoteHostRef) noexcept + : RemoteCaller { connection }, _remoteHostRef { remoteHostRef } {} + + ARAAudioReaderHostRef createAudioReaderForSource (ARAAudioSourceHostRef audioSourceHostRef, bool use64BitSamples) noexcept override; + bool readAudioSamples (ARAAudioReaderHostRef audioReaderHostRef, ARASamplePosition samplePosition, ARASampleCount samplesPerChannel, void* const buffers[]) noexcept override; + void destroyAudioReader (ARAAudioReaderHostRef audioReaderHostRef) noexcept override; + +private: + ARAAudioAccessControllerHostRef _remoteHostRef; +}; + +/*******************************************************************************/ + +void _swap (float* ptr) +{ + auto asIntPtr { reinterpret_cast (ptr) }; +#if defined (_MSC_VER) + *asIntPtr = _byteswap_ulong (*asIntPtr); +#elif defined (__GNUC__) + *asIntPtr = __builtin_bswap32 (*asIntPtr); +#else + #error "not implemented for this compiler." +#endif +} +void _swap (double* ptr) +{ + auto asIntPtr { reinterpret_cast (ptr) }; +#if defined (_MSC_VER) + *asIntPtr = _byteswap_uint64 (*asIntPtr); +#elif defined (__GNUC__) + *asIntPtr = __builtin_bswap64 (*asIntPtr); +#else + #error "not implemented for this compiler." +#endif +} + +template +void _swapBuffer (void* buffer, ARASampleCount sampleCount) +{ + for (auto i { 0 }; i < sampleCount; ++i) + _swap (static_cast (buffer) + i); +} + +ARAAudioReaderHostRef AudioAccessController::createAudioReaderForSource (ARAAudioSourceHostRef audioSourceHostRef, bool use64BitSamples) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + auto remoteAudioReader { new RemoteAudioReader }; + remoteAudioReader->audioSource = fromHostRef (audioSourceHostRef); + remoteAudioReader->sampleSize = (use64BitSamples) ? sizeof (double) : sizeof (float); + if (receiverEndianessMatches ()) + remoteAudioReader->swapFunction = nullptr; + else + remoteAudioReader->swapFunction = (use64BitSamples) ? &_swapBuffer : &_swapBuffer; + remoteCall (remoteAudioReader->mainHostRef, ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, createAudioReaderForSource), + _remoteHostRef, remoteAudioReader->audioSource->mainHostRef, use64BitSamples); + return toHostRef (remoteAudioReader); +} + +bool AudioAccessController::readAudioSamples (ARAAudioReaderHostRef audioReaderHostRef, ARASamplePosition samplePosition, + ARASampleCount samplesPerChannel, void* const buffers[]) noexcept +{ +// this function can be called from other threads! +// ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + auto remoteAudioReader { fromHostRef (audioReaderHostRef) }; + const auto channelCount { static_cast (remoteAudioReader->audioSource->channelCount) }; + + // recursively limit message size to keep IPC responsive + if (samplesPerChannel > 8192) + { + const auto samplesPerChannel1 { samplesPerChannel / 2 }; + const auto result1 { readAudioSamples (audioReaderHostRef, samplePosition, samplesPerChannel1, buffers) }; + + const auto samplesPerChannel2 { samplesPerChannel - samplesPerChannel1 }; + std::vector buffers2; + buffers2.reserve (channelCount); + for (auto i { 0U }; i < channelCount; ++i) + buffers2.emplace_back (static_cast (buffers[i]) + static_cast (samplesPerChannel1) * remoteAudioReader->sampleSize); + + if (result1) + { + return readAudioSamples (audioReaderHostRef, samplePosition + samplesPerChannel1, samplesPerChannel2, buffers2.data ()); + } + else + { + for (auto i { 0U }; i < channelCount; ++i) + std::memset (buffers2[i], 0, static_cast (samplesPerChannel2) * remoteAudioReader->sampleSize); + return false; + } + } + + // custom decoding to deal with float data memory ownership + bool success { false }; + CustomDecodeFunction customDecode { + [&success, &remoteAudioReader, &samplesPerChannel, &channelCount, &buffers] (const MessageDecoder* decoder) -> void + { + const auto bufferSize { remoteAudioReader->sampleSize * static_cast (samplesPerChannel) }; + std::vector resultSizes; + std::vector decoders; + resultSizes.reserve (channelCount); + decoders.reserve (channelCount); + for (auto i { 0U }; i < channelCount; ++i) + { + resultSizes.emplace_back (bufferSize); + decoders.emplace_back (static_cast (buffers[i]), resultSizes[i]); + } + + ArrayArgument channelData { decoders.data (), decoders.size () }; + success = decodeReply (channelData, decoder); + if (success) + ARA_INTERNAL_ASSERT (channelData.count == channelCount); + + for (auto i { 0U }; i < channelCount; ++i) + { + if (success) + { + ARA_INTERNAL_ASSERT (resultSizes[i] == bufferSize); + if (remoteAudioReader->swapFunction) + remoteAudioReader->swapFunction (buffers[i], samplesPerChannel); + } + else + { + std::memset (buffers[i], 0, bufferSize); + } + } + + } }; + remoteCall (customDecode, ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, readAudioSamples), + _remoteHostRef, remoteAudioReader->mainHostRef, samplePosition, samplesPerChannel); + return success; +} + +void AudioAccessController::destroyAudioReader (ARAAudioReaderHostRef audioReaderHostRef) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + auto remoteAudioReader { fromHostRef (audioReaderHostRef) }; + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, destroyAudioReader), _remoteHostRef, remoteAudioReader->mainHostRef); + delete remoteAudioReader; +} + + +/*******************************************************************************/ +//! Implementation of ArchivingControllerInterface that channels all calls through IPC +class ArchivingController : public Host::ArchivingControllerInterface, protected RemoteCaller +{ +public: + ArchivingController (Connection* connection, ARAArchivingControllerHostRef remoteHostRef) noexcept + : RemoteCaller { connection }, _remoteHostRef { remoteHostRef } {} + + ARASize getArchiveSize (ARAArchiveReaderHostRef archiveReaderHostRef) noexcept override; + bool readBytesFromArchive (ARAArchiveReaderHostRef archiveReaderHostRef, ARASize position, ARASize length, ARAByte buffer[]) noexcept override; + bool writeBytesToArchive (ARAArchiveWriterHostRef archiveWriterHostRef, ARASize position, ARASize length, const ARAByte buffer[]) noexcept override; + void notifyDocumentArchivingProgress (float value) noexcept override; + void notifyDocumentUnarchivingProgress (float value) noexcept override; + ARAPersistentID getDocumentArchiveID (ARAArchiveReaderHostRef archiveReaderHostRef) noexcept override; + +private: + ARAArchivingControllerHostRef _remoteHostRef; + std::string _archiveID; +}; + +/*******************************************************************************/ + +ARASize ArchivingController::getArchiveSize (ARAArchiveReaderHostRef archiveReaderHostRef) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARASize size; + remoteCall (size, ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, getArchiveSize), _remoteHostRef, archiveReaderHostRef); + return size; +} + +bool ArchivingController::readBytesFromArchive (ARAArchiveReaderHostRef archiveReaderHostRef, ARASize position, ARASize length, ARAByte buffer[]) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + // recursively limit message size to keep IPC responsive + if (length > 131072) + { + const auto length1 { length / 2 }; + const auto result1 { readBytesFromArchive (archiveReaderHostRef, position, length1, buffer) }; + + const auto length2 { length - length1 }; + buffer += length1; + if (result1) + { + return readBytesFromArchive (archiveReaderHostRef, position + length1, length2, buffer); + } + else + { + std::memset (buffer, 0, length2); + return false; + } + } + + auto resultLength { length }; + BytesDecoder writer { buffer, resultLength }; + remoteCall (writer, ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, readBytesFromArchive), + _remoteHostRef, archiveReaderHostRef, position, length); + if (resultLength == length) + { + return true; + } + else + { + std::memset (buffer, 0, length); + return false; + } +} + +bool ArchivingController::writeBytesToArchive (ARAArchiveWriterHostRef archiveWriterHostRef, ARASize position, ARASize length, const ARAByte buffer[]) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + // recursively limit message size to keep IPC responsive + if (length > 131072) + { + const auto length1 { length / 2 }; + const auto result1 { writeBytesToArchive (archiveWriterHostRef, position, length1, buffer) }; + + const auto length2 { length - length1 }; + buffer += length1; + if (result1) + return writeBytesToArchive (archiveWriterHostRef, position + length1, length2, buffer); + else + return false; + } + + ARABool success; + remoteCall (success, ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, writeBytesToArchive), + _remoteHostRef, archiveWriterHostRef, position, BytesEncoder { buffer, length, false }); + return (success != kARAFalse); +} + +void ArchivingController::notifyDocumentArchivingProgress (float value) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentArchivingProgress), _remoteHostRef, value); +} + +void ArchivingController::notifyDocumentUnarchivingProgress (float value) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress), _remoteHostRef, value); +} + +ARAPersistentID ArchivingController::getDocumentArchiveID (ARAArchiveReaderHostRef archiveReaderHostRef) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + CustomDecodeFunction customDecode { + [this] (const MessageDecoder* decoder) -> void + { + ARAPersistentID persistentID; + decodeReply (persistentID, decoder); + _archiveID.assign (persistentID); + } }; + remoteCall (customDecode, ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, getDocumentArchiveID), _remoteHostRef, archiveReaderHostRef); + return _archiveID.c_str (); +} + + +/*******************************************************************************/ +//! Implementation of ContentAccessControllerInterface that channels all calls through IPC +class ContentAccessController : public Host::ContentAccessControllerInterface, protected RemoteCaller +{ +public: + ContentAccessController (Connection* connection, ARAContentAccessControllerHostRef remoteHostRef) noexcept + : RemoteCaller { connection }, _remoteHostRef { remoteHostRef } {} + + bool isMusicalContextContentAvailable (ARAMusicalContextHostRef musicalContextHostRef, ARAContentType type) noexcept override; + ARAContentGrade getMusicalContextContentGrade (ARAMusicalContextHostRef musicalContextHostRef, ARAContentType type) noexcept override; + ARAContentReaderHostRef createMusicalContextContentReader (ARAMusicalContextHostRef musicalContextHostRef, ARAContentType type, const ARAContentTimeRange* range) noexcept override; + bool isAudioSourceContentAvailable (ARAAudioSourceHostRef audioSourceHostRef, ARAContentType type) noexcept override; + ARAContentGrade getAudioSourceContentGrade (ARAAudioSourceHostRef audioSourceHostRef, ARAContentType type) noexcept override; + ARAContentReaderHostRef createAudioSourceContentReader (ARAAudioSourceHostRef audioSourceHostRef, ARAContentType type, const ARAContentTimeRange* range) noexcept override; + ARAInt32 getContentReaderEventCount (ARAContentReaderHostRef contentReaderHostRef) noexcept override; + const void* getContentReaderDataForEvent (ARAContentReaderHostRef contentReaderHostRef, ARAInt32 eventIndex) noexcept override; + void destroyContentReader (ARAContentReaderHostRef contentReaderHostRef) noexcept override; + +private: + ARAContentAccessControllerHostRef _remoteHostRef; +}; + +/*******************************************************************************/ + +bool ContentAccessController::isMusicalContextContentAvailable (ARAMusicalContextHostRef musicalContextHostRef, ARAContentType type) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARABool result; + remoteCall (result, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, isMusicalContextContentAvailable), + _remoteHostRef, musicalContextHostRef, type); + return (result != kARAFalse); +} + +ARAContentGrade ContentAccessController::getMusicalContextContentGrade (ARAMusicalContextHostRef musicalContextHostRef, ARAContentType type) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARAContentGrade grade; + remoteCall (grade, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getMusicalContextContentGrade), + _remoteHostRef, musicalContextHostRef, type); + return grade; +} + +ARAContentReaderHostRef ContentAccessController::createMusicalContextContentReader (ARAMusicalContextHostRef musicalContextHostRef, ARAContentType type, const ARAContentTimeRange* range) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARAContentReaderHostRef contentReaderHostRef; + remoteCall (contentReaderHostRef, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, createMusicalContextContentReader), + _remoteHostRef, musicalContextHostRef, type, range); + auto contentReader { new RemoteHostContentReader (contentReaderHostRef, type) }; + return toHostRef (contentReader); +} + +bool ContentAccessController::isAudioSourceContentAvailable (ARAAudioSourceHostRef audioSourceHostRef, ARAContentType type) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARABool result; + remoteCall (result, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, isAudioSourceContentAvailable), + _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, type); + return (result != kARAFalse); +} + +ARAContentGrade ContentAccessController::getAudioSourceContentGrade (ARAAudioSourceHostRef audioSourceHostRef, ARAContentType type) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARAContentGrade grade; + remoteCall (grade, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getAudioSourceContentGrade), + _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, type); + return grade; +} + +ARAContentReaderHostRef ContentAccessController::createAudioSourceContentReader (ARAAudioSourceHostRef audioSourceHostRef, ARAContentType type, const ARAContentTimeRange* range) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARAContentReaderHostRef contentReaderHostRef; + remoteCall (contentReaderHostRef, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, createAudioSourceContentReader), + _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, type, range); + auto contentReader { new RemoteHostContentReader (contentReaderHostRef, type) }; + return toHostRef (contentReader); +} + +ARAInt32 ContentAccessController::getContentReaderEventCount (ARAContentReaderHostRef contentReaderHostRef) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto contentReader { fromHostRef (contentReaderHostRef) }; + ARAInt32 count; + remoteCall (count, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderEventCount), + _remoteHostRef, contentReader->remoteHostRef); + return count; +} + +const void* ContentAccessController::getContentReaderDataForEvent (ARAContentReaderHostRef contentReaderHostRef, ARAInt32 eventIndex) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto contentReader { fromHostRef (contentReaderHostRef) }; + const void* result {}; + CustomDecodeFunction customDecode { + [&result, &contentReader] (const MessageDecoder* decoder) -> void + { + result = contentReader->decoder.decode (decoder); + } }; + remoteCall (customDecode, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderDataForEvent), + _remoteHostRef, contentReader->remoteHostRef, eventIndex); + return result; +} + +void ContentAccessController::destroyContentReader (ARAContentReaderHostRef contentReaderHostRef) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto contentReader { fromHostRef (contentReaderHostRef) }; + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, destroyContentReader), _remoteHostRef, contentReader->remoteHostRef); + delete contentReader; +} + + +/*******************************************************************************/ +//! Implementation of ModelUpdateControllerInterface that channels all calls through IPC +class ModelUpdateController : public Host::ModelUpdateControllerInterface, protected RemoteCaller +{ +public: + ModelUpdateController (Connection* connection, ARAModelUpdateControllerHostRef remoteHostRef) noexcept + : RemoteCaller { connection }, _remoteHostRef { remoteHostRef } {} + + void notifyAudioSourceAnalysisProgress (ARAAudioSourceHostRef audioSourceHostRef, ARAAnalysisProgressState state, float value) noexcept override; + void notifyAudioSourceContentChanged (ARAAudioSourceHostRef audioSourceHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept override; + void notifyAudioModificationContentChanged (ARAAudioModificationHostRef audioModificationHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept override; + void notifyPlaybackRegionContentChanged (ARAPlaybackRegionHostRef playbackRegionHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept override; + void notifyDocumentDataChanged () noexcept override; + +private: + ARAModelUpdateControllerHostRef _remoteHostRef; +}; + +/*******************************************************************************/ + +void ModelUpdateController::notifyAudioSourceAnalysisProgress (ARAAudioSourceHostRef audioSourceHostRef, ARAAnalysisProgressState state, float value) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress), + _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, state, value); +} + +void ModelUpdateController::notifyAudioSourceContentChanged (ARAAudioSourceHostRef audioSourceHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged), + _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, range, scopeFlags); +} + +void ModelUpdateController::notifyAudioModificationContentChanged (ARAAudioModificationHostRef audioModificationHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged), + _remoteHostRef, audioModificationHostRef, range, scopeFlags); +} + +void ModelUpdateController::notifyPlaybackRegionContentChanged (ARAPlaybackRegionHostRef playbackRegionHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged), + _remoteHostRef, playbackRegionHostRef, range, scopeFlags); +} + +void ModelUpdateController::notifyDocumentDataChanged () noexcept +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyDocumentDataChanged), + _remoteHostRef); +} + + +/*******************************************************************************/ +//! Implementation of PlaybackControllerInterface that channels all calls through IPC +class PlaybackController : public Host::PlaybackControllerInterface, protected RemoteCaller +{ +public: + PlaybackController (Connection* connection, ARAPlaybackControllerHostRef remoteHostRef) noexcept + : RemoteCaller { connection }, _remoteHostRef { remoteHostRef } {} + + void requestStartPlayback () noexcept override; + void requestStopPlayback () noexcept override; + void requestSetPlaybackPosition (ARATimePosition timePosition) noexcept override; + void requestSetCycleRange (ARATimePosition startTime, ARATimeDuration duration) noexcept override; + void requestEnableCycle (bool enable) noexcept override; + +private: + ARAPlaybackControllerHostRef _remoteHostRef; +}; + +/*******************************************************************************/ + +void PlaybackController::requestStartPlayback () noexcept +{ +// this function can be called from other threads! +// ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestStartPlayback), _remoteHostRef); +} + +void PlaybackController::requestStopPlayback () noexcept +{ +// this function can be called from other threads! +// ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestStopPlayback), _remoteHostRef); +} + +void PlaybackController::requestSetPlaybackPosition (ARATimePosition timePosition) noexcept +{ +// this function can be called from other threads! +// ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestSetPlaybackPosition), _remoteHostRef, timePosition); +} + +void PlaybackController::requestSetCycleRange (ARATimePosition startTime, ARATimeDuration duration) noexcept +{ +// this function can be called from other threads! +// ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestSetCycleRange), _remoteHostRef, startTime, duration); +} + +void PlaybackController::requestEnableCycle (bool enable) noexcept +{ +// this function can be called from other threads! +// ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestEnableCycle), _remoteHostRef, (enable) ? kARATrue : kARAFalse); +} + + +/*******************************************************************************/ +//! Extension of Host::DocumentController that also stores the host instance visible to the plug-in +class DocumentController : public Host::DocumentController +{ +public: + explicit DocumentController (const Host::DocumentControllerHostInstance* hostInstance, const ARADocumentControllerInstance* instance) noexcept + : Host::DocumentController { instance }, + _hostInstance { hostInstance } + {} + + const Host::DocumentControllerHostInstance* getHostInstance () { return _hostInstance; } + +private: + const Host::DocumentControllerHostInstance* _hostInstance; +}; +ARA_MAP_REF (DocumentController, ARADocumentControllerRef) + + +/*******************************************************************************/ +//! Wrapper class for a plug-in extension instance that can forward remote calls to its sub-interfaces +class PlugInExtension +{ +public: + explicit PlugInExtension (const ARAPlugInExtensionInstance* instance) + : _playbackRenderer { instance }, + _editorRenderer { instance }, + _editorView { instance } + {} + + // Getters for ARA specific plug-in role interfaces + Host::PlaybackRenderer* getPlaybackRenderer () { return &_playbackRenderer; } + Host::EditorRenderer* getEditorRenderer () { return &_editorRenderer; } + Host::EditorView* getEditorView () { return &_editorView; } + +private: + Host::PlaybackRenderer _playbackRenderer; + Host::EditorRenderer _editorRenderer; + Host::EditorView _editorView; +}; +ARA_MAP_REF (PlugInExtension, ARAPlugInExtensionRef, ARAPlaybackRendererRef, ARAEditorRendererRef, ARAEditorViewRef) + + +/*******************************************************************************/ + +} // namespace ProxyHostImpl +using namespace ProxyHostImpl; + +/*******************************************************************************/ + + +std::vector _factories {}; +ARAIPCBindingHandler _bindingHandler {}; + +void ARAIPCProxyHostAddFactory (const ARAFactory* factory) +{ + ARA_INTERNAL_ASSERT (factory->highestSupportedApiGeneration >= kARAAPIGeneration_2_0_Final); + ARA_INTERNAL_ASSERT (!ARA::contains (_factories, factory)); + + _factories.emplace_back (factory); +} + +void ARAIPCProxyHostSetBindingHandler (ARAIPCBindingHandler handler) +{ + _bindingHandler = handler; +} + +const ARAFactory* getFactoryWithID (ARAPersistentID factoryID) +{ + for (const auto& factory : _factories) + { + if (0 == std::strcmp (factory->factoryID, factoryID)) + return factory; + } + + ARA_INTERNAL_ASSERT (false && "provided factory ID not previously registered via ARAIPCProxyHostAddFactory()"); + return nullptr; +} + + +/*******************************************************************************/ + +ProxyHost::ProxyHost (Connection* connection) +: RemoteCaller (connection) +{} + +void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, + MessageEncoder* const replyEncoder) +{ + ARA_IPC_LOG ("ProxyHost handles '%s'", decodeHostMessageID (messageID)); + + // ARAFactory + if (messageID == kGetFactoriesCountMethodID) + { + encodeReply (replyEncoder, _factories.size ()); + } + else if (messageID == kGetFactoryMethodID) + { + ARASize index; + decodeArguments (decoder, index); + ARA_INTERNAL_ASSERT (index < _factories.size ()); + encodeReply (replyEncoder, *_factories[index]); + } + else if (messageID == kInitializeARAMethodID) + { + ARAPersistentID factoryID; + ARA::SizedStruct interfaceConfig = { kARAAPIGeneration_2_0_Final, nullptr }; + decodeArguments (decoder, factoryID, interfaceConfig.desiredApiGeneration); + ARA_INTERNAL_ASSERT (interfaceConfig.desiredApiGeneration >= kARAAPIGeneration_2_0_Final); + + if (const ARAFactory* const factory { getFactoryWithID (factoryID) }) + factory->initializeARAWithConfiguration (&interfaceConfig); + } + else if (messageID == kCreateDocumentControllerMethodID) + { + ARAPersistentID factoryID; + ARAAudioAccessControllerHostRef audioAccessControllerHostRef; + ARAArchivingControllerHostRef archivingControllerHostRef; + ARABool provideContentAccessController; + ARAContentAccessControllerHostRef contentAccessControllerHostRef; + ARABool provideModelUpdateController; + ARAModelUpdateControllerHostRef modelUpdateControllerHostRef; + ARABool providePlaybackController; + ARAPlaybackControllerHostRef playbackControllerHostRef; + ARADocumentProperties properties; + decodeArguments (decoder, factoryID, + audioAccessControllerHostRef, archivingControllerHostRef, + provideContentAccessController, contentAccessControllerHostRef, + provideModelUpdateController, modelUpdateControllerHostRef, + providePlaybackController, playbackControllerHostRef, + properties); + + if (const ARAFactory* const factory { getFactoryWithID (factoryID) }) + { + const auto audioAccessController { new AudioAccessController { getConnection (), audioAccessControllerHostRef } }; + const auto archivingController { new ArchivingController { getConnection (), archivingControllerHostRef } }; + const auto contentAccessController { (provideContentAccessController != kARAFalse) ? new ContentAccessController { getConnection (), contentAccessControllerHostRef } : nullptr }; + const auto modelUpdateController { (provideModelUpdateController != kARAFalse) ? new ModelUpdateController { getConnection (), modelUpdateControllerHostRef } : nullptr }; + const auto playbackController { (providePlaybackController != kARAFalse) ? new PlaybackController { getConnection (), playbackControllerHostRef } : nullptr }; + + const auto hostInstance { new Host::DocumentControllerHostInstance { audioAccessController, archivingController, + contentAccessController, modelUpdateController, playbackController } }; + + auto documentControllerInstance { factory->createDocumentControllerWithDocument (hostInstance, &properties) }; + ARA_VALIDATE_API_CONDITION (documentControllerInstance != nullptr); + ARA_VALIDATE_API_INTERFACE (documentControllerInstance->documentControllerInterface, ARADocumentControllerInterface); + auto documentController { new DocumentController (hostInstance, documentControllerInstance) }; + encodeReply (replyEncoder, ARADocumentControllerRef { toRef (documentController) }); + } + } + else if (messageID == kBindToDocumentControllerMethodID) + { + ARAIPCPlugInInstanceRef plugInInstanceRef; + ARADocumentControllerRef controllerRef; + ARAPlugInInstanceRoleFlags knownRoles; + ARAPlugInInstanceRoleFlags assignedRoles; + decodeArguments (decoder, plugInInstanceRef, controllerRef, knownRoles, assignedRoles); + const auto plugInExtensionInstance { _bindingHandler (plugInInstanceRef, fromRef (controllerRef)->getRef (), knownRoles, assignedRoles) }; + const ARAPlugInExtensionRef plugInExtensionRef { toRef ( new PlugInExtension { plugInExtensionInstance } )}; + ARA_INTERNAL_ASSERT (plugInExtensionInstance->plugInExtensionRef == nullptr); // plugInExtensionRef must not be used when ARA 2 is active + const_cast (plugInExtensionInstance)->plugInExtensionRef = plugInExtensionRef; + encodeReply (replyEncoder, plugInExtensionRef ); + } + else if (messageID == kCleanupBindingMethodID) + { + ARAPlugInExtensionRef plugInExtensionRef; + decodeArguments (decoder, plugInExtensionRef); + delete fromRef (plugInExtensionRef); + } + else if (messageID == kUninitializeARAMethodID) + { + ARAPersistentID factoryID; + decodeArguments (decoder, factoryID); + + if (const ARAFactory* const factory { getFactoryWithID (factoryID) }) + factory->uninitializeARA (); + } + + //ARADocumentControllerInterface + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyDocumentController)) + { + ARADocumentControllerRef controllerRef; + decodeArguments (decoder, controllerRef); + auto documentController { fromRef (controllerRef) }; + documentController->destroyDocumentController (); + + delete documentController->getHostInstance ()->getPlaybackController (); + delete documentController->getHostInstance ()->getModelUpdateController (); + delete documentController->getHostInstance ()->getContentAccessController (); + delete documentController->getHostInstance ()->getArchivingController (); + delete documentController->getHostInstance ()->getAudioAccessController (); + delete documentController->getHostInstance (); + delete documentController; + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getFactory)) + { + ARA_INTERNAL_ASSERT (false && "should never be queried here but instead cached from companion API upon setup"); + + ARADocumentControllerRef controllerRef; + decodeArguments (decoder, controllerRef); + + encodeReply (replyEncoder, *(fromRef (controllerRef)->getFactory ())); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, beginEditing)) + { + ARADocumentControllerRef controllerRef; + decodeArguments (decoder, controllerRef); + + fromRef (controllerRef)->beginEditing (); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, endEditing)) + { + ARADocumentControllerRef controllerRef; + decodeArguments (decoder, controllerRef); + + fromRef (controllerRef)->endEditing (); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, notifyModelUpdates)) + { + ARADocumentControllerRef controllerRef; + decodeArguments (decoder, controllerRef); + + fromRef (controllerRef)->notifyModelUpdates (); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, restoreObjectsFromArchive)) + { + ARADocumentControllerRef controllerRef; + ARAArchiveReaderHostRef archiveReaderHostRef; + OptionalArgument filter; + decodeArguments (decoder, controllerRef, archiveReaderHostRef, filter); + + encodeReply (replyEncoder, fromRef (controllerRef)->restoreObjectsFromArchive (archiveReaderHostRef, (filter.second) ? &filter.first : nullptr) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, storeObjectsToArchive)) + { + ARADocumentControllerRef controllerRef; + ARAArchiveWriterHostRef archiveWriterHostRef; + OptionalArgument filter; + decodeArguments (decoder, controllerRef, archiveWriterHostRef, filter); + + std::vector audioSourceRefs; + if (filter.second && (filter.first.audioSourceRefsCount > 0)) + { + audioSourceRefs.reserve (filter.first.audioSourceRefsCount); + for (auto i { 0U }; i < filter.first.audioSourceRefsCount; ++i) + audioSourceRefs.emplace_back (fromRef (filter.first.audioSourceRefs[i])->plugInRef); + + filter.first.audioSourceRefs = audioSourceRefs.data (); + } + + encodeReply (replyEncoder, fromRef (controllerRef)->storeObjectsToArchive (archiveWriterHostRef, (filter.second) ? &filter.first : nullptr) ? kARATrue : kARAFalse); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateDocumentProperties)) + { + ARADocumentControllerRef controllerRef; + ARADocumentProperties properties; + decodeArguments (decoder, controllerRef, properties); + + fromRef (controllerRef)->updateDocumentProperties (&properties); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createMusicalContext)) + { + ARADocumentControllerRef controllerRef; + ARAMusicalContextHostRef hostRef; + ARAMusicalContextProperties properties; + decodeArguments (decoder, controllerRef, hostRef, properties); + + encodeReply (replyEncoder, fromRef (controllerRef)->createMusicalContext (hostRef, &properties)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextProperties)) + { + ARADocumentControllerRef controllerRef; + ARAMusicalContextRef musicalContextRef; + ARAMusicalContextProperties properties; + decodeArguments (decoder, controllerRef, musicalContextRef, properties); + + fromRef (controllerRef)->updateMusicalContextProperties (musicalContextRef, &properties); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextContent)) + { + ARADocumentControllerRef controllerRef; + ARAMusicalContextRef musicalContextRef; + OptionalArgument range; + ARAContentUpdateFlags flags; + decodeArguments (decoder, controllerRef, musicalContextRef, range, flags); + + fromRef (controllerRef)->updateMusicalContextContent (musicalContextRef, (range.second) ? &range.first : nullptr, flags); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyMusicalContext)) + { + ARADocumentControllerRef controllerRef; + ARAMusicalContextRef musicalContextRef; + decodeArguments (decoder, controllerRef, musicalContextRef); + + fromRef (controllerRef)->destroyMusicalContext (musicalContextRef); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createRegionSequence)) + { + ARADocumentControllerRef controllerRef; + ARARegionSequenceHostRef hostRef; + ARARegionSequenceProperties properties; + decodeArguments (decoder, controllerRef, hostRef, properties); + + encodeReply (replyEncoder, fromRef (controllerRef)->createRegionSequence (hostRef, &properties)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateRegionSequenceProperties)) + { + ARADocumentControllerRef controllerRef; + ARARegionSequenceRef regionSequenceRef; + ARARegionSequenceProperties properties; + decodeArguments (decoder, controllerRef, regionSequenceRef, properties); + + fromRef (controllerRef)->updateRegionSequenceProperties (regionSequenceRef, &properties); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyRegionSequence)) + { + ARADocumentControllerRef controllerRef; + ARARegionSequenceRef regionSequenceRef; + decodeArguments (decoder, controllerRef, regionSequenceRef); + + fromRef (controllerRef)->destroyRegionSequence (regionSequenceRef); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioSource)) + { + auto remoteAudioSource { new RemoteAudioSource }; + + ARADocumentControllerRef controllerRef; + ARAAudioSourceProperties properties; + decodeArguments (decoder, controllerRef, remoteAudioSource->mainHostRef, properties); + + remoteAudioSource->channelCount = properties.channelCount; + remoteAudioSource->plugInRef = fromRef (controllerRef)->createAudioSource (toHostRef (remoteAudioSource), &properties); + + encodeReply (replyEncoder, ARAAudioSourceRef { toRef (remoteAudioSource) }); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceProperties)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARAAudioSourceProperties properties; + decodeArguments (decoder, controllerRef, audioSourceRef, properties); + + fromRef (controllerRef)->updateAudioSourceProperties (fromRef (audioSourceRef)->plugInRef, &properties); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceContent)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + OptionalArgument range; + ARAContentUpdateFlags flags; + decodeArguments (decoder, controllerRef, audioSourceRef, range, flags); + + fromRef (controllerRef)->updateAudioSourceContent (fromRef (audioSourceRef)->plugInRef, (range.second) ? &range.first : nullptr, flags); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, enableAudioSourceSamplesAccess)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARABool enable; + decodeArguments (decoder, controllerRef, audioSourceRef, enable); + + fromRef (controllerRef)->enableAudioSourceSamplesAccess (fromRef (audioSourceRef)->plugInRef, (enable) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARABool deactivate; + decodeArguments (decoder, controllerRef, audioSourceRef, deactivate); + + fromRef (controllerRef)->deactivateAudioSourceForUndoHistory (fromRef (audioSourceRef)->plugInRef, (deactivate) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk)) + { + ARADocumentControllerRef controllerRef; + ARAArchiveWriterHostRef archiveWriterHostRef; + ARAAudioSourceRef audioSourceRef; + decodeArguments (decoder, controllerRef, archiveWriterHostRef, audioSourceRef); + + StoreAudioSourceToAudioFileChunkReply reply; + bool openAutomatically; + reply.result = (fromRef (controllerRef)->storeAudioSourceToAudioFileChunk (archiveWriterHostRef, fromRef (audioSourceRef)->plugInRef, + &reply.documentArchiveID, &openAutomatically)) ? kARATrue : kARAFalse; + reply.openAutomatically = (openAutomatically) ? kARATrue : kARAFalse; + encodeReply (replyEncoder, reply); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARAContentType contentType; + decodeArguments (decoder, controllerRef, audioSourceRef, contentType); + + encodeReply (replyEncoder, fromRef (controllerRef)->isAudioSourceContentAnalysisIncomplete (fromRef (audioSourceRef)->plugInRef, contentType)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, requestAudioSourceContentAnalysis)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + std::vector contentTypes; + decodeArguments (decoder, controllerRef, audioSourceRef, contentTypes); + + fromRef (controllerRef)->requestAudioSourceContentAnalysis (fromRef (audioSourceRef)->plugInRef, contentTypes.size (), contentTypes.data ()); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAvailable)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARAContentType contentType; + decodeArguments (decoder, controllerRef, audioSourceRef, contentType); + + encodeReply (replyEncoder, (fromRef (controllerRef)->isAudioSourceContentAvailable (fromRef (audioSourceRef)->plugInRef, contentType)) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getAudioSourceContentGrade)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARAContentType contentType; + decodeArguments (decoder, controllerRef, audioSourceRef, contentType); + + encodeReply (replyEncoder, fromRef (controllerRef)->getAudioSourceContentGrade (fromRef (audioSourceRef)->plugInRef, contentType)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioSourceContentReader)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARAContentType contentType; + OptionalArgument range; + decodeArguments (decoder, controllerRef, audioSourceRef, contentType, range); + + auto remoteContentReader { new RemoteContentReader }; + remoteContentReader->plugInRef = fromRef (controllerRef)->createAudioSourceContentReader (fromRef (audioSourceRef)->plugInRef, contentType, (range.second) ? &range.first : nullptr); + remoteContentReader->contentType = contentType; + encodeReply (replyEncoder, ARAContentReaderRef { toRef (remoteContentReader) }); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyAudioSource)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + decodeArguments (decoder, controllerRef, audioSourceRef); + + auto remoteAudioSource { fromRef (audioSourceRef) }; + fromRef (controllerRef)->destroyAudioSource (remoteAudioSource->plugInRef); + + delete remoteAudioSource; + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioModification)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARAAudioModificationHostRef hostRef; + ARAAudioModificationProperties properties; + decodeArguments (decoder, controllerRef, audioSourceRef, hostRef, properties); + + encodeReply (replyEncoder, fromRef (controllerRef)->createAudioModification (fromRef (audioSourceRef)->plugInRef, hostRef, &properties)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, cloneAudioModification)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + ARAAudioModificationHostRef hostRef; + ARAAudioModificationProperties properties; + decodeArguments (decoder, controllerRef, audioModificationRef, hostRef, properties); + + encodeReply (replyEncoder, fromRef (controllerRef)->cloneAudioModification (audioModificationRef, hostRef, &properties)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioModificationProperties)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + ARAAudioModificationProperties properties; + decodeArguments (decoder, controllerRef, audioModificationRef, properties); + + fromRef (controllerRef)->updateAudioModificationProperties (audioModificationRef, &properties); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + decodeArguments (decoder, controllerRef, audioModificationRef); + + encodeReply (replyEncoder, (fromRef (controllerRef)->isAudioModificationPreservingAudioSourceSignal (audioModificationRef)) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + ARABool deactivate; + decodeArguments (decoder, controllerRef, audioModificationRef, deactivate); + + fromRef (controllerRef)->deactivateAudioModificationForUndoHistory (audioModificationRef, (deactivate) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioModificationContentAvailable)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + ARAContentType contentType; + decodeArguments (decoder, controllerRef, audioModificationRef, contentType); + + encodeReply (replyEncoder, (fromRef (controllerRef)->isAudioModificationContentAvailable (audioModificationRef, contentType)) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getAudioModificationContentGrade)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + ARAContentType contentType; + decodeArguments (decoder, controllerRef, audioModificationRef, contentType); + + encodeReply (replyEncoder, fromRef (controllerRef)->getAudioModificationContentGrade (audioModificationRef, contentType)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioModificationContentReader)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + ARAContentType contentType; + OptionalArgument range; + decodeArguments (decoder, controllerRef, audioModificationRef, contentType, range); + + auto remoteContentReader { new RemoteContentReader }; + remoteContentReader->plugInRef = fromRef (controllerRef)->createAudioModificationContentReader (audioModificationRef, contentType, (range.second) ? &range.first : nullptr); + remoteContentReader->contentType = contentType; + encodeReply (replyEncoder, ARAContentReaderRef { toRef (remoteContentReader) }); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyAudioModification)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + decodeArguments (decoder, controllerRef, audioModificationRef); + + fromRef (controllerRef)->destroyAudioModification (audioModificationRef); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegion)) + { + ARADocumentControllerRef controllerRef; + ARAAudioModificationRef audioModificationRef; + ARAPlaybackRegionHostRef hostRef; + ARAPlaybackRegionProperties properties; + decodeArguments (decoder, controllerRef, audioModificationRef, hostRef, properties); + + encodeReply (replyEncoder, fromRef (controllerRef)->createPlaybackRegion (audioModificationRef, hostRef, &properties)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updatePlaybackRegionProperties)) + { + ARADocumentControllerRef controllerRef; + ARAPlaybackRegionRef playbackRegionRef; + ARAPlaybackRegionProperties properties; + decodeArguments (decoder, controllerRef, playbackRegionRef, properties); + + fromRef (controllerRef)->updatePlaybackRegionProperties (playbackRegionRef, &properties); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime)) + { + ARADocumentControllerRef controllerRef; + ARAPlaybackRegionRef playbackRegionRef; + ARABool wantsHeadTime; + ARABool wantsTailTime; + decodeArguments (decoder, controllerRef, playbackRegionRef, wantsHeadTime, wantsTailTime); + + GetPlaybackRegionHeadAndTailTimeReply reply { 0.0, 0.0 }; + fromRef (controllerRef)->getPlaybackRegionHeadAndTailTime (playbackRegionRef, (wantsHeadTime != kARAFalse) ? &reply.headTime : nullptr, + (wantsTailTime != kARAFalse) ? &reply.tailTime : nullptr); + encodeReply (replyEncoder, reply); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isPlaybackRegionContentAvailable)) + { + ARADocumentControllerRef controllerRef; + ARAPlaybackRegionRef playbackRegionRef; + ARAContentType contentType; + decodeArguments (decoder, controllerRef, playbackRegionRef, contentType); + + encodeReply (replyEncoder, (fromRef (controllerRef)->isPlaybackRegionContentAvailable (playbackRegionRef, contentType)) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionContentGrade)) + { + ARADocumentControllerRef controllerRef; + ARAPlaybackRegionRef playbackRegionRef; + ARAContentType contentType; + decodeArguments (decoder, controllerRef, playbackRegionRef, contentType); + + encodeReply (replyEncoder, fromRef (controllerRef)->getPlaybackRegionContentGrade (playbackRegionRef, contentType)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegionContentReader)) + { + ARADocumentControllerRef controllerRef; + ARAPlaybackRegionRef playbackRegionRef; + ARAContentType contentType; + OptionalArgument range; + decodeArguments (decoder, controllerRef, playbackRegionRef, contentType, range); + + auto remoteContentReader { new RemoteContentReader }; + remoteContentReader->plugInRef = fromRef (controllerRef)->createPlaybackRegionContentReader (playbackRegionRef, contentType, (range.second) ? &range.first : nullptr); + remoteContentReader->contentType = contentType; + encodeReply (replyEncoder, ARAContentReaderRef { toRef (remoteContentReader) }); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyPlaybackRegion)) + { + ARADocumentControllerRef controllerRef; + ARAPlaybackRegionRef playbackRegionRef; + decodeArguments (decoder, controllerRef, playbackRegionRef); + + fromRef (controllerRef)->destroyPlaybackRegion (playbackRegionRef); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getContentReaderEventCount)) + { + ARADocumentControllerRef controllerRef; + ARAContentReaderRef contentReaderRef; + decodeArguments (decoder, controllerRef, contentReaderRef); + + encodeReply (replyEncoder, fromRef (controllerRef)->getContentReaderEventCount (fromRef (contentReaderRef)->plugInRef)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getContentReaderDataForEvent)) + { + ARADocumentControllerRef controllerRef; + ARAContentReaderRef contentReaderRef; + ARAInt32 eventIndex; + decodeArguments (decoder, controllerRef, contentReaderRef, eventIndex); + + auto remoteContentReader { fromRef (contentReaderRef) }; + const void* eventData { fromRef (controllerRef)->getContentReaderDataForEvent (remoteContentReader->plugInRef, eventIndex) }; + encodeContentEvent (replyEncoder, remoteContentReader->contentType, eventData); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyContentReader)) + { + ARADocumentControllerRef controllerRef; + ARAContentReaderRef contentReaderRef; + decodeArguments (decoder, controllerRef, contentReaderRef); + + auto remoteContentReader { fromRef (contentReaderRef) }; + fromRef (controllerRef)->destroyContentReader (remoteContentReader->plugInRef); + + delete remoteContentReader; + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmsCount)) + { + ARADocumentControllerRef controllerRef; + decodeArguments (decoder, controllerRef); + + encodeReply (replyEncoder, fromRef (controllerRef)->getProcessingAlgorithmsCount ()); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmProperties)) + { + ARADocumentControllerRef controllerRef; + ARAInt32 algorithmIndex; + decodeArguments (decoder, controllerRef, algorithmIndex); + + encodeReply (replyEncoder, *(fromRef (controllerRef)->getProcessingAlgorithmProperties (algorithmIndex))); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + decodeArguments (decoder, controllerRef, audioSourceRef); + + encodeReply (replyEncoder, fromRef (controllerRef)->getProcessingAlgorithmForAudioSource (fromRef (audioSourceRef)->plugInRef)); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource)) + { + ARADocumentControllerRef controllerRef; + ARAAudioSourceRef audioSourceRef; + ARAInt32 algorithmIndex; + decodeArguments (decoder, controllerRef, audioSourceRef, algorithmIndex); + + fromRef (controllerRef)->requestProcessingAlgorithmForAudioSource (fromRef (audioSourceRef)->plugInRef, algorithmIndex); + } + + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isLicensedForCapabilities)) + { + ARADocumentControllerRef controllerRef; + ARABool runModalActivationDialogIfNeeded; + std::vector types; + ARAPlaybackTransformationFlags transformationFlags; + decodeArguments (decoder, controllerRef, runModalActivationDialogIfNeeded, types, transformationFlags); + + encodeReply (replyEncoder, (fromRef (controllerRef)->isLicensedForCapabilities ((runModalActivationDialogIfNeeded != kARAFalse), + types.size (), types.data (), transformationFlags)) ? kARATrue : kARAFalse); + } + + // ARAPlaybackRendererInterface + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAPlaybackRendererInterface, addPlaybackRegion)) + { + ARAPlaybackRendererRef playbackRendererRef; + ARAPlaybackRegionRef playbackRegionRef; + decodeArguments (decoder, playbackRendererRef, playbackRegionRef); + + fromRef (playbackRendererRef)->getPlaybackRenderer ()->addPlaybackRegion (playbackRegionRef); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAPlaybackRendererInterface, removePlaybackRegion)) + { + ARAPlaybackRendererRef playbackRendererRef; + ARAPlaybackRegionRef playbackRegionRef; + decodeArguments (decoder, playbackRendererRef, playbackRegionRef); + + fromRef (playbackRendererRef)->getPlaybackRenderer ()->removePlaybackRegion (playbackRegionRef); + } + + // ARAEditorRendererInterface + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, addPlaybackRegion)) + { + ARAEditorRendererRef editorRendererRef; + ARAPlaybackRegionRef playbackRegionRef; + decodeArguments (decoder, editorRendererRef, playbackRegionRef); + + fromRef (editorRendererRef)->getEditorRenderer ()->addPlaybackRegion (playbackRegionRef); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, removePlaybackRegion)) + { + ARAEditorRendererRef editorRendererRef; + ARAPlaybackRegionRef playbackRegionRef; + decodeArguments (decoder, editorRendererRef, playbackRegionRef); + + fromRef (editorRendererRef)->getEditorRenderer ()->removePlaybackRegion (playbackRegionRef); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, addRegionSequence)) + { + ARAEditorRendererRef editorRendererRef; + ARARegionSequenceRef regionSequenceRef; + decodeArguments (decoder, editorRendererRef, regionSequenceRef); + + fromRef (editorRendererRef)->getEditorRenderer ()->addRegionSequence (regionSequenceRef); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, removeRegionSequence)) + { + ARAEditorRendererRef editorRendererRef; + ARARegionSequenceRef regionSequenceRef; + decodeArguments (decoder, editorRendererRef, regionSequenceRef); + + fromRef (editorRendererRef)->getEditorRenderer ()->removeRegionSequence (regionSequenceRef); + } + + // ARAEditorViewInterface + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorViewInterface, notifySelection)) + { + ARAEditorViewRef editorViewRef; + ARAViewSelection selection; + decodeArguments (decoder, editorViewRef, selection); + + fromRef (editorViewRef)->getEditorView ()->notifySelection (&selection); + } + else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorViewInterface, notifyHideRegionSequences)) + { + ARAEditorViewRef editorViewRef; + std::vector regionSequenceRefs; + decodeArguments (decoder, editorViewRef, regionSequenceRefs); + + fromRef (editorViewRef)->getEditorView ()->notifyHideRegionSequences (regionSequenceRefs.size (), regionSequenceRefs.data ()); + } + else + { + ARA_INTERNAL_ASSERT (false && "unhandled message ID"); + } +} + +} // namespace IPC +} // namespace ARA + +#endif // ARA_ENABLE_IPC diff --git a/IPC/ARAIPCProxyHost.h b/IPC/ARAIPCProxyHost.h new file mode 100644 index 0000000..3e44a17 --- /dev/null +++ b/IPC/ARAIPCProxyHost.h @@ -0,0 +1,77 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCProxyHost.h +//! implementation of host-side ARA IPC proxy host +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef ARAIPCProxyHost_h +#define ARAIPCProxyHost_h + +#if defined(__cplusplus) +#include "ARA_Library/IPC/ARAIPCConnection.h" +#include "ARA_Library/IPC/ARAIPCEncoding.h" +#else +#include "ARA_Library/IPC/ARAIPC.h" +#endif + +#if ARA_ENABLE_IPC + + +//! @addtogroup ARA_Library_IPC +//! @{ + +#if defined(__cplusplus) +namespace ARA { +namespace IPC { +extern "C" { + +//! plug-in side implementation of MessageHandler +//! the plug-in uses the C interface below, but this class will be subclassed by specialized implementations +class ProxyHost : public MessageHandler, public RemoteCaller +{ +protected: + explicit ProxyHost (Connection* connection); + +public: + void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, + MessageEncoder* const replyEncoder) override; +}; +#endif + + +//! callback that the proxy uses to execute the binding of an opaque companion API plug-in instance to the given document controller +typedef const ARAPlugInExtensionInstance * (*ARAIPCBindingHandler) (ARAIPCPlugInInstanceRef plugInInstanceRef, + ARADocumentControllerRef controllerRef, + ARAPlugInInstanceRoleFlags knownRoles, ARAPlugInInstanceRoleFlags assignedRoles); + +//! static configuration: add the ARA factories that the proxy host will wrap +void ARAIPCProxyHostAddFactory(const ARAFactory * factory); + +//! static configuration: set the callback to execute the binding of companion API plug-in instances to ARA document controllers +void ARAIPCProxyHostSetBindingHandler(ARAIPCBindingHandler handler); + + +#if defined(__cplusplus) +} // extern "C" +} // namespace IPC +} // namespace ARA +#endif + +//! @} ARA_Library_IPC + + +#endif // ARA_ENABLE_IPC + +#endif // ARAIPCProxyHost_h diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp new file mode 100644 index 0000000..cfb856f --- /dev/null +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -0,0 +1,1848 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCProxyPlugIn.cpp +//! implementation of host-side ARA IPC proxy plug-in +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#include "ARAIPCProxyPlugIn.h" + + +#if ARA_ENABLE_IPC + +#include "ARA_Library/IPC/ARAIPCEncoding.h" +#include "ARA_Library/Dispatch/ARAPlugInDispatch.h" +#include "ARA_Library/Dispatch/ARAHostDispatch.h" + +#include +#include +#include +#include + + +#if ARA_SUPPORT_VERSION_1 + #error "The ARA IPC proxy plug-in implementation does not support ARA 1." +#endif + + +/*******************************************************************************/ +// configuration switches for debug output +// each can be defined as a nonzero integer to enable the associated logging + +// log each entry from the host into the document controller (except for notifyModelUpdates (), which is called too often) +#ifndef ARA_ENABLE_HOST_ENTRY_LOG + #define ARA_ENABLE_HOST_ENTRY_LOG 0 +#endif + +// log the creation and destruction of plug-in objects +#ifndef ARA_ENABLE_OBJECT_LIFETIME_LOG + #define ARA_ENABLE_OBJECT_LIFETIME_LOG 0 +#endif + +// conditional logging helper functions based on the above switches +#if ARA_ENABLE_HOST_ENTRY_LOG + #define ARA_LOG_HOST_ENTRY(object) ARA_LOG ("Host calls into %s (%p)", __FUNCTION__, object); +#else + #define ARA_LOG_HOST_ENTRY(object) ((void) 0) +#endif + +#if ARA_ENABLE_OBJECT_LIFETIME_LOG + #define ARA_LOG_MODELOBJECT_LIFETIME(message, object) ARA_LOG ("Plug success: document controller %p %s %p", object->getDocumentController (), message, object) +#else + #define ARA_LOG_MODELOBJECT_LIFETIME(message, object) ((void) 0) +#endif + + +/*******************************************************************************/ + +namespace ARA { +namespace IPC { +namespace ProxyPlugInImpl { + +struct AudioSource; +struct ContentReader; +struct HostContentReader; +struct HostAudioReader; +class DocumentController; +class PlaybackRenderer; +class EditorRenderer; +class EditorView; +class PlugInExtension; + + +/*******************************************************************************/ +// ObjectRef validation helper class - empty class unless ARA_VALIDATE_API_CALLS is enabled + +template +class InstanceValidator +{ +#if ARA_VALIDATE_API_CALLS +protected: + inline InstanceValidator () noexcept + { + auto result { _instances.insert (this) }; + ARA_INTERNAL_ASSERT (result.second); + } + + inline ~InstanceValidator () + { + auto it { _instances.find (this) }; + ARA_INTERNAL_ASSERT (it != _instances.end ()); + _instances.erase (it); + } + +public: + static inline bool isValid (const InstanceValidator* instance) + { + return _instances.find (instance) != _instances.end (); + } + +private: + static std::set _instances; +#endif +}; + +#if ARA_VALIDATE_API_CALLS +template +std::set*> InstanceValidator::_instances; + +template +inline bool isValidInstance (const SubclassT* instance) +{ + return InstanceValidator::isValid (instance); +} +#endif + + +/*******************************************************************************/ + +struct AudioSource : public InstanceValidator +{ + AudioSource (ARAAudioSourceHostRef hostRef, ARAAudioSourceRef remoteRef, ARAChannelCount channelCount +#if ARA_VALIDATE_API_CALLS + , ARASampleCount sampleCount, ARASampleRate sampleRate +#endif + ) + : _hostRef { hostRef }, _remoteRef { remoteRef }, _channelCount { channelCount } +#if ARA_VALIDATE_API_CALLS + , _sampleCount { sampleCount }, _sampleRate { sampleRate } +#endif + {} + + ARAAudioSourceHostRef _hostRef; + ARAAudioSourceRef _remoteRef; + ARAChannelCount _channelCount; +#if ARA_VALIDATE_API_CALLS + ARASampleCount _sampleCount; + ARASampleRate _sampleRate; +#endif +}; +ARA_MAP_REF (AudioSource, ARAAudioSourceRef) +ARA_MAP_HOST_REF (AudioSource, ARAAudioSourceHostRef) + +struct ContentReader : public InstanceValidator +{ + ContentReader (ARAContentReaderRef remoteRef, ARAContentType type) + : _remoteRef { remoteRef }, _decoder { type } + {} + + ARAContentReaderRef _remoteRef; + ContentEventDecoder _decoder; +}; +ARA_MAP_REF (ContentReader, ARAContentReaderRef) + +struct HostContentReader +{ + ARAContentReaderHostRef hostRef; + ARAContentType contentType; +}; +ARA_MAP_HOST_REF (HostContentReader, ARAContentReaderHostRef) + +struct HostAudioReader +{ + AudioSource* audioSource; + ARAAudioReaderHostRef hostRef; + size_t sampleSize; +}; +ARA_MAP_HOST_REF (HostAudioReader, ARAAudioReaderHostRef) + + +/*******************************************************************************/ +// Implementation of DocumentControllerInterface that channels all calls through IPC + +class DocumentController : public PlugIn::DocumentControllerInterface, public RemoteCaller, public InstanceValidator +{ +public: + DocumentController (Connection* connection, const ARAFactory* factory, const ARADocumentControllerHostInstance* instance, const ARADocumentProperties* properties) noexcept; + +public: + template + using PropertiesPtr = PlugIn::PropertiesPtr; + + // Destruction + void destroyDocumentController () noexcept override; + + // Factory + const ARAFactory* getFactory () const noexcept override; + + // Update Management + void beginEditing () noexcept override; + void endEditing () noexcept override; + void notifyModelUpdates () noexcept override; + + // Archiving + bool restoreObjectsFromArchive (ARAArchiveReaderHostRef archiveReaderHostRef, const ARARestoreObjectsFilter* filter) noexcept override; + bool storeObjectsToArchive (ARAArchiveWriterHostRef archiveWriterHostRef, const ARAStoreObjectsFilter* filter) noexcept override; + bool storeAudioSourceToAudioFileChunk (ARAArchiveWriterHostRef archiveWriterHostRef, ARAAudioSourceRef audioSourceRef, ARAPersistentID* documentArchiveID, bool* openAutomatically) noexcept override; + + // Document Management + void updateDocumentProperties (PropertiesPtr properties) noexcept override; + + // Musical Context Management + ARAMusicalContextRef createMusicalContext (ARAMusicalContextHostRef hostRef, PropertiesPtr properties) noexcept override; + void updateMusicalContextProperties (ARAMusicalContextRef musicalContextRef, PropertiesPtr properties) noexcept override; + void updateMusicalContextContent (ARAMusicalContextRef musicalContextRef, const ARAContentTimeRange* range, ContentUpdateScopes flags) noexcept override; + void destroyMusicalContext (ARAMusicalContextRef musicalContextRef) noexcept override; + + // Region Sequence Management + ARARegionSequenceRef createRegionSequence (ARARegionSequenceHostRef hostRef, PropertiesPtr properties) noexcept override; + void updateRegionSequenceProperties (ARARegionSequenceRef regionSequence, PropertiesPtr properties) noexcept override; + void destroyRegionSequence (ARARegionSequenceRef regionSequence) noexcept override; + + // Audio Source Management + ARAAudioSourceRef createAudioSource (ARAAudioSourceHostRef hostRef, PropertiesPtr properties) noexcept override; + void updateAudioSourceProperties (ARAAudioSourceRef audioSourceRef, PropertiesPtr properties) noexcept override; + void updateAudioSourceContent (ARAAudioSourceRef audioSourceRef, const ARAContentTimeRange* range, ContentUpdateScopes flags) noexcept override; + void enableAudioSourceSamplesAccess (ARAAudioSourceRef audioSourceRef, bool enable) noexcept override; + void deactivateAudioSourceForUndoHistory (ARAAudioSourceRef audioSourceRef, bool deactivate) noexcept override; + void destroyAudioSource (ARAAudioSourceRef audioSourceRef) noexcept override; + + // Audio Modification Management + ARAAudioModificationRef createAudioModification (ARAAudioSourceRef audioSourceRef, ARAAudioModificationHostRef hostRef, PropertiesPtr properties) noexcept override; + ARAAudioModificationRef cloneAudioModification (ARAAudioModificationRef audioModificationRef, ARAAudioModificationHostRef hostRef, PropertiesPtr properties) noexcept override; + void updateAudioModificationProperties (ARAAudioModificationRef audioModificationRef, PropertiesPtr properties) noexcept override; + bool isAudioModificationPreservingAudioSourceSignal (ARAAudioModificationRef audioModificationRef) noexcept override; + void deactivateAudioModificationForUndoHistory (ARAAudioModificationRef audioModificationRef, bool deactivate) noexcept override; + void destroyAudioModification (ARAAudioModificationRef audioModificationRef) noexcept override; + + // Playback Region Management + ARAPlaybackRegionRef createPlaybackRegion (ARAAudioModificationRef audioModificationRef, ARAPlaybackRegionHostRef hostRef, PropertiesPtr properties) noexcept override; + void updatePlaybackRegionProperties (ARAPlaybackRegionRef playbackRegionRef, PropertiesPtr properties) noexcept override; + void getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept override; + void destroyPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override; + + // Content Reader Management + bool isAudioSourceContentAvailable (ARAAudioSourceRef audioSourceRef, ARAContentType type) noexcept override; + ARAContentGrade getAudioSourceContentGrade (ARAAudioSourceRef audioSourceRef, ARAContentType type) noexcept override; + ARAContentReaderRef createAudioSourceContentReader (ARAAudioSourceRef audioSourceRef, ARAContentType type, const ARAContentTimeRange* range) noexcept override; + + bool isAudioModificationContentAvailable (ARAAudioModificationRef audioModificationRef, ARAContentType type) noexcept override; + ARAContentGrade getAudioModificationContentGrade (ARAAudioModificationRef audioModificationRef, ARAContentType type) noexcept override; + ARAContentReaderRef createAudioModificationContentReader (ARAAudioModificationRef audioModificationRef, ARAContentType type, const ARAContentTimeRange* range) noexcept override; + + bool isPlaybackRegionContentAvailable (ARAPlaybackRegionRef playbackRegionRef, ARAContentType type) noexcept override; + ARAContentGrade getPlaybackRegionContentGrade (ARAPlaybackRegionRef playbackRegionRef, ARAContentType type) noexcept override; + ARAContentReaderRef createPlaybackRegionContentReader (ARAPlaybackRegionRef playbackRegionRef, ARAContentType type, const ARAContentTimeRange* range) noexcept override; + + ARAInt32 getContentReaderEventCount (ARAContentReaderRef contentReaderRef) noexcept override; + const void* getContentReaderDataForEvent (ARAContentReaderRef contentReaderRef, ARAInt32 eventIndex) noexcept override; + void destroyContentReader (ARAContentReaderRef contentReaderRef) noexcept override; + + // Controlling Analysis + bool isAudioSourceContentAnalysisIncomplete (ARAAudioSourceRef audioSourceRef, ARAContentType contentType) noexcept override; + void requestAudioSourceContentAnalysis (ARAAudioSourceRef audioSourceRef, ARASize contentTypesCount, const ARAContentType contentTypes[]) noexcept override; + + ARAInt32 getProcessingAlgorithmsCount () noexcept override; + const ARAProcessingAlgorithmProperties* getProcessingAlgorithmProperties (ARAInt32 algorithmIndex) noexcept override; + ARAInt32 getProcessingAlgorithmForAudioSource (ARAAudioSourceRef audioSourceRef) noexcept override; + void requestProcessingAlgorithmForAudioSource (ARAAudioSourceRef audioSourceRef, ARAInt32 algorithmIndex) noexcept override; + + // License Management + bool isLicensedForCapabilities (bool runModalActivationDialogIfNeeded, ARASize contentTypesCount, const ARAContentType contentTypes[], ARAPlaybackTransformationFlags transformationFlags) noexcept override; + + // Accessors for Proxy + const ARADocumentControllerInstance* getInstance () const noexcept { return &_instance; } + ARADocumentControllerRef getRemoteRef () const noexcept { return _remoteRef; } + + // Host Interface Access + PlugIn::HostAudioAccessController* getHostAudioAccessController () noexcept { return &_hostAudioAccessController; } + PlugIn::HostArchivingController* getHostArchivingController () noexcept { return &_hostArchivingController; } + PlugIn::HostContentAccessController* getHostContentAccessController () noexcept { return (_hostContentAccessController.isProvided ()) ? &_hostContentAccessController : nullptr; } + PlugIn::HostModelUpdateController* getHostModelUpdateController () noexcept { return (_hostModelUpdateController.isProvided ()) ? &_hostModelUpdateController : nullptr; } + PlugIn::HostPlaybackController* getHostPlaybackController () noexcept { return (_hostPlaybackController.isProvided ()) ? &_hostPlaybackController : nullptr; } + +private: + void destroyIfUnreferenced () noexcept; + + friend class PlugInExtension; + void addPlugInExtension (PlugInExtension* plugInExtension) noexcept { _plugInExtensions.insert (plugInExtension); } + void removePlugInExtension (PlugInExtension* plugInExtension) noexcept { _plugInExtensions.erase (plugInExtension); if (_plugInExtensions.empty ()) destroyIfUnreferenced (); } + +private: + const ARAFactory* const _factory; + + PlugIn::HostAudioAccessController _hostAudioAccessController; + PlugIn::HostArchivingController _hostArchivingController; + PlugIn::HostContentAccessController _hostContentAccessController; + PlugIn::HostModelUpdateController _hostModelUpdateController; + PlugIn::HostPlaybackController _hostPlaybackController; + + PlugIn::DocumentControllerInstance _instance; + + ARADocumentControllerRef _remoteRef; + + bool _hasBeenDestroyed { false }; + + ARAProcessingAlgorithmProperties _processingAlgorithmData { 0, nullptr, nullptr }; + struct + { + std::string persistentID; + std::string name; + } _processingAlgorithmStrings; + + std::set _plugInExtensions; + + ARA_HOST_MANAGED_OBJECT (DocumentController) +}; +ARA_MAP_HOST_REF (DocumentController, ARAAudioAccessControllerHostRef, ARAArchivingControllerHostRef, + ARAContentAccessControllerHostRef, ARAModelUpdateControllerHostRef, ARAPlaybackControllerHostRef) + + +/*******************************************************************************/ + +DocumentController::DocumentController (Connection* connection, const ARAFactory* factory, const ARADocumentControllerHostInstance* instance, const ARADocumentProperties* properties) noexcept +: RemoteCaller { connection }, + _factory { factory }, + _hostAudioAccessController { instance }, + _hostArchivingController { instance }, + _hostContentAccessController { instance }, + _hostModelUpdateController { instance }, + _hostPlaybackController { instance }, + _instance { this } +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARAAudioAccessControllerHostRef audioAccessControllerHostRef { toHostRef (this) }; + ARAArchivingControllerHostRef archivingControllerHostRef { toHostRef (this) }; + ARAContentAccessControllerHostRef contentAccessControllerHostRef { toHostRef (this) }; + ARAModelUpdateControllerHostRef modelUpdateControllerHostRef { toHostRef (this) }; + ARAPlaybackControllerHostRef playbackControllerHostRef { toHostRef (this) }; + remoteCall (_remoteRef, kCreateDocumentControllerMethodID, _factory->factoryID, + audioAccessControllerHostRef, archivingControllerHostRef, + (_hostContentAccessController.isProvided ()) ? kARATrue : kARAFalse, contentAccessControllerHostRef, + (_hostModelUpdateController.isProvided ()) ? kARATrue : kARAFalse, modelUpdateControllerHostRef, + (_hostPlaybackController.isProvided ()) ? kARATrue : kARAFalse, playbackControllerHostRef, + properties); + + ARA_LOG_MODELOBJECT_LIFETIME ("did create document controller", _remoteRef); +} + +void DocumentController::destroyDocumentController () noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARA_LOG_MODELOBJECT_LIFETIME ("will destroy document controller", _remoteRef); + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyDocumentController), _remoteRef); + + _hasBeenDestroyed = true; + + destroyIfUnreferenced (); +} + +void DocumentController::destroyIfUnreferenced () noexcept +{ + // still in use by host? + if (!_hasBeenDestroyed) + return; + + // still referenced from plug-in instances? + if (!_plugInExtensions.empty ()) + return; + + delete this; +} + +/*******************************************************************************/ + +const ARAFactory* DocumentController::getFactory () const noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + return _factory; +} + +/*******************************************************************************/ + +void DocumentController::beginEditing () noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, beginEditing), _remoteRef); +} + +void DocumentController::endEditing () noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, endEditing), _remoteRef); +} + +void DocumentController::notifyModelUpdates () noexcept +{ +#if ARA_ENABLE_HOST_ENTRY_LOG + static int logCount { 0 }; + constexpr int maxLogCount { 3 }; + if ((++logCount) <= maxLogCount) + { + ARA_LOG_HOST_ENTRY (this); + if (logCount >= maxLogCount) + ARA_LOG ("notifyModelUpdates () called %i times, will now suppress logging future calls to it", maxLogCount); + } +#endif + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + if (!_hostModelUpdateController.isProvided ()) + return; + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, notifyModelUpdates), _remoteRef); +} + +bool DocumentController::restoreObjectsFromArchive (ARAArchiveReaderHostRef archiveReaderHostRef, const ARARestoreObjectsFilter* filter) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARABool success; + remoteCall (success, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, restoreObjectsFromArchive), _remoteRef, archiveReaderHostRef, filter); + return (success != kARAFalse); +} + +bool DocumentController::storeObjectsToArchive (ARAArchiveWriterHostRef archiveWriterHostRef, const ARAStoreObjectsFilter* filter) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARAStoreObjectsFilter tempFilter; + std::vector remoteAudioSourceRefs; + if ((filter != nullptr) && (filter->audioSourceRefsCount > 0)) + { + remoteAudioSourceRefs.reserve (filter->audioSourceRefsCount); + for (auto i { 0U }; i < filter->audioSourceRefsCount; ++i) + remoteAudioSourceRefs.emplace_back (fromRef (filter->audioSourceRefs[i])->_remoteRef); + + tempFilter = *filter; + tempFilter.audioSourceRefs = remoteAudioSourceRefs.data (); + filter = &tempFilter; + } + + ARABool success; + remoteCall (success, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, storeObjectsToArchive), _remoteRef, archiveWriterHostRef, filter); + return (success!= kARAFalse); +} + +bool DocumentController::storeAudioSourceToAudioFileChunk (ARAArchiveWriterHostRef archiveWriterHostRef, ARAAudioSourceRef audioSourceRef, ARAPersistentID* documentArchiveID, bool* openAutomatically) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + ARA_INTERNAL_ASSERT (documentArchiveID != nullptr); + ARA_INTERNAL_ASSERT (openAutomatically != nullptr); + + bool success { false }; + CustomDecodeFunction customDecode { + [this, &success, &documentArchiveID, &openAutomatically] (const MessageDecoder* decoder) -> void + { + StoreAudioSourceToAudioFileChunkReply reply; + decodeReply (reply, decoder); + + // find ID string in factory because our return value is a temporary copy + if (0 == std::strcmp (reply.documentArchiveID, _factory->documentArchiveID)) + { + *documentArchiveID = _factory->documentArchiveID; + } + else + { + *documentArchiveID = nullptr; + for (auto i { 0U }; i < _factory->compatibleDocumentArchiveIDsCount; ++i) + { + if (0 == std::strcmp (reply.documentArchiveID, _factory->compatibleDocumentArchiveIDs[i])) + { + *documentArchiveID = _factory->compatibleDocumentArchiveIDs[i]; + break; + } + } + ARA_INTERNAL_ASSERT (*documentArchiveID != nullptr); + } + + *openAutomatically = (reply.openAutomatically != kARAFalse); + success = (reply.result != kARAFalse); + } }; + remoteCall (customDecode, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk), + _remoteRef, archiveWriterHostRef, audioSource->_remoteRef); + return success; +} + +void DocumentController::updateDocumentProperties (PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARADocumentPropertiesMinSize); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateDocumentProperties), _remoteRef, *properties); +} + +/*******************************************************************************/ + +ARAMusicalContextRef DocumentController::createMusicalContext (ARAMusicalContextHostRef hostRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAMusicalContextPropertiesMinSize); + + ARAMusicalContextRef musicalContextRef; + remoteCall (musicalContextRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createMusicalContext), _remoteRef, hostRef, *properties); + + ARA_LOG_MODELOBJECT_LIFETIME ("did create musical context", musicalContextRef); + return musicalContextRef; +} + +void DocumentController::updateMusicalContextProperties (ARAMusicalContextRef musicalContextRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (musicalContextRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAMusicalContextPropertiesMinSize); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextProperties), _remoteRef, musicalContextRef, *properties); +} + +void DocumentController::updateMusicalContextContent (ARAMusicalContextRef musicalContextRef, const ARAContentTimeRange* range, ContentUpdateScopes flags) noexcept +{ + ARA_LOG_HOST_ENTRY (musicalContextRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextContent), _remoteRef, musicalContextRef, range, flags); +} + +void DocumentController::destroyMusicalContext (ARAMusicalContextRef musicalContextRef) noexcept +{ + ARA_LOG_HOST_ENTRY (musicalContextRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARA_LOG_MODELOBJECT_LIFETIME ("will destroy musical context", musicalContextRef); + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyMusicalContext), _remoteRef, musicalContextRef); +} + +/*******************************************************************************/ + +ARARegionSequenceRef DocumentController::createRegionSequence (ARARegionSequenceHostRef hostRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARARegionSequencePropertiesMinSize); + + ARARegionSequenceRef regionSequenceRef; + remoteCall (regionSequenceRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createRegionSequence), _remoteRef, hostRef, *properties); + + ARA_LOG_MODELOBJECT_LIFETIME ("did create region sequence", regionSequenceRef); + return regionSequenceRef; +} + +void DocumentController::updateRegionSequenceProperties (ARARegionSequenceRef regionSequenceRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (regionSequenceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARARegionSequencePropertiesMinSize); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateRegionSequenceProperties), _remoteRef, regionSequenceRef, *properties); +} + +void DocumentController::destroyRegionSequence (ARARegionSequenceRef regionSequenceRef) noexcept +{ + ARA_LOG_HOST_ENTRY (regionSequenceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARA_LOG_MODELOBJECT_LIFETIME ("will destroy region sequence", regionSequenceRef); + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyRegionSequence), _remoteRef, regionSequenceRef); +} + +/*******************************************************************************/ + +ARAAudioSourceRef DocumentController::createAudioSource (ARAAudioSourceHostRef hostRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioSourcePropertiesMinSize); + + auto audioSource { new AudioSource { hostRef, nullptr, properties->channelCount +#if ARA_VALIDATE_API_CALLS + , properties->sampleCount, properties->sampleRate +#endif + } }; + + remoteCall (audioSource->_remoteRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioSource), + _remoteRef, ARAAudioSourceHostRef { toHostRef (audioSource) }, *properties); + + ARA_LOG_MODELOBJECT_LIFETIME ("did create audio source", audioSourceRef); + return toRef (audioSource); +} + +void DocumentController::updateAudioSourceProperties (ARAAudioSourceRef audioSourceRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioSourcePropertiesMinSize); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceProperties), _remoteRef, audioSource->_remoteRef, *properties); +} + +void DocumentController::updateAudioSourceContent (ARAAudioSourceRef audioSourceRef, const ARAContentTimeRange* range, ContentUpdateScopes flags) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceContent), _remoteRef, audioSource->_remoteRef, range, flags); +} + +void DocumentController::enableAudioSourceSamplesAccess (ARAAudioSourceRef audioSourceRef, bool enable) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, enableAudioSourceSamplesAccess), _remoteRef, audioSource->_remoteRef, (enable) ? kARATrue : kARAFalse); +} + +void DocumentController::deactivateAudioSourceForUndoHistory (ARAAudioSourceRef audioSourceRef, bool deactivate) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory), _remoteRef, audioSource->_remoteRef, (deactivate) ? kARATrue : kARAFalse); +} + +void DocumentController::destroyAudioSource (ARAAudioSourceRef audioSourceRef) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + ARA_LOG_MODELOBJECT_LIFETIME ("will destroy audio source", audioSource->_remoteRef); + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyAudioSource), _remoteRef, audioSource->_remoteRef); + delete audioSource; +} + +/*******************************************************************************/ + +ARAAudioModificationRef DocumentController::createAudioModification (ARAAudioSourceRef audioSourceRef, ARAAudioModificationHostRef hostRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioModificationPropertiesMinSize); + + ARAAudioModificationRef audioModificationRef; + remoteCall (audioModificationRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioModification), + _remoteRef, audioSource->_remoteRef, hostRef, *properties); + + ARA_LOG_MODELOBJECT_LIFETIME ("did create audio modification", audioModificationRef); + return audioModificationRef; +} + +ARAAudioModificationRef DocumentController::cloneAudioModification (ARAAudioModificationRef srcAudioModificationRef, ARAAudioModificationHostRef hostRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (srcAudioModificationRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioModificationPropertiesMinSize); + + ARAAudioModificationRef clonedAudioModificationRef; + remoteCall (clonedAudioModificationRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, cloneAudioModification), + _remoteRef, srcAudioModificationRef, hostRef, *properties); + + ARA_LOG_MODELOBJECT_LIFETIME ("did create cloned audio modification", clonedAudioModificationRef); + return clonedAudioModificationRef; +} + +void DocumentController::updateAudioModificationProperties (ARAAudioModificationRef audioModificationRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (audioModificationRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioModificationPropertiesMinSize); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioModificationProperties), _remoteRef, audioModificationRef, *properties); +} + +bool DocumentController::isAudioModificationPreservingAudioSourceSignal (ARAAudioModificationRef audioModificationRef) noexcept +{ + ARA_LOG_HOST_ENTRY (audioModificationRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARABool result; + remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal), _remoteRef, audioModificationRef); + return (result != kARAFalse); +} + +void DocumentController::deactivateAudioModificationForUndoHistory (ARAAudioModificationRef audioModificationRef, bool deactivate) noexcept +{ + ARA_LOG_HOST_ENTRY (audioModificationRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory), _remoteRef, audioModificationRef, (deactivate) ? kARATrue : kARAFalse); +} + +void DocumentController::destroyAudioModification (ARAAudioModificationRef audioModificationRef) noexcept +{ + ARA_LOG_HOST_ENTRY (audioModificationRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARA_LOG_MODELOBJECT_LIFETIME ("will destroy audio modification", audioModification); + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyAudioModification), _remoteRef, audioModificationRef); +} + +/*******************************************************************************/ + +ARAPlaybackRegionRef DocumentController::createPlaybackRegion (ARAAudioModificationRef audioModificationRef, ARAPlaybackRegionHostRef hostRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this));ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAPlaybackRegionPropertiesMinSize); + + ARAPlaybackRegionRef playbackRegionRef; + remoteCall (playbackRegionRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegion), + _remoteRef, audioModificationRef, hostRef, *properties); + + ARA_LOG_MODELOBJECT_LIFETIME ("did create playback region", playbackRegionRef); + return playbackRegionRef; +} + +void DocumentController::updatePlaybackRegionProperties (ARAPlaybackRegionRef playbackRegionRef, PropertiesPtr properties) noexcept +{ + ARA_LOG_HOST_ENTRY (playbackRegionRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (properties != nullptr); + ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAPlaybackRegionPropertiesMinSize); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updatePlaybackRegionProperties), _remoteRef, playbackRegionRef, *properties); +} + +void DocumentController::getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept +{ + ARA_LOG_HOST_ENTRY (playbackRegionRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); +// this function can be called from other threads! +// ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (headTime != nullptr); + ARA_INTERNAL_ASSERT (tailTime != nullptr); + + GetPlaybackRegionHeadAndTailTimeReply reply; + remoteCall (reply, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime), + _remoteRef, playbackRegionRef, (headTime != nullptr) ? kARATrue : kARAFalse, (tailTime != nullptr) ? kARATrue : kARAFalse); + if (headTime != nullptr) + *headTime = reply.headTime; + if (tailTime != nullptr) + *tailTime = reply.tailTime; +} + +void DocumentController::destroyPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept +{ + ARA_LOG_HOST_ENTRY (playbackRegionRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARA_LOG_MODELOBJECT_LIFETIME ("will destroy playback region", playbackRegionRef); + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyPlaybackRegion), _remoteRef, playbackRegionRef); +} + +/*******************************************************************************/ + +bool DocumentController::isAudioSourceContentAvailable (ARAAudioSourceRef audioSourceRef, ARAContentType type) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + ARABool result; + remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAvailable), _remoteRef, audioSource->_remoteRef, type); + return (result != kARAFalse); +} + +ARAContentGrade DocumentController::getAudioSourceContentGrade (ARAAudioSourceRef audioSourceRef, ARAContentType type) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + ARAContentGrade grade; + remoteCall (grade, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getAudioSourceContentGrade), _remoteRef, audioSource->_remoteRef, type); + return grade; +} + +ARAContentReaderRef DocumentController::createAudioSourceContentReader (ARAAudioSourceRef audioSourceRef, ARAContentType type, const ARAContentTimeRange* range) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + ARAContentReaderRef contentReaderRef; + remoteCall (contentReaderRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioSourceContentReader), + _remoteRef, audioSource->_remoteRef, type, range); + + auto contentReader { new ContentReader { contentReaderRef, type } }; +#if ARA_ENABLE_OBJECT_LIFETIME_LOG + ARA_LOG ("Plug success: did create content reader %p for audio source %p", contentReaderRef, audioSourceRef); +#endif + return toRef (contentReader); +} + +/*******************************************************************************/ + +bool DocumentController::isAudioModificationContentAvailable (ARAAudioModificationRef audioModificationRef, ARAContentType type) noexcept +{ + ARA_LOG_HOST_ENTRY (audioModificationRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARABool result; + remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioModificationContentAvailable), _remoteRef, audioModificationRef, type); + return (result != kARAFalse); +} + +ARAContentGrade DocumentController::getAudioModificationContentGrade (ARAAudioModificationRef audioModificationRef, ARAContentType type) noexcept +{ + ARA_LOG_HOST_ENTRY (audioModificationRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARAContentGrade grade; + remoteCall (grade, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getAudioModificationContentGrade), _remoteRef, audioModificationRef, type); + return grade; +} + +ARAContentReaderRef DocumentController::createAudioModificationContentReader (ARAAudioModificationRef audioModificationRef, ARAContentType type, const ARAContentTimeRange* range) noexcept +{ + ARA_LOG_HOST_ENTRY (audioModificationRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARAContentReaderRef contentReaderRef; + remoteCall (contentReaderRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioModificationContentReader), + _remoteRef, audioModificationRef, type, range); + + auto contentReader { new ContentReader { contentReaderRef, type } }; +#if ARA_ENABLE_OBJECT_LIFETIME_LOG + ARA_LOG ("Plug success: did create content reader %p for audio modification %p", contentReaderRef, audioModificationRef); +#endif + return toRef (contentReader); +} + +/*******************************************************************************/ + +bool DocumentController::isPlaybackRegionContentAvailable (ARAPlaybackRegionRef playbackRegionRef, ARAContentType type) noexcept +{ + ARA_LOG_HOST_ENTRY (playbackRegionRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARABool result; + remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isPlaybackRegionContentAvailable), _remoteRef, playbackRegionRef, type); + return (result != kARAFalse); +} + +ARAContentGrade DocumentController::getPlaybackRegionContentGrade (ARAPlaybackRegionRef playbackRegionRef, ARAContentType type) noexcept +{ + ARA_LOG_HOST_ENTRY (playbackRegionRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARAContentGrade grade; + remoteCall (grade, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionContentGrade), _remoteRef, playbackRegionRef, type); + return grade; +} + +ARAContentReaderRef DocumentController::createPlaybackRegionContentReader (ARAPlaybackRegionRef playbackRegionRef, ARAContentType type, const ARAContentTimeRange* range) noexcept +{ + ARA_LOG_HOST_ENTRY (playbackRegionRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARAContentReaderRef contentReaderRef; + remoteCall (contentReaderRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegionContentReader), + _remoteRef, playbackRegionRef, type, range); + + auto contentReader { new ContentReader { contentReaderRef, type } }; +#if ARA_ENABLE_OBJECT_LIFETIME_LOG + ARA_LOG ("Plug success: did create content reader %p for playback region %p", contentReaderRef, playbackRegionRef); +#endif + return toRef (contentReader); +} + +/*******************************************************************************/ + +ARAInt32 DocumentController::getContentReaderEventCount (ARAContentReaderRef contentReaderRef) noexcept +{ + ARA_LOG_HOST_ENTRY (contentReaderRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto contentReader { fromRef (contentReaderRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (contentReader)); + + ARAInt32 count; + remoteCall (count, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getContentReaderEventCount), _remoteRef, contentReader->_remoteRef); + return count; +} + +const void* DocumentController::getContentReaderDataForEvent (ARAContentReaderRef contentReaderRef, ARAInt32 eventIndex) noexcept +{ + ARA_LOG_HOST_ENTRY (contentReaderRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto contentReader { fromRef (contentReaderRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (contentReader)); + + const void* result {}; + CustomDecodeFunction customDecode { + [&result, &contentReader] (const MessageDecoder* decoder) -> void + { + result = contentReader->_decoder.decode (decoder); + } }; + remoteCall (customDecode, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getContentReaderDataForEvent), + _remoteRef, contentReader->_remoteRef, eventIndex); + return result; +} + +void DocumentController::destroyContentReader (ARAContentReaderRef contentReaderRef) noexcept +{ + ARA_LOG_HOST_ENTRY (contentReaderRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto contentReader { fromRef (contentReaderRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (contentReader)); + + ARA_LOG_MODELOBJECT_LIFETIME ("will destroy content reader", contentReader->remoteRef); + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyContentReader), _remoteRef, contentReader->_remoteRef); + + delete contentReader; +} + +/*******************************************************************************/ + +bool DocumentController::isAudioSourceContentAnalysisIncomplete (ARAAudioSourceRef audioSourceRef, ARAContentType type) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + ARABool result; + remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete), + _remoteRef, audioSource->_remoteRef, type); + return (result != kARAFalse); +} + +void DocumentController::requestAudioSourceContentAnalysis (ARAAudioSourceRef audioSourceRef, ARASize contentTypesCount, const ARAContentType contentTypes[]) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + const ArrayArgument types { contentTypes, contentTypesCount }; + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, requestAudioSourceContentAnalysis), _remoteRef, audioSource->_remoteRef, types); +} + +ARAInt32 DocumentController::getProcessingAlgorithmsCount () noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARAInt32 count; + remoteCall (count, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmsCount), _remoteRef); + return count; +} + +const ARAProcessingAlgorithmProperties* DocumentController::getProcessingAlgorithmProperties (ARAInt32 algorithmIndex) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + CustomDecodeFunction customDecode { + [this] (const MessageDecoder* decoder) -> void + { + ARAProcessingAlgorithmProperties reply; + decodeReply (reply, decoder); + _processingAlgorithmStrings.persistentID = reply.persistentID; + _processingAlgorithmStrings.name = reply.name; + _processingAlgorithmData = reply; + _processingAlgorithmData.persistentID = _processingAlgorithmStrings.persistentID.c_str (); + _processingAlgorithmData.name = _processingAlgorithmStrings.name.c_str (); + } }; + remoteCall (customDecode, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmProperties), _remoteRef, algorithmIndex); + return &_processingAlgorithmData; +} + +ARAInt32 DocumentController::getProcessingAlgorithmForAudioSource (ARAAudioSourceRef audioSourceRef) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + ARAInt32 result; + remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource), _remoteRef, audioSource->_remoteRef); + return result; +} + +void DocumentController::requestProcessingAlgorithmForAudioSource (ARAAudioSourceRef audioSourceRef, ARAInt32 algorithmIndex) noexcept +{ + ARA_LOG_HOST_ENTRY (audioSourceRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + const auto audioSource { fromRef (audioSourceRef) }; + ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource), _remoteRef, audioSource->_remoteRef, algorithmIndex); +} + +/*******************************************************************************/ + +bool DocumentController::isLicensedForCapabilities (bool runModalActivationDialogIfNeeded, ARASize contentTypesCount, const ARAContentType contentTypes[], ARAPlaybackTransformationFlags transformationFlags) noexcept +{ + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + const ArrayArgument types { contentTypes, contentTypesCount }; + ARABool result; + remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isLicensedForCapabilities), + _remoteRef, (runModalActivationDialogIfNeeded) ? kARATrue : kARAFalse, types, transformationFlags); + return (result != kARAFalse); +} + + +/*******************************************************************************/ +// Implementation of PlaybackRendererInterface that channels all calls through IPC + +class PlaybackRenderer : public PlugIn::PlaybackRendererInterface, protected RemoteCaller, public InstanceValidator +{ +public: + explicit PlaybackRenderer (Connection* connection, ARAPlaybackRendererRef remoteRef) noexcept + : RemoteCaller { connection }, + _remoteRef { remoteRef } + {} + + // Inherited public interface used by the C++ dispatcher, to be called by the ARAPlugInDispatch code exclusively. + void addPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override + { + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAPlaybackRendererInterface, addPlaybackRegion), _remoteRef, playbackRegionRef); + } + void removePlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override + { + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAPlaybackRendererInterface, removePlaybackRegion), _remoteRef, playbackRegionRef); + } + +private: + ARAPlaybackRendererRef const _remoteRef; + + ARA_HOST_MANAGED_OBJECT (PlaybackRenderer) +}; + + +/*******************************************************************************/ +// Implementation of EditorRendererInterface that channels all calls through IPC + +class EditorRenderer : public PlugIn::EditorRendererInterface, protected RemoteCaller, public InstanceValidator +{ +public: + explicit EditorRenderer (Connection* connection, ARAEditorRendererRef remoteRef) noexcept + : RemoteCaller { connection }, + _remoteRef { remoteRef } + {} + + // Inherited public interface used by the C++ dispatcher, to be called by the ARAPlugInDispatch code exclusively. + void addPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override + { + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, addPlaybackRegion), _remoteRef, playbackRegionRef); + } + void removePlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override + { + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, removePlaybackRegion), _remoteRef, playbackRegionRef); + } + + void addRegionSequence (ARARegionSequenceRef regionSequenceRef) noexcept override + { + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, addRegionSequence), _remoteRef, regionSequenceRef); + } + void removeRegionSequence (ARARegionSequenceRef regionSequenceRef) noexcept override + { + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, removeRegionSequence), _remoteRef, regionSequenceRef); + } + +private: + ARAEditorRendererRef const _remoteRef; + + ARA_HOST_MANAGED_OBJECT (EditorRenderer) +}; + + +/*******************************************************************************/ +// Implementation of EditorRendererInterface that channels all calls through IPC + +class EditorView : public PlugIn::EditorViewInterface, protected RemoteCaller, public InstanceValidator +{ +public: + explicit EditorView (Connection* connection, ARAEditorViewRef remoteRef) noexcept + : RemoteCaller { connection }, + _remoteRef { remoteRef } + {} + + // Inherited public interface used by the C++ dispatcher, to be called by the ARAPlugInDispatch code exclusively. + void notifySelection (SizedStructPtr selection) noexcept override + { + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (selection != nullptr); + ARA_INTERNAL_ASSERT (selection->structSize >= ARA::kARAViewSelectionMinSize); + + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorViewInterface, notifySelection), _remoteRef, *selection); + } + void notifyHideRegionSequences (ARASize regionSequenceRefsCount, const ARARegionSequenceRef regionSequenceRefs[]) noexcept override + { + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + const ArrayArgument sequences { regionSequenceRefs, regionSequenceRefsCount }; + remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorViewInterface, notifyHideRegionSequences), _remoteRef, sequences); + } + +private: + ARAEditorViewRef const _remoteRef; + + ARA_HOST_MANAGED_OBJECT (EditorView) +}; + + +/*******************************************************************************/ +// implementation of ARAPlugInExtensionInstance that uses the above instance role classes + +class PlugInExtension : public PlugIn::PlugInExtensionInstance, public RemoteCaller +{ +public: + PlugInExtension (Connection* connection, ARAPlugInExtensionRef remoteExtensionRef, + ARADocumentControllerRef documentControllerRef, + ARAPlugInInstanceRoleFlags knownRoles, ARAPlugInInstanceRoleFlags assignedRoles) noexcept + : PlugIn::PlugInExtensionInstance { (((knownRoles & kARAPlaybackRendererRole) == 0) || ((assignedRoles & kARAPlaybackRendererRole) != 0)) ? + new PlaybackRenderer (connection, reinterpret_cast (remoteExtensionRef)) : nullptr, + (((knownRoles & kARAEditorRendererRole) == 0) || ((assignedRoles & kARAEditorRendererRole) != 0)) ? + new EditorRenderer (connection, reinterpret_cast (remoteExtensionRef)) : nullptr, + (((knownRoles & kARAEditorViewRole) == 0) || ((assignedRoles & kARAEditorViewRole) != 0)) ? + new EditorView (connection, reinterpret_cast (remoteExtensionRef)) : nullptr }, + RemoteCaller { connection }, + _documentController { PlugIn::fromRef (documentControllerRef) } + { + plugInExtensionRef = remoteExtensionRef; // we re-use this deprecated ivar to store the remote extension + + ARA_LOG_HOST_ENTRY (this); + ARA_INTERNAL_ASSERT (isValidInstance (_documentController)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + _documentController->addPlugInExtension (this); + +#if ARA_ENABLE_OBJECT_LIFETIME_LOG + ARA_LOG ("Plug success: did create plug-in extension %p (playbackRenderer %p, editorRenderer %p, editorView %p)", this, getPlaybackRenderer (), getEditorRenderer (), getEditorView ()); +#endif + } + + ~PlugInExtension () noexcept + { + ARA_LOG_HOST_ENTRY (this); +#if ARA_ENABLE_OBJECT_LIFETIME_LOG + ARA_LOG ("Plug success: will destroy plug-in extension %p (playbackRenderer %p, editorRenderer %p, editorView %p)", this, getPlaybackRenderer (), getEditorRenderer (), getEditorView ()); +#endif + + remoteCall (kCleanupBindingMethodID, plugInExtensionRef); + + _documentController->removePlugInExtension (this); + + delete getEditorView (); + delete getEditorRenderer (); + delete getPlaybackRenderer (); + } + +private: + DocumentController* const _documentController; + + ARA_HOST_MANAGED_OBJECT (PlugInExtension) +}; + + +/*******************************************************************************/ + +struct RemoteFactory +{ + ARAFactory _factory; + struct + { + std::string factoryID; + std::string plugInName; + std::string manufacturerName; + std::string informationURL; + std::string version; + std::string documentArchiveID; + } _strings; + std::vector _compatibleIDStrings; + std::vector _compatibleIDs; + std::vector _analyzableTypes; +}; + +std::map _factories {}; + + +/*******************************************************************************/ + +} // namespace ProxyPlugInImpl +using namespace ProxyPlugInImpl; + +/*******************************************************************************/ + +#if defined (__GNUC__) + _Pragma ("GCC diagnostic push") + _Pragma ("GCC diagnostic ignored \"-Wunused-function\"") +#endif + +ARA_MAP_IPC_REF (Connection, ARAIPCConnectionRef) + +#if defined (__GNUC__) + _Pragma ("GCC diagnostic pop") +#endif + + +size_t ARAIPCProxyPlugInGetFactoriesCount (ARAIPCConnectionRef connectionRef) +{ + size_t count; + RemoteCaller { fromIPCRef (connectionRef) }.remoteCall (count, kGetFactoriesCountMethodID); + ARA_INTERNAL_ASSERT (count > 0); + return count; +} + +const ARAFactory* ARAIPCProxyPlugInGetFactoryAtIndex (ARAIPCConnectionRef connectionRef, size_t index) +{ + RemoteFactory remoteFactory; + RemoteCaller::CustomDecodeFunction customDecode { + [&remoteFactory] (const MessageDecoder* decoder) -> void + { + decodeReply (remoteFactory._factory, decoder); + + ARA_VALIDATE_API_ARGUMENT (&remoteFactory._factory, remoteFactory._factory.highestSupportedApiGeneration >= kARAAPIGeneration_2_0_Final); + + remoteFactory._strings.factoryID = remoteFactory._factory.factoryID; + remoteFactory._factory.factoryID = remoteFactory._strings.factoryID.c_str (); + + remoteFactory._strings.plugInName = remoteFactory._factory.plugInName; + remoteFactory._factory.plugInName = remoteFactory._strings.plugInName.c_str (); + remoteFactory._strings.manufacturerName = remoteFactory._factory.manufacturerName; + remoteFactory._factory.manufacturerName = remoteFactory._strings.manufacturerName.c_str (); + remoteFactory._strings.informationURL = remoteFactory._factory.informationURL; + remoteFactory._factory.informationURL = remoteFactory._strings.informationURL.c_str (); + remoteFactory._strings.version = remoteFactory._factory.version; + remoteFactory._factory.version = remoteFactory._strings.version.c_str (); + + remoteFactory._strings.documentArchiveID = remoteFactory._factory.documentArchiveID; + remoteFactory._factory.documentArchiveID = remoteFactory._strings.documentArchiveID.c_str (); + + remoteFactory._compatibleIDStrings.reserve (remoteFactory._factory.compatibleDocumentArchiveIDsCount); + remoteFactory._compatibleIDs.reserve (remoteFactory._factory.compatibleDocumentArchiveIDsCount); + for (auto i { 0U }; i < remoteFactory._factory.compatibleDocumentArchiveIDsCount; ++i) + { + remoteFactory._compatibleIDStrings.emplace_back (remoteFactory._factory.compatibleDocumentArchiveIDs[i]); + remoteFactory._compatibleIDs.emplace_back (remoteFactory._compatibleIDStrings[i].c_str ()); + } + remoteFactory._factory.compatibleDocumentArchiveIDs = remoteFactory._compatibleIDs.data (); + + remoteFactory._analyzableTypes.reserve (remoteFactory._factory.analyzeableContentTypesCount); + for (auto i { 0U }; i < remoteFactory._factory.analyzeableContentTypesCount; ++i) + remoteFactory._analyzableTypes.emplace_back (remoteFactory._factory.analyzeableContentTypes[i]); + remoteFactory._factory.analyzeableContentTypes = remoteFactory._analyzableTypes.data (); + } }; + + RemoteCaller { fromIPCRef (connectionRef) }.remoteCall (customDecode, kGetFactoryMethodID, index); + + const auto result { _factories.insert (std::make_pair (remoteFactory._strings.factoryID, remoteFactory)) }; + if (result.second) + { + result.first->second._factory.factoryID = result.first->second._strings.factoryID.c_str (); + + result.first->second._factory.factoryID = result.first->second._strings.factoryID.c_str (); + result.first->second._factory.plugInName = result.first->second._strings.plugInName.c_str (); + result.first->second._factory.manufacturerName = result.first->second._strings.manufacturerName.c_str (); + result.first->second._factory.informationURL = result.first->second._strings.informationURL.c_str (); + result.first->second._factory.version = result.first->second._strings.version.c_str (); + + result.first->second._factory.documentArchiveID = result.first->second._strings.documentArchiveID.c_str (); + + for (auto i { 0U }; i < result.first->second._compatibleIDStrings.size (); ++i) + result.first->second._compatibleIDs[i] = result.first->second._compatibleIDStrings[i].c_str (); + result.first->second._factory.compatibleDocumentArchiveIDs = result.first->second._compatibleIDs.data (); + + result.first->second._factory.analyzeableContentTypes = result.first->second._analyzableTypes.data (); + } + return &result.first->second._factory; +} + +void ARAIPCProxyPlugInInitializeARA (ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID, ARAAPIGeneration desiredApiGeneration) +{ + ARA_INTERNAL_ASSERT (desiredApiGeneration >= kARAAPIGeneration_2_0_Final); + RemoteCaller { fromIPCRef (connectionRef) }.remoteCall (kInitializeARAMethodID, factoryID, desiredApiGeneration); +} + +const ARADocumentControllerInstance* ARAIPCProxyPlugInCreateDocumentControllerWithDocument ( + ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID, + const ARADocumentControllerHostInstance* hostInstance, const ARADocumentProperties* properties) +{ + const auto cached { _factories.find (std::string { factoryID }) }; + ARA_INTERNAL_ASSERT (cached != _factories.end ()); + if (cached == _factories.end ()) + return nullptr; + + auto result { new DocumentController { fromIPCRef (connectionRef), &cached->second._factory, hostInstance, properties } }; + return result->getInstance (); +} + +const ARAPlugInExtensionInstance* ARAIPCProxyPlugInBindToDocumentController (ARAIPCPlugInInstanceRef remoteRef, ARADocumentControllerRef documentControllerRef, + ARAPlugInInstanceRoleFlags knownRoles, ARAPlugInInstanceRoleFlags assignedRoles) +{ + auto documentController { static_cast (PlugIn::fromRef (documentControllerRef)) }; + const auto remoteDocumentControllerRef { documentController->getRemoteRef () }; + + size_t remoteExtensionRef {}; + documentController->remoteCall (remoteExtensionRef, kBindToDocumentControllerMethodID, remoteRef, remoteDocumentControllerRef, knownRoles, assignedRoles); + + return new PlugInExtension { documentController->getConnection (), reinterpret_cast (remoteExtensionRef), documentControllerRef, knownRoles, assignedRoles }; +} + +void ARAIPCProxyPlugInCleanupBinding (const ARAPlugInExtensionInstance* plugInExtensionInstance) +{ + delete static_cast (plugInExtensionInstance); +} + +void ARAIPCProxyPlugInUninitializeARA (ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID) +{ + RemoteCaller { fromIPCRef (connectionRef) }.remoteCall (kUninitializeARAMethodID, factoryID); +} + + +/*******************************************************************************/ + +void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, + MessageEncoder* const replyEncoder) +{ + ARA_IPC_LOG ("ProxyPlugIn handles '%s'", decodePlugInMessageID (messageID)); + + // ARAAudioAccessControllerInterface + if (messageID == ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, createAudioReaderForSource)) + { + ARAAudioAccessControllerHostRef controllerHostRef; + ARAAudioSourceHostRef audioSourceHostRef; + ARABool use64BitSamples; + decodeArguments (decoder, controllerHostRef, audioSourceHostRef, use64BitSamples); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto audioSource { fromHostRef (audioSourceHostRef) }; + ARA_VALIDATE_API_ARGUMENT (audioSourceHostRef, isValidInstance (audioSource)); + + auto reader { new HostAudioReader { audioSource, nullptr, (use64BitSamples != kARAFalse) ? sizeof (double) : sizeof (float) } }; + reader->hostRef = documentController->getHostAudioAccessController ()->createAudioReaderForSource (audioSource->_hostRef, (use64BitSamples) ? kARATrue : kARAFalse); + ARAAudioReaderHostRef audioReaderHostRef { toHostRef (reader) }; + encodeReply (replyEncoder, audioReaderHostRef); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, readAudioSamples)) + { + ARAAudioAccessControllerHostRef controllerHostRef; + ARAAudioReaderHostRef audioReaderHostRef; + ARASamplePosition samplePosition; + ARASampleCount samplesPerChannel; + decodeArguments (decoder, controllerHostRef, audioReaderHostRef, samplePosition, samplesPerChannel); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + auto reader { fromHostRef (audioReaderHostRef) }; + + // \todo using static (plus not copy bytes) here assumes single-threaded callbacks, but currently this is a valid requirement + static std::vector bufferData; + const auto channelCount { static_cast (reader->audioSource->_channelCount) }; + const auto bufferSize { reader->sampleSize * static_cast (samplesPerChannel) }; + const auto allBuffersSize { channelCount * bufferSize }; + if (bufferData.size () < allBuffersSize) + bufferData.resize (allBuffersSize); + + static std::vector sampleBuffers; + static std::vector encoders; + if (sampleBuffers.size () < channelCount) + sampleBuffers.resize (channelCount, nullptr); + if (encoders.size () < channelCount) + encoders.resize (channelCount, { nullptr, 0, false }); + for (auto i { 0U }; i < channelCount; ++i) + { + const auto buffer { bufferData.data () + i * bufferSize }; + sampleBuffers[i] = buffer; + encoders[i] = { buffer, bufferSize, false }; + } + + if (documentController->getHostAudioAccessController ()->readAudioSamples (reader->hostRef, samplePosition, samplesPerChannel, sampleBuffers.data ())) + encodeReply (replyEncoder, ArrayArgument { encoders.data (), encoders.size () }); + // else send empty reply as indication of failure + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, destroyAudioReader)) + { + ARAAudioAccessControllerHostRef controllerHostRef; + ARAAudioReaderHostRef audioReaderHostRef; + decodeArguments (decoder, controllerHostRef, audioReaderHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto reader { fromHostRef (audioReaderHostRef) }; + + documentController->getHostAudioAccessController ()->destroyAudioReader (reader->hostRef); + delete reader; + } + + // ARAArchivingControllerInterface + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, getArchiveSize)) + { + ARAArchivingControllerHostRef controllerHostRef; + ARAArchiveReaderHostRef archiveReaderHostRef; + decodeArguments (decoder, controllerHostRef, archiveReaderHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + encodeReply (replyEncoder, documentController->getHostArchivingController ()->getArchiveSize (archiveReaderHostRef)); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, readBytesFromArchive)) + { + ARAArchivingControllerHostRef controllerHostRef; + ARAArchiveReaderHostRef archiveReaderHostRef; + ARASize position; + ARASize length; + decodeArguments (decoder, controllerHostRef, archiveReaderHostRef, position, length); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + // \todo using static here assumes single-threaded callbacks, but currently this is a valid requirement + static std::vector bytes; + bytes.resize (length); + if (!documentController->getHostArchivingController ()->readBytesFromArchive (archiveReaderHostRef, position, length, bytes.data ())) + bytes.clear (); + encodeReply (replyEncoder, BytesEncoder { bytes, false }); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, writeBytesToArchive)) + { + ARAArchivingControllerHostRef controllerHostRef; + ARAArchiveWriterHostRef archiveWriterHostRef; + ARASize position; + std::vector bytes; + BytesDecoder writer { bytes }; + decodeArguments (decoder, controllerHostRef, archiveWriterHostRef, position, writer); + ARA_INTERNAL_ASSERT (bytes.size () > 0); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + encodeReply (replyEncoder, documentController->getHostArchivingController ()->writeBytesToArchive (archiveWriterHostRef, position, bytes.size (), bytes.data ())); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentArchivingProgress)) + { + ARAArchivingControllerHostRef controllerHostRef; + float value; + decodeArguments (decoder, controllerHostRef, value); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostArchivingController ()->notifyDocumentArchivingProgress (value); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress)) + { + ARAArchivingControllerHostRef controllerHostRef; + float value; + decodeArguments (decoder, controllerHostRef, value); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostArchivingController ()->notifyDocumentUnarchivingProgress (value); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, getDocumentArchiveID)) + { + ARAArchivingControllerHostRef controllerHostRef; + ARAArchiveReaderHostRef archiveReaderHostRef; + decodeArguments (decoder, controllerHostRef, archiveReaderHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + encodeReply (replyEncoder, documentController->getHostArchivingController ()->getDocumentArchiveID (archiveReaderHostRef)); + } + + // ARAContentAccessControllerInterface + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, isMusicalContextContentAvailable)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAMusicalContextHostRef musicalContextHostRef; + ARAContentType contentType; + decodeArguments (decoder, controllerHostRef, musicalContextHostRef, contentType); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + encodeReply (replyEncoder, (documentController->getHostContentAccessController ()->isMusicalContextContentAvailable (musicalContextHostRef, contentType)) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getMusicalContextContentGrade)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAMusicalContextHostRef musicalContextHostRef; + ARAContentType contentType; + decodeArguments (decoder, controllerHostRef, musicalContextHostRef, contentType); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + encodeReply (replyEncoder, documentController->getHostContentAccessController ()->getMusicalContextContentGrade (musicalContextHostRef, contentType)); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, createMusicalContextContentReader)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAMusicalContextHostRef musicalContextHostRef; + ARAContentType contentType; + OptionalArgument range; + decodeArguments (decoder, controllerHostRef, musicalContextHostRef, contentType, range); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + auto hostContentReader { new HostContentReader }; + hostContentReader->hostRef = documentController->getHostContentAccessController ()->createMusicalContextContentReader (musicalContextHostRef, contentType, (range.second) ? &range.first : nullptr); + hostContentReader->contentType = contentType; + + encodeReply (replyEncoder, ARAContentReaderHostRef { toHostRef (hostContentReader) }); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, isAudioSourceContentAvailable)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAAudioSourceHostRef audioSourceHostRef; + ARAContentType contentType; + decodeArguments (decoder, controllerHostRef, audioSourceHostRef, contentType); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto audioSource { fromHostRef (audioSourceHostRef) }; + ARA_VALIDATE_API_ARGUMENT (audioSourceHostRef, isValidInstance (audioSource)); + + encodeReply (replyEncoder, (documentController->getHostContentAccessController ()->isAudioSourceContentAvailable (audioSource->_hostRef, contentType)) ? kARATrue : kARAFalse); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getAudioSourceContentGrade)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAAudioSourceHostRef audioSourceHostRef; + ARAContentType contentType; + decodeArguments (decoder, controllerHostRef, audioSourceHostRef, contentType); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto audioSource { fromHostRef (audioSourceHostRef) }; + ARA_VALIDATE_API_ARGUMENT (audioSourceHostRef, isValidInstance (audioSource)); + + encodeReply (replyEncoder, documentController->getHostContentAccessController ()->getAudioSourceContentGrade (audioSource->_hostRef, contentType)); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, createAudioSourceContentReader)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAAudioSourceHostRef audioSourceHostRef; + ARAContentType contentType; + OptionalArgument range; + decodeArguments (decoder, controllerHostRef, audioSourceHostRef, contentType, range); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto audioSource { fromHostRef (audioSourceHostRef) }; + ARA_VALIDATE_API_ARGUMENT (audioSourceHostRef, isValidInstance (audioSource)); + + auto hostContentReader { new HostContentReader }; + hostContentReader->hostRef = documentController->getHostContentAccessController ()->createAudioSourceContentReader (audioSource->_hostRef, contentType, (range.second) ? &range.first : nullptr); + hostContentReader->contentType = contentType; + encodeReply (replyEncoder, ARAContentReaderHostRef { toHostRef (hostContentReader) }); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderEventCount)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAContentReaderHostRef contentReaderHostRef; + decodeArguments (decoder, controllerHostRef, contentReaderHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto hostContentReader { fromHostRef (contentReaderHostRef) }; + + encodeReply (replyEncoder, documentController->getHostContentAccessController ()->getContentReaderEventCount (hostContentReader->hostRef)); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderDataForEvent)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAContentReaderHostRef contentReaderHostRef; + ARAInt32 eventIndex; + decodeArguments (decoder, controllerHostRef, contentReaderHostRef, eventIndex); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto hostContentReader { fromHostRef (contentReaderHostRef) }; + + const void* eventData { documentController->getHostContentAccessController ()->getContentReaderDataForEvent (hostContentReader->hostRef, eventIndex) }; + encodeContentEvent (replyEncoder, hostContentReader->contentType, eventData); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, destroyContentReader)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAContentReaderHostRef contentReaderHostRef; + decodeArguments (decoder, controllerHostRef, contentReaderHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto hostContentReader { fromHostRef (contentReaderHostRef) }; + + documentController->getHostContentAccessController ()->destroyContentReader (hostContentReader->hostRef); + delete hostContentReader; + } + + // ARAModelUpdateControllerInterface + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAAudioSourceHostRef audioSourceHostRef; + ARAAnalysisProgressState state; + float value; + decodeArguments (decoder, controllerHostRef, audioSourceHostRef, state, value); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto audioSource { fromHostRef (audioSourceHostRef) }; + ARA_VALIDATE_API_ARGUMENT (audioSourceHostRef, isValidInstance (audioSource)); + + documentController->getHostModelUpdateController ()->notifyAudioSourceAnalysisProgress (audioSource->_hostRef, state, value); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAAudioSourceHostRef audioSourceHostRef; + OptionalArgument range; + ARAContentUpdateFlags scopeFlags; + decodeArguments (decoder, controllerHostRef, audioSourceHostRef, range, scopeFlags); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + auto audioSource { fromHostRef (audioSourceHostRef) }; + ARA_VALIDATE_API_ARGUMENT (audioSourceHostRef, isValidInstance (audioSource)); + + documentController->getHostModelUpdateController ()->notifyAudioSourceContentChanged (audioSource->_hostRef, (range.second) ? &range.first : nullptr, scopeFlags); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAAudioModificationHostRef audioModificationHostRef; + OptionalArgument range; + ARAContentUpdateFlags scopeFlags; + decodeArguments (decoder, controllerHostRef, audioModificationHostRef, range, scopeFlags); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostModelUpdateController ()->notifyAudioModificationContentChanged (audioModificationHostRef, (range.second) ? &range.first : nullptr, scopeFlags); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARAPlaybackRegionHostRef playbackRegionHostRef; + OptionalArgument range; + ARAContentUpdateFlags scopeFlags; + decodeArguments (decoder, controllerHostRef, playbackRegionHostRef, range, scopeFlags); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostModelUpdateController ()->notifyPlaybackRegionContentChanged (playbackRegionHostRef, (range.second) ? &range.first : nullptr, scopeFlags); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyDocumentDataChanged)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + decodeArguments (decoder, controllerHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostModelUpdateController ()->notifyDocumentDataChanged (); + } + + // ARAPlaybackControllerInterface + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestStartPlayback)) + { + ARAPlaybackControllerHostRef controllerHostRef; + decodeArguments (decoder, controllerHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostPlaybackController ()->requestStartPlayback (); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestStopPlayback)) + { + ARAPlaybackControllerHostRef controllerHostRef; + decodeArguments (decoder, controllerHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostPlaybackController ()->requestStopPlayback (); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestSetPlaybackPosition)) + { + ARAPlaybackControllerHostRef controllerHostRef; + ARATimePosition timePosition; + decodeArguments (decoder, controllerHostRef, timePosition); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostPlaybackController ()->requestSetPlaybackPosition (timePosition); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestSetCycleRange)) + { + ARAPlaybackControllerHostRef controllerHostRef; + ARATimePosition startTime; + ARATimeDuration duration; + decodeArguments (decoder, controllerHostRef, startTime, duration); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostPlaybackController ()->requestSetCycleRange (startTime, duration); + } + else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestEnableCycle)) + { + ARAPlaybackControllerHostRef controllerHostRef; + ARABool enable; + decodeArguments (decoder, controllerHostRef, enable); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostPlaybackController ()->requestEnableCycle (enable != kARAFalse); + } + else + { + ARA_INTERNAL_ASSERT (false && "unhandled message ID"); + } +} + +} // namespace IPC +} // namespace ARA + +#endif // ARA_ENABLE_IPC diff --git a/IPC/ARAIPCProxyPlugIn.h b/IPC/ARAIPCProxyPlugIn.h new file mode 100644 index 0000000..48ede70 --- /dev/null +++ b/IPC/ARAIPCProxyPlugIn.h @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +//! \file ARAIPCProxyPlugIn.h +//! implementation of host-side ARA IPC proxy plug-in +//! \project ARA SDK Library +//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \license Licensed under the Apache License, Version 2.0 (the "License"); +//! you may not use this file except in compliance with the License. +//! You may obtain a copy of the License at +//! +//! http://www.apache.org/licenses/LICENSE-2.0 +//! +//! Unless required by applicable law or agreed to in writing, software +//! distributed under the License is distributed on an "AS IS" BASIS, +//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//! See the License for the specific language governing permissions and +//! limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef ARAIPCProxyPlugIn_h +#define ARAIPCProxyPlugIn_h + +#if defined(__cplusplus) +#include "ARA_Library/IPC/ARAIPCConnection.h" +#include "ARA_Library/IPC/ARAIPCEncoding.h" +#else +#include "ARA_Library/IPC/ARAIPC.h" +#endif + + +#if ARA_ENABLE_IPC + + +//! @addtogroup ARA_Library_IPC +//! @{ + +#if defined(__cplusplus) +namespace ARA { +namespace IPC { +extern "C" { + +//! host side implementation of MessageHandler +//! the host uses the C interface below, but this class will be subclassed by specialized implementations +class ProxyPlugIn : public MessageHandler, public RemoteCaller +{ +public: + using RemoteCaller::RemoteCaller; + + void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, + MessageEncoder* const replyEncoder) override; +}; +#endif + + +//! counts the factories available through the given message channel +size_t ARAIPCProxyPlugInGetFactoriesCount(ARAIPCConnectionRef connectionRef); + +//! get a static copy of the remote factory data, with all function calls removed +//! index must be smaller than the result of ARAIPCProxyPlugInGetFactoriesCount() +const ARAFactory * ARAIPCProxyPlugInGetFactoryAtIndex(ARAIPCConnectionRef connectionRef, size_t index); + +//! proxy initialization call, to be used instead of ARAFactory.initializeARAWithConfiguration() +// \todo we're currently not supporting propagating ARA assertions through IPC... +void ARAIPCProxyPlugInInitializeARA(ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID, ARAAPIGeneration desiredApiGeneration); + +//! proxy document controller creation call, to be used instead of ARAFactory.createDocumentControllerWithDocument() +const ARADocumentControllerInstance * ARAIPCProxyPlugInCreateDocumentControllerWithDocument(ARAIPCConnectionRef connectionRef, + const ARAPersistentID factoryID, + const ARADocumentControllerHostInstance * hostInstance, + const ARADocumentProperties * properties); + +//! create the proxy plug-in extension when performing the binding to the remote plug-in instance +const ARAPlugInExtensionInstance * ARAIPCProxyPlugInBindToDocumentController(ARAIPCPlugInInstanceRef remoteRef, ARADocumentControllerRef documentControllerRef, + ARAPlugInInstanceRoleFlags knownRoles, ARAPlugInInstanceRoleFlags assignedRoles); + +//! trigger proper teardown of proxy plug-in extension upon destroying a remote plug-in instance that has been bound to ARA +void ARAIPCProxyPlugInCleanupBinding(const ARAPlugInExtensionInstance * plugInExtension); + +//! proxy uninitialization call, to be used instead of ARAFactory.uninitializeARA() +void ARAIPCProxyPlugInUninitializeARA(ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID); + + +#if defined(__cplusplus) +} // extern "C" +} // namespace IPC +} // namespace ARA +#endif + +//! @} ARA_Library_IPC + + +#endif // ARA_ENABLE_IPC + +#endif // ARAIPCProxyPlugIn_h From 48063cbf03e3a01aa3cbdce63569a0114e64fad7 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 6 Nov 2025 13:16:59 +0100 Subject: [PATCH 02/42] Bump version number to 3.0.0 for future development --- ChangeLog.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index d9fd87f..c6b1cfd 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,6 +1,10 @@ -Proposed Features which have been postponed to a later subrelease: -- generic ARA IPC library, providing a proxy host and a proxy plug-in, - based on the IPC Example from earlier SDK releases +This is a development build of the ARA Library 3.0. +=== PRELIMINARY - DO NOT USE FOR SHIPPING PRODUCTS! === + + +Changes since previous releases: +- initial draft of generic ARA IPC library providing a proxy host and a proxy plug-in, + based on heavily refactored IPC Example from earlier SDK releases === ARA SDK 2.3 release (aka 2.3.001) (2025/11/07) === From d75c176eadb6b753a23fafab7c75083d80151063 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Tue, 17 Feb 2026 12:26:19 +0100 Subject: [PATCH 03/42] Bump copyright year to 2026 --- CMakeLists.txt | 2 +- Debug/ARAContentLogger.h | 2 +- Debug/ARAContentValidator.h | 2 +- Debug/ARADebug.c | 2 +- Debug/ARADebug.h | 2 +- Dispatch/ARAContentReader.h | 2 +- Dispatch/ARADispatchBase.h | 2 +- Dispatch/ARAHostDispatch.cpp | 2 +- Dispatch/ARAHostDispatch.h | 2 +- Dispatch/ARAPlugInDispatch.cpp | 2 +- Dispatch/ARAPlugInDispatch.h | 2 +- IPC/ARAIPC.h | 2 +- IPC/ARAIPCAudioUnit_v3.h | 2 +- IPC/ARAIPCAudioUnit_v3.mm | 2 +- IPC/ARAIPCCFEncoding.cpp | 2 +- IPC/ARAIPCCFEncoding.h | 2 +- IPC/ARAIPCConnection.cpp | 2 +- IPC/ARAIPCConnection.h | 2 +- IPC/ARAIPCEncoding.h | 2 +- IPC/ARAIPCMessage.h | 2 +- IPC/ARAIPCProxyHost.cpp | 2 +- IPC/ARAIPCProxyHost.h | 2 +- IPC/ARAIPCProxyPlugIn.cpp | 2 +- IPC/ARAIPCProxyPlugIn.h | 2 +- PlugIn/ARAPlug.cpp | 2 +- PlugIn/ARAPlug.h | 2 +- README.md | 2 +- Utilities/ARAChannelFormat.cpp | 2 +- Utilities/ARAChannelFormat.h | 2 +- Utilities/ARAPitchInterpretation.cpp | 2 +- Utilities/ARAPitchInterpretation.h | 2 +- Utilities/ARASamplePositionConversion.h | 2 +- Utilities/ARAStdVectorUtilities.h | 2 +- Utilities/ARATimelineConversion.h | 2 +- 34 files changed, 34 insertions(+), 34 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59d00ab..ebc50af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # ====================== # ARA Library CMake Integration # -# Copyright (c) 2020-2025, Celemony Software GmbH, All Rights Reserved. +# Copyright (c) 2020-2026, Celemony Software GmbH, All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/Debug/ARAContentLogger.h b/Debug/ARAContentLogger.h index 85e3b5f..9cbacb0 100644 --- a/Debug/ARAContentLogger.h +++ b/Debug/ARAContentLogger.h @@ -2,7 +2,7 @@ //! \file ARAContentLogger.h //! class to conveniently log (and validate) ARA content //! \project ARA SDK Library -//! \copyright Copyright (c) 2018-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2018-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Debug/ARAContentValidator.h b/Debug/ARAContentValidator.h index dff8b8a..e578922 100644 --- a/Debug/ARAContentValidator.h +++ b/Debug/ARAContentValidator.h @@ -2,7 +2,7 @@ //! \file ARAContentValidator.h //! utility classes vor validating content readers //! \project ARA SDK Library -//! \copyright Copyright (c) 2018-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2018-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Debug/ARADebug.c b/Debug/ARADebug.c index f6e828c..de7cc98 100644 --- a/Debug/ARADebug.c +++ b/Debug/ARADebug.c @@ -2,7 +2,7 @@ //! \file ARADebug.c //! debug helpers for the ARA SDK Library //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Debug/ARADebug.h b/Debug/ARADebug.h index b92e7c0..b6aff98 100644 --- a/Debug/ARADebug.h +++ b/Debug/ARADebug.h @@ -2,7 +2,7 @@ //! \file ARADebug.h //! debug helpers for the ARA SDK Library //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Dispatch/ARAContentReader.h b/Dispatch/ARAContentReader.h index 701ca6e..b852df6 100644 --- a/Dispatch/ARAContentReader.h +++ b/Dispatch/ARAContentReader.h @@ -2,7 +2,7 @@ //! \file ARAContentReader.h //! content reading utility classes //! \project ARA SDK Library -//! \copyright Copyright (c) 2018-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2018-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Dispatch/ARADispatchBase.h b/Dispatch/ARADispatchBase.h index c570c4e..05de6f8 100644 --- a/Dispatch/ARADispatchBase.h +++ b/Dispatch/ARADispatchBase.h @@ -5,7 +5,7 @@ //! Typically, this file is not included directly - either ARAHostDispatch.h or //! ARAPlugInDispatch.h will be used instead. //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! Developed in cooperation with PreSonus Software Ltd. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. diff --git a/Dispatch/ARAHostDispatch.cpp b/Dispatch/ARAHostDispatch.cpp index 5444481..15ddf99 100644 --- a/Dispatch/ARAHostDispatch.cpp +++ b/Dispatch/ARAHostDispatch.cpp @@ -3,7 +3,7 @@ //! C-to-C++ adapter for implementing ARA hosts //! Originally written and contributed to the ARA SDK by PreSonus Software Ltd. //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! Developed in cooperation with PreSonus Software Ltd. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index 4463fb6..4d40409 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -3,7 +3,7 @@ //! C-to-C++ adapter for implementing ARA hosts //! Originally written and contributed to the ARA SDK by PreSonus Software Ltd. //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! Developed in cooperation with PreSonus Software Ltd. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. diff --git a/Dispatch/ARAPlugInDispatch.cpp b/Dispatch/ARAPlugInDispatch.cpp index 09d6b9c..10814a3 100644 --- a/Dispatch/ARAPlugInDispatch.cpp +++ b/Dispatch/ARAPlugInDispatch.cpp @@ -2,7 +2,7 @@ //! \file ARAPlugInDispatch.cpp //! C-to-C++ adapter for implementing ARA plug-ins //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Dispatch/ARAPlugInDispatch.h b/Dispatch/ARAPlugInDispatch.h index 8d7e089..105ea50 100644 --- a/Dispatch/ARAPlugInDispatch.h +++ b/Dispatch/ARAPlugInDispatch.h @@ -2,7 +2,7 @@ //! \file ARAPlugInDispatch.h //! C-to-C++ adapter for implementing ARA plug-ins //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPC.h b/IPC/ARAIPC.h index ff56194..cf9dfa2 100644 --- a/IPC/ARAIPC.h +++ b/IPC/ARAIPC.h @@ -4,7 +4,7 @@ //! Typically, this file is not included directly - either ARAIPCProxyHost.h //! ARAIPCProxyPlugIn.h will be used instead. //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCAudioUnit_v3.h b/IPC/ARAIPCAudioUnit_v3.h index 831ea63..7a86c73 100644 --- a/IPC/ARAIPCAudioUnit_v3.h +++ b/IPC/ARAIPCAudioUnit_v3.h @@ -2,7 +2,7 @@ //! \file ARAIPCAudioUnit_v3.h //! Implementation of ARA IPC message sending through AUMessageChannel //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index f39b008..6912b58 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -2,7 +2,7 @@ //! \file ARAIPCAudioUnit_v3.m //! Implementation of ARA IPC message sending through AUMessageChannel //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCCFEncoding.cpp b/IPC/ARAIPCCFEncoding.cpp index baa8152..76af582 100644 --- a/IPC/ARAIPCCFEncoding.cpp +++ b/IPC/ARAIPCCFEncoding.cpp @@ -2,7 +2,7 @@ //! \file ARAIPCCFEncoding.cpp //! Implementation of ARAIPCMessageEn-/Decoder backed by CF(Mutable)Dictionary //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCCFEncoding.h b/IPC/ARAIPCCFEncoding.h index 701e767..05075c0 100644 --- a/IPC/ARAIPCCFEncoding.h +++ b/IPC/ARAIPCCFEncoding.h @@ -2,7 +2,7 @@ //! \file ARAIPCCFEncoding.h //! Implementation of ARAIPCMessageEn-/Decoder backed by CF(Mutable)Dictionary //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 80157a1..969a258 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -2,7 +2,7 @@ //! \file ARAIPCConnection.cpp //! Connection to another process to pass messages to/receive messages from it //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 36137c0..56ce376 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -2,7 +2,7 @@ //! \file ARAIPCConnection.h //! Connection to another process to pass messages to/receive messages from it //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 8862469..619d01a 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -4,7 +4,7 @@ //! Typically, this file is not included directly - either ARAIPCProxyHost.h //! ARAIPCProxyPlugIn.h will be used instead. //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCMessage.h b/IPC/ARAIPCMessage.h index 1503010..2e4bc4e 100644 --- a/IPC/ARAIPCMessage.h +++ b/IPC/ARAIPCMessage.h @@ -2,7 +2,7 @@ //! \file ARAIPCMessage.h //! Abstractions shared by both the ARA IPC proxy host and plug-in //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index 20fdcae..94ae433 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -2,7 +2,7 @@ //! \file ARAIPCProxyHost.cpp //! implementation of host-side ARA IPC proxy host //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCProxyHost.h b/IPC/ARAIPCProxyHost.h index 3e44a17..9fca99a 100644 --- a/IPC/ARAIPCProxyHost.h +++ b/IPC/ARAIPCProxyHost.h @@ -2,7 +2,7 @@ //! \file ARAIPCProxyHost.h //! implementation of host-side ARA IPC proxy host //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp index cfb856f..2a29857 100644 --- a/IPC/ARAIPCProxyPlugIn.cpp +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -2,7 +2,7 @@ //! \file ARAIPCProxyPlugIn.cpp //! implementation of host-side ARA IPC proxy plug-in //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/IPC/ARAIPCProxyPlugIn.h b/IPC/ARAIPCProxyPlugIn.h index 48ede70..cfa8154 100644 --- a/IPC/ARAIPCProxyPlugIn.h +++ b/IPC/ARAIPCProxyPlugIn.h @@ -2,7 +2,7 @@ //! \file ARAIPCProxyPlugIn.h //! implementation of host-side ARA IPC proxy plug-in //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index 5a90e10..1c9dbf4 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -2,7 +2,7 @@ //! \file ARAPlug.cpp //! implementation of base classes for ARA plug-ins //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index 9975d76..59ca6a2 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -2,7 +2,7 @@ //! \file ARAPlug.h //! implementation of base classes for ARA plug-ins //! \project ARA SDK Library -//! \copyright Copyright (c) 2012-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2012-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/README.md b/README.md index 0abf2e1..0a969a9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ARA Audio Random Access: Implementation Library -Copyright (c) 2012-2025, [Celemony Software GmbH](https://www.celemony.com), All Rights Reserved. +Copyright (c) 2012-2026, [Celemony Software GmbH](https://www.celemony.com), All Rights Reserved. Published under the Apache 2.0 license. The ARA_Library Git repository contains an extensive set of C++ classes and utility functions to ease diff --git a/Utilities/ARAChannelFormat.cpp b/Utilities/ARAChannelFormat.cpp index b58056d..e9416c5 100644 --- a/Utilities/ARAChannelFormat.cpp +++ b/Utilities/ARAChannelFormat.cpp @@ -2,7 +2,7 @@ //! \file ARAChannelFormat.cpp //! utility class dealing with the companion-API-dependent surround channel arrangements //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Utilities/ARAChannelFormat.h b/Utilities/ARAChannelFormat.h index 26d1614..8d5a81a 100644 --- a/Utilities/ARAChannelFormat.h +++ b/Utilities/ARAChannelFormat.h @@ -2,7 +2,7 @@ //! \file ARAChannelFormat.h //! utility class dealing with the companion-API-dependent surround channel arrangements //! \project ARA SDK Library -//! \copyright Copyright (c) 2021-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2021-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Utilities/ARAPitchInterpretation.cpp b/Utilities/ARAPitchInterpretation.cpp index d15b639..1c5c86f 100644 --- a/Utilities/ARAPitchInterpretation.cpp +++ b/Utilities/ARAPitchInterpretation.cpp @@ -3,7 +3,7 @@ //! classes to find proper names for pitches, ARAContentChord and ARAContentKeySignature //! if not provided by the API partner //! \project ARA SDK Library -//! \copyright Copyright (c) 2018-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2018-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Utilities/ARAPitchInterpretation.h b/Utilities/ARAPitchInterpretation.h index 1e312a2..9804cc6 100644 --- a/Utilities/ARAPitchInterpretation.h +++ b/Utilities/ARAPitchInterpretation.h @@ -3,7 +3,7 @@ //! classes to find proper names for pitches, ARAContentChord and ARAContentKeySignature //! if not provided by the API partner //! \project ARA SDK Library -//! \copyright Copyright (c) 2018-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2018-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Utilities/ARASamplePositionConversion.h b/Utilities/ARASamplePositionConversion.h index 696c8dc..1036e9c 100644 --- a/Utilities/ARASamplePositionConversion.h +++ b/Utilities/ARASamplePositionConversion.h @@ -3,7 +3,7 @@ //! convenience functions to ensure consistent conversion from //! continuous time to discrete sample positions and vice versa. //! \project ARA SDK Library -//! \copyright Copyright (c) 2018-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2018-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Utilities/ARAStdVectorUtilities.h b/Utilities/ARAStdVectorUtilities.h index 240b020..bc30722 100644 --- a/Utilities/ARAStdVectorUtilities.h +++ b/Utilities/ARAStdVectorUtilities.h @@ -4,7 +4,7 @@ //! STL's std::vector, e.g. when searching derived class pointers //! in base class vectors and vice versa //! \project ARA SDK Library -//! \copyright Copyright (c) 2018-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2018-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at diff --git a/Utilities/ARATimelineConversion.h b/Utilities/ARATimelineConversion.h index 57e7a90..88b6d7f 100644 --- a/Utilities/ARATimelineConversion.h +++ b/Utilities/ARATimelineConversion.h @@ -3,7 +3,7 @@ //! classes to effectively iterate over tempo and signature content readers //! and convert between in seconds or quarters and between quarters and beats //! \project ARA SDK Library -//! \copyright Copyright (c) 2018-2025, Celemony Software GmbH, All Rights Reserved. +//! \copyright Copyright (c) 2018-2026, Celemony Software GmbH, All Rights Reserved. //! \license Licensed under the Apache License, Version 2.0 (the "License"); //! you may not use this file except in compliance with the License. //! You may obtain a copy of the License at From f39bc50a88c0fc6f42cdb4c110f9bb68b48e3dbc Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Tue, 17 Feb 2026 12:26:19 +0100 Subject: [PATCH 04/42] Add API generation 3.0 Draft, starting ARA 3 development --- PlugIn/ARAPlug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index 59ca6a2..3ab5c4a 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -1802,7 +1802,7 @@ class FactoryConfig { return kARAAPIGeneration_2_0_Draft; } #endif //! \copydoc ARAFactory::highestSupportedApiGeneration - virtual ARAAPIGeneration getHighestSupportedApiGeneration () const noexcept { return kARAAPIGeneration_2_X_Draft; } + virtual ARAAPIGeneration getHighestSupportedApiGeneration () const noexcept { return kARAAPIGeneration_3_0_Draft; } virtual DocumentController* createDocumentController (const PlugInEntry* entry, const ARADocumentControllerHostInstance* instance) const noexcept = 0; virtual void destroyDocumentController (DocumentController* documentController) const noexcept { delete documentController; } From eceb61a821086952a7d3d2f07529b538573492fc Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:03 +0200 Subject: [PATCH 05/42] Drop obsolete ARA 1 support, including deprecated ARAPlugInExtensionInterface --- ChangeLog.txt | 1 + Dispatch/ARADispatchBase.h | 8 -- Dispatch/ARAHostDispatch.cpp | 149 --------------------------------- Dispatch/ARAHostDispatch.h | 17 ---- Dispatch/ARAPlugInDispatch.cpp | 64 -------------- Dispatch/ARAPlugInDispatch.h | 9 -- IPC/ARAIPCProxyHost.cpp | 5 -- IPC/ARAIPCProxyPlugIn.cpp | 5 -- PlugIn/ARAPlug.cpp | 41 ++------- PlugIn/ARAPlug.h | 11 +-- 10 files changed, 7 insertions(+), 303 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index c6b1cfd..24e8afd 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -5,6 +5,7 @@ This is a development build of the ARA Library 3.0. Changes since previous releases: - initial draft of generic ARA IPC library providing a proxy host and a proxy plug-in, based on heavily refactored IPC Example from earlier SDK releases +- support for ARA 1 dropped === ARA SDK 2.3 release (aka 2.3.001) (2025/11/07) === diff --git a/Dispatch/ARADispatchBase.h b/Dispatch/ARADispatchBase.h index 05de6f8..6aa9066 100644 --- a/Dispatch/ARADispatchBase.h +++ b/Dispatch/ARADispatchBase.h @@ -52,14 +52,6 @@ */ /*******************************************************************************/ -#if !defined (ARA_SUPPORT_VERSION_1) - #define ARA_SUPPORT_VERSION_1 0 -#endif - -#if ARA_SUPPORT_VERSION_1 && ARA_CPU_ARM - #error "ARA v1 is not supported on ARM architecture" -#endif - namespace ARA { diff --git a/Dispatch/ARAHostDispatch.cpp b/Dispatch/ARAHostDispatch.cpp index 15ddf99..0d134bf 100644 --- a/Dispatch/ARAHostDispatch.cpp +++ b/Dispatch/ARAHostDispatch.cpp @@ -463,31 +463,16 @@ void DocumentController::destroyMusicalContext (ARAMusicalContextRef musicalCont ARARegionSequenceRef DocumentController::createRegionSequence (ARARegionSequenceHostRef hostRef, const ARARegionSequenceProperties* properties) noexcept { -#if ARA_SUPPORT_VERSION_1 - if (!getInterface ().implements ()) - return nullptr; -#endif - return getInterface ()->createRegionSequence (getRef (), hostRef, properties); } void DocumentController::updateRegionSequenceProperties (ARARegionSequenceRef regionSequenceRef, const ARARegionSequenceProperties* properties) noexcept { -#if ARA_SUPPORT_VERSION_1 - if (!getInterface ().implements ()) - return; -#endif - return getInterface ()->updateRegionSequenceProperties (getRef (), regionSequenceRef, properties); } void DocumentController::destroyRegionSequence (ARARegionSequenceRef regionSequenceRef) noexcept { -#if ARA_SUPPORT_VERSION_1 - if (!getInterface ().implements ()) - return; -#endif - return getInterface ()->destroyRegionSequence (getRef (), regionSequenceRef); } @@ -570,15 +555,6 @@ void DocumentController::updatePlaybackRegionProperties (ARAPlaybackRegionRef pl void DocumentController::getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept { -#if ARA_SUPPORT_VERSION_1 - if (!getInterface ().implements ()) - { - *headTime = 0.0; - *tailTime = 0.0; - return; - } -#endif - return getInterface ()->getPlaybackRegionHeadAndTailTime (getRef (), playbackRegionRef, headTime, tailTime); } @@ -697,65 +673,6 @@ bool DocumentController::isLicensedForCapabilities (bool runModalActivationDialo // PlaybackRenderer /*******************************************************************************/ -#if ARA_SUPPORT_VERSION_1 - -bool supportsARA2 (const ARAPlugInExtensionInstance* instance) -{ - return SizedStructPtr (instance).implements (); -} - -class ARA1PlugInExtension : public InterfaceInstance -{ -public: - explicit ARA1PlugInExtension (const ARAPlugInExtensionInstance* instance) noexcept - : BaseType { instance->plugInExtensionRef, instance->plugInExtensionInterface } - {} -}; -// this is not really a host ref but rather a dummy plug-in ref created by the host, -// but since this macro only deals with proper casting this difference does not matter. -ARA_MAP_HOST_REF (ARA1PlugInExtension, ARAPlaybackRendererRef) - -// playback renderer for ARA 1 is forwarding to plug-in extension -namespace ARA1PlaybackRendererAdapterDispatcher -{ - static void ARA_CALL addPlaybackRegion (ARAPlaybackRendererRef playbackRendererRef, ARAPlaybackRegionRef playbackRegionRef) - { - auto plugInExtension { fromHostRef (playbackRendererRef) }; - fromHostRef (playbackRendererRef)->getInterface ()->setPlaybackRegion (plugInExtension->getRef (), playbackRegionRef); - } - - static void ARA_CALL removePlaybackRegion (ARAPlaybackRendererRef playbackRendererRef, ARAPlaybackRegionRef playbackRegionRef) - { - auto plugInExtension { fromHostRef (playbackRendererRef) }; - plugInExtension->getInterface ()->removePlaybackRegion (plugInExtension->getRef (), playbackRegionRef); - } - - static const ARAPlaybackRendererInterface* getInterface () noexcept - { - static const SizedStruct ifc = - { - ARA1PlaybackRendererAdapterDispatcher::addPlaybackRegion, - ARA1PlaybackRendererAdapterDispatcher::removePlaybackRegion - }; - return &ifc; - } -} - -PlaybackRenderer::PlaybackRenderer (const ARAPlugInExtensionInstance* instance) -: BaseType { supportsARA2 (instance) ? instance->playbackRendererRef : toHostRef (new ARA1PlugInExtension (instance)), - supportsARA2 (instance) ? instance->playbackRendererInterface : ARA1PlaybackRendererAdapterDispatcher::getInterface () } -{} - -PlaybackRenderer::~PlaybackRenderer () -{ - // ARA 2 plug-ins will provide their own distinct interface pointers, so this test - // basically equals a dynamic_cast (PlaybackRenderer.getRef ()) - if (getInterface () == ARA1PlaybackRendererAdapterDispatcher::getInterface ()) - delete reinterpret_cast (getRef ()); -} - -#endif // ARA_SUPPORT_VERSION_1 - void PlaybackRenderer::addPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept { getInterface ()->addPlaybackRegion (getRef (), playbackRegionRef); @@ -770,43 +687,6 @@ void PlaybackRenderer::removePlaybackRegion (ARAPlaybackRegionRef playbackRegion // EditorRenderer //************************************************************************************************ -#if ARA_SUPPORT_VERSION_1 - -// editor renderer for ARA 1 is empty stub -namespace ARA1EditorRendererAdapterDispatcher -{ - static void ARA_CALL addPlaybackRegion (ARAEditorRendererRef /*editorRendererRef*/, ARAPlaybackRegionRef /*playbackRegionRef*/) - {} - - static void ARA_CALL removePlaybackRegion (ARAEditorRendererRef /*editorRendererRef*/, ARAPlaybackRegionRef /*playbackRegionRef*/) - {} - - static void ARA_CALL addRegionSequence (ARAEditorRendererRef /*editorRendererRef*/, ARARegionSequenceRef /*regionSequenceRef*/) - {} - - static void ARA_CALL removeRegionSequence (ARAEditorRendererRef /*editorRendererRef*/, ARARegionSequenceRef /*regionSequenceRef*/) - {} - - static const ARAEditorRendererInterface* getInterface () noexcept - { - static const SizedStruct ifc = - { - ARA1EditorRendererAdapterDispatcher::addPlaybackRegion, - ARA1EditorRendererAdapterDispatcher::removePlaybackRegion, - ARA1EditorRendererAdapterDispatcher::addRegionSequence, - ARA1EditorRendererAdapterDispatcher::removeRegionSequence - }; - return &ifc; - } -} - -EditorRenderer::EditorRenderer (const ARAPlugInExtensionInstance* instance) noexcept -: BaseType { supportsARA2 (instance) ? instance->editorRendererRef : nullptr, - supportsARA2 (instance) ? instance->editorRendererInterface : ARA1EditorRendererAdapterDispatcher::getInterface () } -{} - -#endif // ARA_SUPPORT_VERSION_1 - void EditorRenderer::addPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept { getInterface ()->addPlaybackRegion (getRef (), playbackRegionRef); @@ -831,35 +711,6 @@ void EditorRenderer::removeRegionSequence (ARARegionSequenceRef regionSequenceRe // EditorView //************************************************************************************************ -#if ARA_SUPPORT_VERSION_1 - -// editor view for ARA 1 is empty stub -namespace ARA1EditorViewAdapterDispatcher -{ - static void ARA_CALL notifySelection (ARAEditorViewRef /*editorViewRef*/, const ARAViewSelection* /*selection*/) - {} - - static void ARA_CALL notifyHideRegionSequences (ARAEditorViewRef /*editorViewRef*/, ARASize /*regionSequenceRefsCount*/, const ARARegionSequenceRef /*regionSequenceRefs*/[]) - {} - - static const ARAEditorViewInterface* getInterface () noexcept - { - static const SizedStruct ifc = - { - ARA1EditorViewAdapterDispatcher::notifySelection, - ARA1EditorViewAdapterDispatcher::notifyHideRegionSequences - }; - return &ifc; - } -} - -EditorView::EditorView (const ARAPlugInExtensionInstance* instance) noexcept -: BaseType { supportsARA2 (instance) ? instance->editorViewRef : nullptr, - supportsARA2 (instance) ? instance->editorViewInterface : ARA1EditorViewAdapterDispatcher::getInterface () } -{} - -#endif // ARA_SUPPORT_VERSION_1 - void EditorView::notifySelection (const ARAViewSelection* selection) noexcept { getInterface ()->notifySelection (getRef (), selection); diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index 4d40409..cd701a1 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -485,14 +485,9 @@ using ContentReader = ARA::ContentReader { public: -#if ARA_SUPPORT_VERSION_1 - explicit PlaybackRenderer (const ARAPlugInExtensionInstance* instance); - ~PlaybackRenderer (); -#else explicit PlaybackRenderer (const ARAPlugInExtensionInstance* instance) noexcept : BaseType { instance->playbackRendererRef, instance->playbackRendererInterface } {} -#endif //! @name Assigning the playback region(s) for playback rendering //! For details, see \ref Assigning_ARAPlaybackRendererInterface_Regions "ARAPlaybackRendererInterface". @@ -510,13 +505,9 @@ class PlaybackRenderer : public InterfaceInstance { public: -#if ARA_SUPPORT_VERSION_1 - explicit EditorRenderer (const ARAPlugInExtensionInstance* instance) noexcept; -#else explicit EditorRenderer (const ARAPlugInExtensionInstance* instance) noexcept : BaseType { instance->editorRendererRef, instance->editorRendererInterface } {} -#endif //! @name Assigning the playback region(s) for preview while editing //! For details, see \ref Assigning_ARAEditorRendererInterface_Regions "ARAEditorRendererInterface". @@ -536,13 +527,9 @@ class EditorRenderer : public InterfaceInstance { public: -#if ARA_SUPPORT_VERSION_1 - explicit EditorView (const ARAPlugInExtensionInstance* instance) noexcept; -#else explicit EditorView (const ARAPlugInExtensionInstance* instance) noexcept : BaseType { instance->editorViewRef, instance->editorViewInterface } {} -#endif //! @name Host UI notifications //@{ @@ -565,11 +552,7 @@ class EditorView : public InterfaceInstancesetPlaybackRegion (playbackRegionRef); - } - - static void ARA_CALL removePlaybackRegion (ARAPlugInExtensionRef plugInExtensionRef, ARAPlaybackRegionRef playbackRegionRef) noexcept - { - fromRef (plugInExtensionRef)->removePlaybackRegion (playbackRegionRef); - } - - static const ARAPlugInExtensionInterface* getInterface () noexcept - { - static const SizedStruct ifc = - { - ARA1PlugInExtensionDispatcher::setPlaybackRegion, - ARA1PlugInExtensionDispatcher::removePlaybackRegion - }; - return &ifc; - } -} - -#endif - /*******************************************************************************/ // PlugInExtensionInstance /*******************************************************************************/ PlugInExtensionInstance::PlugInExtensionInstance (PlaybackRendererInterface* playbackRenderer, EditorRendererInterface* editorRenderer, EditorViewInterface* editorView) noexcept -#if ARA_SUPPORT_VERSION_1 -: BaseType { toRef (this), ARA1PlugInExtensionDispatcher::getInterface (), -#else : BaseType { nullptr, nullptr, -#endif toRef (playbackRenderer), (playbackRenderer) ? PlaybackRendererDispatcher::getInterface () : nullptr, toRef (editorRenderer), (editorRenderer) ? EditorRendererDispatcher::getInterface () : nullptr, toRef (editorView), (editorView) ? EditorViewDispatcher::getInterface () : nullptr } {} -#if ARA_SUPPORT_VERSION_1 -void PlugInExtensionInstance::setPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept -{ - if (_hasPlaybackRegion) - removePlaybackRegion (_playbackRegionRef); - - if (getPlaybackRenderer ()) - getPlaybackRenderer ()->addPlaybackRegion (playbackRegionRef); - if (getEditorRenderer ()) - getEditorRenderer ()->addPlaybackRegion (playbackRegionRef); - - _playbackRegionRef = playbackRegionRef; - _hasPlaybackRegion = true; -} - -void PlugInExtensionInstance::removePlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept -{ - if (getPlaybackRenderer ()) - getPlaybackRenderer ()->removePlaybackRegion (playbackRegionRef); - if (getEditorRenderer ()) - getEditorRenderer ()->removePlaybackRegion (playbackRegionRef); - - _playbackRegionRef = nullptr; - _hasPlaybackRegion = false; -} -#endif - /*******************************************************************************/ // AudioAccessController /*******************************************************************************/ diff --git a/Dispatch/ARAPlugInDispatch.h b/Dispatch/ARAPlugInDispatch.h index 105ea50..541d8cb 100644 --- a/Dispatch/ARAPlugInDispatch.h +++ b/Dispatch/ARAPlugInDispatch.h @@ -337,15 +337,6 @@ class PlugInExtensionInstance : public SizedStruct -#if ARA_SUPPORT_VERSION_1 - #error "The ARA IPC proxy host implementation does not support ARA 1." -#endif - - namespace ARA { namespace IPC { namespace ProxyHostImpl { diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp index 2a29857..d9d4bd1 100644 --- a/IPC/ARAIPCProxyPlugIn.cpp +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -31,11 +31,6 @@ #include -#if ARA_SUPPORT_VERSION_1 - #error "The ARA IPC proxy plug-in implementation does not support ARA 1." -#endif - - /*******************************************************************************/ // configuration switches for debug output // each can be defined as a nonzero integer to enable the associated logging diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index 1c9dbf4..d8397ee 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -625,29 +625,10 @@ void PlaybackRegion::updateProperties (PropertiesPtr ()) - { - ARA_VALIDATE_API_STATE (_musicalContext == nullptr); - auto regionSequence { fromRef (properties->regionSequenceRef) }; - ARA_VALIDATE_API_ARGUMENT (properties->regionSequenceRef, getDocumentController ()->isValidRegionSequence (regionSequence)); - setRegionSequence (regionSequence); - } - else - { - ARA_VALIDATE_API_STATE (DocumentController::getUsedApiGeneration () < kARAAPIGeneration_2_0_Draft); - ARA_VALIDATE_API_STATE (getRegionSequence () == nullptr); - - auto musicalContext { fromRef (properties->musicalContextRef) }; - ARA_VALIDATE_API_ARGUMENT (properties->musicalContextRef, getDocumentController ()->isValidMusicalContext (musicalContext)); - _musicalContext = musicalContext; - } -#else ARA_VALIDATE_API_ARGUMENT (properties, properties.implements ()); auto regionSequence { fromRef (properties->regionSequenceRef) }; ARA_VALIDATE_API_ARGUMENT (properties->regionSequenceRef, getDocumentController ()->isValidRegionSequence (regionSequence)); setRegionSequence (regionSequence); -#endif } bool PlaybackRegion::intersectsWithAudioModificationTimeRange (ARAContentTimeRange range) const noexcept @@ -1707,11 +1688,7 @@ ARAPlaybackRegionRef DocumentController::createPlaybackRegion (ARAAudioModificat didAddPlaybackRegionToAudioModification (audioModification, playbackRegion); - auto regionSequence { playbackRegion->getRegionSequence () }; -#if ARA_SUPPORT_VERSION_1 - if (regionSequence) -#endif - didAddPlaybackRegionToRegionSequence (regionSequence, playbackRegion); + didAddPlaybackRegionToRegionSequence (playbackRegion->getRegionSequence (), playbackRegion); ARA_LOG_MODELOBJECT_LIFETIME ("did create playback region", playbackRegion); return toRef (playbackRegion); @@ -1741,13 +1718,8 @@ void DocumentController::updatePlaybackRegionProperties (ARAPlaybackRegionRef pl playbackRegion->updateProperties (properties); didUpdatePlaybackRegionProperties (playbackRegion); -#if ARA_SUPPORT_VERSION_1 - if (newSequence) -#endif - { - if (currentSequence != newSequence) - didAddPlaybackRegionToRegionSequence (newSequence, playbackRegion); - } + if (currentSequence != newSequence) + didAddPlaybackRegionToRegionSequence (newSequence, playbackRegion); ARA_LOG_PROPERTY_CHANGES ("did update properties of playback region", playbackRegion); } @@ -1786,10 +1758,7 @@ void DocumentController::destroyPlaybackRegion (ARAPlaybackRegionRef playbackReg ARA_VALIDATE_API_STATE (!contains (playbackRenderer->getPlaybackRegions (), playbackRegion)); #endif -#if ARA_SUPPORT_VERSION_1 - if (playbackRegion->getRegionSequence ()) -#endif - willRemovePlaybackRegionFromRegionSequence (playbackRegion->getRegionSequence (), playbackRegion); + willRemovePlaybackRegionFromRegionSequence (playbackRegion->getRegionSequence (), playbackRegion); willRemovePlaybackRegionFromAudioModification (playbackRegion->getAudioModification (), playbackRegion); @@ -2797,7 +2766,7 @@ PlugInEntry::PlugInEntry (const FactoryConfig* factoryConfig, #if ARA_CPU_ARM ARA_INTERNAL_ASSERT (_factory.lowestSupportedApiGeneration >= kARAAPIGeneration_2_0_Final); #else - ARA_INTERNAL_ASSERT (_factory.lowestSupportedApiGeneration >= kARAAPIGeneration_1_0_Draft); + ARA_INTERNAL_ASSERT (_factory.lowestSupportedApiGeneration >= kARAAPIGeneration_2_0_Draft); #endif ARA_INTERNAL_ASSERT (_factory.highestSupportedApiGeneration >= _factory.lowestSupportedApiGeneration); diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index 3ab5c4a..9415ed1 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -656,10 +656,6 @@ class PlaybackRegion template AudioModification_t* getAudioModification () const noexcept { return static_cast (this->_audioModification); } -#if ARA_SUPPORT_VERSION_1 - template - MusicalContext_t* getMusicalContext () const noexcept { return static_cast ((this->_regionSequence) ? this->_regionSequence->getMusicalContext () : this->_musicalContext); } -#endif //! Retrieve the current underlying RegionSequence instance. template RegionSequence_t* getRegionSequence () const noexcept { return static_cast (this->_regionSequence); } @@ -679,9 +675,6 @@ class PlaybackRegion ARATimePosition _startInPlaybackTime { 0.0 }; ARATimeDuration _durationInPlaybackTime { 0.0 }; RegionSequence* _regionSequence { nullptr }; -#if ARA_SUPPORT_VERSION_1 - MusicalContext* _musicalContext { nullptr }; -#endif bool _timestretchEnabled { false }; bool _timestretchReflectingTempo { false }; bool _contentBasedFadeAtHead { false }; @@ -1794,9 +1787,7 @@ class FactoryConfig //! \copydoc ARAFactory::lowestSupportedApiGeneration virtual ARAAPIGeneration getLowestSupportedApiGeneration () const noexcept -#if ARA_SUPPORT_VERSION_1 - { return kARAAPIGeneration_1_0_Final; } -#elif ARA_CPU_ARM +#if ARA_CPU_ARM { return kARAAPIGeneration_2_0_Final; } #else { return kARAAPIGeneration_2_0_Draft; } From 6bb8e8dbd53ffd2a6539ed1e52d475904b276e07 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:04 +0200 Subject: [PATCH 06/42] Drop obsolete ARA 2.0 Draft support, including monolithic ARA 1 style persistency --- ChangeLog.txt | 3 ++- Dispatch/ARAHostDispatch.cpp | 49 ------------------------------------ Dispatch/ARAHostDispatch.h | 11 -------- 3 files changed, 2 insertions(+), 61 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 24e8afd..efa1f5b 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -5,7 +5,8 @@ This is a development build of the ARA Library 3.0. Changes since previous releases: - initial draft of generic ARA IPC library providing a proxy host and a proxy plug-in, based on heavily refactored IPC Example from earlier SDK releases -- support for ARA 1 dropped +- dropped support for ARA 1 and 2.0 Draft APIs + If previously optionally supporting obsolete ARA 1 persistency, now exclusively use ARA 2 persistency. === ARA SDK 2.3 release (aka 2.3.001) (2025/11/07) === diff --git a/Dispatch/ARAHostDispatch.cpp b/Dispatch/ARAHostDispatch.cpp index 0d134bf..f3fb503 100644 --- a/Dispatch/ARAHostDispatch.cpp +++ b/Dispatch/ARAHostDispatch.cpp @@ -360,62 +360,13 @@ void DocumentController::updateDocumentProperties (const ARADocumentProperties* getInterface ()->updateDocumentProperties (getRef (), properties); } -bool DocumentController::beginRestoringDocumentFromArchive (ARAArchiveReaderHostRef archiveReaderHostRef) noexcept -{ - // begin-/endRestoringDocumentFromArchive () is deprecated, prefer to use the new partial persistency calls if supported by the plug-in - if (supportsPartialPersistency ()) - { - getInterface ()->beginEditing (getRef ()); - return true; - } - else - { - return (getInterface ()->beginRestoringDocumentFromArchive (getRef (), archiveReaderHostRef) != kARAFalse); - } -} - -bool DocumentController::endRestoringDocumentFromArchive (ARAArchiveReaderHostRef archiveReaderHostRef) noexcept -{ - // begin-/endRestoringDocumentFromArchive () is deprecated, prefer to use the new partial persistency calls if supported by the plug-in - if (supportsPartialPersistency ()) - { - const auto result { getInterface ()->restoreObjectsFromArchive (getRef (), archiveReaderHostRef, nullptr) }; - getInterface ()->endEditing (getRef ()); - return (result != kARAFalse); - } - else - { - return (getInterface ()->endRestoringDocumentFromArchive (getRef (), archiveReaderHostRef) != kARAFalse); - } -} - -bool DocumentController::storeDocumentToArchive (ARAArchiveWriterHostRef archiveWriterHostRef) noexcept -{ - // storeDocumentToArchive () is deprecated, prefer to use the new partial persistency calls if supported by the plug-in - if (supportsPartialPersistency ()) - return (getInterface ()->storeObjectsToArchive (getRef (), archiveWriterHostRef, nullptr) != kARAFalse); - else - return (getInterface ()->storeDocumentToArchive (getRef (), archiveWriterHostRef) != kARAFalse); -} - -bool DocumentController::supportsPartialPersistency () noexcept -{ - return getInterface ().implements (); -} - bool DocumentController::restoreObjectsFromArchive (ARAArchiveReaderHostRef archiveReaderHostRef, const ARARestoreObjectsFilter* filter) noexcept { - if (!supportsPartialPersistency ()) - return false; - return (getInterface ()->restoreObjectsFromArchive (getRef (), archiveReaderHostRef, filter) != kARAFalse); } bool DocumentController::storeObjectsToArchive (ARAArchiveWriterHostRef archiveWriterHostRef, const ARAStoreObjectsFilter* filter) noexcept { - if (!supportsPartialPersistency ()) - return false; - return (getInterface ()->storeObjectsToArchive (getRef (), archiveWriterHostRef, filter) != kARAFalse); } diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index cd701a1..ff8dadf 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -292,17 +292,6 @@ class DocumentController : public InterfaceInstance Date: Thu, 30 Apr 2026 23:23:04 +0200 Subject: [PATCH 07/42] ARA_Library and ARA_Examples now consistently require C++14 on all platforms --- CMakeLists.txt | 2 +- ChangeLog.txt | 1 + Dispatch/ARADispatchBase.h | 5 +---- Dispatch/ARAHostDispatch.h | 5 +---- IPC/ARAIPCEncoding.h | 12 ++---------- 5 files changed, 6 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ebc50af..4cb9d77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ function(configure_ARA_Library_target target) # language standards target_compile_features(${target} PUBLIC - cxx_std_11 + cxx_std_14 c_std_11 ) diff --git a/ChangeLog.txt b/ChangeLog.txt index efa1f5b..989d436 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,7 @@ Changes since previous releases: based on heavily refactored IPC Example from earlier SDK releases - dropped support for ARA 1 and 2.0 Draft APIs If previously optionally supporting obsolete ARA 1 persistency, now exclusively use ARA 2 persistency. +- C++14 is now consistently required on all platforms === ARA SDK 2.3 release (aka 2.3.001) (2025/11/07) === diff --git a/Dispatch/ARADispatchBase.h b/Dispatch/ARADispatchBase.h index 6aa9066..4dc455b 100644 --- a/Dispatch/ARADispatchBase.h +++ b/Dispatch/ARADispatchBase.h @@ -207,10 +207,7 @@ struct alignas (alignof (void*)) SizedStruct : public StructType //! Default constructor. //! Special-cases the initializer list constructor only to suppress warnings about missing initializers for default construction. -#if __cplusplus >= 201402L - constexpr -#endif - inline SizedStruct () noexcept + inline constexpr SizedStruct () noexcept : StructType {} { this->structSize = SizedStruct::getImplementedSize (); diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index ff8dadf..2db98c5 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -219,10 +219,7 @@ ARA_MAP_HOST_REF (PlaybackControllerInterface, ARAPlaybackControllerHostRef) class DocumentControllerHostInstance : public SizedStruct { public: -#if __cplusplus >= 201402L - constexpr -#endif - DocumentControllerHostInstance () noexcept : BaseType {} {} + constexpr DocumentControllerHostInstance () noexcept : BaseType {} {} DocumentControllerHostInstance (AudioAccessControllerInterface* audioAccessController, ArchivingControllerInterface* archivingController, ContentAccessControllerInterface* contentAccessController = nullptr, diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 619d01a..35e2885 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -1319,11 +1319,7 @@ class ContentEventDecoder } } - static inline -#if __cplusplus >= 201402L - constexpr -#endif - _DecoderFunction _getDecoderFunctionForContentType (const ARAContentType type) + static inline constexpr _DecoderFunction _getDecoderFunctionForContentType (const ARAContentType type) { switch (type) { @@ -1337,11 +1333,7 @@ class ContentEventDecoder } } - static inline -#if __cplusplus >= 201402L - constexpr -#endif - size_t _getStringMemberOffsetForContentType (const ARAContentType type) + static inline constexpr size_t _getStringMemberOffsetForContentType (const ARAContentType type) { switch (type) { From 8477f83948e1399273bb91663057c2c892e49f3b Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:04 +0200 Subject: [PATCH 08/42] C++17 is now consistently required on all platforms --- CMakeLists.txt | 23 ++++++++++++++++------- Debug/ARAContentLogger.h | 5 ----- Debug/ARADebug.h | 4 ++-- Dispatch/ARADispatchBase.h | 38 ++++++++++++++------------------------ Dispatch/ARAHostDispatch.h | 5 ----- IPC/ARAIPCConnection.cpp | 6 ------ PlugIn/ARAPlug.cpp | 5 +---- 7 files changed, 33 insertions(+), 53 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cb9d77..6050eb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,14 @@ project(ARA_Library LANGUAGES C CXX ) +# language standards + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + # ====================== add_subdirectory("${ARA_API_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ARA_API.build" EXCLUDE_FROM_ALL) @@ -55,13 +63,6 @@ function(configure_ARA_Library_target target) # file grouping ara_group_target_files(${target}) - # language standards - target_compile_features(${target} - PUBLIC - cxx_std_14 - c_std_11 - ) - # ARA_API dependency target_link_libraries(${target} PUBLIC ARA_API) @@ -85,6 +86,14 @@ function(configure_ARA_Library_target target) PRIVATE -DNOMINMAX=1 ) + # /Zc:__cplusplus, added in Visual Studio 2017 version 15.7, is required to make __cplusplus + # accurate, see https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ + if(MSVC AND (MSVC_VERSION GREATER_EQUAL 1914)) + target_compile_options(${target} + PRIVATE + "/Zc:__cplusplus" + ) + endif() elseif(APPLE) if(XCODE) set_target_properties(${target} PROPERTIES diff --git a/Debug/ARAContentLogger.h b/Debug/ARAContentLogger.h index 9cbacb0..fe22b24 100644 --- a/Debug/ARAContentLogger.h +++ b/Debug/ARAContentLogger.h @@ -40,13 +40,8 @@ namespace ARA { struct ContentLogger { -#if __cplusplus >= 201703L template using ContentReaderRef = decltype ((static_cast (nullptr)->*ContentReaderFunctionMapper::createContentReader) (nullptr, kARAContentTypeNotes, nullptr)); -#else - template - using ContentReaderRef = typename std::result_of::createContentReader) (ControllerType, ModelObjectRefType, ARAContentType, const ARAContentTimeRange*)>::type; -#endif template #if ARA_VALIDATE_API_CALLS diff --git a/Debug/ARADebug.h b/Debug/ARADebug.h index b6aff98..d3949eb 100644 --- a/Debug/ARADebug.h +++ b/Debug/ARADebug.h @@ -97,7 +97,7 @@ extern "C" // prevent unused variable warnings /*******************************************************************************/ -#if defined(__cplusplus) && (__cplusplus >= 201703L) +#if defined(__cplusplus) #define ARA_MAYBE_UNUSED_VAR(var) var [[maybe_unused]] #elif defined(__GNUC__) #define ARA_MAYBE_UNUSED_VAR(var) var __attribute__((unused)) @@ -107,7 +107,7 @@ extern "C" #define ARA_MAYBE_UNUSED_VAR(var) var; (void)var #endif -#if defined(__cplusplus) && (__cplusplus >= 201703L) +#if defined(__cplusplus) #define ARA_MAYBE_UNUSED_ARG(var) var [[maybe_unused]] #elif defined(__GNUC__) #define ARA_MAYBE_UNUSED_ARG(var) var __attribute__((unused)) diff --git a/Dispatch/ARADispatchBase.h b/Dispatch/ARADispatchBase.h index 4dc455b..248b5da 100644 --- a/Dispatch/ARADispatchBase.h +++ b/Dispatch/ARADispatchBase.h @@ -31,6 +31,19 @@ #include "ARA_API/ARAInterface.h" +#if !defined (__cplusplus) || ((__cplusplus < 201703L) && (!defined (_MSVC_LANG) || (_MSVC_LANG < 201703L))) + #error "C++17 or higher is required to compile this code." +#endif + +#if !defined(__cpp_nontype_template_parameter_auto) && !defined (__cpp_template_auto) + #error "Template auto support is required to compile this code." +#endif + +#if defined (__clang__) && (__clang_major__ < 10) + #error "Clang 10 or newer is required to compile this code." +#endif + + /*******************************************************************************/ /** Optional ARA 1 backwards compatibility. Hosts and plug-ins can choose support ARA 1 hosts in addition to ARA 2 hosts. @@ -172,16 +185,11 @@ class FromRefConversionHelper : public FromRef __pragma (warning(disable : 4324)) // disable padding warnings, instances of this struct template may need to be padded. #endif -#if defined (__cpp_template_auto) template struct alignas (alignof (void*)) SizedStruct; template struct alignas (alignof (void*)) SizedStruct : public StructType -#else -template -struct alignas (alignof (void*)) SizedStruct : public StructType -#endif { protected: //! \p SizedStruct alias, to allow any derived class to easily reference its templated base class. @@ -257,27 +265,9 @@ class SizedStructPtr //! ARA uses the \p StructType::structSize member as a means of versioning - //! if it is large enough to contain \p member then this function returns true. //! This is a C++ version of ARA_IMPLEMENTS_FIELD. -#if defined (__cpp_template_auto) template inline bool implements () const noexcept - { - #if defined (__clang__) && (__clang_major__ < 10) - // see clang bug: https://bugs.llvm.org/show_bug.cgi?id=35655 - // as workaround, we're copying the code from SizedStruct::getImplementedSize () here - static_assert (std::is_class::value && std::is_standard_layout::value, "C compatible standard layout struct required"); - return this->_ptr->structSize >= reinterpret_cast (&(static_cast (nullptr)->*member)) + sizeof (static_cast (nullptr)->*member); - #else - return this->_ptr->structSize >= SizedStruct::getImplementedSize (); - #endif - } -#else - template - inline bool implements () const noexcept - { - static_assert (std::is_same::value, "must test member of same struct"); - return this->_ptr->structSize >= SizedStruct::getImplementedSize (); - } -#endif + { return this->_ptr->structSize >= SizedStruct::getImplementedSize (); } private: const StructType* _ptr; diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index 2db98c5..2843ca1 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -79,13 +79,8 @@ ARA_DISABLE_DOCUMENTATION_DEPRECATED_WARNINGS_BEGIN /** C++ wrapper for ARA variable-sized properties structs, ensures their proper initialization. */ /*******************************************************************************/ -#if defined (__cpp_template_auto) template using Properties = SizedStruct; -#else - template - using Properties = SizedStruct; -#endif //! @addtogroup ARA_Library_Host_Dispatch_Host_Interfaces Host Interfaces //! @{ diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 969a258..301202d 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -122,12 +122,6 @@ constexpr MessageArgumentKey receiveThreadKey { -2 }; #endif -#if __cplusplus < 201703L -constexpr MessageDispatcher::ThreadRef MessageDispatcher::_invalidThread; -constexpr intptr_t MainThreadMessageDispatcher::_noPendingMessageDecoder; -#endif - - MessageDispatcher::MessageDispatcher (Connection* connection, MessageChannel* messageChannel) : _connection { connection }, _messageChannel { messageChannel } diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index d8397ee..6059530 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -299,11 +299,8 @@ bool AnalysisProgressTracker::decodeIsProgressing (float encodedProgress) noexce bool AnalysisProgressTracker::updateProgress (ARAAnalysisProgressState state, float progress) noexcept { -#if __cplusplus >= 201703L static_assert (decltype (_encodedProgress)::is_always_lock_free); -#else - ARA_INTERNAL_ASSERT (_encodedProgress.is_lock_free ()); -#endif + ARA_INTERNAL_ASSERT (0.0f <= progress); ARA_INTERNAL_ASSERT (progress <= 1.0f); From 2e7e0de602d1d2f85a7b5ec7b1a0b49a79448a42 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:05 +0200 Subject: [PATCH 09/42] Remove ARA_STRUCT_MEMBER macro, it's obsolete in C++17 and later --- Dispatch/ARADispatchBase.h | 18 ++---------------- Dispatch/ARAHostDispatch.cpp | 20 ++++++++++---------- Dispatch/ARAHostDispatch.h | 2 +- Dispatch/ARAPlugInDispatch.cpp | 14 +++++++------- Dispatch/ARAPlugInDispatch.h | 4 ++-- IPC/ARAIPCProxyHost.cpp | 2 +- PlugIn/ARAPlug.cpp | 28 ++++++++++++++-------------- PlugIn/ARAPlug.h | 6 +++--- 8 files changed, 40 insertions(+), 54 deletions(-) diff --git a/Dispatch/ARADispatchBase.h b/Dispatch/ARADispatchBase.h index 248b5da..5a9e6bb 100644 --- a/Dispatch/ARADispatchBase.h +++ b/Dispatch/ARADispatchBase.h @@ -149,20 +149,6 @@ class FromRefConversionHelper : public FromRef ClassName& operator= (ClassName&& other) = delete; #endif -/*******************************************************************************/ -// ARA_STRUCT_MEMBER pre-C++17 utility macro -/** When dealing with ARA variable-sized structures, this eases defining the related C++ template arguments */ -/*******************************************************************************/ - -#if defined (__cpp_template_auto) - // defined here so that code can be written to support both pre-C++17 and C++17 by always using the macro - // note that code which can rely on C++17 and up does not need to use the macro, - // writing e.g. SizedStruct<&ARAFactory::supportedPlaybackTransformationFlags> instead. - #define ARA_STRUCT_MEMBER(StructType, member) &ARA::StructType::member -#else - #define ARA_STRUCT_MEMBER(StructType, member) ARA::StructType, decltype (ARA::StructType::member), &ARA::StructType::member -#endif - /*******************************************************************************/ // SizedStruct @@ -174,7 +160,7 @@ class FromRefConversionHelper : public FromRef //! struct that implements every function up until ARADocumentControllerInterface::storeObjectsToArchive(), //! you can declare a SizedStruct like so: //! \code{.cpp} -//! SizedStruct dci; +//! SizedStruct<&ARA::ARADocumentControllerInterface::storeObjectsToArchive> dci; //! \endcode //! In this example, `dci` won't support any function declared after ARADocumentControllerInterface::storeObjectsToArchive(), //! meaning it won't support ARA Analysis Algorithm selection. @@ -239,7 +225,7 @@ struct alignas (alignof (void*)) SizedStruct : public StructType //! bool supportsAnalysisAlgorithmSelection (ARADocumentControllerInterface* dci) //! { //! SizedStructPtr sizedStructPtr (dci); -//! return sizedStructPtr.implements (); +//! return sizedStructPtr.implements<&ARA::ARADocumentControllerInterface::getProcessingAlgorithmsCount> (); //! } //! \endcode /*******************************************************************************/ diff --git a/Dispatch/ARAHostDispatch.cpp b/Dispatch/ARAHostDispatch.cpp index f3fb503..483ab27 100644 --- a/Dispatch/ARAHostDispatch.cpp +++ b/Dispatch/ARAHostDispatch.cpp @@ -48,7 +48,7 @@ namespace AudioAccessControllerDispatcher static const ARAAudioAccessControllerInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARAAudioAccessControllerInterface::destroyAudioReader> ifc = { AudioAccessControllerDispatcher::createAudioReaderForSource, AudioAccessControllerDispatcher::readAudioSamples, @@ -98,7 +98,7 @@ namespace ArchivingControllerDispatcher static const ARAArchivingControllerInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARAArchivingControllerInterface::getDocumentArchiveID> ifc = { ArchivingControllerDispatcher::getArchiveSize, ArchivingControllerDispatcher::readBytesFromArchive, @@ -167,7 +167,7 @@ namespace ContentAccessControllerDispatcher static const ARAContentAccessControllerInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARAContentAccessControllerInterface::destroyContentReader> ifc = { ContentAccessControllerDispatcher::isMusicalContextContentAvailable, ContentAccessControllerDispatcher::getMusicalContextContentGrade, @@ -220,7 +220,7 @@ namespace ModelUpdateControllerDispatcher static const ARAModelUpdateControllerInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARAModelUpdateControllerInterface::notifyDocumentDataChanged> ifc = { ModelUpdateControllerDispatcher::notifyAudioSourceAnalysisProgress, ModelUpdateControllerDispatcher::notifyAudioSourceContentChanged, @@ -265,7 +265,7 @@ namespace PlaybackControllerDispatcher static const ARAPlaybackControllerInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARAPlaybackControllerInterface::requestEnableCycle> ifc = { PlaybackControllerDispatcher::requestStartPlayback, PlaybackControllerDispatcher::requestStopPlayback, @@ -372,11 +372,11 @@ bool DocumentController::storeObjectsToArchive (ARAArchiveWriterHostRef archiveW bool DocumentController::supportsStoringAudioFileChunks () noexcept { - if (!getInterface ().implements ()) + if (!getInterface ().implements<&ARADocumentControllerInterface::storeAudioSourceToAudioFileChunk> ()) return false; const SizedStructPtr factory { getInterface ()->getFactory (getRef ()) }; - if (!factory.implements ()) + if (!factory.implements<&ARAFactory::supportsStoringAudioFileChunks> ()) return false; return (factory->supportsStoringAudioFileChunks != kARAFalse); } @@ -474,7 +474,7 @@ void DocumentController::updateAudioModificationProperties (ARAAudioModification bool DocumentController::supportsIsAudioModificationPreservingAudioSourceSignal () noexcept { - return getInterface ().implements (); + return getInterface ().implements<&ARADocumentControllerInterface::isAudioModificationPreservingAudioSourceSignal> (); } bool DocumentController::isAudioModificationPreservingAudioSourceSignal (ARAAudioModificationRef audioModificationRef) noexcept @@ -591,7 +591,7 @@ bool DocumentController::supportsProcessingAlgorithms () noexcept ARAInt32 DocumentController::getProcessingAlgorithmsCount () noexcept { - if (getInterface ().implements ()) + if (getInterface ().implements<&ARADocumentControllerInterface::requestProcessingAlgorithmForAudioSource> ()) return getInterface ()->getProcessingAlgorithmsCount (getRef ()); else return 0; @@ -614,7 +614,7 @@ void DocumentController::requestProcessingAlgorithmForAudioSource (ARAAudioSourc bool DocumentController::isLicensedForCapabilities (bool runModalActivationDialogIfNeeded, ARASize contentTypesCount, const ARAContentType contentTypes[], ARAPlaybackTransformationFlags transformationFlags) noexcept { - if (!getInterface ().implements ()) + if (!getInterface ().implements<&ARADocumentControllerInterface::isLicensedForCapabilities> ()) return true; return (getInterface ()->isLicensedForCapabilities (getRef (), (runModalActivationDialogIfNeeded) ? kARATrue : kARAFalse, contentTypesCount, contentTypes, transformationFlags) != kARAFalse); diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index 2843ca1..cde0b05 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -211,7 +211,7 @@ ARA_MAP_HOST_REF (PlaybackControllerInterface, ARAPlaybackControllerHostRef) /** Wrapper class for the ARADocumentControllerHostInstance. */ /*******************************************************************************/ -class DocumentControllerHostInstance : public SizedStruct +class DocumentControllerHostInstance : public SizedStruct<&ARADocumentControllerHostInstance::playbackControllerInterface> { public: constexpr DocumentControllerHostInstance () noexcept : BaseType {} {} diff --git a/Dispatch/ARAPlugInDispatch.cpp b/Dispatch/ARAPlugInDispatch.cpp index 8e30b36..035a9f9 100644 --- a/Dispatch/ARAPlugInDispatch.cpp +++ b/Dispatch/ARAPlugInDispatch.cpp @@ -342,7 +342,7 @@ namespace DocumentControllerDispatcher static const ARADocumentControllerInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARADocumentControllerInterface::isAudioModificationPreservingAudioSourceSignal> ifc = { DocumentControllerDispatcher::destroyDocumentController, DocumentControllerDispatcher::getFactory, @@ -429,7 +429,7 @@ namespace PlaybackRendererDispatcher static const ARAPlaybackRendererInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARAPlaybackRendererInterface::removePlaybackRegion> ifc = { PlaybackRendererDispatcher::addPlaybackRegion, PlaybackRendererDispatcher::removePlaybackRegion @@ -466,7 +466,7 @@ namespace EditorRendererDispatcher static const ARAEditorRendererInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARAEditorRendererInterface::removeRegionSequence> ifc = { EditorRendererDispatcher::addPlaybackRegion, EditorRendererDispatcher::removePlaybackRegion, @@ -495,7 +495,7 @@ namespace EditorViewDispatcher static const ARAEditorViewInterface* getInterface () noexcept { - static const SizedStruct ifc = + static const SizedStruct<&ARAEditorViewInterface::notifyHideRegionSequences> ifc = { EditorViewDispatcher::notifySelection, EditorViewDispatcher::notifyHideRegionSequences @@ -569,7 +569,7 @@ void HostArchivingController::notifyDocumentUnarchivingProgress (float value) no ARAPersistentID HostArchivingController::getDocumentArchiveID (ARAArchiveReaderHostRef archiveReaderHostRef) noexcept { // getDocumentArchiveID() was added in the ARA 2.0 final release, so check its presence here to support older hosts - if (getInterface ().implements ()) + if (getInterface ().implements<&ARAArchivingControllerInterface::getDocumentArchiveID> ()) return getInterface ()->getDocumentArchiveID (getRef (), archiveReaderHostRef); return nullptr; } @@ -649,14 +649,14 @@ void HostModelUpdateController::notifyPlaybackRegionContentChanged (ARAPlaybackR const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept { // notifyPlaybackRegionContentChanged was optional in the ARA 2.0 draft, so check its presence here to be safe - if (getInterface ().implements ()) + if (getInterface ().implements<&ARAModelUpdateControllerInterface::notifyPlaybackRegionContentChanged> ()) getInterface ()->notifyPlaybackRegionContentChanged (getRef (), playbackRegionHostRef, range, scopeFlags); } void HostModelUpdateController::notifyDocumentDataChanged () noexcept { // notifyDocumentDataChanged was added in ARA 2.3 draft, so check its presence here - if (getInterface ().implements ()) + if (getInterface ().implements<&ARAModelUpdateControllerInterface::notifyDocumentDataChanged> ()) getInterface ()->notifyDocumentDataChanged (getRef ()); } diff --git a/Dispatch/ARAPlugInDispatch.h b/Dispatch/ARAPlugInDispatch.h index 541d8cb..a52b1d9 100644 --- a/Dispatch/ARAPlugInDispatch.h +++ b/Dispatch/ARAPlugInDispatch.h @@ -250,7 +250,7 @@ ARA_MAP_REF (DocumentControllerInterface, ARADocumentControllerRef) /** Wrapper class for the ARADocumentControllerInstance. */ /*******************************************************************************/ -class DocumentControllerInstance : public SizedStruct +class DocumentControllerInstance : public SizedStruct<&ARADocumentControllerInstance::documentControllerInterface> { public: explicit DocumentControllerInstance (DocumentControllerInterface* documentController) noexcept; @@ -324,7 +324,7 @@ ARA_MAP_REF (EditorViewInterface, ARAEditorViewRef) /** Wrapper class for ARAPlugInExtensionInstance. */ /*******************************************************************************/ -class PlugInExtensionInstance : public SizedStruct +class PlugInExtensionInstance : public SizedStruct<&ARAPlugInExtensionInstance::editorViewInterface> { public: explicit PlugInExtensionInstance (PlaybackRendererInterface* playbackRenderer, EditorRendererInterface* editorRenderer, EditorViewInterface* editorView) noexcept; diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index 8d3d078..170b226 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -676,7 +676,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD else if (messageID == kInitializeARAMethodID) { ARAPersistentID factoryID; - ARA::SizedStruct interfaceConfig = { kARAAPIGeneration_2_0_Final, nullptr }; + ARA::SizedStruct<&ARA::ARAInterfaceConfiguration::assertFunctionAddress> interfaceConfig = { kARAAPIGeneration_2_0_Final, nullptr }; decodeArguments (decoder, factoryID, interfaceConfig.desiredApiGeneration); ARA_INTERNAL_ASSERT (interfaceConfig.desiredApiGeneration >= kARAAPIGeneration_2_0_Final); diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index 6059530..b45b23b 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -430,17 +430,17 @@ const OptionalProperty& MusicalContext::getEffectiveName () const void MusicalContext::updateProperties (PropertiesPtr properties) noexcept { - if (properties.implements ()) + if (properties.implements<&ARAMusicalContextProperties::name> ()) _name = properties->name; else _name = nullptr; - if (properties.implements ()) + if (properties.implements<&ARAMusicalContextProperties::orderIndex> ()) _orderIndex = properties->orderIndex; else _orderIndex = 0; // for position, we have no markup for "unknown" position - we'll just remain unsorted - if (properties.implements ()) + if (properties.implements<&ARAMusicalContextProperties::color> ()) _color = properties->color; else _color = nullptr; @@ -471,7 +471,7 @@ void RegionSequence::updateProperties (PropertiesPtrname; _orderIndex = properties->orderIndex; - if (properties.implements ()) + if (properties.implements<&ARARegionSequenceProperties::color> ()) _color = properties->color; else _color = nullptr; @@ -519,7 +519,7 @@ void AudioSource::updateProperties (PropertiesPtr prop _sampleRate = properties->sampleRate; _merits64BitSamples = (properties->merits64BitSamples != kARAFalse); - if (properties.implements ()) + if (properties.implements<&ARAAudioSourceProperties::channelArrangement> ()) _channelFormat.update (properties->channelCount, properties->channelArrangementDataType, properties->channelArrangement); else _channelFormat.update (properties->channelCount, kARAChannelArrangementUndefined, nullptr); @@ -612,17 +612,17 @@ void PlaybackRegion::updateProperties (PropertiesPtrtransformationFlags & kARAPlaybackTransformationContentBasedFadeAtHead) != 0); _contentBasedFadeAtTail = ((properties->transformationFlags & kARAPlaybackTransformationContentBasedFadeAtTail) != 0); - if (properties.implements ()) + if (properties.implements<&ARAPlaybackRegionProperties::name> ()) _name = properties->name; else _name = nullptr; - if (properties.implements ()) + if (properties.implements<&ARAPlaybackRegionProperties::color> ()) _color = properties->color; else _color = nullptr; - ARA_VALIDATE_API_ARGUMENT (properties, properties.implements ()); + ARA_VALIDATE_API_ARGUMENT (properties, properties.implements<&ARAPlaybackRegionProperties::regionSequenceRef> ()); auto regionSequence { fromRef (properties->regionSequenceRef) }; ARA_VALIDATE_API_ARGUMENT (properties->regionSequenceRef, getDocumentController ()->isValidRegionSequence (regionSequence)); setRegionSequence (regionSequence); @@ -1189,10 +1189,10 @@ bool DocumentControllerDelegate::doStoreAudioSourceToAudioFileChunk (HostArchive *openAutomatically = false; ARAAudioSourceRef audioSourceRef { toRef (audioSource) }; - const ARA::SizedStruct filter { ARA::kARATrue, - 1U, &audioSourceRef, - 0U, nullptr - }; + const SizedStruct<&ARAStoreObjectsFilter::audioModificationRefs> filter { kARATrue, + 1U, &audioSourceRef, + 0U, nullptr + }; const StoreObjectsFilter storeObjectsFilter { &filter }; return doStoreObjectsToArchive (archiveWriter, &storeObjectsFilter); } @@ -1260,7 +1260,7 @@ void DocumentController::updateMusicalContextProperties (ARAMusicalContextRef mu ARA_VALIDATE_API_ARGUMENT (musicalContextRef, isValidMusicalContext (musicalContext)); ARA_VALIDATE_API_STRUCT_PTR (properties, ARAMusicalContextProperties); - if (properties.implements ()) + if (properties.implements<&ARAMusicalContextProperties::orderIndex> ()) { if (properties->orderIndex != musicalContext->getOrderIndex ()) { @@ -2845,7 +2845,7 @@ const ARADocumentControllerInstance* PlugInEntry::createDocumentControllerWithDo ARA_VALIDATE_API_INTERFACE (hostInstance->audioAccessControllerInterface, ARAAudioAccessControllerInterface); ARA_VALIDATE_API_INTERFACE (hostInstance->archivingControllerInterface, ARAArchivingControllerInterface); if (_usedApiGeneration >= kARAAPIGeneration_2_0_Final) - ARA_VALIDATE_API_ARGUMENT (hostInstance->archivingControllerInterface, SizedStructPtr (hostInstance->archivingControllerInterface).implements ()); + ARA_VALIDATE_API_ARGUMENT (hostInstance->archivingControllerInterface, SizedStructPtr (hostInstance->archivingControllerInterface).implements<&ARAArchivingControllerInterface::getDocumentArchiveID> ()); if (hostInstance->contentAccessControllerInterface) ARA_VALIDATE_API_INTERFACE (hostInstance->contentAccessControllerInterface, ARAContentAccessControllerInterface); if (hostInstance->modelUpdateControllerInterface) diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index 9415ed1..d747884 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -1374,9 +1374,9 @@ class DocumentController : public DocumentControllerInterface, //! Internal helper template class for HostContentReader. template #if ARA_VALIDATE_API_CALLS -using HostContentReaderBase = ARA::ContentReader>; +using HostContentReaderBase = ARA::ContentReader>; #else -using HostContentReaderBase = ARA::ContentReader>; +using HostContentReaderBase = ARA::ContentReader>; #endif /*******************************************************************************/ @@ -1943,7 +1943,7 @@ class PlugInEntry private: const FactoryConfig* const _factoryConfig; - const SizedStruct _factory; + const SizedStruct<&ARAFactory::supportsStoringAudioFileChunks> _factory; ARAAPIGeneration _usedApiGeneration { 0 }; ARA_DISABLE_COPY_AND_MOVE (PlugInEntry) From b18234dd8f1bfbabb382dc56c4c7aebdb2a96705 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:05 +0200 Subject: [PATCH 10/42] Improve C++ template usage based on C++17 now being required --- Debug/ARAContentLogger.h | 4 +- Dispatch/ARADispatchBase.h | 2 +- Dispatch/ARAHostDispatch.h | 2 +- Dispatch/ARAPlugInDispatch.h | 2 +- IPC/ARAIPC.h | 2 +- IPC/ARAIPCEncoding.h | 123 +++++++++++------------------- PlugIn/ARAPlug.h | 4 +- Utilities/ARAStdVectorUtilities.h | 8 +- 8 files changed, 57 insertions(+), 90 deletions(-) diff --git a/Debug/ARAContentLogger.h b/Debug/ARAContentLogger.h index fe22b24..ff310bb 100644 --- a/Debug/ARAContentLogger.h +++ b/Debug/ARAContentLogger.h @@ -190,12 +190,12 @@ struct ContentLogger // internal helper for log () - template ::type = true> + template = true> static inline void logEventIteration (ContentReader& reader, ARAInt32 i) { logEvent (i, reader[i]); } - template ::type = true> + template = true> static inline void logEventIteration (ContentReader& reader, ARAInt32 i) { const auto event { reader[i] }; diff --git a/Dispatch/ARADispatchBase.h b/Dispatch/ARADispatchBase.h index 5a9e6bb..4b1da05 100644 --- a/Dispatch/ARADispatchBase.h +++ b/Dispatch/ARADispatchBase.h @@ -187,7 +187,7 @@ struct alignas (alignof (void*)) SizedStruct : public StructType //! the size of the struct as though \p member was the last implemented field. static constexpr inline size_t getImplementedSize () { - static_assert (std::is_class::value && std::is_standard_layout::value, "C compatible standard layout struct required"); + static_assert (std::is_class_v && std::is_standard_layout_v, "C compatible standard layout struct required"); return reinterpret_cast (&(static_cast (nullptr)->*member)) + sizeof (static_cast (nullptr)->*member); } diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index cde0b05..3bfb0a6 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -62,7 +62,7 @@ ARA_DISABLE_DOCUMENTATION_DEPRECATED_WARNINGS_BEGIN #if !defined (ARA_MAP_HOST_REF) #define ARA_MAP_HOST_REF(HostClassType, FirstHostRefType, ...) \ static inline ARA::ToRefConversionHelper toHostRef (const HostClassType* ptr) noexcept { return ARA::ToRefConversionHelper { ptr }; } \ - template , HostRefType>::value, bool>::type = true> \ + template , HostRefType>, bool> = true> \ static inline DesiredHostClassType* fromHostRef (HostRefType ref) noexcept { HostClassType* object { ARA::FromRefConversionHelper (ref) }; return static_cast (object); } #endif diff --git a/Dispatch/ARAPlugInDispatch.h b/Dispatch/ARAPlugInDispatch.h index a52b1d9..3470abe 100644 --- a/Dispatch/ARAPlugInDispatch.h +++ b/Dispatch/ARAPlugInDispatch.h @@ -56,7 +56,7 @@ namespace PlugIn { #if !defined (ARA_MAP_REF) #define ARA_MAP_REF(ClassType, FirstRefType, ...) \ static inline ARA::ToRefConversionHelper toRef (const ClassType* ptr) noexcept { return ARA::ToRefConversionHelper { ptr }; } \ - template , RefType>::value, bool>::type = true> \ + template , RefType>, bool> = true> \ static inline DesiredClassType* fromRef (RefType ref) noexcept { ClassType* object { ARA::FromRefConversionHelper (ref) }; return static_cast (object); } #endif diff --git a/IPC/ARAIPC.h b/IPC/ARAIPC.h index cf9dfa2..ea601cb 100644 --- a/IPC/ARAIPC.h +++ b/IPC/ARAIPC.h @@ -47,7 +47,7 @@ extern "C" { #if !defined (ARA_MAP_IPC_REF) #define ARA_MAP_IPC_REF(IPCClassType, FirstIPCRefType, ...) \ static inline ARA::ToRefConversionHelper toIPCRef (const IPCClassType* ptr) noexcept { return ARA::ToRefConversionHelper { ptr }; } \ - template , IPCRefType>::value, bool>::type = true> \ + template , IPCRefType>, bool> = true> \ static inline DesiredIPCClassType* fromIPCRef (IPCRefType ref) noexcept { IPCClassType* object { ARA::FromRefConversionHelper (ref) }; return static_cast (object); } #endif diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 35e2885..65dde8a 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -119,16 +119,10 @@ struct ArrayArgument // private helper template to detect ARA ref types template -struct _IsRefType -{ - static constexpr bool value { false }; -}; +inline constexpr bool _IsRefType { false }; #define ARA_IPC_SPECIALIZE_FOR_REF_TYPE(Type) \ template<> \ -struct _IsRefType \ -{ \ - static constexpr bool value { true }; \ -}; +inline constexpr bool _IsRefType { true }; ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAMusicalContextRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARARegionSequenceRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAAudioSourceRef) @@ -160,23 +154,6 @@ ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCPlugInInstanceRef) #undef ARA_IPC_SPECIALIZE_FOR_REF_TYPE -// helper template to identify pointers to ARA structs in message arguments -template -struct _IsStructPointerArg -{ - struct _False - { - static constexpr bool value { false }; - }; - struct _True - { - static constexpr bool value { true }; - }; - using type = typename std::conditional::value && - std::is_pointer::value && !std::is_same::value, _True, _False>::type; -}; - - //------------------------------------------------------------------------------ // private primitive wrappers for MessageEn-/Decoder C API //------------------------------------------------------------------------------ @@ -264,12 +241,12 @@ inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgume // templated overloads of the IPCMessageEn-/Decoder primitives for ARA (host) ref types, // which are stored as size_t -template::value, bool>::type = true> +template, bool> = true> inline void _appendToMessage (MessageEncoder* encoder, const MessageArgumentKey argKey, const T argValue) { encoder->appendSize (argKey, reinterpret_cast (argValue)); } -template::value, bool>::type = true> +template, bool> = true> inline bool _readFromMessage (const MessageDecoder* decoder, const MessageArgumentKey argKey, T& argValue) { // \todo is there a safe/proper way across all compilers for this cast to avoid the copy? @@ -433,11 +410,11 @@ template<> struct _ValueEncoder : public _CompoundValueEncoderBase (value.member), sizeof (value.member), true }; \ _encodeAndAppend (encoder, offsetof (StructType, member), tmp_##member); #define ARA_IPC_ENCODE_EMBEDDED_ARRAY(member) \ - const ArrayArgument::type> tmp_##member { value.member, std::extent::value }; \ + const ArrayArgument> tmp_##member { value.member, std::extent_v }; \ _encodeAndAppend (encoder, offsetof (StructType, member), tmp_##member); #define ARA_IPC_ENCODE_VARIABLE_ARRAY(member, count) \ if ((value.count > 0) && (value.member != nullptr)) { \ - const ArrayArgument::type> tmp_##member { value.member, value.count }; \ + const ArrayArgument> tmp_##member { value.member, value.count }; \ _encodeAndAppend (encoder, offsetof (StructType, member), tmp_##member); \ } #define ARA_IPC_ENCODE_OPTIONAL_MEMBER(member) \ @@ -446,7 +423,7 @@ template<> struct _ValueEncoder : public _CompoundValueEncoderBase offsetof (std::remove_reference::type, member)) + (value.structSize > offsetof (std::remove_reference_t, member)) #define ARA_IPC_ENCODE_ADDENDUM_MEMBER(member) \ if (ARA_IPC_HAS_ADDENDUM_MEMBER (member)) \ ARA_IPC_ENCODE_MEMBER (member) @@ -484,13 +461,13 @@ template<> struct _ValueDecoder : public _CompoundValueDecoderBase::type> tmp_##member { result.member, std::extent::value }; \ + ArrayArgument> tmp_##member { result.member, std::extent_v }; \ success &= _readAndDecode (tmp_##member, decoder, offsetof (StructType, member)); \ ARA_INTERNAL_ASSERT (success); #define ARA_IPC_DECODE_VARIABLE_ARRAY(member, count, updateCount) \ /* \todo the outer struct contains a pointer to the inner struct, so we need some */ \ /* thread-safe place to store it - is there a better way to achieve this? */ \ - thread_local std::vector::type>::type> tmp_##member; \ + thread_local std::vector>> tmp_##member; \ if (_readAndDecode (tmp_##member, decoder, offsetof (StructType, member))) { \ result.member = tmp_##member.data (); \ if (updateCount) { result.count = tmp_##member.size (); } \ @@ -520,7 +497,7 @@ template<> struct _ValueDecoder : public _CompoundValueDecoderBase::type>::type cache; \ + thread_local std::remove_const_t> cache; \ success &= _ValueDecoder::decode (cache, subDecoder_##member); \ ARA_INTERNAL_ASSERT (success); \ result.member = &cache; \ @@ -923,49 +900,21 @@ inline bool _readAndDecode (ValueT& result, const MessageDecoder* decoder, const // private helper for decodeArguments() to deal with optional arguments template -struct _IsOptionalArgument -{ - static constexpr bool value { false }; -}; +inline constexpr bool _IsOptionalArgument { false }; template -struct _IsOptionalArgument> -{ - static constexpr bool value { true }; -}; - -// private helpers for encodeArguments() and decodeArguments() to deal with the variable arguments one at a time -inline void _encodeArgumentsHelper (MessageEncoder* /*encoder*/, const MessageArgumentKey /*argKey*/) -{ -} -template::type::value, bool>::type = true> -inline void _encodeArgumentsHelper (MessageEncoder* encoder, const MessageArgumentKey argKey, const ArgT& argValue, const MoreArgs &... moreArgs) -{ - _encodeAndAppend (encoder, argKey, argValue); - _encodeArgumentsHelper (encoder, argKey + 1, moreArgs...); -} -template::type::value, bool>::type = true> -inline void _encodeArgumentsHelper (MessageEncoder* encoder, const MessageArgumentKey argKey, const ArgT& argValue, const MoreArgs &... moreArgs) -{ - if (argValue != nullptr) - _encodeAndAppend (encoder, argKey, *argValue); - _encodeArgumentsHelper (encoder, argKey + 1, moreArgs...); -} -template -inline void _encodeArgumentsHelper (MessageEncoder* encoder, const MessageArgumentKey argKey, const std::nullptr_t& /*argValue*/, const MoreArgs &... moreArgs) -{ - _encodeArgumentsHelper (encoder, argKey + 1, moreArgs...); -} +inline constexpr bool _IsOptionalArgument> { true }; +// private helpers for decodeArguments() to deal with the variable arguments one at a time inline void _decodeArgumentsHelper (const MessageDecoder* /*decoder*/, const MessageArgumentKey /*argKey*/) { } -template::value, bool>::type = true> +template, bool> = true> inline void _decodeArgumentsHelper (const MessageDecoder* decoder, const MessageArgumentKey argKey, ArgT& argValue, MoreArgs &... moreArgs) { _readAndDecode (argValue, decoder, argKey); _decodeArgumentsHelper (decoder, argKey + 1, moreArgs...); } -template::value, bool>::type = true> +template, bool> = true> inline void _decodeArgumentsHelper (const MessageDecoder* decoder, const MessageArgumentKey argKey, ArgT& argValue, MoreArgs &... moreArgs) { argValue.second = _readAndDecode (argValue.first, decoder, argKey); @@ -1089,13 +1038,13 @@ inline bool operator!= (const MethodID methodID, const MessageID messageID) // "global" messages for startup and teardown that are not calculated based on interface structs -constexpr auto kGetFactoriesCountMethodID { MethodID::createWithARAGlobalMessageID<1> () }; -constexpr auto kGetFactoryMethodID { MethodID::createWithARAGlobalMessageID<2> () }; -constexpr auto kInitializeARAMethodID { MethodID::createWithARAGlobalMessageID<3> () }; -constexpr auto kCreateDocumentControllerMethodID { MethodID::createWithARAGlobalMessageID<4> () }; -constexpr auto kBindToDocumentControllerMethodID { MethodID::createWithARAGlobalMessageID<5> () }; -constexpr auto kCleanupBindingMethodID { MethodID::createWithARAGlobalMessageID<6> () }; -constexpr auto kUninitializeARAMethodID { MethodID::createWithARAGlobalMessageID<7> () }; +inline constexpr auto kGetFactoriesCountMethodID { MethodID::createWithARAGlobalMessageID<1> () }; +inline constexpr auto kGetFactoryMethodID { MethodID::createWithARAGlobalMessageID<2> () }; +inline constexpr auto kInitializeARAMethodID { MethodID::createWithARAGlobalMessageID<3> () }; +inline constexpr auto kCreateDocumentControllerMethodID { MethodID::createWithARAGlobalMessageID<4> () }; +inline constexpr auto kBindToDocumentControllerMethodID { MethodID::createWithARAGlobalMessageID<5> () }; +inline constexpr auto kCleanupBindingMethodID { MethodID::createWithARAGlobalMessageID<6> () }; +inline constexpr auto kUninitializeARAMethodID { MethodID::createWithARAGlobalMessageID<7> () }; // for debugging: translate IDs of messages sent by the host back to human-readable text @@ -1225,21 +1174,39 @@ inline const char* decodePlugInMessageID (const MessageID messageID) return "unknown message ID"; } +// helper template to identify pointers to ARA structs in message arguments +template +inline constexpr auto _IsStructPointerArg = !_IsRefType && std::is_pointer_v && !std::is_same_v; + +template, int> = 0> +inline void _encodeAndAppendHelper (MessageEncoder* encoder, const MessageArgumentKey argKey, const ArgT& argValue) +{ + _encodeAndAppend (encoder, argKey, argValue); +} +template, int> = 0> +inline void _encodeAndAppendHelper (MessageEncoder* encoder, const MessageArgumentKey argKey, const ArgT& argValue) +{ + if (argValue != nullptr) + _encodeAndAppend (encoder, argKey, *argValue); +} +inline void _encodeAndAppendHelper (MessageEncoder* /*encoder*/, const MessageArgumentKey /*argKey*/, const std::nullptr_t& /*argValue*/) +{} // caller side: create a message with the specified arguments template inline void encodeArguments (MessageEncoder* encoder, const Args &... args) { - _encodeArgumentsHelper (encoder, 0, args...); + MessageArgumentKey key { 0 }; + ((_encodeAndAppendHelper(encoder, key++, args)), ...); } // caller side: decode the received reply to a sent message -template::value || !std::is_pod::value, bool>::type = true> +template || !std::is_pod_v, bool> = true> inline bool decodeReply (RetT& result, const MessageDecoder* decoder) { return _readAndDecode (result, decoder, 0); } -template::value && std::is_pod::value, bool>::type = true> +template && std::is_pod_v, bool> = true> inline bool decodeReply (RetT& result, const MessageDecoder* decoder) { return _ValueDecoder::decode (result, decoder); @@ -1259,13 +1226,13 @@ template using OptionalArgument = typename std::pair; // callee side: encode the reply to a received message -template::value || !std::is_pod::value, bool>::type = true> +template || !std::is_pod_v, bool> = true> inline void encodeReply (MessageEncoder* encoder, const ValueT& value) { ARA_INTERNAL_ASSERT (encoder != nullptr); _encodeAndAppend (encoder, 0, value); } -template::value && std::is_pod::value, bool>::type = true> +template && std::is_pod_v, bool> = true> inline void encodeReply (MessageEncoder* encoder, const ValueT& value) { ARA_INTERNAL_ASSERT (encoder != nullptr); diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index d747884..f907c32 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -182,9 +182,9 @@ class OptionalProperty inline bool operator!= (const OptionalProperty& other) const noexcept { return !(*this == other._data); } private: - template::value, bool>::type = true> + template, bool> = true> static constexpr inline size_t getAllocSize (const T* /*value*/) noexcept { return sizeof (T); } - template::value, bool>::type = true> + template, bool> = true> static inline size_t getAllocSize (const ARAUtf8String value) noexcept { return std::strlen (value) + 1; } private: diff --git a/Utilities/ARAStdVectorUtilities.h b/Utilities/ARAStdVectorUtilities.h index bc30722..3db36f2 100644 --- a/Utilities/ARAStdVectorUtilities.h +++ b/Utilities/ARAStdVectorUtilities.h @@ -34,13 +34,13 @@ namespace ARA { //! Up-casting vectors of base class pointers to vectors of derived class pointers. //@{ -template ::value || std::is_convertible::value), bool>::type = true> +template || std::is_convertible_v), bool> = true> inline std::vector& vector_cast (std::vector& container) noexcept { return reinterpret_cast&> (container); } -template ::value || std::is_convertible::value), bool>::type = true> +template || std::is_convertible_v), bool> = true> inline std::vector const& vector_cast (std::vector const& container) noexcept { return reinterpret_cast const&> (container); @@ -53,7 +53,7 @@ inline std::vector const& vector_cast (std::vector const& container) noexc //! Returns true if found & erased, otherwise false. //@{ -template ::value || std::is_convertible::value, bool>::type = true> +template || std::is_convertible_v, bool> = true> inline bool find_erase (std::vector& container, const U& element) noexcept { const auto it { std::find (container.begin (), container.end (), element) }; @@ -70,7 +70,7 @@ inline bool find_erase (std::vector& container, const U& element) noexcept //! Determine if an element exists in a vector. //@{ -template ::value || std::is_convertible::value, bool>::type = true> +template || std::is_convertible_v, bool> = true> inline bool contains (std::vector const& container, const U& element) noexcept { return std::find (container.begin (), container.end (), element) != container.end (); From 97589818fc18d839d3e4e4b00bab40852230b7af Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:06 +0200 Subject: [PATCH 11/42] Add prototypes or static declaration to all internal functions, allowing to compile with missing prototypes warnings enabled --- Debug/ARADebug.c | 1 + IPC/ARAIPCCFEncoding.cpp | 2 +- IPC/ARAIPCProxyHost.cpp | 6 +++--- PlugIn/ARAPlug.cpp | 20 ++++++++++---------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Debug/ARADebug.c b/Debug/ARADebug.c index de7cc98..d3fcea5 100644 --- a/Debug/ARADebug.c +++ b/Debug/ARADebug.c @@ -256,6 +256,7 @@ void ARASetExternalAssertReference(ARAAssertFunction * address) _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wmissing-noreturn\"") #endif +void ARAAssertionFailure(ARAAssertCategory category, const void * problematicArgument, const char * file, int line, const char * diagnosis); void ARAAssertionFailure(ARAAssertCategory category, const void * problematicArgument, const char * file, int line, const char * diagnosis) { #if ARA_ENABLE_INTERNAL_ASSERTS diff --git a/IPC/ARAIPCCFEncoding.cpp b/IPC/ARAIPCCFEncoding.cpp index 76af582..4cb9306 100644 --- a/IPC/ARAIPCCFEncoding.cpp +++ b/IPC/ARAIPCCFEncoding.cpp @@ -54,7 +54,7 @@ class _CFReleaser // wrap key value into CFString (no reference count transferred to caller) -CFStringRef _getEncodedKey (MessageArgumentKey argKey) +static CFStringRef _getEncodedKey (MessageArgumentKey argKey) { // \todo All plist formats available for CFPropertyListCreateData () in createEncodedMessage () need CFString keys. // Once we switch to the more modern (NS)XPC API we shall be able to use CFNumber keys directly... diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index 170b226..28f1828 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -99,7 +99,7 @@ class AudioAccessController : public Host::AudioAccessControllerInterface, prote /*******************************************************************************/ -void _swap (float* ptr) +static void _swap (float* ptr) { auto asIntPtr { reinterpret_cast (ptr) }; #if defined (_MSC_VER) @@ -110,7 +110,7 @@ void _swap (float* ptr) #error "not implemented for this compiler." #endif } -void _swap (double* ptr) +static void _swap (double* ptr) { auto asIntPtr { reinterpret_cast (ptr) }; #if defined (_MSC_VER) @@ -637,7 +637,7 @@ void ARAIPCProxyHostSetBindingHandler (ARAIPCBindingHandler handler) _bindingHandler = handler; } -const ARAFactory* getFactoryWithID (ARAPersistentID factoryID) +static const ARAFactory* getFactoryWithID (ARAPersistentID factoryID) { for (const auto& factory : _factories) { diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index b45b23b..b3f86a7 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -113,7 +113,7 @@ struct SortByOrderIndex /*******************************************************************************/ // stream operator for color (r,g,b) -std::ostream& operator<< (std::ostream& oss, const ARAColor& color) +[[maybe_unused]] static std::ostream& operator<< (std::ostream& oss, const ARAColor& color) { oss << "(" << color.r << "," << color.g << "," << color.b << ")"; return oss; @@ -150,7 +150,7 @@ std::ostream& operator<< (std::ostream& oss, const OptionalPropertygetHostRef () << "):" << playbackRegion->getName () << ", playback time:" << playbackRegion->getStartInPlaybackTime () << " to " << playbackRegion->getEndInPlaybackTime (); @@ -165,7 +165,7 @@ void logToStream (const PlaybackRegion* playbackRegion, std::ostringstream& oss, oss << "\n"; } -void logToStream (const AudioModification* audioModification, std::ostringstream& oss, bool detailed, bool recursive, std::string indentation) +[[maybe_unused]] static void logToStream (const AudioModification* audioModification, std::ostringstream& oss, bool detailed, bool recursive, std::string indentation) { oss << indentation << audioModification << "(" << audioModification->getHostRef () << "):" << audioModification->getName () << ", ID: \"" << audioModification->getPersistentID () << "\""; @@ -179,7 +179,7 @@ void logToStream (const AudioModification* audioModification, std::ostringstream } } -void logToStream (const AudioSource* audioSource, std::ostringstream& oss, bool detailed, bool recursive, std::string indentation) +[[maybe_unused]] static void logToStream (const AudioSource* audioSource, std::ostringstream& oss, bool detailed, bool recursive, std::string indentation) { oss << indentation << audioSource << "(" << audioSource->getHostRef () << "):" << audioSource->getName () << ", ID: \"" << audioSource->getPersistentID () << "\"\n"; if (detailed) @@ -193,7 +193,7 @@ void logToStream (const AudioSource* audioSource, std::ostringstream& oss, bool } } -void logToStream (const RegionSequence* regionSequence, std::ostringstream& oss, bool detailed, bool /*recursive*/, std::string indentation) +[[maybe_unused]] static void logToStream (const RegionSequence* regionSequence, std::ostringstream& oss, bool detailed, bool /*recursive*/, std::string indentation) { oss << indentation << regionSequence << "(" << regionSequence->getHostRef () << "):" << regionSequence->getName (); if (detailed) @@ -223,7 +223,7 @@ void logToStream (const RegionSequence* regionSequence, std::ostringstream& oss, } } -void logToStream (const MusicalContext* musicalContext, std::ostringstream& oss, bool detailed, bool recursive, std::string indentation) +[[maybe_unused]] static void logToStream (const MusicalContext* musicalContext, std::ostringstream& oss, bool detailed, bool recursive, std::string indentation) { oss << indentation << musicalContext << "(" << musicalContext->getHostRef () << "):" << musicalContext->getName (); if (detailed) @@ -239,7 +239,7 @@ void logToStream (const MusicalContext* musicalContext, std::ostringstream& oss, } } -void logToStream (const Document* document, std::ostringstream& oss, bool detailed, bool recursive, std::string indentation) +[[maybe_unused]] static void logToStream (const Document* document, std::ostringstream& oss, bool detailed, bool recursive, std::string indentation) { oss << indentation << document << ":" << document->getName () << "\n"; @@ -2367,7 +2367,7 @@ void HostArchiveWriter::notifyDocumentArchivingProgress (float value) noexcept /*******************************************************************************/ -std::vector _convertPlaybackRegionsArray (const DocumentController* ARA_MAYBE_UNUSED_ARG (documentController), ARASize playbackRegionsCount, const ARAPlaybackRegionRef playbackRegionRefs[]) +static std::vector _convertPlaybackRegionsArray (const DocumentController* ARA_MAYBE_UNUSED_ARG (documentController), ARASize playbackRegionsCount, const ARAPlaybackRegionRef playbackRegionRefs[]) { std::vector playbackRegions; if (playbackRegionsCount > 0) @@ -2385,7 +2385,7 @@ std::vector _convertPlaybackRegionsArray (const DocumentControl return playbackRegions; } -std::vector _convertRegionSequencesArray (const DocumentController* ARA_MAYBE_UNUSED_ARG (documentController), ARASize regionSequenceRefsCount, const ARARegionSequenceRef regionSequenceRefs[]) +static std::vector _convertRegionSequencesArray (const DocumentController* ARA_MAYBE_UNUSED_ARG (documentController), ARASize regionSequenceRefsCount, const ARARegionSequenceRef regionSequenceRefs[]) { std::vector regionSequences; if (regionSequenceRefsCount > 0) @@ -2446,7 +2446,7 @@ std::vector ViewSelection::getEffectiveRegionSequences () const return result; } -ARAContentTimeRange getUnionTimeRangeOfPlaybackRegions (std::vector const& playbackRegions) noexcept +static ARAContentTimeRange getUnionTimeRangeOfPlaybackRegions (std::vector const& playbackRegions) noexcept { ARA_INTERNAL_ASSERT (!playbackRegions.empty ()); From 2c1b5e627feb71fcd6f5b52ef0e3c31c3579a59b Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:06 +0200 Subject: [PATCH 12/42] Using C++17 allows to limit ARA_MAYBE_UNUSED_VAR/ARG to C code --- Debug/ARADebug.h | 28 +++++++++++++--------------- IPC/ARAIPCCFEncoding.cpp | 4 ++-- PlugIn/ARAPlug.cpp | 4 ++-- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Debug/ARADebug.h b/Debug/ARADebug.h index d3949eb..9ea6d47 100644 --- a/Debug/ARADebug.h +++ b/Debug/ARADebug.h @@ -97,22 +97,20 @@ extern "C" // prevent unused variable warnings /*******************************************************************************/ -#if defined(__cplusplus) - #define ARA_MAYBE_UNUSED_VAR(var) var [[maybe_unused]] -#elif defined(__GNUC__) - #define ARA_MAYBE_UNUSED_VAR(var) var __attribute__((unused)) -#elif defined(_MSC_VER) - #define ARA_MAYBE_UNUSED_VAR(var) var; (false ? (void)var : (void)false) -#else - #define ARA_MAYBE_UNUSED_VAR(var) var; (void)var -#endif +#if !defined(__cplusplus) + #if defined(__GNUC__) + #define ARA_MAYBE_UNUSED_VAR(var) var __attribute__((unused)) + #elif defined(_MSC_VER) + #define ARA_MAYBE_UNUSED_VAR(var) var; (false ? (void)var : (void)false) + #else + #define ARA_MAYBE_UNUSED_VAR(var) var; (void)var + #endif -#if defined(__cplusplus) - #define ARA_MAYBE_UNUSED_ARG(var) var [[maybe_unused]] -#elif defined(__GNUC__) - #define ARA_MAYBE_UNUSED_ARG(var) var __attribute__((unused)) -#else - #define ARA_MAYBE_UNUSED_ARG(var) var + #if defined(__GNUC__) + #define ARA_MAYBE_UNUSED_ARG(var) var __attribute__((unused)) + #else + #define ARA_MAYBE_UNUSED_ARG(var) var + #endif #endif diff --git a/IPC/ARAIPCCFEncoding.cpp b/IPC/ARAIPCCFEncoding.cpp index 4cb9306..99777b3 100644 --- a/IPC/ARAIPCCFEncoding.cpp +++ b/IPC/ARAIPCCFEncoding.cpp @@ -262,8 +262,8 @@ bool CFMessageDecoder::readString (MessageArgumentKey argKey, const char ** argV { const auto length { CFStringGetLength (string) }; std::string temp; // \todo does not work: { static_cast (length), char { 0 } }; - temp.assign ( static_cast (length) , char { 0 } ); - CFIndex ARA_MAYBE_UNUSED_VAR (count) { CFStringGetBytes (string, CFRangeMake (0, length), kCFStringEncodingUTF8, 0, false, (UInt8*)(&temp[0]), length, nullptr) }; + temp.assign (static_cast (length) , char { 0 }); + [[maybe_unused]] const auto count { CFStringGetBytes (string, CFRangeMake (0, length), kCFStringEncodingUTF8, 0, false, (UInt8*)(&temp[0]), length, nullptr) }; ARA_INTERNAL_ASSERT (count == length); static std::set strings; // \todo static cache of "undecodeable" strings requires single-threaded use - maybe make iVar instead? strings.insert (temp); diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index b3f86a7..058e3cf 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -2367,7 +2367,7 @@ void HostArchiveWriter::notifyDocumentArchivingProgress (float value) noexcept /*******************************************************************************/ -static std::vector _convertPlaybackRegionsArray (const DocumentController* ARA_MAYBE_UNUSED_ARG (documentController), ARASize playbackRegionsCount, const ARAPlaybackRegionRef playbackRegionRefs[]) +static std::vector _convertPlaybackRegionsArray ([[maybe_unused]] const DocumentController* documentController, ARASize playbackRegionsCount, const ARAPlaybackRegionRef playbackRegionRefs[]) { std::vector playbackRegions; if (playbackRegionsCount > 0) @@ -2385,7 +2385,7 @@ static std::vector _convertPlaybackRegionsArray (const Document return playbackRegions; } -static std::vector _convertRegionSequencesArray (const DocumentController* ARA_MAYBE_UNUSED_ARG (documentController), ARASize regionSequenceRefsCount, const ARARegionSequenceRef regionSequenceRefs[]) +static std::vector _convertRegionSequencesArray ([[maybe_unused]] const DocumentController* documentController, ARASize regionSequenceRefsCount, const ARARegionSequenceRef regionSequenceRefs[]) { std::vector regionSequences; if (regionSequenceRefsCount > 0) From 5ddec54eea0db7de758620a8af46c522096180e7 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:09 +0200 Subject: [PATCH 13/42] Remove unused function left over from older version of IPC encoding --- IPC/ARAIPCEncoding.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 65dde8a..71a1bc3 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -996,18 +996,6 @@ class MethodID return (messageID < kReservedMessageIDRangeStart) || (kReservedMessageIDRangeEnd <= messageID); } - template - static inline constexpr bool isMessageToHostInterface (MessageID messageID) - { - return _getHostInterfaceID () == (messageID % 8); - } - - template - static inline constexpr bool isMessageToPlugInInterface (MessageID messageID) - { - return _getPlugInInterfaceID () == (messageID % 8); - } - constexpr MessageID getMessageID () const { return _id; } private: From 441d582891f14580e69196b73d9b0bf00a4109b1 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:09 +0200 Subject: [PATCH 14/42] Fix some warnings in IPC code --- IPC/ARAIPCConnection.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 301202d..0893997 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -357,7 +357,7 @@ void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID { const auto previousRemoteTargetThread { _remoteTargetThread }; ThreadRef remoteTargetThread; - const auto success { decoder->readThreadRef (sendThreadKey, &remoteTargetThread) }; + [[maybe_unused]] const auto success { decoder->readThreadRef (sendThreadKey, &remoteTargetThread) }; ARA_INTERNAL_ASSERT (success); _remoteTargetThread = remoteTargetThread; @@ -388,7 +388,7 @@ BOOL ConvertToRealHandle(HANDLE h, HANDLE _GetRealCurrentThread () { HANDLE currentThread {}; - auto success { ConvertToRealHandle (::GetCurrentThread (), FALSE, ¤tThread) }; + [[maybe_unused]] const auto success { ConvertToRealHandle (::GetCurrentThread (), FALSE, ¤tThread) }; ARA_INTERNAL_ASSERT (success); return currentThread; } @@ -497,7 +497,7 @@ void Connection::dispatchToCreationThread (DispatchableFunction func) { #if defined (_WIN32) auto funcPtr { new DispatchableFunction { func } }; - const auto result { ::QueueUserAPC (APCRouteNewTransactionFunc, _creationThreadHandle, reinterpret_cast (funcPtr)) }; + [[maybe_unused]] const auto result { ::QueueUserAPC (APCRouteNewTransactionFunc, _creationThreadHandle, reinterpret_cast (funcPtr)) }; ARA_INTERNAL_ASSERT (result != 0); #elif defined (__APPLE__) _mutex.lock (); From 3448b84ce61f532af815ffb0e02b4ac71ecea4df Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:09 +0200 Subject: [PATCH 15/42] IPC main thread dispatcher does not need to dispatch to main thread if it is already in the process loop --- IPC/ARAIPCConnection.cpp | 33 +++++++++++++++++++++++++++------ IPC/ARAIPCConnection.h | 2 ++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 0893997..d1bbb1f 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -108,7 +108,13 @@ namespace ARA { namespace IPC { -// keys to store the threading information in the IPC messages +// key to indicate whether an outgoing call is made in response to a currently handled incoming call +// or a new call, which is necessary to deal with the decoupled main threads concurrency +// to optimize for performance, it is only added when the call is a response, being a new call is implicit +// (it is also never added to replies because they always are a response anyways) +constexpr MessageArgumentKey isResponseKey { -1 }; + +// keys to store the threading information in the IPC messages for OtherThreadsMessageDispatcher constexpr MessageArgumentKey sendThreadKey { -1 }; constexpr MessageArgumentKey receiveThreadKey { -2 }; @@ -187,11 +193,15 @@ void MainThreadMessageDispatcher::sendMessage (MessageID messageID, MessageEncod Connection::ReplyHandler replyHandler, void* replyHandlerUserData) { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + ARA_INTERNAL_ASSERT (!isReply (messageID)); const auto previousPendingReplyHandler { _pendingReplyHandler }; const PendingReplyHandler pendingReplyHandler { replyHandler, replyHandlerUserData, previousPendingReplyHandler }; _pendingReplyHandler = &pendingReplyHandler; - + + if (_processingMessagesCount > 0) + encoder->appendInt32 (isResponseKey, 1); + _sendMessage (messageID, encoder); while (previousPendingReplyHandler != _pendingReplyHandler) @@ -217,7 +227,9 @@ void MainThreadMessageDispatcher::processPendingMessageIfNeeded () } else { + ++_processingMessagesCount; auto replyEncoder { _handleReceivedMessage (pendingMessageID, pendingMessageDecoder) }; + --_processingMessagesCount; _sendMessage (0, replyEncoder); } } @@ -225,21 +237,30 @@ void MainThreadMessageDispatcher::processPendingMessageIfNeeded () void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) { + const auto isResponse { isReply (messageID) || // replies implicitly are responses + (decoder && decoder->hasDataForKey (isResponseKey)) }; + _pendingMessageID = messageID; _pendingMessageDecoder.store (decoder, std::memory_order_release); getConnection ()->signalMesssageReceived (); - if (getConnection ()->wasCreatedOnCurrentThread ()) + // if on creation thread, responses can be processed immediately, and + // new transaction can only be processed immediately when no other transaction is going on + if (getConnection ()->wasCreatedOnCurrentThread () && + (isResponse || (_pendingReplyHandler == nullptr))) { processPendingMessageIfNeeded (); } else { if (isReply (messageID)) - ARA_IPC_LOG ("dispatches received reply from thread %p to creation thread", _getCurrentThread ()); + ARA_IPC_LOG ("dispatched received reply from thread %p to creation thread", _getCurrentThread ()); else - ARA_IPC_LOG ("dispatches received message with ID %i from thread %p to creation thread", messageID, _getCurrentThread ()); - getConnection ()->dispatchToCreationThread (std::bind (&MainThreadMessageDispatcher::processPendingMessageIfNeeded, this)); + ARA_IPC_LOG ("dispatched received message with ID %i from thread %p to creation thread", messageID, _getCurrentThread ()); + + // only new transactions must be dispatched, otherwise the target thread is already waiting for the message received signal + if (!isResponse) + getConnection ()->dispatchToCreationThread (std::bind (&MainThreadMessageDispatcher::processPendingMessageIfNeeded, this)); } } diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 56ce376..d1fff92 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -264,6 +264,8 @@ class MainThreadMessageDispatcher : public MessageDispatcher void processPendingMessageIfNeeded (); private: + int32_t _processingMessagesCount { 0 }; + // \todo this is not allowed for some reason, so we must cast at every use of _noPendingMessageDecoder... // static constexpr auto _noPendingMessageDecoder { reinterpret_cast (static_cast (-1)) }; static constexpr auto _noPendingMessageDecoder { static_cast (-1) }; From 1424a6c1973fa900dd2676d44ca537a5b4188c9a Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:10 +0200 Subject: [PATCH 16/42] Improve IPC debug output --- IPC/ARAIPCAudioUnit_v3.mm | 4 ++ IPC/ARAIPCConnection.cpp | 85 +++++++++++++++++++++++++++------------ IPC/ARAIPCConnection.h | 17 ++++---- IPC/ARAIPCEncoding.h | 16 +++++--- IPC/ARAIPCProxyHost.cpp | 2 - IPC/ARAIPCProxyPlugIn.cpp | 2 - 6 files changed, 81 insertions(+), 45 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 6912b58..90a64ee 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -233,6 +233,8 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override #endif } + bool sendsHostMessages () const override { return true; } + private: AUProxyPlugIn (NSObject * _Nonnull mainChannel, NSObject * _Nonnull otherChannel, @@ -323,6 +325,8 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCConnectionRef _Nonnull prox setMessageHandler (this); } + bool sendsHostMessages () const override { return false; } + private: static const ARAPlugInExtensionInstance * ARA_CALL handleBinding (ARAIPCPlugInInstanceRef plugInInstanceRef, ARADocumentControllerRef controllerRef, diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index d1bbb1f..26966d6 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -36,6 +36,18 @@ #endif +#if ARA_ENABLE_DEBUG_OUTPUT && 0 + #define ARA_IPC_LOG(...) ARA_LOG ("ARA IPC " __VA_ARGS__) + #define ARA_IPC_DECODE_MESSAGE_FORMAT " %i=%s " + #define ARA_IPC_DECODE_SENT_MESSAGE_ARGS(messageID) messageID, (getConnection ()->sendsHostMessages ()) ? decodeHostMessageID (messageID) : decodePlugInMessageID (messageID) + #define ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS(messageID) messageID, (!getConnection ()->sendsHostMessages ()) ? decodeHostMessageID (messageID) : decodePlugInMessageID (messageID) + #define ARA_IPC_LABEL_THREAD_FORMAT " %sthread %p" + #define ARA_IPC_LABEL_THREAD_ARGS (getConnection ()->wasCreatedOnCurrentThread ()) ? "creation " : "other ", MessageDispatcher::getCurrentThread () +#else + #define ARA_IPC_LOG(...) ((void) 0) +#endif + + // ARA IPC design overview // // ARA API calls can be stacked multiple times. @@ -145,7 +157,7 @@ MessageDispatcher::~MessageDispatcher () delete _messageChannel; } -MessageDispatcher::ThreadRef MessageDispatcher::_getCurrentThread () +MessageDispatcher::ThreadRef MessageDispatcher::getCurrentThread () { const auto thisThread { std::this_thread::get_id () }; const auto result { *reinterpret_cast (&thisThread) }; @@ -153,12 +165,14 @@ MessageDispatcher::ThreadRef MessageDispatcher::_getCurrentThread () return result; } -void MessageDispatcher::_sendMessage (MessageID messageID, MessageEncoder* encoder) +void MessageDispatcher::_sendMessage (MessageID messageID, MessageEncoder* encoder, [[maybe_unused]] bool isNewTransaction) { if (isReply (messageID)) - ARA_IPC_LOG ("replies to message on thread %p", _getCurrentThread ()); + ARA_IPC_LOG ("replies to message on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); else - ARA_IPC_LOG ("sends message with ID %i on thread %p", messageID, _getCurrentThread ()); + ARA_IPC_LOG ("sends" ARA_IPC_DECODE_MESSAGE_FORMAT "on" ARA_IPC_LABEL_THREAD_FORMAT "%s", + ARA_IPC_DECODE_SENT_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS, + (isNewTransaction) ? " (new transaction)" : ""); _messageChannel->sendMessage (messageID, encoder); @@ -167,7 +181,7 @@ void MessageDispatcher::_sendMessage (MessageID messageID, MessageEncoder* encod void MessageDispatcher::_handleReply (const MessageDecoder* decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) { - ARA_IPC_LOG ("handles received reply on thread %p", _getCurrentThread ()); + ARA_IPC_LOG ("handles reply on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); if (replyHandler) (replyHandler) (decoder, replyHandlerUserData); else @@ -179,7 +193,8 @@ MessageEncoder* MessageDispatcher::_handleReceivedMessage (MessageID messageID, { ARA_INTERNAL_ASSERT (!isReply (messageID)); - ARA_IPC_LOG ("handles received message with ID %i on thread %p", messageID, _getCurrentThread ()); + ARA_IPC_LOG ("handles" ARA_IPC_DECODE_MESSAGE_FORMAT "on" ARA_IPC_LABEL_THREAD_FORMAT, + ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS); auto replyEncoder { _connection->createEncoder () }; _connection->getMessageHandler ()->handleReceivedMessage (messageID, decoder, replyEncoder); @@ -199,10 +214,11 @@ void MainThreadMessageDispatcher::sendMessage (MessageID messageID, MessageEncod const PendingReplyHandler pendingReplyHandler { replyHandler, replyHandlerUserData, previousPendingReplyHandler }; _pendingReplyHandler = &pendingReplyHandler; - if (_processingMessagesCount > 0) + const auto isResponse { _processingMessagesCount > 0 }; + if (isResponse) encoder->appendInt32 (isResponseKey, 1); - _sendMessage (messageID, encoder); + _sendMessage (messageID, encoder, !isResponse); while (previousPendingReplyHandler != _pendingReplyHandler) { @@ -230,7 +246,7 @@ void MainThreadMessageDispatcher::processPendingMessageIfNeeded () ++_processingMessagesCount; auto replyEncoder { _handleReceivedMessage (pendingMessageID, pendingMessageDecoder) }; --_processingMessagesCount; - _sendMessage (0, replyEncoder); + _sendMessage (0, replyEncoder, false); } } } @@ -240,24 +256,38 @@ void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, con const auto isResponse { isReply (messageID) || // replies implicitly are responses (decoder && decoder->hasDataForKey (isResponseKey)) }; - _pendingMessageID = messageID; - _pendingMessageDecoder.store (decoder, std::memory_order_release); - getConnection ()->signalMesssageReceived (); - // if on creation thread, responses can be processed immediately, and // new transaction can only be processed immediately when no other transaction is going on - if (getConnection ()->wasCreatedOnCurrentThread () && - (isResponse || (_pendingReplyHandler == nullptr))) + const auto processSynchronously { getConnection ()->wasCreatedOnCurrentThread () && + (isResponse || (_pendingReplyHandler == nullptr)) }; + + if (processSynchronously) { - processPendingMessageIfNeeded (); + if (isReply (messageID)) + ARA_IPC_LOG ("processes reply on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); + else + ARA_IPC_LOG ("processes" ARA_IPC_DECODE_MESSAGE_FORMAT "on" ARA_IPC_LABEL_THREAD_FORMAT, + ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS); } else { if (isReply (messageID)) - ARA_IPC_LOG ("dispatched received reply from thread %p to creation thread", _getCurrentThread ()); + ARA_IPC_LOG ("dispatches reply from" ARA_IPC_LABEL_THREAD_FORMAT " to creation thread", ARA_IPC_LABEL_THREAD_ARGS); else - ARA_IPC_LOG ("dispatched received message with ID %i from thread %p to creation thread", messageID, _getCurrentThread ()); + ARA_IPC_LOG ("dispatches" ARA_IPC_DECODE_MESSAGE_FORMAT "from" ARA_IPC_LABEL_THREAD_FORMAT " to creation thread", + ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS); + } + + _pendingMessageID = messageID; + _pendingMessageDecoder.store (decoder, std::memory_order_release); + getConnection ()->signalMesssageReceived (); + if (processSynchronously) + { + processPendingMessageIfNeeded (); + } + else + { // only new transactions must be dispatched, otherwise the target thread is already waiting for the message received signal if (!isResponse) getConnection ()->dispatchToCreationThread (std::bind (&MainThreadMessageDispatcher::processPendingMessageIfNeeded, this)); @@ -272,13 +302,14 @@ thread_local const OtherThreadsMessageDispatcher::PendingReplyHandler* _pendingR void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, MessageEncoder* encoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) { - const auto currentThread { _getCurrentThread () }; + const auto currentThread { getCurrentThread () }; encoder->appendThreadRef (sendThreadKey, currentThread); - if (_remoteTargetThread != _invalidThread) + const auto isResponse { _remoteTargetThread != _invalidThread }; + if (isResponse) encoder->appendThreadRef (receiveThreadKey, _remoteTargetThread); _sendLock.lock (); - _sendMessage (messageID, encoder); + _sendMessage (messageID, encoder, !isResponse); _sendLock.unlock (); if (getConnection ()->wasCreatedOnCurrentThread ()) @@ -333,25 +364,29 @@ void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, c if (decoder->readThreadRef (receiveThreadKey, &targetThread)) { ARA_INTERNAL_ASSERT (targetThread != _invalidThread); - if (targetThread == _getCurrentThread ()) + if (targetThread == getCurrentThread ()) { if (isReply (messageID)) { + ARA_IPC_LOG ("processes reply on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); _handleReply (decoder, _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; } else { + ARA_IPC_LOG ("processes" ARA_IPC_DECODE_MESSAGE_FORMAT "on" ARA_IPC_LABEL_THREAD_FORMAT, + ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS); _processReceivedMessage (messageID, decoder); } } else { if (isReply (messageID)) - ARA_IPC_LOG ("dispatches received reply from thread %p to sending thread %p", _getCurrentThread (), targetThread); + ARA_IPC_LOG ("dispatches reply from" ARA_IPC_LABEL_THREAD_FORMAT " to sending thread %p", ARA_IPC_LABEL_THREAD_ARGS, targetThread); else - ARA_IPC_LOG ("dispatches received message with ID %i from thread %p to sending thread %p", messageID, _getCurrentThread (), targetThread); + ARA_IPC_LOG ("dispatches" ARA_IPC_DECODE_MESSAGE_FORMAT "from" ARA_IPC_LABEL_THREAD_FORMAT " to sending thread %p", + ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS, targetThread); _routeLock.lock (); RoutedMessage* message { _getRoutedMessageForThread (_invalidThread) }; @@ -387,7 +422,7 @@ void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID replyEncoder->appendThreadRef (receiveThreadKey, remoteTargetThread); _sendLock.lock (); - _sendMessage (0, replyEncoder); + _sendMessage (0, replyEncoder, false); _sendLock.unlock (); _remoteTargetThread = previousRemoteTargetThread; diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index d1fff92..2d0c50d 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -51,13 +51,6 @@ class MainThreadMessageDispatcher; class OtherThreadsMessageDispatcher; -#if ARA_ENABLE_DEBUG_OUTPUT && 0 - #define ARA_IPC_LOG(...) ARA_LOG ("ARA IPC " __VA_ARGS__) -#else - #define ARA_IPC_LOG(...) ((void) 0) -#endif - - //! delegate interface for processing messages received through an IPC connection class MessageHandler { @@ -164,6 +157,10 @@ class Connection //! in order to wake it up void signalMesssageReceived (); + //! intended for debug output only: helper to properly decode message IDs + virtual bool sendsHostMessages () const = 0; + +private: #if defined (__APPLE__) static void performRunloopSource (void* info); #endif @@ -237,14 +234,14 @@ class MessageDispatcher static bool isReply (MessageID messageID) { return messageID == 0; } + static ThreadRef getCurrentThread (); + protected: - void _sendMessage (MessageID messageID, MessageEncoder* encoder); + void _sendMessage (MessageID messageID, MessageEncoder* encoder, bool isNewTransaction); bool _waitForMessage (); void _handleReply (const MessageDecoder* decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData); MessageEncoder* _handleReceivedMessage (MessageID messageID, const MessageDecoder* decoder); - static ThreadRef _getCurrentThread (); - private: Connection* const _connection; MessageChannel* const _messageChannel; diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 71a1bc3..d55f608 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -1036,7 +1036,7 @@ inline constexpr auto kUninitializeARAMethodID { MethodID::createWithARAGlobalMe // for debugging: translate IDs of messages sent by the host back to human-readable text -inline const char* decodeHostMessageID (const MessageID messageID) +inline const char* decodeHostMessageID (const MessageID messageID, const bool omitInterfaceName = true) { switch (messageID) { @@ -1052,7 +1052,7 @@ inline const char* decodeHostMessageID (const MessageID messageID) #undef ARA_IPC_GLOBAL_MESSAGE_CASE #define ARA_IPC_PLUGIN_INTERFACE_CASE(StructT, member) \ - case ARA_IPC_PLUGIN_METHOD_ID (StructT, member).getMessageID (): return #StructT "::" #member; + case ARA_IPC_PLUGIN_METHOD_ID (StructT, member).getMessageID (): return (omitInterfaceName) ? #member : #StructT "::" #member; ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyDocumentController) ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getFactory) ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, beginEditing) @@ -1117,17 +1117,19 @@ inline const char* decodeHostMessageID (const MessageID messageID) ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorViewInterface, notifyHideRegionSequences) #undef ARA_IPC_PLUGIN_INTERFACE_CASE } + if (MethodID::isCustomMessageID (messageID)) + return "custom message"; ARA_INTERNAL_ASSERT (false); - return "unknown message ID"; + return "unknown message"; } // for debugging: translate IDs of messages sent by the host back to human-readable text -inline const char* decodePlugInMessageID (const MessageID messageID) +inline const char* decodePlugInMessageID (const MessageID messageID, const bool omitInterfaceName = true) { switch (messageID) { #define ARA_IPC_HOST_INTERFACE_CASE(StructT, member) \ - case ARA_IPC_HOST_METHOD_ID (StructT, member).getMessageID (): return #StructT "::" #member; + case ARA_IPC_HOST_METHOD_ID (StructT, member).getMessageID (): return (omitInterfaceName) ? #member : #StructT "::" #member; ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, createAudioReaderForSource) ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, readAudioSamples) ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, destroyAudioReader) @@ -1158,8 +1160,10 @@ inline const char* decodePlugInMessageID (const MessageID messageID) ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestEnableCycle) #undef ARA_IPC_HOST_INTERFACE_CASE } + if (MethodID::isCustomMessageID (messageID)) + return "custom message"; ARA_INTERNAL_ASSERT (false); - return "unknown message ID"; + return "unknown message"; } // helper template to identify pointers to ARA structs in message arguments diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index 28f1828..c7cbb71 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -659,8 +659,6 @@ ProxyHost::ProxyHost (Connection* connection) void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, MessageEncoder* const replyEncoder) { - ARA_IPC_LOG ("ProxyHost handles '%s'", decodeHostMessageID (messageID)); - // ARAFactory if (messageID == kGetFactoriesCountMethodID) { diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp index d9d4bd1..c0486fa 100644 --- a/IPC/ARAIPCProxyPlugIn.cpp +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -1429,8 +1429,6 @@ void ARAIPCProxyPlugInUninitializeARA (ARAIPCConnectionRef connectionRef, const void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, MessageEncoder* const replyEncoder) { - ARA_IPC_LOG ("ProxyPlugIn handles '%s'", decodePlugInMessageID (messageID)); - // ARAAudioAccessControllerInterface if (messageID == ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, createAudioReaderForSource)) { From 3b24594f4f01ef73b85065b05482b74de12f9fdc Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:10 +0200 Subject: [PATCH 17/42] Unify IPC method ID generation for host and plug-in methods --- IPC/ARAIPCEncoding.h | 229 +++++++++++++++++++------------------- IPC/ARAIPCProxyHost.cpp | 174 ++++++++++++++--------------- IPC/ARAIPCProxyPlugIn.cpp | 172 ++++++++++++++-------------- 3 files changed, 287 insertions(+), 288 deletions(-) diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index d55f608..052ab87 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -921,30 +921,29 @@ inline void _decodeArgumentsHelper (const MessageDecoder* decoder, const Message _decodeArgumentsHelper (decoder, argKey + 1, moreArgs...); } -// private helpers for ARA_IPC_HOST_METHOD_ID and ARA_IPC_PLUGIN_METHOD_ID +// private helper for ARA_IPC_METHOD_ID template -constexpr MessageID _getHostInterfaceID (); +constexpr MessageID _getInterfaceID (); +// host interface specializations template<> -constexpr MessageID _getHostInterfaceID () { return 0; } +constexpr MessageID _getInterfaceID () { return 0; } template<> -constexpr MessageID _getHostInterfaceID () { return 1; } +constexpr MessageID _getInterfaceID () { return 1; } template<> -constexpr MessageID _getHostInterfaceID () { return 2; } +constexpr MessageID _getInterfaceID () { return 2; } template<> -constexpr MessageID _getHostInterfaceID () { return 3; } +constexpr MessageID _getInterfaceID () { return 3; } template<> -constexpr MessageID _getHostInterfaceID () { return 4; } - -template -constexpr MessageID _getPlugInInterfaceID (); +constexpr MessageID _getInterfaceID () { return 4; } +// plug-in interface specializations template<> -constexpr MessageID _getPlugInInterfaceID () { return 0; } +constexpr MessageID _getInterfaceID () { return 0; } template<> -constexpr MessageID _getPlugInInterfaceID () { return 1; } +constexpr MessageID _getInterfaceID () { return 1; } template<> -constexpr MessageID _getPlugInInterfaceID () { return 2; } +constexpr MessageID _getInterfaceID () { return 2; } template<> -constexpr MessageID _getPlugInInterfaceID () { return 3; } +constexpr MessageID _getInterfaceID () { return 3; } //------------------------------------------------------------------------------ @@ -1021,8 +1020,7 @@ inline bool operator!= (const MethodID methodID, const MessageID messageID) // create a MethodID for a given host-side or plug-in-side ARA method -#define ARA_IPC_HOST_METHOD_ID(StructT, member) MethodID::createWithARAInterfaceIDAndOffset <_getHostInterfaceID (), offsetof (StructT, member)> () -#define ARA_IPC_PLUGIN_METHOD_ID(StructT, member) MethodID::createWithARAInterfaceIDAndOffset <_getPlugInInterfaceID (), offsetof (StructT, member)> () +#define ARA_IPC_METHOD_ID(StructT, member) MethodID::createWithARAInterfaceIDAndOffset <_getInterfaceID (), offsetof (StructT, member)> () // "global" messages for startup and teardown that are not calculated based on interface structs @@ -1035,13 +1033,18 @@ inline constexpr auto kCleanupBindingMethodID { MethodID::createWithARAGlobalMes inline constexpr auto kUninitializeARAMethodID { MethodID::createWithARAGlobalMessageID<7> () }; +// private helpers for decodeHost/PlugInMessageID() +#define ARA_IPC_GLOBAL_MESSAGE_CASE(methodID) \ + case methodID.getMessageID (): return #methodID; + +#define ARA_IPC_INTERFACE_MESSAGE_CASE(StructT, member) \ + case ARA_IPC_METHOD_ID (StructT, member).getMessageID (): return (omitInterfaceName) ? #member : #StructT "::" #member; + // for debugging: translate IDs of messages sent by the host back to human-readable text inline const char* decodeHostMessageID (const MessageID messageID, const bool omitInterfaceName = true) { switch (messageID) { - #define ARA_IPC_GLOBAL_MESSAGE_CASE(methodID) \ - case methodID.getMessageID (): return #methodID; ARA_IPC_GLOBAL_MESSAGE_CASE (kGetFactoriesCountMethodID); ARA_IPC_GLOBAL_MESSAGE_CASE (kGetFactoryMethodID); ARA_IPC_GLOBAL_MESSAGE_CASE (kInitializeARAMethodID); @@ -1049,73 +1052,69 @@ inline const char* decodeHostMessageID (const MessageID messageID, const bool om ARA_IPC_GLOBAL_MESSAGE_CASE (kBindToDocumentControllerMethodID); ARA_IPC_GLOBAL_MESSAGE_CASE (kCleanupBindingMethodID); ARA_IPC_GLOBAL_MESSAGE_CASE (kUninitializeARAMethodID); -#undef ARA_IPC_GLOBAL_MESSAGE_CASE -#define ARA_IPC_PLUGIN_INTERFACE_CASE(StructT, member) \ - case ARA_IPC_PLUGIN_METHOD_ID (StructT, member).getMessageID (): return (omitInterfaceName) ? #member : #StructT "::" #member; - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyDocumentController) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getFactory) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, beginEditing) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, endEditing) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, notifyModelUpdates) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, beginRestoringDocumentFromArchive) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, endRestoringDocumentFromArchive) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, storeDocumentToArchive) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateDocumentProperties) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createMusicalContext) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateMusicalContextProperties) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateMusicalContextContent) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyMusicalContext) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createAudioSource) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateAudioSourceProperties) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateAudioSourceContent) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, enableAudioSourceSamplesAccess) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyAudioSource) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createAudioModification) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, cloneAudioModification) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateAudioModificationProperties) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyAudioModification) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createPlaybackRegion) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updatePlaybackRegionProperties) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyPlaybackRegion) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isAudioSourceContentAvailable) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, requestAudioSourceContentAnalysis) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getAudioSourceContentGrade) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createAudioSourceContentReader) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isAudioModificationContentAvailable) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getAudioModificationContentGrade) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createAudioModificationContentReader) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isPlaybackRegionContentAvailable) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getPlaybackRegionContentGrade) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createPlaybackRegionContentReader) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getContentReaderEventCount) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getContentReaderDataForEvent) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyContentReader) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, createRegionSequence) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, updateRegionSequenceProperties) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, destroyRegionSequence) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, restoreObjectsFromArchive) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, storeObjectsToArchive) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmsCount) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmProperties) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isLicensedForCapabilities) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARAPlaybackRendererInterface, addPlaybackRegion) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARAPlaybackRendererInterface, removePlaybackRegion) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorRendererInterface, addPlaybackRegion) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorRendererInterface, removePlaybackRegion) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorRendererInterface, addRegionSequence) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorRendererInterface, removeRegionSequence) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorViewInterface, notifySelection) - ARA_IPC_PLUGIN_INTERFACE_CASE (ARAEditorViewInterface, notifyHideRegionSequences) -#undef ARA_IPC_PLUGIN_INTERFACE_CASE + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, destroyDocumentController) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getFactory) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, beginEditing) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, endEditing) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, notifyModelUpdates) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, beginRestoringDocumentFromArchive) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, endRestoringDocumentFromArchive) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, storeDocumentToArchive) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, updateDocumentProperties) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, createMusicalContext) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, updateMusicalContextProperties) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, updateMusicalContextContent) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, destroyMusicalContext) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, createAudioSource) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, updateAudioSourceProperties) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, updateAudioSourceContent) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, enableAudioSourceSamplesAccess) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, destroyAudioSource) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, createAudioModification) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, cloneAudioModification) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, updateAudioModificationProperties) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, destroyAudioModification) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, createPlaybackRegion) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, updatePlaybackRegionProperties) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, destroyPlaybackRegion) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isAudioSourceContentAvailable) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, requestAudioSourceContentAnalysis) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getAudioSourceContentGrade) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, createAudioSourceContentReader) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isAudioModificationContentAvailable) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getAudioModificationContentGrade) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, createAudioModificationContentReader) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isPlaybackRegionContentAvailable) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getPlaybackRegionContentGrade) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, createPlaybackRegionContentReader) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getContentReaderEventCount) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getContentReaderDataForEvent) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, destroyContentReader) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, createRegionSequence) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, updateRegionSequenceProperties) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, destroyRegionSequence) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, restoreObjectsFromArchive) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, storeObjectsToArchive) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmsCount) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmProperties) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isLicensedForCapabilities) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackRendererInterface, addPlaybackRegion) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackRendererInterface, removePlaybackRegion) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAEditorRendererInterface, addPlaybackRegion) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAEditorRendererInterface, removePlaybackRegion) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAEditorRendererInterface, addRegionSequence) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAEditorRendererInterface, removeRegionSequence) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAEditorViewInterface, notifySelection) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAEditorViewInterface, notifyHideRegionSequences) } if (MethodID::isCustomMessageID (messageID)) return "custom message"; @@ -1128,37 +1127,34 @@ inline const char* decodePlugInMessageID (const MessageID messageID, const bool { switch (messageID) { -#define ARA_IPC_HOST_INTERFACE_CASE(StructT, member) \ - case ARA_IPC_HOST_METHOD_ID (StructT, member).getMessageID (): return (omitInterfaceName) ? #member : #StructT "::" #member; - ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, createAudioReaderForSource) - ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, readAudioSamples) - ARA_IPC_HOST_INTERFACE_CASE (ARAAudioAccessControllerInterface, destroyAudioReader) - ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, getArchiveSize) - ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, readBytesFromArchive) - ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, writeBytesToArchive) - ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, notifyDocumentArchivingProgress) - ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress) - ARA_IPC_HOST_INTERFACE_CASE (ARAArchivingControllerInterface, getDocumentArchiveID) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, isMusicalContextContentAvailable) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, getMusicalContextContentGrade) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, createMusicalContextContentReader) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, isAudioSourceContentAvailable) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, getAudioSourceContentGrade) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, createAudioSourceContentReader) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, getContentReaderEventCount) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, getContentReaderDataForEvent) - ARA_IPC_HOST_INTERFACE_CASE (ARAContentAccessControllerInterface, destroyContentReader) - ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress) - ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged) - ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged) - ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged) - ARA_IPC_HOST_INTERFACE_CASE (ARAModelUpdateControllerInterface, notifyDocumentDataChanged) - ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestStartPlayback) - ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestStopPlayback) - ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestSetPlaybackPosition) - ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestSetCycleRange) - ARA_IPC_HOST_INTERFACE_CASE (ARAPlaybackControllerInterface, requestEnableCycle) -#undef ARA_IPC_HOST_INTERFACE_CASE + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAAudioAccessControllerInterface, createAudioReaderForSource) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAAudioAccessControllerInterface, readAudioSamples) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAAudioAccessControllerInterface, destroyAudioReader) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAArchivingControllerInterface, getArchiveSize) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAArchivingControllerInterface, readBytesFromArchive) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAArchivingControllerInterface, writeBytesToArchive) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAArchivingControllerInterface, notifyDocumentArchivingProgress) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAArchivingControllerInterface, getDocumentArchiveID) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, isMusicalContextContentAvailable) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, getMusicalContextContentGrade) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, createMusicalContextContentReader) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, isAudioSourceContentAvailable) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, getAudioSourceContentGrade) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, createAudioSourceContentReader) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, getContentReaderEventCount) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, getContentReaderDataForEvent) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAContentAccessControllerInterface, destroyContentReader) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAModelUpdateControllerInterface, notifyDocumentDataChanged) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackControllerInterface, requestStartPlayback) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackControllerInterface, requestStopPlayback) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackControllerInterface, requestSetPlaybackPosition) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackControllerInterface, requestSetCycleRange) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackControllerInterface, requestEnableCycle) } if (MethodID::isCustomMessageID (messageID)) return "custom message"; @@ -1166,6 +1162,9 @@ inline const char* decodePlugInMessageID (const MessageID messageID, const bool return "unknown message"; } +#undef ARA_IPC_GLOBAL_MESSAGE_CASE +#undef ARA_IPC_INTERFACE_MESSAGE_CASE + // helper template to identify pointers to ARA structs in message arguments template inline constexpr auto _IsStructPointerArg = !_IsRefType && std::is_pointer_v && !std::is_same_v; diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index c7cbb71..31d507f 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -139,7 +139,7 @@ ARAAudioReaderHostRef AudioAccessController::createAudioReaderForSource (ARAAudi remoteAudioReader->swapFunction = nullptr; else remoteAudioReader->swapFunction = (use64BitSamples) ? &_swapBuffer : &_swapBuffer; - remoteCall (remoteAudioReader->mainHostRef, ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, createAudioReaderForSource), + remoteCall (remoteAudioReader->mainHostRef, ARA_IPC_METHOD_ID (ARAAudioAccessControllerInterface, createAudioReaderForSource), _remoteHostRef, remoteAudioReader->audioSource->mainHostRef, use64BitSamples); return toHostRef (remoteAudioReader); } @@ -212,7 +212,7 @@ bool AudioAccessController::readAudioSamples (ARAAudioReaderHostRef audioReaderH } } }; - remoteCall (customDecode, ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, readAudioSamples), + remoteCall (customDecode, ARA_IPC_METHOD_ID (ARAAudioAccessControllerInterface, readAudioSamples), _remoteHostRef, remoteAudioReader->mainHostRef, samplePosition, samplesPerChannel); return success; } @@ -221,7 +221,7 @@ void AudioAccessController::destroyAudioReader (ARAAudioReaderHostRef audioReade { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); auto remoteAudioReader { fromHostRef (audioReaderHostRef) }; - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, destroyAudioReader), _remoteHostRef, remoteAudioReader->mainHostRef); + remoteCall (ARA_IPC_METHOD_ID (ARAAudioAccessControllerInterface, destroyAudioReader), _remoteHostRef, remoteAudioReader->mainHostRef); delete remoteAudioReader; } @@ -252,7 +252,7 @@ ARASize ArchivingController::getArchiveSize (ARAArchiveReaderHostRef archiveRead { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARASize size; - remoteCall (size, ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, getArchiveSize), _remoteHostRef, archiveReaderHostRef); + remoteCall (size, ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, getArchiveSize), _remoteHostRef, archiveReaderHostRef); return size; } @@ -281,7 +281,7 @@ bool ArchivingController::readBytesFromArchive (ARAArchiveReaderHostRef archiveR auto resultLength { length }; BytesDecoder writer { buffer, resultLength }; - remoteCall (writer, ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, readBytesFromArchive), + remoteCall (writer, ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, readBytesFromArchive), _remoteHostRef, archiveReaderHostRef, position, length); if (resultLength == length) { @@ -313,7 +313,7 @@ bool ArchivingController::writeBytesToArchive (ARAArchiveWriterHostRef archiveWr } ARABool success; - remoteCall (success, ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, writeBytesToArchive), + remoteCall (success, ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, writeBytesToArchive), _remoteHostRef, archiveWriterHostRef, position, BytesEncoder { buffer, length, false }); return (success != kARAFalse); } @@ -321,13 +321,13 @@ bool ArchivingController::writeBytesToArchive (ARAArchiveWriterHostRef archiveWr void ArchivingController::notifyDocumentArchivingProgress (float value) noexcept { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentArchivingProgress), _remoteHostRef, value); + remoteCall (ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentArchivingProgress), _remoteHostRef, value); } void ArchivingController::notifyDocumentUnarchivingProgress (float value) noexcept { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress), _remoteHostRef, value); + remoteCall (ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress), _remoteHostRef, value); } ARAPersistentID ArchivingController::getDocumentArchiveID (ARAArchiveReaderHostRef archiveReaderHostRef) noexcept @@ -340,7 +340,7 @@ ARAPersistentID ArchivingController::getDocumentArchiveID (ARAArchiveReaderHostR decodeReply (persistentID, decoder); _archiveID.assign (persistentID); } }; - remoteCall (customDecode, ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, getDocumentArchiveID), _remoteHostRef, archiveReaderHostRef); + remoteCall (customDecode, ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, getDocumentArchiveID), _remoteHostRef, archiveReaderHostRef); return _archiveID.c_str (); } @@ -373,7 +373,7 @@ bool ContentAccessController::isMusicalContextContentAvailable (ARAMusicalContex { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARABool result; - remoteCall (result, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, isMusicalContextContentAvailable), + remoteCall (result, ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, isMusicalContextContentAvailable), _remoteHostRef, musicalContextHostRef, type); return (result != kARAFalse); } @@ -382,7 +382,7 @@ ARAContentGrade ContentAccessController::getMusicalContextContentGrade (ARAMusic { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAContentGrade grade; - remoteCall (grade, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getMusicalContextContentGrade), + remoteCall (grade, ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, getMusicalContextContentGrade), _remoteHostRef, musicalContextHostRef, type); return grade; } @@ -391,7 +391,7 @@ ARAContentReaderHostRef ContentAccessController::createMusicalContextContentRead { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAContentReaderHostRef contentReaderHostRef; - remoteCall (contentReaderHostRef, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, createMusicalContextContentReader), + remoteCall (contentReaderHostRef, ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, createMusicalContextContentReader), _remoteHostRef, musicalContextHostRef, type, range); auto contentReader { new RemoteHostContentReader (contentReaderHostRef, type) }; return toHostRef (contentReader); @@ -401,7 +401,7 @@ bool ContentAccessController::isAudioSourceContentAvailable (ARAAudioSourceHostR { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARABool result; - remoteCall (result, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, isAudioSourceContentAvailable), + remoteCall (result, ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, isAudioSourceContentAvailable), _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, type); return (result != kARAFalse); } @@ -410,7 +410,7 @@ ARAContentGrade ContentAccessController::getAudioSourceContentGrade (ARAAudioSou { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAContentGrade grade; - remoteCall (grade, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getAudioSourceContentGrade), + remoteCall (grade, ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, getAudioSourceContentGrade), _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, type); return grade; } @@ -419,7 +419,7 @@ ARAContentReaderHostRef ContentAccessController::createAudioSourceContentReader { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAContentReaderHostRef contentReaderHostRef; - remoteCall (contentReaderHostRef, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, createAudioSourceContentReader), + remoteCall (contentReaderHostRef, ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, createAudioSourceContentReader), _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, type, range); auto contentReader { new RemoteHostContentReader (contentReaderHostRef, type) }; return toHostRef (contentReader); @@ -430,7 +430,7 @@ ARAInt32 ContentAccessController::getContentReaderEventCount (ARAContentReaderHo ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); const auto contentReader { fromHostRef (contentReaderHostRef) }; ARAInt32 count; - remoteCall (count, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderEventCount), + remoteCall (count, ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderEventCount), _remoteHostRef, contentReader->remoteHostRef); return count; } @@ -445,7 +445,7 @@ const void* ContentAccessController::getContentReaderDataForEvent (ARAContentRea { result = contentReader->decoder.decode (decoder); } }; - remoteCall (customDecode, ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderDataForEvent), + remoteCall (customDecode, ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderDataForEvent), _remoteHostRef, contentReader->remoteHostRef, eventIndex); return result; } @@ -454,7 +454,7 @@ void ContentAccessController::destroyContentReader (ARAContentReaderHostRef cont { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); const auto contentReader { fromHostRef (contentReaderHostRef) }; - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, destroyContentReader), _remoteHostRef, contentReader->remoteHostRef); + remoteCall (ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, destroyContentReader), _remoteHostRef, contentReader->remoteHostRef); delete contentReader; } @@ -482,35 +482,35 @@ class ModelUpdateController : public Host::ModelUpdateControllerInterface, prote void ModelUpdateController::notifyAudioSourceAnalysisProgress (ARAAudioSourceHostRef audioSourceHostRef, ARAAnalysisProgressState state, float value) noexcept { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress), + remoteCall (ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress), _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, state, value); } void ModelUpdateController::notifyAudioSourceContentChanged (ARAAudioSourceHostRef audioSourceHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged), + remoteCall (ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged), _remoteHostRef, fromHostRef (audioSourceHostRef)->mainHostRef, range, scopeFlags); } void ModelUpdateController::notifyAudioModificationContentChanged (ARAAudioModificationHostRef audioModificationHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged), + remoteCall (ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged), _remoteHostRef, audioModificationHostRef, range, scopeFlags); } void ModelUpdateController::notifyPlaybackRegionContentChanged (ARAPlaybackRegionHostRef playbackRegionHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged), + remoteCall (ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged), _remoteHostRef, playbackRegionHostRef, range, scopeFlags); } void ModelUpdateController::notifyDocumentDataChanged () noexcept { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyDocumentDataChanged), + remoteCall (ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyDocumentDataChanged), _remoteHostRef); } @@ -539,35 +539,35 @@ void PlaybackController::requestStartPlayback () noexcept { // this function can be called from other threads! // ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestStartPlayback), _remoteHostRef); + remoteCall (ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestStartPlayback), _remoteHostRef); } void PlaybackController::requestStopPlayback () noexcept { // this function can be called from other threads! // ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestStopPlayback), _remoteHostRef); + remoteCall (ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestStopPlayback), _remoteHostRef); } void PlaybackController::requestSetPlaybackPosition (ARATimePosition timePosition) noexcept { // this function can be called from other threads! // ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestSetPlaybackPosition), _remoteHostRef, timePosition); + remoteCall (ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestSetPlaybackPosition), _remoteHostRef, timePosition); } void PlaybackController::requestSetCycleRange (ARATimePosition startTime, ARATimeDuration duration) noexcept { // this function can be called from other threads! // ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestSetCycleRange), _remoteHostRef, startTime, duration); + remoteCall (ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestSetCycleRange), _remoteHostRef, startTime, duration); } void PlaybackController::requestEnableCycle (bool enable) noexcept { // this function can be called from other threads! // ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestEnableCycle), _remoteHostRef, (enable) ? kARATrue : kARAFalse); + remoteCall (ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestEnableCycle), _remoteHostRef, (enable) ? kARATrue : kARAFalse); } @@ -747,7 +747,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD } //ARADocumentControllerInterface - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyDocumentController)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyDocumentController)) { ARADocumentControllerRef controllerRef; decodeArguments (decoder, controllerRef); @@ -762,7 +762,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD delete documentController->getHostInstance (); delete documentController; } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getFactory)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getFactory)) { ARA_INTERNAL_ASSERT (false && "should never be queried here but instead cached from companion API upon setup"); @@ -772,21 +772,21 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, *(fromRef (controllerRef)->getFactory ())); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, beginEditing)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, beginEditing)) { ARADocumentControllerRef controllerRef; decodeArguments (decoder, controllerRef); fromRef (controllerRef)->beginEditing (); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, endEditing)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, endEditing)) { ARADocumentControllerRef controllerRef; decodeArguments (decoder, controllerRef); fromRef (controllerRef)->endEditing (); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, notifyModelUpdates)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, notifyModelUpdates)) { ARADocumentControllerRef controllerRef; decodeArguments (decoder, controllerRef); @@ -794,7 +794,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->notifyModelUpdates (); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, restoreObjectsFromArchive)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, restoreObjectsFromArchive)) { ARADocumentControllerRef controllerRef; ARAArchiveReaderHostRef archiveReaderHostRef; @@ -803,7 +803,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->restoreObjectsFromArchive (archiveReaderHostRef, (filter.second) ? &filter.first : nullptr) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, storeObjectsToArchive)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, storeObjectsToArchive)) { ARADocumentControllerRef controllerRef; ARAArchiveWriterHostRef archiveWriterHostRef; @@ -823,7 +823,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->storeObjectsToArchive (archiveWriterHostRef, (filter.second) ? &filter.first : nullptr) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateDocumentProperties)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateDocumentProperties)) { ARADocumentControllerRef controllerRef; ARADocumentProperties properties; @@ -832,7 +832,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updateDocumentProperties (&properties); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createMusicalContext)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createMusicalContext)) { ARADocumentControllerRef controllerRef; ARAMusicalContextHostRef hostRef; @@ -841,7 +841,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->createMusicalContext (hostRef, &properties)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextProperties)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextProperties)) { ARADocumentControllerRef controllerRef; ARAMusicalContextRef musicalContextRef; @@ -850,7 +850,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updateMusicalContextProperties (musicalContextRef, &properties); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextContent)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextContent)) { ARADocumentControllerRef controllerRef; ARAMusicalContextRef musicalContextRef; @@ -860,7 +860,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updateMusicalContextContent (musicalContextRef, (range.second) ? &range.first : nullptr, flags); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyMusicalContext)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyMusicalContext)) { ARADocumentControllerRef controllerRef; ARAMusicalContextRef musicalContextRef; @@ -869,7 +869,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->destroyMusicalContext (musicalContextRef); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createRegionSequence)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createRegionSequence)) { ARADocumentControllerRef controllerRef; ARARegionSequenceHostRef hostRef; @@ -878,7 +878,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->createRegionSequence (hostRef, &properties)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateRegionSequenceProperties)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateRegionSequenceProperties)) { ARADocumentControllerRef controllerRef; ARARegionSequenceRef regionSequenceRef; @@ -887,7 +887,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updateRegionSequenceProperties (regionSequenceRef, &properties); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyRegionSequence)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyRegionSequence)) { ARADocumentControllerRef controllerRef; ARARegionSequenceRef regionSequenceRef; @@ -896,7 +896,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->destroyRegionSequence (regionSequenceRef); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioSource)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createAudioSource)) { auto remoteAudioSource { new RemoteAudioSource }; @@ -909,7 +909,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, ARAAudioSourceRef { toRef (remoteAudioSource) }); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceProperties)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceProperties)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -918,7 +918,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updateAudioSourceProperties (fromRef (audioSourceRef)->plugInRef, &properties); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceContent)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceContent)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -928,7 +928,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updateAudioSourceContent (fromRef (audioSourceRef)->plugInRef, (range.second) ? &range.first : nullptr, flags); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, enableAudioSourceSamplesAccess)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, enableAudioSourceSamplesAccess)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -937,7 +937,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->enableAudioSourceSamplesAccess (fromRef (audioSourceRef)->plugInRef, (enable) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -946,7 +946,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->deactivateAudioSourceForUndoHistory (fromRef (audioSourceRef)->plugInRef, (deactivate) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk)) { ARADocumentControllerRef controllerRef; ARAArchiveWriterHostRef archiveWriterHostRef; @@ -960,7 +960,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD reply.openAutomatically = (openAutomatically) ? kARATrue : kARAFalse; encodeReply (replyEncoder, reply); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -969,7 +969,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->isAudioSourceContentAnalysisIncomplete (fromRef (audioSourceRef)->plugInRef, contentType)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, requestAudioSourceContentAnalysis)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, requestAudioSourceContentAnalysis)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -978,7 +978,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->requestAudioSourceContentAnalysis (fromRef (audioSourceRef)->plugInRef, contentTypes.size (), contentTypes.data ()); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAvailable)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAvailable)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -987,7 +987,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, (fromRef (controllerRef)->isAudioSourceContentAvailable (fromRef (audioSourceRef)->plugInRef, contentType)) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getAudioSourceContentGrade)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getAudioSourceContentGrade)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -996,7 +996,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->getAudioSourceContentGrade (fromRef (audioSourceRef)->plugInRef, contentType)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioSourceContentReader)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createAudioSourceContentReader)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -1009,7 +1009,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD remoteContentReader->contentType = contentType; encodeReply (replyEncoder, ARAContentReaderRef { toRef (remoteContentReader) }); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyAudioSource)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyAudioSource)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -1021,7 +1021,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD delete remoteAudioSource; } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioModification)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createAudioModification)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -1031,7 +1031,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->createAudioModification (fromRef (audioSourceRef)->plugInRef, hostRef, &properties)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, cloneAudioModification)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, cloneAudioModification)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1041,7 +1041,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->cloneAudioModification (audioModificationRef, hostRef, &properties)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioModificationProperties)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateAudioModificationProperties)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1050,7 +1050,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updateAudioModificationProperties (audioModificationRef, &properties); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1058,7 +1058,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, (fromRef (controllerRef)->isAudioModificationPreservingAudioSourceSignal (audioModificationRef)) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1067,7 +1067,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->deactivateAudioModificationForUndoHistory (audioModificationRef, (deactivate) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioModificationContentAvailable)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isAudioModificationContentAvailable)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1076,7 +1076,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, (fromRef (controllerRef)->isAudioModificationContentAvailable (audioModificationRef, contentType)) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getAudioModificationContentGrade)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getAudioModificationContentGrade)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1085,7 +1085,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->getAudioModificationContentGrade (audioModificationRef, contentType)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioModificationContentReader)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createAudioModificationContentReader)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1098,7 +1098,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD remoteContentReader->contentType = contentType; encodeReply (replyEncoder, ARAContentReaderRef { toRef (remoteContentReader) }); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyAudioModification)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyAudioModification)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1107,7 +1107,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->destroyAudioModification (audioModificationRef); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegion)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegion)) { ARADocumentControllerRef controllerRef; ARAAudioModificationRef audioModificationRef; @@ -1117,7 +1117,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->createPlaybackRegion (audioModificationRef, hostRef, &properties)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updatePlaybackRegionProperties)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updatePlaybackRegionProperties)) { ARADocumentControllerRef controllerRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1126,7 +1126,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updatePlaybackRegionProperties (playbackRegionRef, &properties); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime)) { ARADocumentControllerRef controllerRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1139,7 +1139,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD (wantsTailTime != kARAFalse) ? &reply.tailTime : nullptr); encodeReply (replyEncoder, reply); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isPlaybackRegionContentAvailable)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isPlaybackRegionContentAvailable)) { ARADocumentControllerRef controllerRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1148,7 +1148,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, (fromRef (controllerRef)->isPlaybackRegionContentAvailable (playbackRegionRef, contentType)) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionContentGrade)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionContentGrade)) { ARADocumentControllerRef controllerRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1157,7 +1157,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->getPlaybackRegionContentGrade (playbackRegionRef, contentType)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegionContentReader)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegionContentReader)) { ARADocumentControllerRef controllerRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1170,7 +1170,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD remoteContentReader->contentType = contentType; encodeReply (replyEncoder, ARAContentReaderRef { toRef (remoteContentReader) }); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyPlaybackRegion)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyPlaybackRegion)) { ARADocumentControllerRef controllerRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1179,7 +1179,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->destroyPlaybackRegion (playbackRegionRef); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getContentReaderEventCount)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getContentReaderEventCount)) { ARADocumentControllerRef controllerRef; ARAContentReaderRef contentReaderRef; @@ -1187,7 +1187,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->getContentReaderEventCount (fromRef (contentReaderRef)->plugInRef)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getContentReaderDataForEvent)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getContentReaderDataForEvent)) { ARADocumentControllerRef controllerRef; ARAContentReaderRef contentReaderRef; @@ -1198,7 +1198,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD const void* eventData { fromRef (controllerRef)->getContentReaderDataForEvent (remoteContentReader->plugInRef, eventIndex) }; encodeContentEvent (replyEncoder, remoteContentReader->contentType, eventData); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyContentReader)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyContentReader)) { ARADocumentControllerRef controllerRef; ARAContentReaderRef contentReaderRef; @@ -1210,14 +1210,14 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD delete remoteContentReader; } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmsCount)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmsCount)) { ARADocumentControllerRef controllerRef; decodeArguments (decoder, controllerRef); encodeReply (replyEncoder, fromRef (controllerRef)->getProcessingAlgorithmsCount ()); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmProperties)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmProperties)) { ARADocumentControllerRef controllerRef; ARAInt32 algorithmIndex; @@ -1225,7 +1225,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, *(fromRef (controllerRef)->getProcessingAlgorithmProperties (algorithmIndex))); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -1233,7 +1233,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD encodeReply (replyEncoder, fromRef (controllerRef)->getProcessingAlgorithmForAudioSource (fromRef (audioSourceRef)->plugInRef)); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource)) { ARADocumentControllerRef controllerRef; ARAAudioSourceRef audioSourceRef; @@ -1243,7 +1243,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->requestProcessingAlgorithmForAudioSource (fromRef (audioSourceRef)->plugInRef, algorithmIndex); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isLicensedForCapabilities)) + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isLicensedForCapabilities)) { ARADocumentControllerRef controllerRef; ARABool runModalActivationDialogIfNeeded; @@ -1256,7 +1256,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD } // ARAPlaybackRendererInterface - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAPlaybackRendererInterface, addPlaybackRegion)) + else if (messageID == ARA_IPC_METHOD_ID (ARAPlaybackRendererInterface, addPlaybackRegion)) { ARAPlaybackRendererRef playbackRendererRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1264,7 +1264,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (playbackRendererRef)->getPlaybackRenderer ()->addPlaybackRegion (playbackRegionRef); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAPlaybackRendererInterface, removePlaybackRegion)) + else if (messageID == ARA_IPC_METHOD_ID (ARAPlaybackRendererInterface, removePlaybackRegion)) { ARAPlaybackRendererRef playbackRendererRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1274,7 +1274,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD } // ARAEditorRendererInterface - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, addPlaybackRegion)) + else if (messageID == ARA_IPC_METHOD_ID (ARAEditorRendererInterface, addPlaybackRegion)) { ARAEditorRendererRef editorRendererRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1282,7 +1282,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (editorRendererRef)->getEditorRenderer ()->addPlaybackRegion (playbackRegionRef); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, removePlaybackRegion)) + else if (messageID == ARA_IPC_METHOD_ID (ARAEditorRendererInterface, removePlaybackRegion)) { ARAEditorRendererRef editorRendererRef; ARAPlaybackRegionRef playbackRegionRef; @@ -1290,7 +1290,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (editorRendererRef)->getEditorRenderer ()->removePlaybackRegion (playbackRegionRef); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, addRegionSequence)) + else if (messageID == ARA_IPC_METHOD_ID (ARAEditorRendererInterface, addRegionSequence)) { ARAEditorRendererRef editorRendererRef; ARARegionSequenceRef regionSequenceRef; @@ -1298,7 +1298,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (editorRendererRef)->getEditorRenderer ()->addRegionSequence (regionSequenceRef); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, removeRegionSequence)) + else if (messageID == ARA_IPC_METHOD_ID (ARAEditorRendererInterface, removeRegionSequence)) { ARAEditorRendererRef editorRendererRef; ARARegionSequenceRef regionSequenceRef; @@ -1308,7 +1308,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD } // ARAEditorViewInterface - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorViewInterface, notifySelection)) + else if (messageID == ARA_IPC_METHOD_ID (ARAEditorViewInterface, notifySelection)) { ARAEditorViewRef editorViewRef; ARAViewSelection selection; @@ -1316,7 +1316,7 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (editorViewRef)->getEditorView ()->notifySelection (&selection); } - else if (messageID == ARA_IPC_PLUGIN_METHOD_ID (ARAEditorViewInterface, notifyHideRegionSequences)) + else if (messageID == ARA_IPC_METHOD_ID (ARAEditorViewInterface, notifyHideRegionSequences)) { ARAEditorViewRef editorViewRef; std::vector regionSequenceRefs; diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp index c0486fa..8c3fe7c 100644 --- a/IPC/ARAIPCProxyPlugIn.cpp +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -350,7 +350,7 @@ void DocumentController::destroyDocumentController () noexcept ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARA_LOG_MODELOBJECT_LIFETIME ("will destroy document controller", _remoteRef); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyDocumentController), _remoteRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyDocumentController), _remoteRef); _hasBeenDestroyed = true; @@ -389,7 +389,7 @@ void DocumentController::beginEditing () noexcept ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, beginEditing), _remoteRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, beginEditing), _remoteRef); } void DocumentController::endEditing () noexcept @@ -398,7 +398,7 @@ void DocumentController::endEditing () noexcept ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, endEditing), _remoteRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, endEditing), _remoteRef); } void DocumentController::notifyModelUpdates () noexcept @@ -419,7 +419,7 @@ void DocumentController::notifyModelUpdates () noexcept if (!_hostModelUpdateController.isProvided ()) return; - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, notifyModelUpdates), _remoteRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, notifyModelUpdates), _remoteRef); } bool DocumentController::restoreObjectsFromArchive (ARAArchiveReaderHostRef archiveReaderHostRef, const ARARestoreObjectsFilter* filter) noexcept @@ -429,7 +429,7 @@ bool DocumentController::restoreObjectsFromArchive (ARAArchiveReaderHostRef arch ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARABool success; - remoteCall (success, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, restoreObjectsFromArchive), _remoteRef, archiveReaderHostRef, filter); + remoteCall (success, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, restoreObjectsFromArchive), _remoteRef, archiveReaderHostRef, filter); return (success != kARAFalse); } @@ -453,7 +453,7 @@ bool DocumentController::storeObjectsToArchive (ARAArchiveWriterHostRef archiveW } ARABool success; - remoteCall (success, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, storeObjectsToArchive), _remoteRef, archiveWriterHostRef, filter); + remoteCall (success, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, storeObjectsToArchive), _remoteRef, archiveWriterHostRef, filter); return (success!= kARAFalse); } @@ -496,7 +496,7 @@ bool DocumentController::storeAudioSourceToAudioFileChunk (ARAArchiveWriterHostR *openAutomatically = (reply.openAutomatically != kARAFalse); success = (reply.result != kARAFalse); } }; - remoteCall (customDecode, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk), + remoteCall (customDecode, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk), _remoteRef, archiveWriterHostRef, audioSource->_remoteRef); return success; } @@ -509,7 +509,7 @@ void DocumentController::updateDocumentProperties (PropertiesPtrstructSize >= ARA::kARADocumentPropertiesMinSize); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateDocumentProperties), _remoteRef, *properties); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateDocumentProperties), _remoteRef, *properties); } /*******************************************************************************/ @@ -523,7 +523,7 @@ ARAMusicalContextRef DocumentController::createMusicalContext (ARAMusicalContext ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAMusicalContextPropertiesMinSize); ARAMusicalContextRef musicalContextRef; - remoteCall (musicalContextRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createMusicalContext), _remoteRef, hostRef, *properties); + remoteCall (musicalContextRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createMusicalContext), _remoteRef, hostRef, *properties); ARA_LOG_MODELOBJECT_LIFETIME ("did create musical context", musicalContextRef); return musicalContextRef; @@ -537,7 +537,7 @@ void DocumentController::updateMusicalContextProperties (ARAMusicalContextRef mu ARA_INTERNAL_ASSERT (properties != nullptr); ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAMusicalContextPropertiesMinSize); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextProperties), _remoteRef, musicalContextRef, *properties); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextProperties), _remoteRef, musicalContextRef, *properties); } void DocumentController::updateMusicalContextContent (ARAMusicalContextRef musicalContextRef, const ARAContentTimeRange* range, ContentUpdateScopes flags) noexcept @@ -546,7 +546,7 @@ void DocumentController::updateMusicalContextContent (ARAMusicalContextRef music ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextContent), _remoteRef, musicalContextRef, range, flags); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateMusicalContextContent), _remoteRef, musicalContextRef, range, flags); } void DocumentController::destroyMusicalContext (ARAMusicalContextRef musicalContextRef) noexcept @@ -556,7 +556,7 @@ void DocumentController::destroyMusicalContext (ARAMusicalContextRef musicalCont ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARA_LOG_MODELOBJECT_LIFETIME ("will destroy musical context", musicalContextRef); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyMusicalContext), _remoteRef, musicalContextRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyMusicalContext), _remoteRef, musicalContextRef); } /*******************************************************************************/ @@ -570,7 +570,7 @@ ARARegionSequenceRef DocumentController::createRegionSequence (ARARegionSequence ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARARegionSequencePropertiesMinSize); ARARegionSequenceRef regionSequenceRef; - remoteCall (regionSequenceRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createRegionSequence), _remoteRef, hostRef, *properties); + remoteCall (regionSequenceRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createRegionSequence), _remoteRef, hostRef, *properties); ARA_LOG_MODELOBJECT_LIFETIME ("did create region sequence", regionSequenceRef); return regionSequenceRef; @@ -584,7 +584,7 @@ void DocumentController::updateRegionSequenceProperties (ARARegionSequenceRef re ARA_INTERNAL_ASSERT (properties != nullptr); ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARARegionSequencePropertiesMinSize); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateRegionSequenceProperties), _remoteRef, regionSequenceRef, *properties); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateRegionSequenceProperties), _remoteRef, regionSequenceRef, *properties); } void DocumentController::destroyRegionSequence (ARARegionSequenceRef regionSequenceRef) noexcept @@ -594,7 +594,7 @@ void DocumentController::destroyRegionSequence (ARARegionSequenceRef regionSeque ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARA_LOG_MODELOBJECT_LIFETIME ("will destroy region sequence", regionSequenceRef); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyRegionSequence), _remoteRef, regionSequenceRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyRegionSequence), _remoteRef, regionSequenceRef); } /*******************************************************************************/ @@ -613,7 +613,7 @@ ARAAudioSourceRef DocumentController::createAudioSource (ARAAudioSourceHostRef h #endif } }; - remoteCall (audioSource->_remoteRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioSource), + remoteCall (audioSource->_remoteRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createAudioSource), _remoteRef, ARAAudioSourceHostRef { toHostRef (audioSource) }, *properties); ARA_LOG_MODELOBJECT_LIFETIME ("did create audio source", audioSourceRef); @@ -630,7 +630,7 @@ void DocumentController::updateAudioSourceProperties (ARAAudioSourceRef audioSou ARA_INTERNAL_ASSERT (properties != nullptr); ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioSourcePropertiesMinSize); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceProperties), _remoteRef, audioSource->_remoteRef, *properties); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceProperties), _remoteRef, audioSource->_remoteRef, *properties); } void DocumentController::updateAudioSourceContent (ARAAudioSourceRef audioSourceRef, const ARAContentTimeRange* range, ContentUpdateScopes flags) noexcept @@ -641,7 +641,7 @@ void DocumentController::updateAudioSourceContent (ARAAudioSourceRef audioSource auto audioSource { fromRef (audioSourceRef) }; ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceContent), _remoteRef, audioSource->_remoteRef, range, flags); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateAudioSourceContent), _remoteRef, audioSource->_remoteRef, range, flags); } void DocumentController::enableAudioSourceSamplesAccess (ARAAudioSourceRef audioSourceRef, bool enable) noexcept @@ -652,7 +652,7 @@ void DocumentController::enableAudioSourceSamplesAccess (ARAAudioSourceRef audio const auto audioSource { fromRef (audioSourceRef) }; ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, enableAudioSourceSamplesAccess), _remoteRef, audioSource->_remoteRef, (enable) ? kARATrue : kARAFalse); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, enableAudioSourceSamplesAccess), _remoteRef, audioSource->_remoteRef, (enable) ? kARATrue : kARAFalse); } void DocumentController::deactivateAudioSourceForUndoHistory (ARAAudioSourceRef audioSourceRef, bool deactivate) noexcept @@ -663,7 +663,7 @@ void DocumentController::deactivateAudioSourceForUndoHistory (ARAAudioSourceRef const auto audioSource { fromRef (audioSourceRef) }; ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory), _remoteRef, audioSource->_remoteRef, (deactivate) ? kARATrue : kARAFalse); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, deactivateAudioSourceForUndoHistory), _remoteRef, audioSource->_remoteRef, (deactivate) ? kARATrue : kARAFalse); } void DocumentController::destroyAudioSource (ARAAudioSourceRef audioSourceRef) noexcept @@ -675,7 +675,7 @@ void DocumentController::destroyAudioSource (ARAAudioSourceRef audioSourceRef) n ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); ARA_LOG_MODELOBJECT_LIFETIME ("will destroy audio source", audioSource->_remoteRef); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyAudioSource), _remoteRef, audioSource->_remoteRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyAudioSource), _remoteRef, audioSource->_remoteRef); delete audioSource; } @@ -692,7 +692,7 @@ ARAAudioModificationRef DocumentController::createAudioModification (ARAAudioSou ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioModificationPropertiesMinSize); ARAAudioModificationRef audioModificationRef; - remoteCall (audioModificationRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioModification), + remoteCall (audioModificationRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createAudioModification), _remoteRef, audioSource->_remoteRef, hostRef, *properties); ARA_LOG_MODELOBJECT_LIFETIME ("did create audio modification", audioModificationRef); @@ -708,7 +708,7 @@ ARAAudioModificationRef DocumentController::cloneAudioModification (ARAAudioModi ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioModificationPropertiesMinSize); ARAAudioModificationRef clonedAudioModificationRef; - remoteCall (clonedAudioModificationRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, cloneAudioModification), + remoteCall (clonedAudioModificationRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, cloneAudioModification), _remoteRef, srcAudioModificationRef, hostRef, *properties); ARA_LOG_MODELOBJECT_LIFETIME ("did create cloned audio modification", clonedAudioModificationRef); @@ -723,7 +723,7 @@ void DocumentController::updateAudioModificationProperties (ARAAudioModification ARA_INTERNAL_ASSERT (properties != nullptr); ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAAudioModificationPropertiesMinSize); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updateAudioModificationProperties), _remoteRef, audioModificationRef, *properties); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updateAudioModificationProperties), _remoteRef, audioModificationRef, *properties); } bool DocumentController::isAudioModificationPreservingAudioSourceSignal (ARAAudioModificationRef audioModificationRef) noexcept @@ -733,7 +733,7 @@ bool DocumentController::isAudioModificationPreservingAudioSourceSignal (ARAAudi ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARABool result; - remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal), _remoteRef, audioModificationRef); + remoteCall (result, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal), _remoteRef, audioModificationRef); return (result != kARAFalse); } @@ -743,7 +743,7 @@ void DocumentController::deactivateAudioModificationForUndoHistory (ARAAudioModi ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory), _remoteRef, audioModificationRef, (deactivate) ? kARATrue : kARAFalse); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, deactivateAudioModificationForUndoHistory), _remoteRef, audioModificationRef, (deactivate) ? kARATrue : kARAFalse); } void DocumentController::destroyAudioModification (ARAAudioModificationRef audioModificationRef) noexcept @@ -753,7 +753,7 @@ void DocumentController::destroyAudioModification (ARAAudioModificationRef audio ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARA_LOG_MODELOBJECT_LIFETIME ("will destroy audio modification", audioModification); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyAudioModification), _remoteRef, audioModificationRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyAudioModification), _remoteRef, audioModificationRef); } /*******************************************************************************/ @@ -766,7 +766,7 @@ ARAPlaybackRegionRef DocumentController::createPlaybackRegion (ARAAudioModificat ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAPlaybackRegionPropertiesMinSize); ARAPlaybackRegionRef playbackRegionRef; - remoteCall (playbackRegionRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegion), + remoteCall (playbackRegionRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegion), _remoteRef, audioModificationRef, hostRef, *properties); ARA_LOG_MODELOBJECT_LIFETIME ("did create playback region", playbackRegionRef); @@ -781,7 +781,7 @@ void DocumentController::updatePlaybackRegionProperties (ARAPlaybackRegionRef pl ARA_INTERNAL_ASSERT (properties != nullptr); ARA_INTERNAL_ASSERT (properties->structSize >= ARA::kARAPlaybackRegionPropertiesMinSize); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, updatePlaybackRegionProperties), _remoteRef, playbackRegionRef, *properties); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updatePlaybackRegionProperties), _remoteRef, playbackRegionRef, *properties); } void DocumentController::getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept @@ -794,7 +794,7 @@ void DocumentController::getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef ARA_INTERNAL_ASSERT (tailTime != nullptr); GetPlaybackRegionHeadAndTailTimeReply reply; - remoteCall (reply, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime), + remoteCall (reply, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime), _remoteRef, playbackRegionRef, (headTime != nullptr) ? kARATrue : kARAFalse, (tailTime != nullptr) ? kARATrue : kARAFalse); if (headTime != nullptr) *headTime = reply.headTime; @@ -809,7 +809,7 @@ void DocumentController::destroyPlaybackRegion (ARAPlaybackRegionRef playbackReg ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARA_LOG_MODELOBJECT_LIFETIME ("will destroy playback region", playbackRegionRef); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyPlaybackRegion), _remoteRef, playbackRegionRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyPlaybackRegion), _remoteRef, playbackRegionRef); } /*******************************************************************************/ @@ -821,7 +821,7 @@ bool DocumentController::isAudioSourceContentAvailable (ARAAudioSourceRef audioS ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); ARABool result; - remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAvailable), _remoteRef, audioSource->_remoteRef, type); + remoteCall (result, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAvailable), _remoteRef, audioSource->_remoteRef, type); return (result != kARAFalse); } @@ -834,7 +834,7 @@ ARAContentGrade DocumentController::getAudioSourceContentGrade (ARAAudioSourceRe ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); ARAContentGrade grade; - remoteCall (grade, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getAudioSourceContentGrade), _remoteRef, audioSource->_remoteRef, type); + remoteCall (grade, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getAudioSourceContentGrade), _remoteRef, audioSource->_remoteRef, type); return grade; } @@ -847,7 +847,7 @@ ARAContentReaderRef DocumentController::createAudioSourceContentReader (ARAAudio ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); ARAContentReaderRef contentReaderRef; - remoteCall (contentReaderRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioSourceContentReader), + remoteCall (contentReaderRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createAudioSourceContentReader), _remoteRef, audioSource->_remoteRef, type, range); auto contentReader { new ContentReader { contentReaderRef, type } }; @@ -866,7 +866,7 @@ bool DocumentController::isAudioModificationContentAvailable (ARAAudioModificati ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARABool result; - remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioModificationContentAvailable), _remoteRef, audioModificationRef, type); + remoteCall (result, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isAudioModificationContentAvailable), _remoteRef, audioModificationRef, type); return (result != kARAFalse); } @@ -877,7 +877,7 @@ ARAContentGrade DocumentController::getAudioModificationContentGrade (ARAAudioMo ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAContentGrade grade; - remoteCall (grade, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getAudioModificationContentGrade), _remoteRef, audioModificationRef, type); + remoteCall (grade, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getAudioModificationContentGrade), _remoteRef, audioModificationRef, type); return grade; } @@ -888,7 +888,7 @@ ARAContentReaderRef DocumentController::createAudioModificationContentReader (AR ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAContentReaderRef contentReaderRef; - remoteCall (contentReaderRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createAudioModificationContentReader), + remoteCall (contentReaderRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createAudioModificationContentReader), _remoteRef, audioModificationRef, type, range); auto contentReader { new ContentReader { contentReaderRef, type } }; @@ -907,7 +907,7 @@ bool DocumentController::isPlaybackRegionContentAvailable (ARAPlaybackRegionRef ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARABool result; - remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isPlaybackRegionContentAvailable), _remoteRef, playbackRegionRef, type); + remoteCall (result, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isPlaybackRegionContentAvailable), _remoteRef, playbackRegionRef, type); return (result != kARAFalse); } @@ -918,7 +918,7 @@ ARAContentGrade DocumentController::getPlaybackRegionContentGrade (ARAPlaybackRe ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAContentGrade grade; - remoteCall (grade, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionContentGrade), _remoteRef, playbackRegionRef, type); + remoteCall (grade, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionContentGrade), _remoteRef, playbackRegionRef, type); return grade; } @@ -929,7 +929,7 @@ ARAContentReaderRef DocumentController::createPlaybackRegionContentReader (ARAPl ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAContentReaderRef contentReaderRef; - remoteCall (contentReaderRef, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegionContentReader), + remoteCall (contentReaderRef, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, createPlaybackRegionContentReader), _remoteRef, playbackRegionRef, type, range); auto contentReader { new ContentReader { contentReaderRef, type } }; @@ -950,7 +950,7 @@ ARAInt32 DocumentController::getContentReaderEventCount (ARAContentReaderRef con ARA_INTERNAL_ASSERT (isValidInstance (contentReader)); ARAInt32 count; - remoteCall (count, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getContentReaderEventCount), _remoteRef, contentReader->_remoteRef); + remoteCall (count, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getContentReaderEventCount), _remoteRef, contentReader->_remoteRef); return count; } @@ -968,7 +968,7 @@ const void* DocumentController::getContentReaderDataForEvent (ARAContentReaderRe { result = contentReader->_decoder.decode (decoder); } }; - remoteCall (customDecode, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getContentReaderDataForEvent), + remoteCall (customDecode, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getContentReaderDataForEvent), _remoteRef, contentReader->_remoteRef, eventIndex); return result; } @@ -982,7 +982,7 @@ void DocumentController::destroyContentReader (ARAContentReaderRef contentReader ARA_INTERNAL_ASSERT (isValidInstance (contentReader)); ARA_LOG_MODELOBJECT_LIFETIME ("will destroy content reader", contentReader->remoteRef); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, destroyContentReader), _remoteRef, contentReader->_remoteRef); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, destroyContentReader), _remoteRef, contentReader->_remoteRef); delete contentReader; } @@ -998,7 +998,7 @@ bool DocumentController::isAudioSourceContentAnalysisIncomplete (ARAAudioSourceR ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); ARABool result; - remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete), + remoteCall (result, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isAudioSourceContentAnalysisIncomplete), _remoteRef, audioSource->_remoteRef, type); return (result != kARAFalse); } @@ -1012,7 +1012,7 @@ void DocumentController::requestAudioSourceContentAnalysis (ARAAudioSourceRef au ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); const ArrayArgument types { contentTypes, contentTypesCount }; - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, requestAudioSourceContentAnalysis), _remoteRef, audioSource->_remoteRef, types); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, requestAudioSourceContentAnalysis), _remoteRef, audioSource->_remoteRef, types); } ARAInt32 DocumentController::getProcessingAlgorithmsCount () noexcept @@ -1022,7 +1022,7 @@ ARAInt32 DocumentController::getProcessingAlgorithmsCount () noexcept ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARAInt32 count; - remoteCall (count, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmsCount), _remoteRef); + remoteCall (count, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmsCount), _remoteRef); return count; } @@ -1043,7 +1043,7 @@ const ARAProcessingAlgorithmProperties* DocumentController::getProcessingAlgorit _processingAlgorithmData.persistentID = _processingAlgorithmStrings.persistentID.c_str (); _processingAlgorithmData.name = _processingAlgorithmStrings.name.c_str (); } }; - remoteCall (customDecode, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmProperties), _remoteRef, algorithmIndex); + remoteCall (customDecode, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmProperties), _remoteRef, algorithmIndex); return &_processingAlgorithmData; } @@ -1056,7 +1056,7 @@ ARAInt32 DocumentController::getProcessingAlgorithmForAudioSource (ARAAudioSourc ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); ARAInt32 result; - remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource), _remoteRef, audioSource->_remoteRef); + remoteCall (result, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getProcessingAlgorithmForAudioSource), _remoteRef, audioSource->_remoteRef); return result; } @@ -1068,7 +1068,7 @@ void DocumentController::requestProcessingAlgorithmForAudioSource (ARAAudioSourc const auto audioSource { fromRef (audioSourceRef) }; ARA_INTERNAL_ASSERT (isValidInstance (audioSource)); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource), _remoteRef, audioSource->_remoteRef, algorithmIndex); + remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, requestProcessingAlgorithmForAudioSource), _remoteRef, audioSource->_remoteRef, algorithmIndex); } /*******************************************************************************/ @@ -1081,7 +1081,7 @@ bool DocumentController::isLicensedForCapabilities (bool runModalActivationDialo const ArrayArgument types { contentTypes, contentTypesCount }; ARABool result; - remoteCall (result, ARA_IPC_PLUGIN_METHOD_ID (ARADocumentControllerInterface, isLicensedForCapabilities), + remoteCall (result, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isLicensedForCapabilities), _remoteRef, (runModalActivationDialogIfNeeded) ? kARATrue : kARAFalse, types, transformationFlags); return (result != kARAFalse); } @@ -1105,7 +1105,7 @@ class PlaybackRenderer : public PlugIn::PlaybackRendererInterface, protected Rem ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAPlaybackRendererInterface, addPlaybackRegion), _remoteRef, playbackRegionRef); + remoteCall (ARA_IPC_METHOD_ID (ARAPlaybackRendererInterface, addPlaybackRegion), _remoteRef, playbackRegionRef); } void removePlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override { @@ -1113,7 +1113,7 @@ class PlaybackRenderer : public PlugIn::PlaybackRendererInterface, protected Rem ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAPlaybackRendererInterface, removePlaybackRegion), _remoteRef, playbackRegionRef); + remoteCall (ARA_IPC_METHOD_ID (ARAPlaybackRendererInterface, removePlaybackRegion), _remoteRef, playbackRegionRef); } private: @@ -1141,7 +1141,7 @@ class EditorRenderer : public PlugIn::EditorRendererInterface, protected RemoteC ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, addPlaybackRegion), _remoteRef, playbackRegionRef); + remoteCall (ARA_IPC_METHOD_ID (ARAEditorRendererInterface, addPlaybackRegion), _remoteRef, playbackRegionRef); } void removePlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override { @@ -1149,7 +1149,7 @@ class EditorRenderer : public PlugIn::EditorRendererInterface, protected RemoteC ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, removePlaybackRegion), _remoteRef, playbackRegionRef); + remoteCall (ARA_IPC_METHOD_ID (ARAEditorRendererInterface, removePlaybackRegion), _remoteRef, playbackRegionRef); } void addRegionSequence (ARARegionSequenceRef regionSequenceRef) noexcept override @@ -1158,7 +1158,7 @@ class EditorRenderer : public PlugIn::EditorRendererInterface, protected RemoteC ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, addRegionSequence), _remoteRef, regionSequenceRef); + remoteCall (ARA_IPC_METHOD_ID (ARAEditorRendererInterface, addRegionSequence), _remoteRef, regionSequenceRef); } void removeRegionSequence (ARARegionSequenceRef regionSequenceRef) noexcept override { @@ -1166,7 +1166,7 @@ class EditorRenderer : public PlugIn::EditorRendererInterface, protected RemoteC ARA_INTERNAL_ASSERT (isValidInstance (this)); ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorRendererInterface, removeRegionSequence), _remoteRef, regionSequenceRef); + remoteCall (ARA_IPC_METHOD_ID (ARAEditorRendererInterface, removeRegionSequence), _remoteRef, regionSequenceRef); } private: @@ -1196,7 +1196,7 @@ class EditorView : public PlugIn::EditorViewInterface, protected RemoteCaller, p ARA_INTERNAL_ASSERT (selection != nullptr); ARA_INTERNAL_ASSERT (selection->structSize >= ARA::kARAViewSelectionMinSize); - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorViewInterface, notifySelection), _remoteRef, *selection); + remoteCall (ARA_IPC_METHOD_ID (ARAEditorViewInterface, notifySelection), _remoteRef, *selection); } void notifyHideRegionSequences (ARASize regionSequenceRefsCount, const ARARegionSequenceRef regionSequenceRefs[]) noexcept override { @@ -1205,7 +1205,7 @@ class EditorView : public PlugIn::EditorViewInterface, protected RemoteCaller, p ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); const ArrayArgument sequences { regionSequenceRefs, regionSequenceRefsCount }; - remoteCall (ARA_IPC_PLUGIN_METHOD_ID (ARAEditorViewInterface, notifyHideRegionSequences), _remoteRef, sequences); + remoteCall (ARA_IPC_METHOD_ID (ARAEditorViewInterface, notifyHideRegionSequences), _remoteRef, sequences); } private: @@ -1430,7 +1430,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag MessageEncoder* const replyEncoder) { // ARAAudioAccessControllerInterface - if (messageID == ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, createAudioReaderForSource)) + if (messageID == ARA_IPC_METHOD_ID (ARAAudioAccessControllerInterface, createAudioReaderForSource)) { ARAAudioAccessControllerHostRef controllerHostRef; ARAAudioSourceHostRef audioSourceHostRef; @@ -1447,7 +1447,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag ARAAudioReaderHostRef audioReaderHostRef { toHostRef (reader) }; encodeReply (replyEncoder, audioReaderHostRef); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, readAudioSamples)) + else if (messageID == ARA_IPC_METHOD_ID (ARAAudioAccessControllerInterface, readAudioSamples)) { ARAAudioAccessControllerHostRef controllerHostRef; ARAAudioReaderHostRef audioReaderHostRef; @@ -1485,7 +1485,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, ArrayArgument { encoders.data (), encoders.size () }); // else send empty reply as indication of failure } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAAudioAccessControllerInterface, destroyAudioReader)) + else if (messageID == ARA_IPC_METHOD_ID (ARAAudioAccessControllerInterface, destroyAudioReader)) { ARAAudioAccessControllerHostRef controllerHostRef; ARAAudioReaderHostRef audioReaderHostRef; @@ -1500,7 +1500,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag } // ARAArchivingControllerInterface - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, getArchiveSize)) + else if (messageID == ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, getArchiveSize)) { ARAArchivingControllerHostRef controllerHostRef; ARAArchiveReaderHostRef archiveReaderHostRef; @@ -1511,7 +1511,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, documentController->getHostArchivingController ()->getArchiveSize (archiveReaderHostRef)); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, readBytesFromArchive)) + else if (messageID == ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, readBytesFromArchive)) { ARAArchivingControllerHostRef controllerHostRef; ARAArchiveReaderHostRef archiveReaderHostRef; @@ -1529,7 +1529,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag bytes.clear (); encodeReply (replyEncoder, BytesEncoder { bytes, false }); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, writeBytesToArchive)) + else if (messageID == ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, writeBytesToArchive)) { ARAArchivingControllerHostRef controllerHostRef; ARAArchiveWriterHostRef archiveWriterHostRef; @@ -1544,7 +1544,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, documentController->getHostArchivingController ()->writeBytesToArchive (archiveWriterHostRef, position, bytes.size (), bytes.data ())); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentArchivingProgress)) + else if (messageID == ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentArchivingProgress)) { ARAArchivingControllerHostRef controllerHostRef; float value; @@ -1555,7 +1555,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostArchivingController ()->notifyDocumentArchivingProgress (value); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress)) + else if (messageID == ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, notifyDocumentUnarchivingProgress)) { ARAArchivingControllerHostRef controllerHostRef; float value; @@ -1566,7 +1566,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostArchivingController ()->notifyDocumentUnarchivingProgress (value); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAArchivingControllerInterface, getDocumentArchiveID)) + else if (messageID == ARA_IPC_METHOD_ID (ARAArchivingControllerInterface, getDocumentArchiveID)) { ARAArchivingControllerHostRef controllerHostRef; ARAArchiveReaderHostRef archiveReaderHostRef; @@ -1579,7 +1579,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag } // ARAContentAccessControllerInterface - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, isMusicalContextContentAvailable)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, isMusicalContextContentAvailable)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAMusicalContextHostRef musicalContextHostRef; @@ -1591,7 +1591,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, (documentController->getHostContentAccessController ()->isMusicalContextContentAvailable (musicalContextHostRef, contentType)) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getMusicalContextContentGrade)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, getMusicalContextContentGrade)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAMusicalContextHostRef musicalContextHostRef; @@ -1603,7 +1603,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, documentController->getHostContentAccessController ()->getMusicalContextContentGrade (musicalContextHostRef, contentType)); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, createMusicalContextContentReader)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, createMusicalContextContentReader)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAMusicalContextHostRef musicalContextHostRef; @@ -1620,7 +1620,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, ARAContentReaderHostRef { toHostRef (hostContentReader) }); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, isAudioSourceContentAvailable)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, isAudioSourceContentAvailable)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAAudioSourceHostRef audioSourceHostRef; @@ -1634,7 +1634,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, (documentController->getHostContentAccessController ()->isAudioSourceContentAvailable (audioSource->_hostRef, contentType)) ? kARATrue : kARAFalse); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getAudioSourceContentGrade)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, getAudioSourceContentGrade)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAAudioSourceHostRef audioSourceHostRef; @@ -1648,7 +1648,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, documentController->getHostContentAccessController ()->getAudioSourceContentGrade (audioSource->_hostRef, contentType)); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, createAudioSourceContentReader)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, createAudioSourceContentReader)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAAudioSourceHostRef audioSourceHostRef; @@ -1666,7 +1666,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag hostContentReader->contentType = contentType; encodeReply (replyEncoder, ARAContentReaderHostRef { toHostRef (hostContentReader) }); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderEventCount)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderEventCount)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAContentReaderHostRef contentReaderHostRef; @@ -1678,7 +1678,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag encodeReply (replyEncoder, documentController->getHostContentAccessController ()->getContentReaderEventCount (hostContentReader->hostRef)); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderDataForEvent)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, getContentReaderDataForEvent)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAContentReaderHostRef contentReaderHostRef; @@ -1692,7 +1692,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag const void* eventData { documentController->getHostContentAccessController ()->getContentReaderDataForEvent (hostContentReader->hostRef, eventIndex) }; encodeContentEvent (replyEncoder, hostContentReader->contentType, eventData); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAContentAccessControllerInterface, destroyContentReader)) + else if (messageID == ARA_IPC_METHOD_ID (ARAContentAccessControllerInterface, destroyContentReader)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAContentReaderHostRef contentReaderHostRef; @@ -1707,7 +1707,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag } // ARAModelUpdateControllerInterface - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress)) + else if (messageID == ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceAnalysisProgress)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAAudioSourceHostRef audioSourceHostRef; @@ -1722,7 +1722,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostModelUpdateController ()->notifyAudioSourceAnalysisProgress (audioSource->_hostRef, state, value); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged)) + else if (messageID == ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioSourceContentChanged)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAAudioSourceHostRef audioSourceHostRef; @@ -1737,7 +1737,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostModelUpdateController ()->notifyAudioSourceContentChanged (audioSource->_hostRef, (range.second) ? &range.first : nullptr, scopeFlags); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged)) + else if (messageID == ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyAudioModificationContentChanged)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAAudioModificationHostRef audioModificationHostRef; @@ -1750,7 +1750,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostModelUpdateController ()->notifyAudioModificationContentChanged (audioModificationHostRef, (range.second) ? &range.first : nullptr, scopeFlags); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged)) + else if (messageID == ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyPlaybackRegionContentChanged)) { ARAModelUpdateControllerHostRef controllerHostRef; ARAPlaybackRegionHostRef playbackRegionHostRef; @@ -1763,7 +1763,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostModelUpdateController ()->notifyPlaybackRegionContentChanged (playbackRegionHostRef, (range.second) ? &range.first : nullptr, scopeFlags); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAModelUpdateControllerInterface, notifyDocumentDataChanged)) + else if (messageID == ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyDocumentDataChanged)) { ARAModelUpdateControllerHostRef controllerHostRef; decodeArguments (decoder, controllerHostRef); @@ -1775,7 +1775,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag } // ARAPlaybackControllerInterface - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestStartPlayback)) + else if (messageID == ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestStartPlayback)) { ARAPlaybackControllerHostRef controllerHostRef; decodeArguments (decoder, controllerHostRef); @@ -1785,7 +1785,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostPlaybackController ()->requestStartPlayback (); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestStopPlayback)) + else if (messageID == ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestStopPlayback)) { ARAPlaybackControllerHostRef controllerHostRef; decodeArguments (decoder, controllerHostRef); @@ -1795,7 +1795,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostPlaybackController ()->requestStopPlayback (); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestSetPlaybackPosition)) + else if (messageID == ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestSetPlaybackPosition)) { ARAPlaybackControllerHostRef controllerHostRef; ARATimePosition timePosition; @@ -1806,7 +1806,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostPlaybackController ()->requestSetPlaybackPosition (timePosition); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestSetCycleRange)) + else if (messageID == ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestSetCycleRange)) { ARAPlaybackControllerHostRef controllerHostRef; ARATimePosition startTime; @@ -1818,7 +1818,7 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostPlaybackController ()->requestSetCycleRange (startTime, duration); } - else if (messageID == ARA_IPC_HOST_METHOD_ID (ARAPlaybackControllerInterface, requestEnableCycle)) + else if (messageID == ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestEnableCycle)) { ARAPlaybackControllerHostRef controllerHostRef; ARABool enable; From c4875e351941dc1c11ddd4e8137519fdf4a60af9 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:10 +0200 Subject: [PATCH 18/42] More consistent formatting/code structure --- IPC/ARAIPCConnection.cpp | 4 ++-- IPC/ARAIPCConnection.h | 4 ++-- PlugIn/ARAPlug.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 26966d6..11a4984 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -458,7 +458,7 @@ void APCRouteNewTransactionFunc (ULONG_PTR parameter) #elif defined (__APPLE__) -void Connection::performRunloopSource (void* info) +void Connection::_performRunLoopSource (void* info) { auto connection { reinterpret_cast (info) }; connection->_mutex.lock (); @@ -497,7 +497,7 @@ Connection::Connection (WaitForMessageDelegate waitForMessageDelegate, void* del #elif defined (__APPLE__) _creationThreadRunLoop { CFRunLoopGetCurrent () } { - CFRunLoopSourceContext context { 0, this, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, performRunloopSource }; + CFRunLoopSourceContext context { 0, this, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, _performRunLoopSource }; _runloopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 0, &context); CFRunLoopAddSource (_creationThreadRunLoop, _runloopSource, kCFRunLoopCommonModes); } diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 2d0c50d..7c2ffa5 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -160,9 +160,9 @@ class Connection //! intended for debug output only: helper to properly decode message IDs virtual bool sendsHostMessages () const = 0; -private: #if defined (__APPLE__) - static void performRunloopSource (void* info); +private: + static void _performRunLoopSource (void* info); #endif private: diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index 058e3cf..2f000f1 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -689,13 +689,13 @@ RestoreObjectsFilter::RestoreObjectsFilter (const ARARestoreObjectsFilter* filte { for (const auto& audioSource : document->getAudioSources ()) { - auto audioSourceID { audioSource->getPersistentID ().c_str () }; + const auto audioSourceID { audioSource->getPersistentID ().c_str () }; ARA_VALIDATE_API_STATE (_audioSourcesByID.count (audioSourceID) == 0); // make sure all current audio source persistentIDs are unique _audioSourcesByID[audioSourceID] = audioSource; for (const auto& audioModification : audioSource->getAudioModifications ()) { - auto audioModificationID { audioModification->getPersistentID ().c_str () }; + const auto audioModificationID { audioModification->getPersistentID ().c_str () }; ARA_VALIDATE_API_STATE (_audioModificationsByID.count (audioModificationID) == 0); // make sure all current audio modification persistentIDs are unique _audioModificationsByID[audioModificationID] = audioModification; } @@ -1124,7 +1124,7 @@ bool DocumentController::restoreObjectsFromArchive (ARAArchiveReaderHostRef arch } #endif - const RestoreObjectsFilter restoreObjectsFilter (filter, getDocument ()); + const RestoreObjectsFilter restoreObjectsFilter { filter, getDocument () }; return doRestoreObjectsFromArchive (&archiveReader, &restoreObjectsFilter); } From 0e71581a3f69b41e4667f9ddbeabfffe55e6c317 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:10 +0200 Subject: [PATCH 19/42] Move IPC dispatcher subclass-specific message argument keys into respective subclass --- IPC/ARAIPCConnection.cpp | 26 ++++++++------------------ IPC/ARAIPCConnection.h | 11 +++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 11a4984..8a36ab3 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -120,17 +120,6 @@ namespace ARA { namespace IPC { -// key to indicate whether an outgoing call is made in response to a currently handled incoming call -// or a new call, which is necessary to deal with the decoupled main threads concurrency -// to optimize for performance, it is only added when the call is a response, being a new call is implicit -// (it is also never added to replies because they always are a response anyways) -constexpr MessageArgumentKey isResponseKey { -1 }; - -// keys to store the threading information in the IPC messages for OtherThreadsMessageDispatcher -constexpr MessageArgumentKey sendThreadKey { -1 }; -constexpr MessageArgumentKey receiveThreadKey { -2 }; - - #if defined (_WIN32) #define readThreadRef readInt32 #define appendThreadRef appendInt32 @@ -216,7 +205,7 @@ void MainThreadMessageDispatcher::sendMessage (MessageID messageID, MessageEncod const auto isResponse { _processingMessagesCount > 0 }; if (isResponse) - encoder->appendInt32 (isResponseKey, 1); + encoder->appendInt32 (kIsResponseKey, 1); _sendMessage (messageID, encoder, !isResponse); @@ -254,7 +243,7 @@ void MainThreadMessageDispatcher::processPendingMessageIfNeeded () void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) { const auto isResponse { isReply (messageID) || // replies implicitly are responses - (decoder && decoder->hasDataForKey (isResponseKey)) }; + (decoder && decoder->hasDataForKey (kIsResponseKey)) }; // if on creation thread, responses can be processed immediately, and // new transaction can only be processed immediately when no other transaction is going on @@ -303,10 +292,10 @@ void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, MessageEnc Connection::ReplyHandler replyHandler, void* replyHandlerUserData) { const auto currentThread { getCurrentThread () }; - encoder->appendThreadRef (sendThreadKey, currentThread); + encoder->appendThreadRef (kSendThreadKey, currentThread); const auto isResponse { _remoteTargetThread != _invalidThread }; if (isResponse) - encoder->appendThreadRef (receiveThreadKey, _remoteTargetThread); + encoder->appendThreadRef (kReceiveThreadKey, _remoteTargetThread); _sendLock.lock (); _sendMessage (messageID, encoder, !isResponse); @@ -361,7 +350,7 @@ OtherThreadsMessageDispatcher::RoutedMessage* OtherThreadsMessageDispatcher::_ge void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) { ThreadRef targetThread; - if (decoder->readThreadRef (receiveThreadKey, &targetThread)) + if (decoder->readThreadRef (kReceiveThreadKey, &targetThread)) { ARA_INTERNAL_ASSERT (targetThread != _invalidThread); if (targetThread == getCurrentThread ()) @@ -413,13 +402,14 @@ void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID { const auto previousRemoteTargetThread { _remoteTargetThread }; ThreadRef remoteTargetThread; - [[maybe_unused]] const auto success { decoder->readThreadRef (sendThreadKey, &remoteTargetThread) }; + [[maybe_unused]] const auto success { decoder->readThreadRef (kSendThreadKey, &remoteTargetThread) }; ARA_INTERNAL_ASSERT (success); + ARA_INTERNAL_ASSERT (remoteTargetThread != _invalidThread); _remoteTargetThread = remoteTargetThread; auto replyEncoder { _handleReceivedMessage (messageID, decoder) }; - replyEncoder->appendThreadRef (receiveThreadKey, remoteTargetThread); + replyEncoder->appendThreadRef (kReceiveThreadKey, remoteTargetThread); _sendLock.lock (); _sendMessage (0, replyEncoder, false); diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 7c2ffa5..667495b 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -260,6 +260,13 @@ class MainThreadMessageDispatcher : public MessageDispatcher void processPendingMessageIfNeeded (); +private: + // key to indicate whether an outgoing call is made in response to a currently handled incoming call + // or a new call, which is necessary to deal with the decoupled main threads concurrency + // to optimize for performance, it is only added when the call is a response, being a new call is implicit + // (it is also never added to replies because they always are a response anyways) + static constexpr MessageArgumentKey kIsResponseKey { -1 }; + private: int32_t _processingMessagesCount { 0 }; @@ -284,6 +291,10 @@ class OtherThreadsMessageDispatcher : public MessageDispatcher void routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) override; private: + // keys to store the threading information in the IPC messages + static constexpr MessageArgumentKey kSendThreadKey { -1 }; + static constexpr MessageArgumentKey kReceiveThreadKey { -2 }; + struct RoutedMessage { MessageID _messageID { 0 }; From a13ff63447ee8ed4a09b5b3ee9dfcb6ff43109c4 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:11 +0200 Subject: [PATCH 20/42] Improve IPC documentation --- IPC/ARAIPCAudioUnit_v3.h | 4 ++-- IPC/ARAIPCAudioUnit_v3.mm | 2 +- IPC/ARAIPCConnection.cpp | 28 +++++++++++++++++----------- IPC/ARAIPCConnection.h | 6 +++--- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.h b/IPC/ARAIPCAudioUnit_v3.h index 7a86c73..41a1bec 100644 --- a/IPC/ARAIPCAudioUnit_v3.h +++ b/IPC/ARAIPCAudioUnit_v3.h @@ -76,7 +76,7 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize(ARAIPCConnectionRef _Nonnull proxy -//! plug-in side:implementation for AUMessageChannel -init... +//! plug-in side: implementation for AUMessageChannel -init... //! will initialize the proxy on demand - make sure to add all factories via ARAIPCProxyHostAddFactory() //! before the first call to this function and to call ARAIPCAUProxyHostUninitialize() if the call was made ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageChannel(NSObject * _Nonnull audioUnitChannel, @@ -85,7 +85,7 @@ ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageCha //! plug-in side: implementation for AUMessageChannel -callAudioUnit: NSDictionary * _Nonnull ARA_CALL ARAIPCAUProxyHostCommandHandler(ARAIPCMessageChannelRef _Nonnull messageChannelRef, NSDictionary * _Nonnull message); -//! plug-in side:implementation for AUMessageChannel -dealloc +//! plug-in side: implementation for AUMessageChannel -dealloc void ARA_CALL ARAIPCAUProxyHostUninitializeMessageChannel(ARAIPCMessageChannelRef _Nonnull messageChannelRef); //! plug-in side: static cleanup upon shutdown diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 90a64ee..a866d17 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -58,7 +58,7 @@ API_AVAILABLE_BEGIN(macos(13.0), ios(16.0)) -// key for transaction locking through the IPC channel +// key to store message ID in XPC message constexpr NSString * _messageIDKey { @"msgID" }; diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 8a36ab3..9afcbd6 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -71,10 +71,10 @@ // // In this loop, extra efforts might be necessary to handle the IPC threading: // When e.g. using Audio Unit AUMessageChannel, Grand Central Dispatch will deliver -// the incoming IPC messages on some undefined thread. In such cases, a dispatch -// from the receiving thread to the target thread is necessary, implemented via +// the incoming IPC messages on some undefined GDC worker thread, requiring a dispatch +// from the receiving thread to the target thread. This is implemented via // a condition variable that the receive thread awakes when a message comes in. -// Instead of looping, a sending thread will then wait on this condition for the reply. +// Instead of looping, a sending thread will wait on this condition for the reply. // // While most ARA communication is happening on the main thread, there are // several calls that may be made from other threads. This poses several challenges @@ -100,20 +100,26 @@ // on the main thread or on any other thread, and chooses the appropriate channel accordingly. // On the receiving side, calls coming in on the main thread channel are forwarded to the main // thread (unless the receive code already runs there), and for the other threads the code is -// executed directly on the receive thread. +// executed directly on the receiving thread. +// Because there's no mechanism to synchronize thread priorities between the host and the +// plug-in for the non-main-threads, plug-ins will no longer be able to rely on their thread +// priority configuration with respect to ARA calls. For this reason, it is recommended to +// restrict these calls to audio sample reading, and order these explicitly before making them. // -// Replies or callbacks are routed back to the originating thread in the sender. This is done -// by adding a token when sending a message that identifies the sending thread, and replies and -// callbacks pass this token back to allow for proper dispatching from the receive thread to the -// thread that initiated the transaction. +// Replies or callbacks made when processing an IPC call are routed back to the originating +// thread in the sender. For non-main-thread communication, this is done by adding a token when +// sending a message which identifies the sending thread, and this token passed back alongside +// the reply or stacked callback to allow for proper dispatching from the IPC receive thread to +// the thread that initiated the transaction. // Since the actual ARA code is agnostic to IPC being used, the receiving side uses // thread local storage to make the sender's thread token available for all stacked calls // that the ARA code might make in response to the message. // // Note that there is a crucial difference between the dispatch of a new transaction and -// the dispatch of any follow-up messages in the transaction: the initial message is dispatched -// to a thread that is potentially executing other code as well in some form of run loop, -// whereas the follow-ups need to dispatch to a thread that is currently blocking inside ARA code. +// the dispatch of any follow-up replies or callbacks in the transaction: the initial message +// is dispatched to a thread that is potentially executing other code as well in some form of +// run loop, whereas the follow-ups will be dispatched to a thread that is currently blocking +// inside ARA code. namespace ARA { diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 667495b..9ef9bce 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -88,7 +88,7 @@ class MessageChannel //! @} -//! IPC connection: gateway for sending and receiving messages, +//! IPC connection: client interface for sending and receiving messages, //! utilizing potentially multiple MessageChannel instances //! @{ class Connection @@ -100,14 +100,14 @@ class Connection //! set the message channel for all main thread communication //! Must be done before sending or receiving the first message on any channel. - //! The connection takes ownership of the channel and deletes them upon teardown. + //! The connection takes ownership of the channel and deletes it upon teardown. void setMainThreadChannel (MessageChannel* messageChannel); MainThreadMessageDispatcher* getMainThreadDispatcher () const { return _mainThreadDispatcher; } //! set the message channel for all non-main thread communication //! Must be done before sending or receiving the first message on any channel. - //! The connection takes ownership of the channel and deletes them upon teardown. + //! The connection takes ownership of the channel and deletes it upon teardown. void setOtherThreadsChannel (MessageChannel* messageChannel); OtherThreadsMessageDispatcher* getOtherThreadsDispatcher () const { return _otherThreadsDispatcher; } From 730b949202d09230e74c92de23daa93116faf01a Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:11 +0200 Subject: [PATCH 21/42] IPC MessageHandler changed from delegate interface to std::function<> --- IPC/ARAIPCAudioUnit_v3.mm | 4 ++-- IPC/ARAIPCConnection.cpp | 8 +------- IPC/ARAIPCConnection.h | 22 ++++++++-------------- IPC/ARAIPCProxyHost.h | 4 ++-- IPC/ARAIPCProxyPlugIn.h | 6 +++--- 5 files changed, 16 insertions(+), 28 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index a866d17..6922f2c 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -247,7 +247,7 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override { setMainThreadChannel (new ProxyPlugInMessageChannel { mainChannel }); setOtherThreadsChannel (new ProxyPlugInMessageChannel { otherChannel }); - setMessageHandler (this); + setMessageHandler (ProxyPlugIn::handleReceivedMessage); #if !__has_feature(objc_arc) [_initAU retain]; #endif @@ -322,7 +322,7 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCConnectionRef _Nonnull prox AUConnection { nullptr, nullptr } { ARAIPCProxyHostSetBindingHandler (handleBinding); - setMessageHandler (this); + setMessageHandler ([this] (auto&& ...args) { handleReceivedMessage (args...); }); } bool sendsHostMessages () const override { return false; } diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 9afcbd6..6d67c6d 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -191,7 +191,7 @@ MessageEncoder* MessageDispatcher::_handleReceivedMessage (MessageID messageID, ARA_IPC_LOG ("handles" ARA_IPC_DECODE_MESSAGE_FORMAT "on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS); auto replyEncoder { _connection->createEncoder () }; - _connection->getMessageHandler ()->handleReceivedMessage (messageID, decoder, replyEncoder); + _connection->getMessageHandler () (messageID, decoder, replyEncoder); delete decoder; @@ -530,12 +530,6 @@ void Connection::setOtherThreadsChannel (MessageChannel* messageChannel) _otherThreadsDispatcher = new OtherThreadsMessageDispatcher { this, messageChannel }; } -void Connection::setMessageHandler (MessageHandler* messageHandler) -{ - ARA_INTERNAL_ASSERT (_messageHandler == nullptr); - _messageHandler = messageHandler; -} - void Connection::sendMessage (MessageID messageID, MessageEncoder* encoder, ReplyHandler replyHandler, void* replyHandlerUserData) { ARA_INTERNAL_ASSERT ((_mainThreadDispatcher != nullptr) && (_otherThreadsDispatcher != nullptr) && (_messageHandler != nullptr)); diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 9ef9bce..117a86c 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -51,17 +51,11 @@ class MainThreadMessageDispatcher; class OtherThreadsMessageDispatcher; -//! delegate interface for processing messages received through an IPC connection -class MessageHandler -{ -public: - virtual ~MessageHandler () = default; - - //! IPC connections will call this method for incoming messages after - //! after filtering replies and routing them to the correct thread. - virtual void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, - MessageEncoder* const replyEncoder) = 0; -}; +//! delegate function for processing messages received through an IPC connection +//! IPC connections will call this function for incoming messages after +//! filtering replies and routing them to the correct thread. +using MessageHandler = std::function; //! IPC message channel: primitive for sending and receiving messages @@ -115,9 +109,9 @@ class Connection //! set the message handler for all communication on all threads //! Must be done before sending or receiving the first message on any channel. //! The connection does not take ownership of the message handler. - void setMessageHandler (MessageHandler* messageHandler); + void setMessageHandler (MessageHandler&& messageHandler) { _messageHandler = std::move(messageHandler); } - MessageHandler* getMessageHandler () const { return _messageHandler; } + const MessageHandler& getMessageHandler () const { return _messageHandler; } //! Reply Handler: a function passed to sendMessage () that is called to process the reply to a message //! decoder will be nullptr if incoming message was empty @@ -171,7 +165,7 @@ class Connection void* const _waitForMessageSemaphore; // concrete type is platform-dependent MainThreadMessageDispatcher* _mainThreadDispatcher {}; OtherThreadsMessageDispatcher* _otherThreadsDispatcher {}; - MessageHandler* _messageHandler {}; + MessageHandler _messageHandler; std::thread::id const _creationThreadID; #if defined (_WIN32) HANDLE const _creationThreadHandle; diff --git a/IPC/ARAIPCProxyHost.h b/IPC/ARAIPCProxyHost.h index 9fca99a..68f119b 100644 --- a/IPC/ARAIPCProxyHost.h +++ b/IPC/ARAIPCProxyHost.h @@ -39,14 +39,14 @@ extern "C" { //! plug-in side implementation of MessageHandler //! the plug-in uses the C interface below, but this class will be subclassed by specialized implementations -class ProxyHost : public MessageHandler, public RemoteCaller +class ProxyHost : public RemoteCaller { protected: explicit ProxyHost (Connection* connection); public: void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, - MessageEncoder* const replyEncoder) override; + MessageEncoder* const replyEncoder); }; #endif diff --git a/IPC/ARAIPCProxyPlugIn.h b/IPC/ARAIPCProxyPlugIn.h index cfa8154..bbd1e70 100644 --- a/IPC/ARAIPCProxyPlugIn.h +++ b/IPC/ARAIPCProxyPlugIn.h @@ -40,13 +40,13 @@ extern "C" { //! host side implementation of MessageHandler //! the host uses the C interface below, but this class will be subclassed by specialized implementations -class ProxyPlugIn : public MessageHandler, public RemoteCaller +class ProxyPlugIn : public RemoteCaller { public: using RemoteCaller::RemoteCaller; - void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, - MessageEncoder* const replyEncoder) override; + static void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, + MessageEncoder* const replyEncoder); }; #endif From 1e4182868633d0781173c23e46ed6007d75b6396 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:11 +0200 Subject: [PATCH 22/42] External IPC interface uses ARAIPCProxyPlugIn/HostRef instead of ARAIPCConnectionRef --- IPC/ARAIPC.h | 11 ++++++----- IPC/ARAIPCAudioUnit_v3.h | 10 +++++----- IPC/ARAIPCAudioUnit_v3.mm | 16 ++++++++-------- IPC/ARAIPCEncoding.h | 3 ++- IPC/ARAIPCProxyPlugIn.cpp | 22 +++++++++++----------- IPC/ARAIPCProxyPlugIn.h | 10 +++++----- 6 files changed, 37 insertions(+), 35 deletions(-) diff --git a/IPC/ARAIPC.h b/IPC/ARAIPC.h index ea601cb..c1df0de 100644 --- a/IPC/ARAIPC.h +++ b/IPC/ARAIPC.h @@ -60,20 +60,21 @@ extern "C" { //! IPC reference markup type identifier. \br\br //! Examples: \br -//! ::ARAARAIPCConnectionRef \br +//! ::ARAARAIPCProxyHostRef \br +//! ::ARAARAIPCProxyPlugInRef \br //! ::ARAIPCMessageChannelRef \br //! ::ARAIPCPlugInInstanceRef \br #define ARA_IPC_REF(IPCRefType) struct IPCRefType##MarkupType * IPCRefType +//! C-compatible wrapper of ARA IPC ProxyHost +typedef ARA_IPC_REF(ARAIPCProxyHostRef); -//! C-compatible wrapper of ARA IPC Connection -typedef ARA_IPC_REF(ARAIPCConnectionRef); - +//! C-compatible wrapper of ARA IPC ProxyPlugIn +typedef ARA_IPC_REF(ARAIPCProxyPlugInRef); //! C-compatible wrapper of ARA IPC MessageChannel typedef ARA_IPC_REF(ARAIPCMessageChannelRef); - //! to keep the IPC decoupled from the companion API in use, the IPC code uses //! an opaque encapsulation to represent a companion API plug-in instance typedef ARA_IPC_REF(ARAIPCPlugInInstanceRef); diff --git a/IPC/ARAIPCAudioUnit_v3.h b/IPC/ARAIPCAudioUnit_v3.h index 41a1bec..633500c 100644 --- a/IPC/ARAIPCAudioUnit_v3.h +++ b/IPC/ARAIPCAudioUnit_v3.h @@ -56,13 +56,13 @@ typedef void (*ARAMainThreadWaitForMessageDelegate) (void * _Nullable delegateUs //! must be balanced with ARAIPCAUProxyPlugInUninitialize() //! the optional delegate will be called periodically when the IPC needs to block the main thread, //! with the opaque user data pointer being passed back into the call -ARAIPCConnectionRef _Nullable ARA_CALL ARAIPCAUProxyPlugInInitialize(AUAudioUnit * _Nonnull audioUnit, - ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, - void * _Nullable delegateUserData); +ARAIPCProxyPlugInRef _Nullable ARA_CALL ARAIPCAUProxyPlugInInitialize(AUAudioUnit * _Nonnull audioUnit, + ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, + void * _Nullable delegateUserData); //! allows the host to let the plug-in perform ARA IPC on the main thread when otherwise //! blocking it for an extended period of time -void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCConnectionRef _Nonnull proxyRef); +void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks(ARAIPCProxyPlugInRef _Nonnull proxyPlugInRef); //! host side: Audio Unit specialization of ARAIPCProxyPlugInBindToDocumentController() //! must be balanced with ARAIPCProxyPlugInCleanupBinding() when the given audioUnit is destroyed @@ -72,7 +72,7 @@ const ARAPlugInExtensionInstance * _Nonnull ARA_CALL ARAIPCAUProxyPlugInBindToDo ARAPlugInInstanceRoleFlags assignedRoles); //! host side: uninitialize the proxy component set up in ARAIPCAUProxyPlugInInitialize() -void ARA_CALL ARAIPCAUProxyPlugInUninitialize(ARAIPCConnectionRef _Nonnull proxyRef); +void ARA_CALL ARAIPCAUProxyPlugInUninitialize(ARAIPCProxyPlugInRef _Nonnull proxyPlugInRef); diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 6922f2c..08e4e04 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -266,7 +266,7 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override #endif ARA_MAP_IPC_REF (AudioUnitMessageChannel, ARAIPCMessageChannelRef) -ARA_MAP_IPC_REF (AUConnection, ARAIPCConnectionRef) +ARA_MAP_IPC_REF (AUProxyPlugIn, ARAIPCProxyPlugInRef) #if defined (__GNUC__) _Pragma ("GCC diagnostic pop") @@ -279,16 +279,16 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override // host side: proxy plug-in C adapter #if !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY -ARAIPCConnectionRef ARA_CALL ARAIPCAUProxyPlugInInitialize (AUAudioUnit * _Nonnull audioUnit, - ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, - void * _Nullable delegateUserData) +ARAIPCProxyPlugInRef ARA_CALL ARAIPCAUProxyPlugInInitialize (AUAudioUnit * _Nonnull audioUnit, + ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, + void * _Nullable delegateUserData) { return toIPCRef (AUProxyPlugIn::createWithAudioUnit (audioUnit, waitForMessageDelegate, delegateUserData)); } -void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCConnectionRef _Nonnull proxyRef) +void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCProxyPlugInRef _Nonnull proxyPlugInRef) { - fromIPCRef (proxyRef)->performPendingMainThreadTasks (); + fromIPCRef (proxyPlugInRef)->performPendingMainThreadTasks (); } const ARAPlugInExtensionInstance * _Nonnull ARA_CALL ARAIPCAUProxyPlugInBindToDocumentController (AUAudioUnit * _Nonnull audioUnit, @@ -303,9 +303,9 @@ void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCConnection return plugInExtensionInstance; } -void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCConnectionRef _Nonnull proxyRef) +void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCProxyPlugInRef _Nonnull proxyPlugInRef) { - delete fromIPCRef (proxyRef); + delete fromIPCRef (proxyPlugInRef); } #endif // !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 052ab87..8e4b55c 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -148,7 +148,8 @@ ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAArchiveWriterHostRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAContentAccessControllerHostRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAModelUpdateControllerHostRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAPlaybackControllerHostRef) -ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCConnectionRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCProxyHostRef) +ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCProxyPlugInRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCMessageChannelRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCPlugInInstanceRef) #undef ARA_IPC_SPECIALIZE_FOR_REF_TYPE diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp index 8c3fe7c..86d0b07 100644 --- a/IPC/ARAIPCProxyPlugIn.cpp +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -1303,22 +1303,22 @@ using namespace ProxyPlugInImpl; _Pragma ("GCC diagnostic ignored \"-Wunused-function\"") #endif -ARA_MAP_IPC_REF (Connection, ARAIPCConnectionRef) +ARA_MAP_IPC_REF (ProxyPlugIn, ARAIPCProxyPlugInRef) #if defined (__GNUC__) _Pragma ("GCC diagnostic pop") #endif -size_t ARAIPCProxyPlugInGetFactoriesCount (ARAIPCConnectionRef connectionRef) +size_t ARAIPCProxyPlugInGetFactoriesCount (ARAIPCProxyPlugInRef proxyPlugInRef) { size_t count; - RemoteCaller { fromIPCRef (connectionRef) }.remoteCall (count, kGetFactoriesCountMethodID); + fromIPCRef (proxyPlugInRef)->remoteCall (count, kGetFactoriesCountMethodID); ARA_INTERNAL_ASSERT (count > 0); return count; } -const ARAFactory* ARAIPCProxyPlugInGetFactoryAtIndex (ARAIPCConnectionRef connectionRef, size_t index) +const ARAFactory* ARAIPCProxyPlugInGetFactoryAtIndex (ARAIPCProxyPlugInRef proxyPlugInRef, size_t index) { RemoteFactory remoteFactory; RemoteCaller::CustomDecodeFunction customDecode { @@ -1358,7 +1358,7 @@ const ARAFactory* ARAIPCProxyPlugInGetFactoryAtIndex (ARAIPCConnectionRef connec remoteFactory._factory.analyzeableContentTypes = remoteFactory._analyzableTypes.data (); } }; - RemoteCaller { fromIPCRef (connectionRef) }.remoteCall (customDecode, kGetFactoryMethodID, index); + fromIPCRef (proxyPlugInRef)->remoteCall (customDecode, kGetFactoryMethodID, index); const auto result { _factories.insert (std::make_pair (remoteFactory._strings.factoryID, remoteFactory)) }; if (result.second) @@ -1382,14 +1382,14 @@ const ARAFactory* ARAIPCProxyPlugInGetFactoryAtIndex (ARAIPCConnectionRef connec return &result.first->second._factory; } -void ARAIPCProxyPlugInInitializeARA (ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID, ARAAPIGeneration desiredApiGeneration) +void ARAIPCProxyPlugInInitializeARA (ARAIPCProxyPlugInRef proxyPlugInRef, const ARAPersistentID factoryID, ARAAPIGeneration desiredApiGeneration) { ARA_INTERNAL_ASSERT (desiredApiGeneration >= kARAAPIGeneration_2_0_Final); - RemoteCaller { fromIPCRef (connectionRef) }.remoteCall (kInitializeARAMethodID, factoryID, desiredApiGeneration); + fromIPCRef (proxyPlugInRef)->remoteCall (kInitializeARAMethodID, factoryID, desiredApiGeneration); } const ARADocumentControllerInstance* ARAIPCProxyPlugInCreateDocumentControllerWithDocument ( - ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID, + ARAIPCProxyPlugInRef proxyPlugInRef, const ARAPersistentID factoryID, const ARADocumentControllerHostInstance* hostInstance, const ARADocumentProperties* properties) { const auto cached { _factories.find (std::string { factoryID }) }; @@ -1397,7 +1397,7 @@ const ARADocumentControllerInstance* ARAIPCProxyPlugInCreateDocumentControllerWi if (cached == _factories.end ()) return nullptr; - auto result { new DocumentController { fromIPCRef (connectionRef), &cached->second._factory, hostInstance, properties } }; + auto result { new DocumentController { fromIPCRef (proxyPlugInRef)->getConnection (), &cached->second._factory, hostInstance, properties } }; return result->getInstance (); } @@ -1418,9 +1418,9 @@ void ARAIPCProxyPlugInCleanupBinding (const ARAPlugInExtensionInstance* plugInEx delete static_cast (plugInExtensionInstance); } -void ARAIPCProxyPlugInUninitializeARA (ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID) +void ARAIPCProxyPlugInUninitializeARA (ARAIPCProxyPlugInRef proxyPlugInRef, const ARAPersistentID factoryID) { - RemoteCaller { fromIPCRef (connectionRef) }.remoteCall (kUninitializeARAMethodID, factoryID); + fromIPCRef (proxyPlugInRef)->remoteCall (kUninitializeARAMethodID, factoryID); } diff --git a/IPC/ARAIPCProxyPlugIn.h b/IPC/ARAIPCProxyPlugIn.h index bbd1e70..53611ba 100644 --- a/IPC/ARAIPCProxyPlugIn.h +++ b/IPC/ARAIPCProxyPlugIn.h @@ -52,18 +52,18 @@ class ProxyPlugIn : public RemoteCaller //! counts the factories available through the given message channel -size_t ARAIPCProxyPlugInGetFactoriesCount(ARAIPCConnectionRef connectionRef); +size_t ARAIPCProxyPlugInGetFactoriesCount(ARAIPCProxyPlugInRef proxyPlugInRef); //! get a static copy of the remote factory data, with all function calls removed //! index must be smaller than the result of ARAIPCProxyPlugInGetFactoriesCount() -const ARAFactory * ARAIPCProxyPlugInGetFactoryAtIndex(ARAIPCConnectionRef connectionRef, size_t index); +const ARAFactory * ARAIPCProxyPlugInGetFactoryAtIndex(ARAIPCProxyPlugInRef proxyPlugInRef, size_t index); //! proxy initialization call, to be used instead of ARAFactory.initializeARAWithConfiguration() // \todo we're currently not supporting propagating ARA assertions through IPC... -void ARAIPCProxyPlugInInitializeARA(ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID, ARAAPIGeneration desiredApiGeneration); +void ARAIPCProxyPlugInInitializeARA(ARAIPCProxyPlugInRef proxyPlugInRef, const ARAPersistentID factoryID, ARAAPIGeneration desiredApiGeneration); //! proxy document controller creation call, to be used instead of ARAFactory.createDocumentControllerWithDocument() -const ARADocumentControllerInstance * ARAIPCProxyPlugInCreateDocumentControllerWithDocument(ARAIPCConnectionRef connectionRef, +const ARADocumentControllerInstance * ARAIPCProxyPlugInCreateDocumentControllerWithDocument(ARAIPCProxyPlugInRef proxyPlugInRef, const ARAPersistentID factoryID, const ARADocumentControllerHostInstance * hostInstance, const ARADocumentProperties * properties); @@ -76,7 +76,7 @@ const ARAPlugInExtensionInstance * ARAIPCProxyPlugInBindToDocumentController(ARA void ARAIPCProxyPlugInCleanupBinding(const ARAPlugInExtensionInstance * plugInExtension); //! proxy uninitialization call, to be used instead of ARAFactory.uninitializeARA() -void ARAIPCProxyPlugInUninitializeARA(ARAIPCConnectionRef connectionRef, const ARAPersistentID factoryID); +void ARAIPCProxyPlugInUninitializeARA(ARAIPCProxyPlugInRef proxyPlugInRef, const ARAPersistentID factoryID); #if defined(__cplusplus) From 5d9fd648c7dcfd78f4c29c29bf8def5be4dee444 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:12 +0200 Subject: [PATCH 23/42] IPCProxyPlugIn/Host take ownership of its associated IPCConnection --- IPC/ARAIPCAudioUnit_v3.mm | 51 +++++++++++++++++++++++++-------------- IPC/ARAIPCProxyHost.cpp | 5 ++-- IPC/ARAIPCProxyHost.h | 4 ++- IPC/ARAIPCProxyPlugIn.h | 8 +++++- 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 08e4e04..3f228f4 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -85,6 +85,27 @@ void performPendingMainThreadTasks () } }; +class AUPlugInConnection : public AUConnection +{ +public: + using AUConnection::AUConnection; + + bool sendsHostMessages () const override + { + return true; + } +}; + +class AUHostConnection : public AUConnection +{ +public: + using AUConnection::AUConnection; + + bool sendsHostMessages () const override + { + return false; + } +}; // message channel base class for both proxy implementations class AudioUnitMessageChannel : public MessageChannel @@ -202,7 +223,7 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override // host side: proxy plug-in implementation #if !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY -class AUProxyPlugIn : public ProxyPlugIn, public AUConnection +class AUProxyPlugIn : public ProxyPlugIn { public: static AUProxyPlugIn* createWithAudioUnit (AUAudioUnit * _Nonnull audioUnit, @@ -226,28 +247,25 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override audioUnit, waitForMessageDelegate, delegateUserData }; } - ~AUProxyPlugIn () override + ~AUProxyPlugIn () { #if !__has_feature(objc_arc) [_initAU release]; #endif } - bool sendsHostMessages () const override { return true; } - private: AUProxyPlugIn (NSObject * _Nonnull mainChannel, NSObject * _Nonnull otherChannel, AUAudioUnit * _Nonnull initAU, ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, void * _Nullable delegateUserData) - : ProxyPlugIn { this }, - AUConnection { waitForMessageDelegate, delegateUserData }, + : ProxyPlugIn { std::make_unique (waitForMessageDelegate, delegateUserData) }, _initAU { initAU } { - setMainThreadChannel (new ProxyPlugInMessageChannel { mainChannel }); - setOtherThreadsChannel (new ProxyPlugInMessageChannel { otherChannel }); - setMessageHandler (ProxyPlugIn::handleReceivedMessage); + getConnection ()->setMainThreadChannel (new ProxyPlugInMessageChannel { mainChannel }); + getConnection ()->setOtherThreadsChannel (new ProxyPlugInMessageChannel { otherChannel }); + getConnection ()->setMessageHandler (ProxyPlugIn::handleReceivedMessage); #if !__has_feature(objc_arc) [_initAU retain]; #endif @@ -288,7 +306,7 @@ ARAIPCProxyPlugInRef ARA_CALL ARAIPCAUProxyPlugInInitialize (AUAudioUnit * _Nonn void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCProxyPlugInRef _Nonnull proxyPlugInRef) { - fromIPCRef (proxyPlugInRef)->performPendingMainThreadTasks (); + static_cast (fromIPCRef (proxyPlugInRef)->getConnection ())->performPendingMainThreadTasks (); } const ARAPlugInExtensionInstance * _Nonnull ARA_CALL ARAIPCAUProxyPlugInBindToDocumentController (AUAudioUnit * _Nonnull audioUnit, @@ -314,19 +332,16 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCProxyPlugInRef _Nonnull pro // plug-in side: proxy host C adapter #if !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY -class AUProxyHost : public ProxyHost, public AUConnection +class AUProxyHost : public ProxyHost { public: AUProxyHost () - : ProxyHost { this }, - AUConnection { nullptr, nullptr } + : ProxyHost { std::make_unique ( nullptr, nullptr ) } { ARAIPCProxyHostSetBindingHandler (handleBinding); - setMessageHandler ([this] (auto&& ...args) { handleReceivedMessage (args...); }); + getConnection ()->setMessageHandler ([this] (auto&& ...args) { handleReceivedMessage (args...); }); } - bool sendsHostMessages () const override { return false; } - private: static const ARAPlugInExtensionInstance * ARA_CALL handleBinding (ARAIPCPlugInInstanceRef plugInInstanceRef, ARADocumentControllerRef controllerRef, @@ -359,9 +374,9 @@ ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageCha auto result { new ProxyHostMessageChannel { audioUnitChannel } }; if (isMainThreadChannel) - _proxyHost->setMainThreadChannel (result); + _proxyHost->getConnection ()->setMainThreadChannel (result); else - _proxyHost->setOtherThreadsChannel (result); + _proxyHost->getConnection ()->setOtherThreadsChannel (result); return toIPCRef (result); } diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index 31d507f..7baf7e8 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -652,8 +652,9 @@ static const ARAFactory* getFactoryWithID (ARAPersistentID factoryID) /*******************************************************************************/ -ProxyHost::ProxyHost (Connection* connection) -: RemoteCaller (connection) +ProxyHost::ProxyHost (std::unique_ptr && connection) +: RemoteCaller { connection.get () }, + _connection { std::move (connection) } {} void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, diff --git a/IPC/ARAIPCProxyHost.h b/IPC/ARAIPCProxyHost.h index 68f119b..06f7271 100644 --- a/IPC/ARAIPCProxyHost.h +++ b/IPC/ARAIPCProxyHost.h @@ -42,11 +42,13 @@ extern "C" { class ProxyHost : public RemoteCaller { protected: - explicit ProxyHost (Connection* connection); + explicit ProxyHost (std::unique_ptr && connection); public: void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, MessageEncoder* const replyEncoder); +private: + const std::unique_ptr _connection; }; #endif diff --git a/IPC/ARAIPCProxyPlugIn.h b/IPC/ARAIPCProxyPlugIn.h index 53611ba..8e52b2e 100644 --- a/IPC/ARAIPCProxyPlugIn.h +++ b/IPC/ARAIPCProxyPlugIn.h @@ -43,10 +43,16 @@ extern "C" { class ProxyPlugIn : public RemoteCaller { public: - using RemoteCaller::RemoteCaller; + ProxyPlugIn (std::unique_ptr && connection) + : RemoteCaller (connection.get ()), + _connection (std::move (connection)) + {} static void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, MessageEncoder* const replyEncoder); + +private: + const std::unique_ptr _connection; }; #endif From 1c2146ad3b810fb0a8cd2a889b353fedf04d60fb Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:12 +0200 Subject: [PATCH 24/42] Simplify IPC debug logging --- IPC/ARAIPCAudioUnit_v3.mm | 26 ++------------------------ IPC/ARAIPCConnection.cpp | 10 ++++++++-- IPC/ARAIPCConnection.h | 7 +++++-- IPC/ARAIPCProxyHost.cpp | 4 +++- IPC/ARAIPCProxyPlugIn.cpp | 7 +++++++ IPC/ARAIPCProxyPlugIn.h | 5 +---- 6 files changed, 26 insertions(+), 33 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 3f228f4..1707c4a 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -85,28 +85,6 @@ void performPendingMainThreadTasks () } }; -class AUPlugInConnection : public AUConnection -{ -public: - using AUConnection::AUConnection; - - bool sendsHostMessages () const override - { - return true; - } -}; - -class AUHostConnection : public AUConnection -{ -public: - using AUConnection::AUConnection; - - bool sendsHostMessages () const override - { - return false; - } -}; - // message channel base class for both proxy implementations class AudioUnitMessageChannel : public MessageChannel { @@ -260,7 +238,7 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override AUAudioUnit * _Nonnull initAU, ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, void * _Nullable delegateUserData) - : ProxyPlugIn { std::make_unique (waitForMessageDelegate, delegateUserData) }, + : ProxyPlugIn { std::make_unique (waitForMessageDelegate, delegateUserData) }, _initAU { initAU } { getConnection ()->setMainThreadChannel (new ProxyPlugInMessageChannel { mainChannel }); @@ -336,7 +314,7 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCProxyPlugInRef _Nonnull pro { public: AUProxyHost () - : ProxyHost { std::make_unique ( nullptr, nullptr ) } + : ProxyHost { std::make_unique ( nullptr, nullptr ) } { ARAIPCProxyHostSetBindingHandler (handleBinding); getConnection ()->setMessageHandler ([this] (auto&& ...args) { handleReceivedMessage (args...); }); diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 6d67c6d..02c856e 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -39,8 +39,8 @@ #if ARA_ENABLE_DEBUG_OUTPUT && 0 #define ARA_IPC_LOG(...) ARA_LOG ("ARA IPC " __VA_ARGS__) #define ARA_IPC_DECODE_MESSAGE_FORMAT " %i=%s " - #define ARA_IPC_DECODE_SENT_MESSAGE_ARGS(messageID) messageID, (getConnection ()->sendsHostMessages ()) ? decodeHostMessageID (messageID) : decodePlugInMessageID (messageID) - #define ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS(messageID) messageID, (!getConnection ()->sendsHostMessages ()) ? decodeHostMessageID (messageID) : decodePlugInMessageID (messageID) + #define ARA_IPC_DECODE_SENT_MESSAGE_ARGS(messageID) messageID, (_debugAsHost) ? decodeHostMessageID (messageID) : decodePlugInMessageID (messageID) + #define ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS(messageID) messageID, (!_debugAsHost) ? decodeHostMessageID (messageID) : decodePlugInMessageID (messageID) #define ARA_IPC_LABEL_THREAD_FORMAT " %sthread %p" #define ARA_IPC_LABEL_THREAD_ARGS (getConnection ()->wasCreatedOnCurrentThread ()) ? "creation " : "other ", MessageDispatcher::getCurrentThread () #else @@ -134,6 +134,7 @@ namespace IPC { #define appendThreadRef appendSize #endif +static bool _debugAsHost {}; MessageDispatcher::MessageDispatcher (Connection* connection, MessageChannel* messageChannel) : _connection { connection }, @@ -592,6 +593,11 @@ void Connection::signalMesssageReceived () #endif } +void Connection::_setDebugMessageHint (bool isHost) +{ + _debugAsHost = isHost; +} + } // namespace IPC } // namespace ARA diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 117a86c..dc3c4df 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -151,8 +151,8 @@ class Connection //! in order to wake it up void signalMesssageReceived (); - //! intended for debug output only: helper to properly decode message IDs - virtual bool sendsHostMessages () const = 0; + // internal API: debug output helper + static void _setDebugMessageHint (bool isHost); #if defined (__APPLE__) private: @@ -239,6 +239,9 @@ class MessageDispatcher private: Connection* const _connection; MessageChannel* const _messageChannel; + +protected: + static bool _debugAsHost; }; //! single-threaded variant for main thread communication only diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index 7baf7e8..f9e8a55 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -655,7 +655,9 @@ static const ARAFactory* getFactoryWithID (ARAPersistentID factoryID) ProxyHost::ProxyHost (std::unique_ptr && connection) : RemoteCaller { connection.get () }, _connection { std::move (connection) } -{} +{ + Connection::_setDebugMessageHint (false); +} void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, MessageEncoder* const replyEncoder) diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp index 86d0b07..24ee738 100644 --- a/IPC/ARAIPCProxyPlugIn.cpp +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -1426,6 +1426,13 @@ void ARAIPCProxyPlugInUninitializeARA (ARAIPCProxyPlugInRef proxyPlugInRef, cons /*******************************************************************************/ +ProxyPlugIn::ProxyPlugIn (std::unique_ptr && connection) +: RemoteCaller (connection.get ()), + _connection (std::move (connection)) +{ + Connection::_setDebugMessageHint (true); +} + void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, MessageEncoder* const replyEncoder) { diff --git a/IPC/ARAIPCProxyPlugIn.h b/IPC/ARAIPCProxyPlugIn.h index 8e52b2e..3c34274 100644 --- a/IPC/ARAIPCProxyPlugIn.h +++ b/IPC/ARAIPCProxyPlugIn.h @@ -43,10 +43,7 @@ extern "C" { class ProxyPlugIn : public RemoteCaller { public: - ProxyPlugIn (std::unique_ptr && connection) - : RemoteCaller (connection.get ()), - _connection (std::move (connection)) - {} + ProxyPlugIn (std::unique_ptr && connection); static void handleReceivedMessage (const MessageID messageID, const MessageDecoder* const decoder, MessageEncoder* const replyEncoder); From a848766bf7e40d792c400a4209c7e941406daf9a Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:12 +0200 Subject: [PATCH 25/42] Make MessageHandler a creation parameter of IPC Connection --- IPC/ARAIPCAudioUnit_v3.mm | 6 ++---- IPC/ARAIPCConnection.cpp | 5 +++-- IPC/ARAIPCConnection.h | 11 +++-------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 1707c4a..4cf4cfa 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -238,12 +238,11 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override AUAudioUnit * _Nonnull initAU, ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, void * _Nullable delegateUserData) - : ProxyPlugIn { std::make_unique (waitForMessageDelegate, delegateUserData) }, + : ProxyPlugIn { std::make_unique (ProxyPlugIn::handleReceivedMessage, waitForMessageDelegate, delegateUserData) }, _initAU { initAU } { getConnection ()->setMainThreadChannel (new ProxyPlugInMessageChannel { mainChannel }); getConnection ()->setOtherThreadsChannel (new ProxyPlugInMessageChannel { otherChannel }); - getConnection ()->setMessageHandler (ProxyPlugIn::handleReceivedMessage); #if !__has_feature(objc_arc) [_initAU retain]; #endif @@ -314,10 +313,9 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCProxyPlugInRef _Nonnull pro { public: AUProxyHost () - : ProxyHost { std::make_unique ( nullptr, nullptr ) } + : ProxyHost { std::make_unique ( [this] (auto&& ...args) { handleReceivedMessage (args...); }, nullptr, nullptr ) } { ARAIPCProxyHostSetBindingHandler (handleBinding); - getConnection ()->setMessageHandler ([this] (auto&& ...args) { handleReceivedMessage (args...); }); } private: diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 02c856e..0985c80 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -475,8 +475,9 @@ void Connection::_performRunLoopSource (void* info) #endif -Connection::Connection (WaitForMessageDelegate waitForMessageDelegate, void* delegateUserData) -: _waitForMessageDelegate { waitForMessageDelegate }, +Connection::Connection (MessageHandler&& messageHandler, WaitForMessageDelegate waitForMessageDelegate, void* delegateUserData) +: _messageHandler { std::move (messageHandler) }, + _waitForMessageDelegate { waitForMessageDelegate }, _delegateUserData { delegateUserData }, #if __cplusplus >= 202002L _waitForMessageSemaphore { new std::binary_semaphore { 0 } }, diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index dc3c4df..977bf24 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -89,7 +89,7 @@ class Connection { public: using WaitForMessageDelegate = void (*) (void* delegateUserData); - explicit Connection (WaitForMessageDelegate waitForMessageDelegate, void* delegateUserData); + explicit Connection (MessageHandler&& messageHandler, WaitForMessageDelegate waitForMessageDelegate = nullptr, void* delegateUserData = nullptr); virtual ~Connection (); //! set the message channel for all main thread communication @@ -106,11 +106,6 @@ class Connection OtherThreadsMessageDispatcher* getOtherThreadsDispatcher () const { return _otherThreadsDispatcher; } - //! set the message handler for all communication on all threads - //! Must be done before sending or receiving the first message on any channel. - //! The connection does not take ownership of the message handler. - void setMessageHandler (MessageHandler&& messageHandler) { _messageHandler = std::move(messageHandler); } - const MessageHandler& getMessageHandler () const { return _messageHandler; } //! Reply Handler: a function passed to sendMessage () that is called to process the reply to a message @@ -160,12 +155,12 @@ class Connection #endif private: + const MessageHandler _messageHandler; const WaitForMessageDelegate _waitForMessageDelegate; void* const _delegateUserData; - void* const _waitForMessageSemaphore; // concrete type is platform-dependent + void* const _waitForMessageSemaphore; // concrete type is platform-dependent MainThreadMessageDispatcher* _mainThreadDispatcher {}; OtherThreadsMessageDispatcher* _otherThreadsDispatcher {}; - MessageHandler _messageHandler; std::thread::id const _creationThreadID; #if defined (_WIN32) HANDLE const _creationThreadHandle; From 75607b91c81667c75e180eab7d34c6db63c5f33e Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:12 +0200 Subject: [PATCH 26/42] Fix double delete of IPC Channel in AUv3 case --- IPC/ARAIPCAudioUnit_v3.h | 3 --- IPC/ARAIPCAudioUnit_v3.mm | 5 ----- 2 files changed, 8 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.h b/IPC/ARAIPCAudioUnit_v3.h index 633500c..5e57622 100644 --- a/IPC/ARAIPCAudioUnit_v3.h +++ b/IPC/ARAIPCAudioUnit_v3.h @@ -85,9 +85,6 @@ ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageCha //! plug-in side: implementation for AUMessageChannel -callAudioUnit: NSDictionary * _Nonnull ARA_CALL ARAIPCAUProxyHostCommandHandler(ARAIPCMessageChannelRef _Nonnull messageChannelRef, NSDictionary * _Nonnull message); -//! plug-in side: implementation for AUMessageChannel -dealloc -void ARA_CALL ARAIPCAUProxyHostUninitializeMessageChannel(ARAIPCMessageChannelRef _Nonnull messageChannelRef); - //! plug-in side: static cleanup upon shutdown void ARA_CALL ARAIPCAUProxyHostUninitialize(void); diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 4cf4cfa..5878fc2 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -364,11 +364,6 @@ ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageCha return [NSDictionary dictionary]; // \todo it would yield better performance if -callAudioUnit: would allow nil as return value } -void ARA_CALL ARAIPCAUProxyHostUninitializeMessageChannel (ARAIPCMessageChannelRef _Nonnull messageChannelRef) -{ - delete fromIPCRef (messageChannelRef); -} - void ARA_CALL ARAIPCAUProxyHostUninitialize (void) { if (_proxyHost) From f837b9a6aa4eca40f4349ef4c5c1644398a8d0ff Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:13 +0200 Subject: [PATCH 27/42] IPC code uses std::unique_ptr<> to express ownership --- IPC/ARAIPCAudioUnit_v3.mm | 26 ++++++------ IPC/ARAIPCCFEncoding.cpp | 16 +++----- IPC/ARAIPCCFEncoding.h | 18 +++++---- IPC/ARAIPCConnection.cpp | 83 +++++++++++++++++---------------------- IPC/ARAIPCConnection.h | 56 ++++++++++++-------------- IPC/ARAIPCEncoding.h | 21 +++++----- IPC/ARAIPCMessage.h | 9 +++-- 7 files changed, 105 insertions(+), 124 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 5878fc2..9db8665 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -68,9 +68,9 @@ public: using Connection::Connection; - MessageEncoder * createEncoder () override + std::unique_ptr createEncoder () override { - return new CFMessageEncoder {}; + return CFMessageEncoder::create (); } bool receiverEndianessMatches () override @@ -109,13 +109,13 @@ void routeReceivedMessage (NSDictionary * _Nonnull message) { ARA_INTERNAL_ASSERT (![NSThread isMainThread]); const MessageID messageID { [(NSNumber *) [message objectForKey:_messageIDKey] intValue] }; - const auto decoder { new CFMessageDecoder { (__bridge CFDictionaryRef) message } }; - getMessageDispatcher ()->routeReceivedMessage (messageID, decoder); + auto decoder { std::make_unique ((__bridge CFDictionaryRef) message) }; + getMessageDispatcher ()->routeReceivedMessage (messageID, std::move (decoder)); } - void sendMessage (MessageID messageID, MessageEncoder * encoder) override + void sendMessage (MessageID messageID, std::unique_ptr && encoder) override { - const auto dictionary { static_cast (encoder)->copyDictionary () }; + const auto dictionary { static_cast (encoder.get ())->copyDictionary () }; #if !__has_feature(objc_arc) auto message { (__bridge NSMutableDictionary *) dictionary }; #else @@ -241,8 +241,8 @@ void sendMessage (MessageID messageID, MessageEncoder * encoder) override : ProxyPlugIn { std::make_unique (ProxyPlugIn::handleReceivedMessage, waitForMessageDelegate, delegateUserData) }, _initAU { initAU } { - getConnection ()->setMainThreadChannel (new ProxyPlugInMessageChannel { mainChannel }); - getConnection ()->setOtherThreadsChannel (new ProxyPlugInMessageChannel { otherChannel }); + getConnection ()->setMainThreadChannel (std::make_unique (mainChannel)); + getConnection ()->setOtherThreadsChannel (std::make_unique (otherChannel)); #if !__has_feature(objc_arc) [_initAU retain]; #endif @@ -348,13 +348,15 @@ ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageCha else dispatch_sync (dispatch_get_main_queue (), createProxyIfNeeded); - auto result { new ProxyHostMessageChannel { audioUnitChannel } }; + auto channel { std::make_unique (audioUnitChannel) }; + const auto result { toIPCRef (channel.get ()) }; + if (isMainThreadChannel) - _proxyHost->getConnection ()->setMainThreadChannel (result); + _proxyHost->getConnection ()->setMainThreadChannel (std::move (channel)); else - _proxyHost->getConnection ()->setOtherThreadsChannel (result); + _proxyHost->getConnection ()->setOtherThreadsChannel (std::move (channel)); - return toIPCRef (result); + return result; } NSDictionary * _Nonnull ARA_CALL ARAIPCAUProxyHostCommandHandler (ARAIPCMessageChannelRef _Nonnull messageChannelRef, NSDictionary * _Nonnull message) diff --git a/IPC/ARAIPCCFEncoding.cpp b/IPC/ARAIPCCFEncoding.cpp index 99777b3..c33736c 100644 --- a/IPC/ARAIPCCFEncoding.cpp +++ b/IPC/ARAIPCCFEncoding.cpp @@ -131,9 +131,9 @@ void CFMessageEncoder::appendBytes (MessageArgumentKey argKey, const uint8_t * a CFRelease (argObject); } -MessageEncoder* CFMessageEncoder::appendSubMessage (MessageArgumentKey argKey) +std::unique_ptr CFMessageEncoder::appendSubMessage (MessageArgumentKey argKey) { - auto argObject = new CFMessageEncoder {}; + auto argObject { CFMessageEncoder::create () }; CFDictionarySetValue (_dictionary, _getEncodedKey (argKey), argObject->_dictionary); return argObject; } @@ -152,10 +152,6 @@ __attribute__((cf_returns_retained)) CFDataRef CFMessageEncoder::createMessageEn } -CFMessageDecoder::CFMessageDecoder (CFDictionaryRef dictionary) -: CFMessageDecoder::CFMessageDecoder (dictionary, true) -{} - CFMessageDecoder::CFMessageDecoder (CFDictionaryRef dictionary, bool retain) : _dictionary { dictionary } { @@ -296,13 +292,13 @@ void CFMessageDecoder::readBytes (MessageArgumentKey argKey, uint8_t* argValue) CFDataGetBytes (bytes, CFRangeMake (0, length), argValue); } -MessageDecoder* CFMessageDecoder::readSubMessage (MessageArgumentKey argKey) const +std::unique_ptr CFMessageDecoder::readSubMessage (MessageArgumentKey argKey) const { auto dictionary { (CFDictionaryRef) CFDictionaryGetValue (_dictionary, _getEncodedKey (argKey)) }; ARA_INTERNAL_ASSERT (!dictionary || (CFGetTypeID (dictionary) == CFDictionaryGetTypeID ())); if (dictionary == nullptr) return nullptr; - return new CFMessageDecoder { dictionary }; + return std::make_unique (dictionary); } bool CFMessageDecoder::hasDataForKey (MessageArgumentKey argKey) const @@ -310,14 +306,14 @@ bool CFMessageDecoder::hasDataForKey (MessageArgumentKey argKey) const return CFDictionaryContainsKey (_dictionary, _getEncodedKey (argKey)); } -CFMessageDecoder* CFMessageDecoder::createWithMessageData (CFDataRef messageData) +std::unique_ptr CFMessageDecoder::createWithMessageData (CFDataRef messageData) { if (CFDataGetLength (messageData) == 0) return nullptr; auto dictionary { (CFDictionaryRef) CFPropertyListCreateWithData (kCFAllocatorDefault, messageData, kCFPropertyListImmutable, nullptr, nullptr) }; ARA_INTERNAL_ASSERT (dictionary && (CFGetTypeID (dictionary) == CFDictionaryGetTypeID ())); - return new CFMessageDecoder { dictionary, false }; + return std::make_unique (dictionary, false); } #if defined (__APPLE__) diff --git a/IPC/ARAIPCCFEncoding.h b/IPC/ARAIPCCFEncoding.h index 05075c0..7a9e94b 100644 --- a/IPC/ARAIPCCFEncoding.h +++ b/IPC/ARAIPCCFEncoding.h @@ -25,6 +25,7 @@ #include +#include //! @addtogroup ARA_Library_IPC @@ -38,7 +39,6 @@ namespace IPC { class CFMessageEncoder : public MessageEncoder { public: - CFMessageEncoder (); ~CFMessageEncoder () override; void appendInt32 (MessageArgumentKey argKey, int32_t argValue) override; @@ -48,11 +48,16 @@ class CFMessageEncoder : public MessageEncoder void appendDouble (MessageArgumentKey argKey, double argValue) override; void appendString (MessageArgumentKey argKey, const char * argValue) override; void appendBytes (MessageArgumentKey argKey, const uint8_t * argValue, size_t argSize, bool copy) override; - MessageEncoder* appendSubMessage (MessageArgumentKey argKey) override; + std::unique_ptr appendSubMessage (MessageArgumentKey argKey) override; __attribute__((cf_returns_retained)) CFMutableDictionaryRef copyDictionary () const; __attribute__((cf_returns_retained)) CFDataRef createMessageEncoderData () const; + static std::unique_ptr create () { return std::unique_ptr { new CFMessageEncoder }; } + +private: + CFMessageEncoder (); + private: CFMutableDictionaryRef const _dictionary; }; @@ -61,7 +66,7 @@ class CFMessageEncoder : public MessageEncoder class CFMessageDecoder : public MessageDecoder { public: - explicit CFMessageDecoder (CFDictionaryRef dictionary); + CFMessageDecoder (CFDictionaryRef dictionary, bool retain = true); ~CFMessageDecoder () override; bool readInt32 (MessageArgumentKey argKey, int32_t* argValue) const override; @@ -72,13 +77,10 @@ class CFMessageDecoder : public MessageDecoder bool readString (MessageArgumentKey argKey, const char ** argValue) const override; bool readBytesSize (MessageArgumentKey argKey, size_t* argSize) const override; void readBytes (MessageArgumentKey argKey, uint8_t* argValue) const override; - MessageDecoder* readSubMessage (MessageArgumentKey argKey) const override; + std::unique_ptr readSubMessage (MessageArgumentKey argKey) const override; bool hasDataForKey (MessageArgumentKey argKey) const override; - static CFMessageDecoder* createWithMessageData (CFDataRef messageData); - -private: - CFMessageDecoder (CFDictionaryRef dictionary, bool retain); + static std::unique_ptr createWithMessageData (CFDataRef messageData); private: CFDictionaryRef const _dictionary; diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 0985c80..167dc8a 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -136,9 +136,9 @@ namespace IPC { static bool _debugAsHost {}; -MessageDispatcher::MessageDispatcher (Connection* connection, MessageChannel* messageChannel) +MessageDispatcher::MessageDispatcher (Connection* connection, std::unique_ptr && messageChannel) : _connection { connection }, - _messageChannel { messageChannel } + _messageChannel { std::move (messageChannel) } { static_assert (sizeof (std::thread::id) == sizeof (ThreadRef), "the current implementation relies on a specific thread ID size"); // unfortunately at least in clang std::thread::id's c'tor isn't constexpr @@ -148,11 +148,6 @@ MessageDispatcher::MessageDispatcher (Connection* connection, MessageChannel* me _messageChannel->setMessageDispatcher (this); } -MessageDispatcher::~MessageDispatcher () -{ - delete _messageChannel; -} - MessageDispatcher::ThreadRef MessageDispatcher::getCurrentThread () { const auto thisThread { std::this_thread::get_id () }; @@ -161,7 +156,7 @@ MessageDispatcher::ThreadRef MessageDispatcher::getCurrentThread () return result; } -void MessageDispatcher::_sendMessage (MessageID messageID, MessageEncoder* encoder, [[maybe_unused]] bool isNewTransaction) +void MessageDispatcher::_sendMessage (MessageID messageID, std::unique_ptr && encoder, [[maybe_unused]] bool isNewTransaction) { if (isReply (messageID)) ARA_IPC_LOG ("replies to message on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); @@ -170,37 +165,32 @@ void MessageDispatcher::_sendMessage (MessageID messageID, MessageEncoder* encod ARA_IPC_DECODE_SENT_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS, (isNewTransaction) ? " (new transaction)" : ""); - _messageChannel->sendMessage (messageID, encoder); - - delete encoder; + _messageChannel->sendMessage (messageID, std::move (encoder)); } -void MessageDispatcher::_handleReply (const MessageDecoder* decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) +void MessageDispatcher::_handleReply (std::unique_ptr && decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) { ARA_IPC_LOG ("handles reply on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); if (replyHandler) - (replyHandler) (decoder, replyHandlerUserData); + (replyHandler) (decoder.get (), replyHandlerUserData); else ARA_INTERNAL_ASSERT (!decoder || !decoder->hasDataForKey (0)); // replies should be empty when not handled (i.e. void functions) - delete decoder; } -MessageEncoder* MessageDispatcher::_handleReceivedMessage (MessageID messageID, const MessageDecoder* decoder) +std::unique_ptr MessageDispatcher::_handleReceivedMessage (MessageID messageID, std::unique_ptr && decoder) { ARA_INTERNAL_ASSERT (!isReply (messageID)); ARA_IPC_LOG ("handles" ARA_IPC_DECODE_MESSAGE_FORMAT "on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS); auto replyEncoder { _connection->createEncoder () }; - _connection->getMessageHandler () (messageID, decoder, replyEncoder); - - delete decoder; + _connection->getMessageHandler () (messageID, decoder.get (), replyEncoder.get ()); return replyEncoder; } -void MainThreadMessageDispatcher::sendMessage (MessageID messageID, MessageEncoder* encoder, +void MainThreadMessageDispatcher::sendMessage (MessageID messageID, std::unique_ptr && encoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); @@ -214,7 +204,7 @@ void MainThreadMessageDispatcher::sendMessage (MessageID messageID, MessageEncod if (isResponse) encoder->appendInt32 (kIsResponseKey, 1); - _sendMessage (messageID, encoder, !isResponse); + _sendMessage (messageID, std::move (encoder), !isResponse); while (previousPendingReplyHandler != _pendingReplyHandler) { @@ -230,24 +220,25 @@ void MainThreadMessageDispatcher::processPendingMessageIfNeeded () const auto pendingMessageDecoder { _pendingMessageDecoder.exchange (reinterpret_cast (_noPendingMessageDecoder), std::memory_order_acquire) }; if (pendingMessageDecoder != reinterpret_cast (_noPendingMessageDecoder)) { + std::unique_ptr ownedPendingMessageDecoder { pendingMessageDecoder }; const auto pendingMessageID { _pendingMessageID }; if (isReply (pendingMessageID)) { ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); - _handleReply (pendingMessageDecoder, _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); + _handleReply (std::move (ownedPendingMessageDecoder), _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; } else { ++_processingMessagesCount; - auto replyEncoder { _handleReceivedMessage (pendingMessageID, pendingMessageDecoder) }; + auto replyEncoder { _handleReceivedMessage (pendingMessageID, std::move (ownedPendingMessageDecoder)) }; --_processingMessagesCount; - _sendMessage (0, replyEncoder, false); + _sendMessage (0, std::move (replyEncoder), false); } } } -void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) +void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) { const auto isResponse { isReply (messageID) || // replies implicitly are responses (decoder && decoder->hasDataForKey (kIsResponseKey)) }; @@ -275,7 +266,7 @@ void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, con } _pendingMessageID = messageID; - _pendingMessageDecoder.store (decoder, std::memory_order_release); + _pendingMessageDecoder.store (decoder.release (), std::memory_order_release); getConnection ()->signalMesssageReceived (); if (processSynchronously) @@ -295,7 +286,7 @@ void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, con thread_local OtherThreadsMessageDispatcher::ThreadRef _remoteTargetThread { 0 }; thread_local const OtherThreadsMessageDispatcher::PendingReplyHandler* _pendingReplyHandler { nullptr }; -void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, MessageEncoder* encoder, +void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, std::unique_ptr && encoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) { const auto currentThread { getCurrentThread () }; @@ -305,7 +296,7 @@ void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, MessageEnc encoder->appendThreadRef (kReceiveThreadKey, _remoteTargetThread); _sendLock.lock (); - _sendMessage (messageID, encoder, !isResponse); + _sendMessage (messageID, std::move (encoder), !isResponse); _sendLock.unlock (); if (getConnection ()->wasCreatedOnCurrentThread ()) @@ -327,18 +318,18 @@ void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, MessageEnc { return _getRoutedMessageForThread (currentThread) != nullptr; }); RoutedMessage* message = _getRoutedMessageForThread (currentThread); const auto receivedMessageID { message->_messageID }; - const auto receivedDecoder { message->_decoder }; + auto receivedDecoder { std::move (message->_decoder) }; message->_targetThread = _invalidThread; lock.unlock (); if (isReply (receivedMessageID)) { - _handleReply (receivedDecoder, replyHandler, replyHandlerUserData); // will also delete receivedDecoder + _handleReply (std::move (receivedDecoder), replyHandler, replyHandlerUserData); break; } else { - _processReceivedMessage (receivedMessageID, receivedDecoder); // will also delete receivedDecoder + _processReceivedMessage (receivedMessageID, std::move (receivedDecoder)); } } } @@ -354,7 +345,7 @@ OtherThreadsMessageDispatcher::RoutedMessage* OtherThreadsMessageDispatcher::_ge return nullptr; } -void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) +void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) { ThreadRef targetThread; if (decoder->readThreadRef (kReceiveThreadKey, &targetThread)) @@ -366,14 +357,14 @@ void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, c { ARA_IPC_LOG ("processes reply on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); - _handleReply (decoder, _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); + _handleReply (std::move (decoder), _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; } else { ARA_IPC_LOG ("processes" ARA_IPC_DECODE_MESSAGE_FORMAT "on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS); - _processReceivedMessage (messageID, decoder); + _processReceivedMessage (messageID, std::move (decoder)); } } else @@ -392,7 +383,7 @@ void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, c message = &_routedMessages.back (); } message->_messageID = messageID; - message->_decoder = decoder; + message->_decoder = std::move (decoder); message->_targetThread = targetThread; _routeReceiveCondition.notify_all (); _routeLock.unlock (); @@ -401,11 +392,11 @@ void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, c else { ARA_INTERNAL_ASSERT (!isReply (messageID)); - _processReceivedMessage (messageID, decoder); + _processReceivedMessage (messageID, std::move (decoder)); } } -void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID, const MessageDecoder* decoder) +void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID, std::unique_ptr && decoder) { const auto previousRemoteTargetThread { _remoteTargetThread }; ThreadRef remoteTargetThread; @@ -414,12 +405,12 @@ void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID ARA_INTERNAL_ASSERT (remoteTargetThread != _invalidThread); _remoteTargetThread = remoteTargetThread; - auto replyEncoder { _handleReceivedMessage (messageID, decoder) }; + auto replyEncoder { _handleReceivedMessage (messageID, std::move (decoder)) }; replyEncoder->appendThreadRef (kReceiveThreadKey, remoteTargetThread); _sendLock.lock (); - _sendMessage (0, replyEncoder, false); + _sendMessage (0, std::move (replyEncoder), false); _sendLock.unlock (); _remoteTargetThread = previousRemoteTargetThread; @@ -507,8 +498,6 @@ Connection::~Connection () CFRunLoopRemoveSource (_creationThreadRunLoop, _runloopSource, kCFRunLoopCommonModes); CFRelease (_runloopSource); #endif - delete _otherThreadsDispatcher; - delete _mainThreadDispatcher; #if __cplusplus >= 202002L delete static_cast (_waitForMessageSemaphore); #elif defined (_WIN32) @@ -520,25 +509,25 @@ Connection::~Connection () #endif } -void Connection::setMainThreadChannel (MessageChannel* messageChannel) +void Connection::setMainThreadChannel (std::unique_ptr && messageChannel) { ARA_INTERNAL_ASSERT (_mainThreadDispatcher == nullptr); - _mainThreadDispatcher = new MainThreadMessageDispatcher { this, messageChannel }; + _mainThreadDispatcher = std::make_unique (this, std::move (messageChannel)); } -void Connection::setOtherThreadsChannel (MessageChannel* messageChannel) +void Connection::setOtherThreadsChannel (std::unique_ptr && messageChannel) { ARA_INTERNAL_ASSERT (_otherThreadsDispatcher == nullptr); - _otherThreadsDispatcher = new OtherThreadsMessageDispatcher { this, messageChannel }; + _otherThreadsDispatcher = std::make_unique (this, std::move (messageChannel)); } -void Connection::sendMessage (MessageID messageID, MessageEncoder* encoder, ReplyHandler replyHandler, void* replyHandlerUserData) +void Connection::sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler replyHandler, void* replyHandlerUserData) { ARA_INTERNAL_ASSERT ((_mainThreadDispatcher != nullptr) && (_otherThreadsDispatcher != nullptr) && (_messageHandler != nullptr)); if (wasCreatedOnCurrentThread ()) - _mainThreadDispatcher->sendMessage (messageID, encoder, replyHandler, replyHandlerUserData); + _mainThreadDispatcher->sendMessage (messageID, std::move (encoder), replyHandler, replyHandlerUserData); else - _otherThreadsDispatcher->sendMessage (messageID, encoder, replyHandler, replyHandlerUserData); + _otherThreadsDispatcher->sendMessage (messageID, std::move (encoder), replyHandler, replyHandlerUserData); } void Connection::dispatchToCreationThread (DispatchableFunction func) diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 977bf24..aab71a0 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -66,7 +67,7 @@ class MessageChannel virtual ~MessageChannel () = default; //! implemented by subclasses to perform the actual message (or reply) sending - virtual void sendMessage (MessageID messageID, MessageEncoder* encoder) = 0; + virtual void sendMessage (MessageID messageID, std::unique_ptr && encoder) = 0; void setMessageDispatcher (MessageDispatcher* messageDispatcher) { @@ -94,17 +95,15 @@ class Connection //! set the message channel for all main thread communication //! Must be done before sending or receiving the first message on any channel. - //! The connection takes ownership of the channel and deletes it upon teardown. - void setMainThreadChannel (MessageChannel* messageChannel); + void setMainThreadChannel (std::unique_ptr && messageChannel); - MainThreadMessageDispatcher* getMainThreadDispatcher () const { return _mainThreadDispatcher; } + MainThreadMessageDispatcher* getMainThreadDispatcher () const { return _mainThreadDispatcher.get (); } //! set the message channel for all non-main thread communication //! Must be done before sending or receiving the first message on any channel. - //! The connection takes ownership of the channel and deletes it upon teardown. - void setOtherThreadsChannel (MessageChannel* messageChannel); + void setOtherThreadsChannel (std::unique_ptr && messageChannel); - OtherThreadsMessageDispatcher* getOtherThreadsDispatcher () const { return _otherThreadsDispatcher; } + OtherThreadsMessageDispatcher* getOtherThreadsDispatcher () const { return _otherThreadsDispatcher.get (); } const MessageHandler& getMessageHandler () const { return _messageHandler; } @@ -113,17 +112,15 @@ class Connection using ReplyHandler = void (ARA_CALL *) (const MessageDecoder* decoder, void* userData); //! send an encoded messages to the receiving process - //! The encoder will be deleted after sending the message. //! If an empty reply ("void") is expected, the replyHandler should be nullptr. //! This method can be called from any thread, concurrent calls will be serialized. //! The calling thread will be blocked until the receiver has processed the message and //! returned a (potentially empty) reply, which will be forwarded to the replyHandler. - void sendMessage (MessageID messageID, MessageEncoder* encoder, + void sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler replyHandler, void* replyHandlerUserData); //! implemented by subclasses: generate an encoder to encode a new message, - //! later passed to sendMessage(), which will destroy the encoder after sending - virtual MessageEncoder* createEncoder () = 0; + virtual std::unique_ptr createEncoder () = 0; //! implemented by subclasses: indicate byte order mismatch between sending //! and receiving machine @@ -159,8 +156,8 @@ class Connection const WaitForMessageDelegate _waitForMessageDelegate; void* const _delegateUserData; void* const _waitForMessageSemaphore; // concrete type is platform-dependent - MainThreadMessageDispatcher* _mainThreadDispatcher {}; - OtherThreadsMessageDispatcher* _otherThreadsDispatcher {}; + std::unique_ptr _mainThreadDispatcher {}; + std::unique_ptr _otherThreadsDispatcher {}; std::thread::id const _creationThreadID; #if defined (_WIN32) HANDLE const _creationThreadHandle; @@ -193,25 +190,22 @@ class MessageDispatcher static constexpr ThreadRef _invalidThread { 0 }; public: - //! the dispatcher takes ownership of the channel and will delete it upon teardown - explicit MessageDispatcher (Connection* connection, MessageChannel* messageChannel); - virtual ~MessageDispatcher (); + explicit MessageDispatcher (Connection* connection, std::unique_ptr && messageChannel); + virtual ~MessageDispatcher () = default; Connection* getConnection () const { return _connection; } - MessageChannel* getMessageChannel () const { return _messageChannel; } + MessageChannel* getMessageChannel () const { return _messageChannel.get (); } //! send an encoded messages to the receiving process - //! The encoder will be deleted after sending the message. //! If an empty reply ("void") is expected, the replyHandler should be nullptr. //! This method can be called from any thread, concurrent calls will be serialized. //! The calling thread will be blocked until the receiver has processed the message and //! returned a (potentially empty) reply, which will be forwarded to the replyHandler. - virtual void sendMessage (MessageID messageID, MessageEncoder* encoder, + virtual void sendMessage (MessageID messageID, std::unique_ptr && encoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) = 0; //! route an incoming message to the correct target thread - //! takes ownership of the decoder and will eventually delete it - virtual void routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) = 0; + virtual void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) = 0; // protected: this does not work with thread_local... struct PendingReplyHandler @@ -226,14 +220,14 @@ class MessageDispatcher static ThreadRef getCurrentThread (); protected: - void _sendMessage (MessageID messageID, MessageEncoder* encoder, bool isNewTransaction); + void _sendMessage (MessageID messageID, std::unique_ptr && encoder, bool isNewTransaction); bool _waitForMessage (); - void _handleReply (const MessageDecoder* decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData); - MessageEncoder* _handleReceivedMessage (MessageID messageID, const MessageDecoder* decoder); + void _handleReply (std::unique_ptr && decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData); + std::unique_ptr _handleReceivedMessage (MessageID messageID, std::unique_ptr && decoder); private: Connection* const _connection; - MessageChannel* const _messageChannel; + const std::unique_ptr _messageChannel; protected: static bool _debugAsHost; @@ -245,10 +239,10 @@ class MainThreadMessageDispatcher : public MessageDispatcher public: using MessageDispatcher::MessageDispatcher; - void sendMessage (MessageID messageID, MessageEncoder* encoder, + void sendMessage (MessageID messageID, std::unique_ptr && encoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) override; - void routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) override; + void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) override; void processPendingMessageIfNeeded (); @@ -277,10 +271,10 @@ class OtherThreadsMessageDispatcher : public MessageDispatcher public: using MessageDispatcher::MessageDispatcher; - void sendMessage (MessageID messageID, MessageEncoder* encoder, + void sendMessage (MessageID messageID, std::unique_ptr && encoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) override; - void routeReceivedMessage (MessageID messageID, const MessageDecoder* decoder) override; + void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) override; private: // keys to store the threading information in the IPC messages @@ -290,12 +284,12 @@ class OtherThreadsMessageDispatcher : public MessageDispatcher struct RoutedMessage { MessageID _messageID { 0 }; - const MessageDecoder* _decoder { nullptr }; + std::unique_ptr _decoder; ThreadRef _targetThread { _invalidThread }; }; RoutedMessage* _getRoutedMessageForThread (ThreadRef thread); - void _processReceivedMessage (MessageID messageID, const MessageDecoder* decoder); + void _processReceivedMessage (MessageID messageID, std::unique_ptr && decoder); private: // incoming data is stored in _routedMessages by the receive handler for the diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 8e4b55c..64ea8df 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -328,8 +328,7 @@ struct _CompoundValueEncoderBase { auto subEncoder { encoder->appendSubMessage (argKey) }; ARA_INTERNAL_ASSERT (subEncoder != nullptr); - _ValueEncoder::encode (subEncoder, argValue); - delete subEncoder; + _ValueEncoder::encode (subEncoder.get (), argValue); } }; template @@ -340,8 +339,7 @@ struct _CompoundValueDecoderBase auto subDecoder { decoder->readSubMessage (argKey) }; if (subDecoder == nullptr) return false; - const auto success { _ValueDecoder::decode (result, subDecoder) }; - delete subDecoder; + const auto success { _ValueDecoder::decode (result, subDecoder.get ()) }; return success; } }; @@ -499,11 +497,10 @@ template<> struct _ValueDecoder : public _CompoundValueDecoderBase> cache; \ - success &= _ValueDecoder::decode (cache, subDecoder_##member); \ + success &= _ValueDecoder::decode (cache, subDecoder_##member.get ()); \ ARA_INTERNAL_ASSERT (success); \ result.member = &cache; \ { OPTIONAL_UPDATE_MACRO } \ - delete subDecoder_##member; \ } \ else { \ result.member = nullptr; \ @@ -1350,34 +1347,34 @@ class RemoteCaller void remoteCall (const MethodID methodID, const Args &... args) const { auto encoder { _connection->createEncoder () }; - encodeArguments (encoder, args...); - _connection->sendMessage (methodID.getMessageID (), encoder, nullptr, nullptr); + encodeArguments (encoder.get (), args...); + _connection->sendMessage (methodID.getMessageID (), std::move (encoder), nullptr, nullptr); } template void remoteCall (RetT& result, const MethodID methodID, const Args &... args) const { auto encoder { _connection->createEncoder () }; - encodeArguments (encoder, args...); + encodeArguments (encoder.get (), args...); const auto replyHandler { [] (const MessageDecoder* decoder, void* userData) -> void { ARA_INTERNAL_ASSERT (decoder); decodeReply (*reinterpret_cast (userData), decoder); } }; - _connection->sendMessage (methodID.getMessageID (), encoder, replyHandler, &result); + _connection->sendMessage (methodID.getMessageID (), std::move (encoder), replyHandler, &result); } template void remoteCall (CustomDecodeFunction& decodeFunction, const MethodID methodID, const Args &... args) const { auto encoder { _connection->createEncoder () }; - encodeArguments (encoder, args...); + encodeArguments (encoder.get (), args...); const auto replyHandler { [] (const MessageDecoder* decoder, void* userData) -> void { ARA_INTERNAL_ASSERT (decoder); (*reinterpret_cast (userData)) (decoder); } }; - _connection->sendMessage (methodID.getMessageID (), encoder, replyHandler, &decodeFunction); + _connection->sendMessage (methodID.getMessageID (), std::move (encoder), replyHandler, &decodeFunction); } bool receiverEndianessMatches () const { return _connection->receiverEndianessMatches (); } diff --git a/IPC/ARAIPCMessage.h b/IPC/ARAIPCMessage.h index 2e4bc4e..9b3bf54 100644 --- a/IPC/ARAIPCMessage.h +++ b/IPC/ARAIPCMessage.h @@ -24,6 +24,9 @@ #if ARA_ENABLE_IPC +#include + + //! @addtogroup ARA_Library_IPC //! @{ @@ -69,8 +72,7 @@ class MessageEncoder virtual void appendBytes (MessageArgumentKey argKey, const uint8_t* argValue, size_t argSize, bool copy) = 0; //! sub-messages to encode compound types - //! The caller is responsible for deleting the returned sub-encoder after use. - virtual MessageEncoder* appendSubMessage (MessageArgumentKey argKey) = 0; + virtual std::unique_ptr appendSubMessage (MessageArgumentKey argKey) = 0; }; //! @} @@ -109,8 +111,7 @@ class MessageDecoder //! sub-messages to decode compound types //! returns nullptr if key not found or if the value for the key is not representing a sub-message - //! The caller is responsible for deleting the encoder after use. - virtual MessageDecoder* readSubMessage (MessageArgumentKey argKey) const = 0; + virtual std::unique_ptr readSubMessage (MessageArgumentKey argKey) const = 0; //! test whether a given argument is present in the message virtual bool hasDataForKey (MessageArgumentKey argKey) const = 0; From 438877f61cc7bdfa0edd2debed694d3a40eccb3d Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:13 +0200 Subject: [PATCH 28/42] IPC Connection encapsulates WaitForMessageDelegate as std::function<> --- IPC/ARAIPCAudioUnit_v3.mm | 5 +++-- IPC/ARAIPCConnection.cpp | 7 +++---- IPC/ARAIPCConnection.h | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 9db8665..fc9a82d 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -238,7 +238,8 @@ void sendMessage (MessageID messageID, std::unique_ptr && encode AUAudioUnit * _Nonnull initAU, ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, void * _Nullable delegateUserData) - : ProxyPlugIn { std::make_unique (ProxyPlugIn::handleReceivedMessage, waitForMessageDelegate, delegateUserData) }, + : ProxyPlugIn { std::make_unique (ProxyPlugIn::handleReceivedMessage, + (waitForMessageDelegate) ? [waitForMessageDelegate, delegateUserData] { waitForMessageDelegate(delegateUserData); } : Connection::WaitForMessageDelegate {}) }, _initAU { initAU } { getConnection ()->setMainThreadChannel (std::make_unique (mainChannel)); @@ -313,7 +314,7 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCProxyPlugInRef _Nonnull pro { public: AUProxyHost () - : ProxyHost { std::make_unique ( [this] (auto&& ...args) { handleReceivedMessage (args...); }, nullptr, nullptr ) } + : ProxyHost { std::make_unique ( [this] (auto&& ...args) { handleReceivedMessage (args...); }) } { ARAIPCProxyHostSetBindingHandler (handleBinding); } diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 167dc8a..c0b02fc 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -466,10 +466,9 @@ void Connection::_performRunLoopSource (void* info) #endif -Connection::Connection (MessageHandler&& messageHandler, WaitForMessageDelegate waitForMessageDelegate, void* delegateUserData) +Connection::Connection (MessageHandler&& messageHandler, WaitForMessageDelegate && waitForMessageDelegate) : _messageHandler { std::move (messageHandler) }, - _waitForMessageDelegate { waitForMessageDelegate }, - _delegateUserData { delegateUserData }, + _waitForMessageDelegate { std::move (waitForMessageDelegate) }, #if __cplusplus >= 202002L _waitForMessageSemaphore { new std::binary_semaphore { 0 } }, #elif defined (_WIN32) @@ -565,7 +564,7 @@ bool Connection::waitForMessageOnCreationThread () return true; if (_waitForMessageDelegate) - _waitForMessageDelegate (_delegateUserData); + _waitForMessageDelegate (); return false; } diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index aab71a0..0954933 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -89,8 +89,8 @@ class MessageChannel class Connection { public: - using WaitForMessageDelegate = void (*) (void* delegateUserData); - explicit Connection (MessageHandler&& messageHandler, WaitForMessageDelegate waitForMessageDelegate = nullptr, void* delegateUserData = nullptr); + using WaitForMessageDelegate = std::function; + explicit Connection (MessageHandler&& messageHandler, WaitForMessageDelegate && waitForMessageDelegate = {}); virtual ~Connection (); //! set the message channel for all main thread communication @@ -154,7 +154,6 @@ class Connection private: const MessageHandler _messageHandler; const WaitForMessageDelegate _waitForMessageDelegate; - void* const _delegateUserData; void* const _waitForMessageSemaphore; // concrete type is platform-dependent std::unique_ptr _mainThreadDispatcher {}; std::unique_ptr _otherThreadsDispatcher {}; From 7a47da53051b93369adc7e8d7033ffd81678556e Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:13 +0200 Subject: [PATCH 29/42] Instead of subclassing, provide IPC Connection delegate data as construction parameter --- IPC/ARAIPCAudioUnit_v3.mm | 39 +++++++++++---------------------------- IPC/ARAIPCConnection.cpp | 7 +++++-- IPC/ARAIPCConnection.h | 17 ++++++++++------- 3 files changed, 26 insertions(+), 37 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index fc9a82d..9f3f30b 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -62,29 +62,6 @@ constexpr NSString * _messageIDKey { @"msgID" }; -// connection implementation for both proxy implementations -class AUConnection : public Connection -{ -public: - using Connection::Connection; - - std::unique_ptr createEncoder () override - { - return CFMessageEncoder::create (); - } - - bool receiverEndianessMatches () override - { - // \todo shouldn't the AUMessageChannel provide this information? - return true; - } - - void performPendingMainThreadTasks () - { - getMainThreadDispatcher ()->processPendingMessageIfNeeded (); - } -}; - // message channel base class for both proxy implementations class AudioUnitMessageChannel : public MessageChannel { @@ -238,8 +215,12 @@ void sendMessage (MessageID messageID, std::unique_ptr && encode AUAudioUnit * _Nonnull initAU, ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, void * _Nullable delegateUserData) - : ProxyPlugIn { std::make_unique (ProxyPlugIn::handleReceivedMessage, - (waitForMessageDelegate) ? [waitForMessageDelegate, delegateUserData] { waitForMessageDelegate(delegateUserData); } : Connection::WaitForMessageDelegate {}) }, + : ProxyPlugIn { std::make_unique (&CFMessageEncoder::create, + ProxyPlugIn::handleReceivedMessage, + true, // \todo shouldn't the AUMessageChannel provide this information? + (waitForMessageDelegate) ? + [waitForMessageDelegate, delegateUserData] { waitForMessageDelegate(delegateUserData); } : + Connection::WaitForMessageDelegate {}) }, _initAU { initAU } { getConnection ()->setMainThreadChannel (std::make_unique (mainChannel)); @@ -284,7 +265,7 @@ ARAIPCProxyPlugInRef ARA_CALL ARAIPCAUProxyPlugInInitialize (AUAudioUnit * _Nonn void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCProxyPlugInRef _Nonnull proxyPlugInRef) { - static_cast (fromIPCRef (proxyPlugInRef)->getConnection ())->performPendingMainThreadTasks (); + fromIPCRef (proxyPlugInRef)->getConnection ()->getMainThreadDispatcher ()->processPendingMessageIfNeeded (); } const ARAPlugInExtensionInstance * _Nonnull ARA_CALL ARAIPCAUProxyPlugInBindToDocumentController (AUAudioUnit * _Nonnull audioUnit, @@ -314,7 +295,9 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCProxyPlugInRef _Nonnull pro { public: AUProxyHost () - : ProxyHost { std::make_unique ( [this] (auto&& ...args) { handleReceivedMessage (args...); }) } + : ProxyHost { std::make_unique (&CFMessageEncoder::create, + [this] (auto&& ...args) { handleReceivedMessage (args...); }, + true) } // \todo shouldn't the AUMessageChannel provide this information? { ARAIPCProxyHostSetBindingHandler (handleBinding); } @@ -343,7 +326,7 @@ ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageCha if (!_proxyHost) _proxyHost = new AUProxyHost {}; }}; - + if ([NSThread isMainThread]) createProxyIfNeeded (); else diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index c0b02fc..e140d4f 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -466,8 +466,11 @@ void Connection::_performRunLoopSource (void* info) #endif -Connection::Connection (MessageHandler&& messageHandler, WaitForMessageDelegate && waitForMessageDelegate) -: _messageHandler { std::move (messageHandler) }, +Connection::Connection (MessageEncoderFactory && messageEncoderFactory, MessageHandler&& messageHandler, + bool receiverEndianessMatches, WaitForMessageDelegate && waitForMessageDelegate) +: _messageEncoderFactory { std::move (messageEncoderFactory) }, + _messageHandler { std::move (messageHandler) }, + _receiverEndianessMatches { receiverEndianessMatches }, _waitForMessageDelegate { std::move (waitForMessageDelegate) }, #if __cplusplus >= 202002L _waitForMessageSemaphore { new std::binary_semaphore { 0 } }, diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 0954933..c60b767 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -89,9 +89,11 @@ class MessageChannel class Connection { public: + using MessageEncoderFactory = std::function ()>; using WaitForMessageDelegate = std::function; - explicit Connection (MessageHandler&& messageHandler, WaitForMessageDelegate && waitForMessageDelegate = {}); - virtual ~Connection (); + Connection (MessageEncoderFactory && messageEncoderFactory, MessageHandler && messageHandler, + bool receiverEndianessMatches, WaitForMessageDelegate && waitForMessageDelegate = {}); + ~Connection (); //! set the message channel for all main thread communication //! Must be done before sending or receiving the first message on any channel. @@ -119,12 +121,11 @@ class Connection void sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler replyHandler, void* replyHandlerUserData); - //! implemented by subclasses: generate an encoder to encode a new message, - virtual std::unique_ptr createEncoder () = 0; + //! message encoder factory for users of the connection + std::unique_ptr createEncoder () { return _messageEncoderFactory (); } - //! implemented by subclasses: indicate byte order mismatch between sending - //! and receiving machine - virtual bool receiverEndianessMatches () = 0; + //! indicate byte order mismatch between sending and receiving machine + bool receiverEndianessMatches () const { return _receiverEndianessMatches; } bool wasCreatedOnCurrentThread () const { return std::this_thread::get_id () == _creationThreadID; } @@ -152,7 +153,9 @@ class Connection #endif private: + const MessageEncoderFactory _messageEncoderFactory; const MessageHandler _messageHandler; + const bool _receiverEndianessMatches; const WaitForMessageDelegate _waitForMessageDelegate; void* const _waitForMessageSemaphore; // concrete type is platform-dependent std::unique_ptr _mainThreadDispatcher {}; From 02e5af9d0aa7945882c0c21e9f848948dfcd6508 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:14 +0200 Subject: [PATCH 30/42] IPC ReplyHandler encapsulated into std::function<> --- IPC/ARAIPCConnection.cpp | 24 ++++++++++++------------ IPC/ARAIPCConnection.h | 24 +++++++++++------------- IPC/ARAIPCEncoding.h | 26 +++++++++++++------------- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index e140d4f..e4be36d 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -168,11 +168,11 @@ void MessageDispatcher::_sendMessage (MessageID messageID, std::unique_ptrsendMessage (messageID, std::move (encoder)); } -void MessageDispatcher::_handleReply (std::unique_ptr && decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData) +void MessageDispatcher::_handleReply (std::unique_ptr && decoder, ReplyHandler && replyHandler) { ARA_IPC_LOG ("handles reply on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); if (replyHandler) - (replyHandler) (decoder.get (), replyHandlerUserData); + replyHandler (decoder.get ()); else ARA_INTERNAL_ASSERT (!decoder || !decoder->hasDataForKey (0)); // replies should be empty when not handled (i.e. void functions) } @@ -191,13 +191,13 @@ std::unique_ptr MessageDispatcher::_handleReceivedMessage (Messa void MainThreadMessageDispatcher::sendMessage (MessageID messageID, std::unique_ptr && encoder, - Connection::ReplyHandler replyHandler, void* replyHandlerUserData) + ReplyHandler && replyHandler) { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); ARA_INTERNAL_ASSERT (!isReply (messageID)); const auto previousPendingReplyHandler { _pendingReplyHandler }; - const PendingReplyHandler pendingReplyHandler { replyHandler, replyHandlerUserData, previousPendingReplyHandler }; + const PendingReplyHandler pendingReplyHandler { &replyHandler, previousPendingReplyHandler }; _pendingReplyHandler = &pendingReplyHandler; const auto isResponse { _processingMessagesCount > 0 }; @@ -225,7 +225,7 @@ void MainThreadMessageDispatcher::processPendingMessageIfNeeded () if (isReply (pendingMessageID)) { ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); - _handleReply (std::move (ownedPendingMessageDecoder), _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); + _handleReply (std::move (ownedPendingMessageDecoder), std::move (*_pendingReplyHandler->_replyHandler)); _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; } else @@ -287,7 +287,7 @@ thread_local OtherThreadsMessageDispatcher::ThreadRef _remoteTargetThread { 0 }; thread_local const OtherThreadsMessageDispatcher::PendingReplyHandler* _pendingReplyHandler { nullptr }; void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, std::unique_ptr && encoder, - Connection::ReplyHandler replyHandler, void* replyHandlerUserData) + ReplyHandler && replyHandler) { const auto currentThread { getCurrentThread () }; encoder->appendThreadRef (kSendThreadKey, currentThread); @@ -302,7 +302,7 @@ void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, std::uniqu if (getConnection ()->wasCreatedOnCurrentThread ()) { const auto previousPendingReplyHandler { _pendingReplyHandler }; - const PendingReplyHandler pendingReplyHandler { replyHandler, replyHandlerUserData, previousPendingReplyHandler }; + const PendingReplyHandler pendingReplyHandler { &replyHandler, previousPendingReplyHandler }; _pendingReplyHandler = &pendingReplyHandler; do { @@ -324,7 +324,7 @@ void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, std::uniqu if (isReply (receivedMessageID)) { - _handleReply (std::move (receivedDecoder), replyHandler, replyHandlerUserData); + _handleReply (std::move (receivedDecoder), std::move (replyHandler)); break; } else @@ -357,7 +357,7 @@ void OtherThreadsMessageDispatcher::routeReceivedMessage (MessageID messageID, s { ARA_IPC_LOG ("processes reply on" ARA_IPC_LABEL_THREAD_FORMAT, ARA_IPC_LABEL_THREAD_ARGS); ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); - _handleReply (std::move (decoder), _pendingReplyHandler->_replyHandler, _pendingReplyHandler->_replyHandlerUserData); + _handleReply (std::move (decoder), std::move (*_pendingReplyHandler->_replyHandler)); _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; } else @@ -523,13 +523,13 @@ void Connection::setOtherThreadsChannel (std::unique_ptr && mess _otherThreadsDispatcher = std::make_unique (this, std::move (messageChannel)); } -void Connection::sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler replyHandler, void* replyHandlerUserData) +void Connection::sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler && replyHandler) { ARA_INTERNAL_ASSERT ((_mainThreadDispatcher != nullptr) && (_otherThreadsDispatcher != nullptr) && (_messageHandler != nullptr)); if (wasCreatedOnCurrentThread ()) - _mainThreadDispatcher->sendMessage (messageID, std::move (encoder), replyHandler, replyHandlerUserData); + _mainThreadDispatcher->sendMessage (messageID, std::move (encoder), std::move (replyHandler)); else - _otherThreadsDispatcher->sendMessage (messageID, std::move (encoder), replyHandler, replyHandlerUserData); + _otherThreadsDispatcher->sendMessage (messageID, std::move (encoder), std::move (replyHandler)); } void Connection::dispatchToCreationThread (DispatchableFunction func) diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index c60b767..a59399f 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -58,6 +58,10 @@ class OtherThreadsMessageDispatcher; using MessageHandler = std::function; +//! Reply Handler: a function passed to sendMessage() that is called to process the reply to a message +//! decoder will be nullptr if incoming message was empty +using ReplyHandler = std::function; + //! IPC message channel: primitive for sending and receiving messages //! @{ @@ -109,17 +113,12 @@ class Connection const MessageHandler& getMessageHandler () const { return _messageHandler; } - //! Reply Handler: a function passed to sendMessage () that is called to process the reply to a message - //! decoder will be nullptr if incoming message was empty - using ReplyHandler = void (ARA_CALL *) (const MessageDecoder* decoder, void* userData); - //! send an encoded messages to the receiving process - //! If an empty reply ("void") is expected, the replyHandler should be nullptr. + //! If an empty reply ("void") is expected, the replyHandler shall be omitted. //! This method can be called from any thread, concurrent calls will be serialized. //! The calling thread will be blocked until the receiver has processed the message and //! returned a (potentially empty) reply, which will be forwarded to the replyHandler. - void sendMessage (MessageID messageID, std::unique_ptr && encoder, - ReplyHandler replyHandler, void* replyHandlerUserData); + void sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler && replyHandler = {}); //! message encoder factory for users of the connection std::unique_ptr createEncoder () { return _messageEncoderFactory (); } @@ -204,7 +203,7 @@ class MessageDispatcher //! The calling thread will be blocked until the receiver has processed the message and //! returned a (potentially empty) reply, which will be forwarded to the replyHandler. virtual void sendMessage (MessageID messageID, std::unique_ptr && encoder, - Connection::ReplyHandler replyHandler, void* replyHandlerUserData) = 0; + ReplyHandler && replyHandler) = 0; //! route an incoming message to the correct target thread virtual void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) = 0; @@ -212,8 +211,7 @@ class MessageDispatcher // protected: this does not work with thread_local... struct PendingReplyHandler { - Connection::ReplyHandler _replyHandler; - void* _replyHandlerUserData; + ReplyHandler* const _replyHandler; const PendingReplyHandler* _prevPendingReplyHandler; }; @@ -224,7 +222,7 @@ class MessageDispatcher protected: void _sendMessage (MessageID messageID, std::unique_ptr && encoder, bool isNewTransaction); bool _waitForMessage (); - void _handleReply (std::unique_ptr && decoder, Connection::ReplyHandler replyHandler, void* replyHandlerUserData); + void _handleReply (std::unique_ptr && decoder, ReplyHandler && replyHandler); std::unique_ptr _handleReceivedMessage (MessageID messageID, std::unique_ptr && decoder); private: @@ -242,7 +240,7 @@ class MainThreadMessageDispatcher : public MessageDispatcher using MessageDispatcher::MessageDispatcher; void sendMessage (MessageID messageID, std::unique_ptr && encoder, - Connection::ReplyHandler replyHandler, void* replyHandlerUserData) override; + ReplyHandler && replyHandler) override; void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) override; @@ -274,7 +272,7 @@ class OtherThreadsMessageDispatcher : public MessageDispatcher using MessageDispatcher::MessageDispatcher; void sendMessage (MessageID messageID, std::unique_ptr && encoder, - Connection::ReplyHandler replyHandler, void* replyHandlerUserData) override; + ReplyHandler && replyHandler) override; void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) override; diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 64ea8df..67a6c63 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -1348,7 +1348,7 @@ class RemoteCaller { auto encoder { _connection->createEncoder () }; encodeArguments (encoder.get (), args...); - _connection->sendMessage (methodID.getMessageID (), std::move (encoder), nullptr, nullptr); + _connection->sendMessage (methodID.getMessageID (), std::move (encoder)); } template @@ -1356,12 +1356,12 @@ class RemoteCaller { auto encoder { _connection->createEncoder () }; encodeArguments (encoder.get (), args...); - const auto replyHandler { [] (const MessageDecoder* decoder, void* userData) -> void - { - ARA_INTERNAL_ASSERT (decoder); - decodeReply (*reinterpret_cast (userData), decoder); - } }; - _connection->sendMessage (methodID.getMessageID (), std::move (encoder), replyHandler, &result); + _connection->sendMessage (methodID.getMessageID (), std::move (encoder), + [&result] (const MessageDecoder* decoder) + { + ARA_INTERNAL_ASSERT (decoder); + decodeReply (result, decoder); + }); } template @@ -1369,12 +1369,12 @@ class RemoteCaller { auto encoder { _connection->createEncoder () }; encodeArguments (encoder.get (), args...); - const auto replyHandler { [] (const MessageDecoder* decoder, void* userData) -> void - { - ARA_INTERNAL_ASSERT (decoder); - (*reinterpret_cast (userData)) (decoder); - } }; - _connection->sendMessage (methodID.getMessageID (), std::move (encoder), replyHandler, &decodeFunction); + _connection->sendMessage (methodID.getMessageID (), std::move (encoder), + [&decodeFunction] (const MessageDecoder* decoder) + { + ARA_INTERNAL_ASSERT (decoder); + decodeFunction (decoder); + }); } bool receiverEndianessMatches () const { return _connection->receiverEndianessMatches (); } From 181f67eae59d2b9eeef4ec0c7b2b9e9aff09e941 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:14 +0200 Subject: [PATCH 31/42] Made IPC MessageDispatcher and its subclasses private details of Connection implementation --- IPC/ARAIPCAudioUnit_v3.mm | 10 +-- IPC/ARAIPCConnection.cpp | 131 ++++++++++++++++++++++++++++++++- IPC/ARAIPCConnection.h | 151 +++----------------------------------- 3 files changed, 146 insertions(+), 146 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 9f3f30b..31a6521 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -82,12 +82,12 @@ #endif public: - void routeReceivedMessage (NSDictionary * _Nonnull message) + void routeReceivedDictionary (NSDictionary * _Nonnull message) { ARA_INTERNAL_ASSERT (![NSThread isMainThread]); const MessageID messageID { [(NSNumber *) [message objectForKey:_messageIDKey] intValue] }; auto decoder { std::make_unique ((__bridge CFDictionaryRef) message) }; - getMessageDispatcher ()->routeReceivedMessage (messageID, std::move (decoder)); + routeReceivedMessage (messageID, std::move (decoder)); } void sendMessage (MessageID messageID, std::unique_ptr && encoder) override @@ -150,7 +150,7 @@ void sendMessage (MessageID messageID, std::unique_ptr && encode _audioUnitChannel.callHostBlock = ^NSDictionary * _Nullable (NSDictionary * _Nonnull message) { - routeReceivedMessage (message); + routeReceivedDictionary (message); return [NSDictionary dictionary]; // \todo it would yield better performance if the callHostBlock would allow nil as return value }; @@ -265,7 +265,7 @@ ARAIPCProxyPlugInRef ARA_CALL ARAIPCAUProxyPlugInInitialize (AUAudioUnit * _Nonn void ARA_CALL ARAIPCAUProxyPlugInPerformPendingMainThreadTasks (ARAIPCProxyPlugInRef _Nonnull proxyPlugInRef) { - fromIPCRef (proxyPlugInRef)->getConnection ()->getMainThreadDispatcher ()->processPendingMessageIfNeeded (); + fromIPCRef (proxyPlugInRef)->getConnection ()->processPendingMessageOnCreationThreadIfNeeded (); } const ARAPlugInExtensionInstance * _Nonnull ARA_CALL ARAIPCAUProxyPlugInBindToDocumentController (AUAudioUnit * _Nonnull audioUnit, @@ -346,7 +346,7 @@ ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageCha NSDictionary * _Nonnull ARA_CALL ARAIPCAUProxyHostCommandHandler (ARAIPCMessageChannelRef _Nonnull messageChannelRef, NSDictionary * _Nonnull message) { if ([message count]) // \todo filter dummy message sent in ProxyPlugInMessageChannel(), see there - static_cast (fromIPCRef (messageChannelRef))->routeReceivedMessage (message); + static_cast (fromIPCRef (messageChannelRef))->routeReceivedDictionary (message); return [NSDictionary dictionary]; // \todo it would yield better performance if -callAudioUnit: would allow nil as return value } diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index e4be36d..00b028f 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -136,6 +136,50 @@ namespace IPC { static bool _debugAsHost {}; +//------------------------------------------------------------------------------ + +class MessageDispatcher +{ +public: // needs to be public for thread-local variables (which cannot be class members) +#if defined (_WIN32) + using ThreadRef = int32_t; +#elif defined (__APPLE__) + using ThreadRef = size_t; +#else + #error "not yet implemented on this platform" +#endif + static constexpr ThreadRef _invalidThread { 0 }; + +public: + explicit MessageDispatcher (Connection* connection, std::unique_ptr && messageChannel); + virtual ~MessageDispatcher () = default; + +// protected: this does not work with thread_local... + struct PendingReplyHandler + { + ReplyHandler* const _replyHandler; + const PendingReplyHandler* _prevPendingReplyHandler; + }; + + static bool isReply (MessageID messageID) { return messageID == 0; } + + static ThreadRef getCurrentThread (); + +protected: + Connection* getConnection () const { return _connection; } + MessageChannel* getMessageChannel () const { return _messageChannel.get (); } + + void _sendMessage (MessageID messageID, std::unique_ptr && encoder, bool isNewTransaction); + bool _waitForMessage (); + void _handleReply (std::unique_ptr && decoder, ReplyHandler && replyHandler); + std::unique_ptr _handleReceivedMessage (MessageID messageID, std::unique_ptr && decoder); + +private: + Connection* const _connection; + const std::unique_ptr _messageChannel; +}; + + MessageDispatcher::MessageDispatcher (Connection* connection, std::unique_ptr && messageChannel) : _connection { connection }, _messageChannel { std::move (messageChannel) } @@ -144,8 +188,6 @@ MessageDispatcher::MessageDispatcher (Connection* connection, std::unique_ptr(&_invalidThread), "the current implementation relies on invalid thread IDs being 0"); ARA_INTERNAL_ASSERT (std::thread::id {} == *reinterpret_cast(&_invalidThread)); - - _messageChannel->setMessageDispatcher (this); } MessageDispatcher::ThreadRef MessageDispatcher::getCurrentThread () @@ -189,6 +231,45 @@ std::unique_ptr MessageDispatcher::_handleReceivedMessage (Messa return replyEncoder; } +//------------------------------------------------------------------------------ + +// single-threaded variant for main thread communication only +class MainThreadMessageDispatcher : public MessageDispatcher +{ +public: + MainThreadMessageDispatcher (Connection* connection, std::unique_ptr && messageChannel) + : MessageDispatcher { connection, std::move (messageChannel) } + { + getMessageChannel ()->_receivedMessageRouter = [this] (MessageID messageID, std::unique_ptr && decoder) + { routeReceivedMessage (messageID, std::move (decoder)); }; + } + + void sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler && replyHandler); + + void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder); + + void processPendingMessageIfNeeded (); + +private: + // key to indicate whether an outgoing call is made in response (reply or callback) to a + // currently handled incoming call, or a new call + // This distinction is necessary to deal with the decoupled main threads concurrency. + // To optimize for performance, it is only added when the call is a response, while being a + // new call is implicit. It is also not added to replies because they always are a response. + static constexpr MessageArgumentKey kIsResponseKey { -1 }; + +private: + int32_t _processingMessagesCount { 0 }; + +// \todo this is not allowed for some reason, so we must cast at every use of _noPendingMessageDecoder... +// static constexpr auto _noPendingMessageDecoder { reinterpret_cast (static_cast (-1)) }; + static constexpr auto _noPendingMessageDecoder { static_cast (-1) }; + MessageID _pendingMessageID { 0 }; // read/write _pendingMessageDecoder with proper barrier before/after reading/writing this + std::atomic _pendingMessageDecoder { reinterpret_cast (_noPendingMessageDecoder) }; + + const PendingReplyHandler* _pendingReplyHandler { nullptr }; +}; + void MainThreadMessageDispatcher::sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler && replyHandler) @@ -281,6 +362,46 @@ void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, std } } +//------------------------------------------------------------------------------ + +// multi-threaded variant for all non-main thread communication +class OtherThreadsMessageDispatcher : public MessageDispatcher +{ +public: + OtherThreadsMessageDispatcher (Connection* connection, std::unique_ptr && messageChannel) + : MessageDispatcher { connection, std::move (messageChannel) } + { + getMessageChannel ()->_receivedMessageRouter = [this] (MessageID messageID, std::unique_ptr && decoder) + { routeReceivedMessage (messageID, std::move (decoder)); }; + } + + void sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler && replyHandler); + + void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder); + +private: + // keys to store the threading information in the IPC messages + static constexpr MessageArgumentKey kSendThreadKey { -1 }; + static constexpr MessageArgumentKey kReceiveThreadKey { -2 }; + + struct RoutedMessage + { + MessageID _messageID { 0 }; + std::unique_ptr _decoder; + ThreadRef _targetThread { _invalidThread }; + }; + RoutedMessage* _getRoutedMessageForThread (ThreadRef thread); + + void _processReceivedMessage (MessageID messageID, std::unique_ptr && decoder); + +private: + // incoming data is stored in _routedMessages by the receive handler for the + // sending threads waiting to pick it up (signalled via _routeReceiveCondition) + std::condition_variable _routeReceiveCondition; + std::vector _routedMessages { 12 }; // we shouldn't use more than a handful of threads concurrently for the IPC + std::mutex _sendLock; + std::mutex _routeLock; +}; // actually "static" members of OtherThreadsMessageDispatcher, but for some reason C++ doesn't allow this... thread_local OtherThreadsMessageDispatcher::ThreadRef _remoteTargetThread { 0 }; @@ -416,6 +537,7 @@ void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID _remoteTargetThread = previousRemoteTargetThread; } +//------------------------------------------------------------------------------ #if defined (_WIN32) @@ -572,6 +694,11 @@ bool Connection::waitForMessageOnCreationThread () return false; } +void Connection::processPendingMessageOnCreationThreadIfNeeded () +{ + _mainThreadDispatcher->processPendingMessageIfNeeded (); +} + void Connection::signalMesssageReceived () { #if __cplusplus >= 202002L diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index a59399f..5d12b90 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -73,16 +73,19 @@ class MessageChannel //! implemented by subclasses to perform the actual message (or reply) sending virtual void sendMessage (MessageID messageID, std::unique_ptr && encoder) = 0; - void setMessageDispatcher (MessageDispatcher* messageDispatcher) +protected: + //! called by subclasses when messages arrive + void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) { - ARA_INTERNAL_ASSERT (_messageDispatcher == nullptr); - _messageDispatcher = messageDispatcher; + ARA_INTERNAL_ASSERT (_receivedMessageRouter); + _receivedMessageRouter (messageID, std::move (decoder)); } - MessageDispatcher* getMessageDispatcher () const { return _messageDispatcher; } - private: - MessageDispatcher* _messageDispatcher {}; + // configuration hook for the MessageDispatcher constructors + friend class MainThreadMessageDispatcher; + friend class OtherThreadsMessageDispatcher; + std::function &&)> _receivedMessageRouter {}; }; //! @} @@ -103,14 +106,10 @@ class Connection //! Must be done before sending or receiving the first message on any channel. void setMainThreadChannel (std::unique_ptr && messageChannel); - MainThreadMessageDispatcher* getMainThreadDispatcher () const { return _mainThreadDispatcher.get (); } - //! set the message channel for all non-main thread communication //! Must be done before sending or receiving the first message on any channel. void setOtherThreadsChannel (std::unique_ptr && messageChannel); - OtherThreadsMessageDispatcher* getOtherThreadsDispatcher () const { return _otherThreadsDispatcher.get (); } - const MessageHandler& getMessageHandler () const { return _messageHandler; } //! send an encoded messages to the receiving process @@ -139,6 +138,9 @@ class Connection //! returns true if a message was received, false otherwise bool waitForMessageOnCreationThread (); + //! spins message processing on the creation thread in case the thread is blocked by some outer loop + void processPendingMessageOnCreationThreadIfNeeded (); + //! message dispatcher need to call this when routing a message to the creation thread //! in order to wake it up void signalMesssageReceived (); @@ -171,135 +173,6 @@ class Connection #error "not yet implemented on this platform" #endif }; -//! @} - - -//! IPC message dispatcher: handles threading restrictions for MessageChannel access -//! @{ - -//! abstract base class -class MessageDispatcher -{ -public: // needs to be public for thread-local variables (which cannot be class members) -#if defined (_WIN32) - using ThreadRef = int32_t; -#elif defined (__APPLE__) - using ThreadRef = size_t; -#else - #error "not yet implemented on this platform" -#endif - static constexpr ThreadRef _invalidThread { 0 }; - -public: - explicit MessageDispatcher (Connection* connection, std::unique_ptr && messageChannel); - virtual ~MessageDispatcher () = default; - - Connection* getConnection () const { return _connection; } - MessageChannel* getMessageChannel () const { return _messageChannel.get (); } - - //! send an encoded messages to the receiving process - //! If an empty reply ("void") is expected, the replyHandler should be nullptr. - //! This method can be called from any thread, concurrent calls will be serialized. - //! The calling thread will be blocked until the receiver has processed the message and - //! returned a (potentially empty) reply, which will be forwarded to the replyHandler. - virtual void sendMessage (MessageID messageID, std::unique_ptr && encoder, - ReplyHandler && replyHandler) = 0; - - //! route an incoming message to the correct target thread - virtual void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) = 0; - -// protected: this does not work with thread_local... - struct PendingReplyHandler - { - ReplyHandler* const _replyHandler; - const PendingReplyHandler* _prevPendingReplyHandler; - }; - - static bool isReply (MessageID messageID) { return messageID == 0; } - - static ThreadRef getCurrentThread (); - -protected: - void _sendMessage (MessageID messageID, std::unique_ptr && encoder, bool isNewTransaction); - bool _waitForMessage (); - void _handleReply (std::unique_ptr && decoder, ReplyHandler && replyHandler); - std::unique_ptr _handleReceivedMessage (MessageID messageID, std::unique_ptr && decoder); - -private: - Connection* const _connection; - const std::unique_ptr _messageChannel; - -protected: - static bool _debugAsHost; -}; - -//! single-threaded variant for main thread communication only -class MainThreadMessageDispatcher : public MessageDispatcher -{ -public: - using MessageDispatcher::MessageDispatcher; - - void sendMessage (MessageID messageID, std::unique_ptr && encoder, - ReplyHandler && replyHandler) override; - - void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) override; - - void processPendingMessageIfNeeded (); - -private: - // key to indicate whether an outgoing call is made in response to a currently handled incoming call - // or a new call, which is necessary to deal with the decoupled main threads concurrency - // to optimize for performance, it is only added when the call is a response, being a new call is implicit - // (it is also never added to replies because they always are a response anyways) - static constexpr MessageArgumentKey kIsResponseKey { -1 }; - -private: - int32_t _processingMessagesCount { 0 }; - -// \todo this is not allowed for some reason, so we must cast at every use of _noPendingMessageDecoder... -// static constexpr auto _noPendingMessageDecoder { reinterpret_cast (static_cast (-1)) }; - static constexpr auto _noPendingMessageDecoder { static_cast (-1) }; - MessageID _pendingMessageID { 0 }; // read/write _pendingMessageDecoder with proper barrier before/after reading/writing this - std::atomic _pendingMessageDecoder { reinterpret_cast (_noPendingMessageDecoder) }; - - const PendingReplyHandler* _pendingReplyHandler { nullptr }; -}; - -//! multi-threaded variant for all non-main thread communication -class OtherThreadsMessageDispatcher : public MessageDispatcher -{ -public: - using MessageDispatcher::MessageDispatcher; - - void sendMessage (MessageID messageID, std::unique_ptr && encoder, - ReplyHandler && replyHandler) override; - - void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) override; - -private: - // keys to store the threading information in the IPC messages - static constexpr MessageArgumentKey kSendThreadKey { -1 }; - static constexpr MessageArgumentKey kReceiveThreadKey { -2 }; - - struct RoutedMessage - { - MessageID _messageID { 0 }; - std::unique_ptr _decoder; - ThreadRef _targetThread { _invalidThread }; - }; - RoutedMessage* _getRoutedMessageForThread (ThreadRef thread); - - void _processReceivedMessage (MessageID messageID, std::unique_ptr && decoder); - -private: - // incoming data is stored in _routedMessages by the receive handler for the - // sending threads waiting to pick it up (signalled via _routeReceiveCondition) - std::condition_variable _routeReceiveCondition; - std::vector _routedMessages { 12 }; // we shouldn't use more than a handful of threads concurrently for the IPC - std::mutex _sendLock; - std::mutex _routeLock; -}; - } // namespace IPC } // namespace ARA From 36a69d6529c7137cdec34b8ccfccc7f76ebc7d34 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:14 +0200 Subject: [PATCH 32/42] AudioUnit implementation of AUMessageChannel moved from example code to IPC library as dynamic ObjC class --- IPC/ARAIPC.h | 3 - IPC/ARAIPCAudioUnit_v3.h | 13 +- IPC/ARAIPCAudioUnit_v3.mm | 254 ++++++++++++++++++++++++++++++++++---- IPC/ARAIPCEncoding.h | 1 - 4 files changed, 232 insertions(+), 39 deletions(-) diff --git a/IPC/ARAIPC.h b/IPC/ARAIPC.h index c1df0de..86eaa6f 100644 --- a/IPC/ARAIPC.h +++ b/IPC/ARAIPC.h @@ -72,9 +72,6 @@ typedef ARA_IPC_REF(ARAIPCProxyHostRef); //! C-compatible wrapper of ARA IPC ProxyPlugIn typedef ARA_IPC_REF(ARAIPCProxyPlugInRef); -//! C-compatible wrapper of ARA IPC MessageChannel -typedef ARA_IPC_REF(ARAIPCMessageChannelRef); - //! to keep the IPC decoupled from the companion API in use, the IPC code uses //! an opaque encapsulation to represent a companion API plug-in instance typedef ARA_IPC_REF(ARAIPCPlugInInstanceRef); diff --git a/IPC/ARAIPCAudioUnit_v3.h b/IPC/ARAIPCAudioUnit_v3.h index 5e57622..328128c 100644 --- a/IPC/ARAIPCAudioUnit_v3.h +++ b/IPC/ARAIPCAudioUnit_v3.h @@ -76,17 +76,8 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize(ARAIPCProxyPlugInRef _Nonnull prox -//! plug-in side: implementation for AUMessageChannel -init... -//! will initialize the proxy on demand - make sure to add all factories via ARAIPCProxyHostAddFactory() -//! before the first call to this function and to call ARAIPCAUProxyHostUninitialize() if the call was made -ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageChannel(NSObject * _Nonnull audioUnitChannel, - bool isMainThreadChannel); - -//! plug-in side: implementation for AUMessageChannel -callAudioUnit: -NSDictionary * _Nonnull ARA_CALL ARAIPCAUProxyHostCommandHandler(ARAIPCMessageChannelRef _Nonnull messageChannelRef, NSDictionary * _Nonnull message); - -//! plug-in side: static cleanup upon shutdown -void ARA_CALL ARAIPCAUProxyHostUninitialize(void); +//! plug-in side: implementation for AUAudioUnit messageChannelFor: +id _Nullable ARA_CALL ARAIPCAUProxyHostMessageChannelFor(NSString * _Nonnull channelName); API_AVAILABLE_END diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 31a6521..7e2e3ab 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -32,8 +32,24 @@ #error "configuration mismatch: enabling ARA_AUDIOUNITV3_IPC_IS_AVAILABLE requires enabling ARA_ENABLE_IPC too" #endif +#import "CoreFoundation/CoreFoundation.h" +#import + #include +#if __cplusplus >= 202002L + #include +#else + // crude hack to use std::bit_width in C++17 + namespace std + { + constexpr size_t bit_width (size_t n) + { + return (n <= 1) ? n : 1 + bit_width (n / 2); + } + } +#endif + // JUCE hotfix: because JUCE directly includes the .cpp/.mm files from this SDK instead of properly // compiling the ARA_IPC_Library, these switches allow for skipping the host- or the @@ -158,7 +174,7 @@ void sendMessage (MessageID messageID, std::unique_ptr && encode // remote side until the first message is sent. However, if another channel is assigned // its (same) callHostBlock, then the pending callHostBlock for the first channel is // lost for some reason. We need to send an empty dummy message to work around this bug, - // which them must be filtered in ARAIPCAUProxyHostCommandHandler(). + // which them must be filtered before calling routeReceivedDictionary(). [audioUnitChannel callAudioUnit:[NSDictionary dictionary]]; } @@ -237,25 +253,13 @@ void sendMessage (MessageID messageID, std::unique_ptr && encode #endif // !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY -#if defined (__GNUC__) - _Pragma ("GCC diagnostic push") - _Pragma ("GCC diagnostic ignored \"-Wunguarded-availability\"") -#endif +// host side: proxy plug-in C adapter +#if !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY -ARA_MAP_IPC_REF (AudioUnitMessageChannel, ARAIPCMessageChannelRef) ARA_MAP_IPC_REF (AUProxyPlugIn, ARAIPCProxyPlugInRef) -#if defined (__GNUC__) - _Pragma ("GCC diagnostic pop") -#endif - - extern "C" { - -// host side: proxy plug-in C adapter -#if !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY - ARAIPCProxyPlugInRef ARA_CALL ARAIPCAUProxyPlugInInitialize (AUAudioUnit * _Nonnull audioUnit, ARAMainThreadWaitForMessageDelegate _Nullable waitForMessageDelegate, void * _Nullable delegateUserData) @@ -285,6 +289,8 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCProxyPlugInRef _Nonnull pro delete fromIPCRef (proxyPlugInRef); } +} // extern "C" + #endif // !ARA_AUDIOUNITV3_IPC_PROXY_HOST_ONLY @@ -314,9 +320,11 @@ void ARA_CALL ARAIPCAUProxyPlugInUninitialize (ARAIPCProxyPlugInRef _Nonnull pro AUProxyHost* _proxyHost; +typedef ARA_IPC_REF(ARAIPCMessageChannelRef); +ARA_MAP_IPC_REF (AudioUnitMessageChannel, ARAIPCMessageChannelRef) -ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageChannel (NSObject * _Nonnull audioUnitChannel, - bool isMainThreadChannel) +static ARAIPCMessageChannelRef _Nullable initializeMessageChannel (NSObject * _Nonnull audioUnitChannel, + bool isMainThreadChannel) { // \todo the connection currently stores the creation thread as main thread for Windows compatibility, // so we need to make sure the proxy is created on the main thread @@ -343,28 +351,226 @@ ARAIPCMessageChannelRef _Nullable ARA_CALL ARAIPCAUProxyHostInitializeMessageCha return result; } -NSDictionary * _Nonnull ARA_CALL ARAIPCAUProxyHostCommandHandler (ARAIPCMessageChannelRef _Nonnull messageChannelRef, NSDictionary * _Nonnull message) +#endif // !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY + + +// plug-in side: NSObject implementation +#if !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY + + +/* +The following code implements the below class at runtime to work around potential ObjC class name conflicts + +@interface ARAIPCAUMessageChannelImpl : NSObject +@end + +@implementation ARAIPCAUMessageChannel { + ARA::IPC::ARAIPCMessageChannelRef _messageChannelRef; +} + +@synthesize callHostBlock = _callHostBlock; + +- (instancetype)initAsMainThreadChannel:(BOOL) isMainThreadChannel { + self = [super init]; + + if (self == nil) { return nil; } + + _callHostBlock = nil; + _messageChannelRef = initializeMessageChannel (self, isMainThreadChannel); + + return self; +} + +- (NSDictionary * _Nonnull)callAudioUnit:(NSDictionary *)message { + if ([message count]) + static_cast (fromIPCRef (_messageChannelRef))->routeReceivedDictionary (message); + return [NSDictionary dictionary]; +} + +@end +*/ + +// helper to add ivars to an ObjC class +template +void addObjCIVar (Class cls, const char * name) +{ + constexpr auto size { sizeof (T) }; + constexpr auto alignment { static_cast (std::bit_width (size)) }; + [[maybe_unused]] const auto success = class_addIvar (cls, name, size, alignment, @encode (T)); + ARA_INTERNAL_ASSERT (success); +} + +// helpers to get/set a scalar ivar of an ObjC object +template +inline T getScalarIVar (id self, Ivar ivar) +{ + const auto offset { ivar_getOffset (ivar) }; + const auto bytes = static_cast ((__bridge void *) self); + return *reinterpret_cast (bytes + offset); +} + +template +inline void setScalarIVar (id self, Ivar ivar, T value) { - if ([message count]) // \todo filter dummy message sent in ProxyPlugInMessageChannel(), see there + const auto offset { ivar_getOffset (ivar) }; + auto bytes = static_cast ((__bridge void *) self); + *reinterpret_cast (bytes + offset) = value; +} + + +// optimization of object_getInstanceVariable: cache the ivars of our class +Ivar messageChannelRefIvar { nullptr }; +Ivar callHostBlockIvar { nullptr }; + + +// methods of our class +extern "C" { + +static CallHostBlock callHostBlock_getter (id self, SEL /*_cmd*/) +{ + return object_getIvar (self, callHostBlockIvar); +} + +static void callHostBlock_setter (id self, SEL /*_cmd*/, CallHostBlock newBlock) +{ + id oldBlock { object_getIvar (self, callHostBlockIvar) }; + if (oldBlock != newBlock) + { + if (oldBlock) + Block_release ((__bridge void *)oldBlock); + if (newBlock) + newBlock = (__bridge CallHostBlock) Block_copy ((__bridge void *)newBlock); + object_setIvar (self, callHostBlockIvar, newBlock); + } +} + +static NSDictionary * _Nonnull callAudioUnit_impl (id self, SEL /*_cmd*/, NSDictionary * message) +{ + const auto messageChannelRef { getScalarIVar (self, messageChannelRefIvar) }; + if ([message count]) // \todo filter dummy message sent in ProxyPlugInMessageChannel, see there static_cast (fromIPCRef (messageChannelRef))->routeReceivedDictionary (message); return [NSDictionary dictionary]; // \todo it would yield better performance if -callAudioUnit: would allow nil as return value } -void ARA_CALL ARAIPCAUProxyHostUninitialize (void) +} // extern "C" + + +// creation of the ARAIPCAUMessageChannelImpl Class +static Class createMessageChannelObjCClass () { - if (_proxyHost) - delete _proxyHost; + // create a unique class name by appending a unique address to the base name + std::string className { "ARAIPCAUMessageChannelImpl" + std::to_string (reinterpret_cast (&messageChannelRefIvar)) }; + ARA_INTERNAL_ASSERT (!objc_getClass (className.c_str ())); + + // allocate class + Class cls { objc_allocateClassPair ([NSObject class], className.c_str (), 0) }; + ARA_INTERNAL_ASSERT (cls); + + // add iVars + addObjCIVar (cls, "_messageChannelRef"); + addObjCIVar (cls, "_callHostBlock"); + + // add properties + objc_property_attribute_t attr[] { { "T", "@?" }, // type block + { "C", "" }, // C = copy + { "V", "_callHostBlock" } }; // backing i-var + [[maybe_unused]] auto success { class_addProperty (cls, "callHostBlock", attr, sizeof (attr) / sizeof (attr[0])) }; + + // add getters/setters + success = class_addMethod (cls, @selector(callHostBlock), (IMP)callHostBlock_getter, "@@:"); + ARA_INTERNAL_ASSERT (success); + class_addMethod (cls, @selector(setCallHostBlock:), (IMP)callHostBlock_setter, "v@:@"); + ARA_INTERNAL_ASSERT (success); + + // add methods + class_addMethod (cls, @selector(callAudioUnit:), (IMP)callAudioUnit_impl, "@@:@"); + ARA_INTERNAL_ASSERT (success); + + // declare conformance to + const auto messageChannelProtocol { objc_getProtocol ("AUMessageChannel") }; + ARA_INTERNAL_ASSERT (messageChannelProtocol); + success = class_addProtocol (cls, (Protocol * _Nonnull)messageChannelProtocol); + ARA_INTERNAL_ASSERT (success); + + // activate class + objc_registerClassPair (cls); + + // update cached ivar ptrs after activation + messageChannelRefIvar = class_getInstanceVariable (cls, "_messageChannelRef"); + ARA_INTERNAL_ASSERT (messageChannelRefIvar); + callHostBlockIvar = class_getInstanceVariable (cls, "_callHostBlock"); + ARA_INTERNAL_ASSERT (callHostBlockIvar); + + return cls; } -#endif // !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY +// creation and designated initialisation of instances of ARAIPCAUMessageChannelImpl Class +static NSObject * createAndInitMessageChannel (Class cls, bool isMainThreadChannel) +{ + NSObject * obj { class_createInstance (cls, 0) }; + ARA_INTERNAL_ASSERT (obj); + ARA_INTERNAL_ASSERT ([obj conformsToProtocol:@protocol(AUMessageChannel)]); + obj = [obj init]; + ARA_INTERNAL_ASSERT (obj); + + object_setIvar (obj, callHostBlockIvar, nil); + + const auto messageChannelRef { initializeMessageChannel (obj, isMainThreadChannel) }; + setScalarIVar (obj, messageChannelRefIvar, messageChannelRef); + + return obj; +} + + +NSObject * __strong _mainMessageChannel { nil }; +NSObject * __strong _otherMessageChannel { nil }; + +static void createSharedMessageChannels () +{ + ARA_INTERNAL_ASSERT(!_mainMessageChannel && !_otherMessageChannel); + + const auto cls { createMessageChannelObjCClass () }; + _mainMessageChannel = createAndInitMessageChannel (cls, true); + _otherMessageChannel = createAndInitMessageChannel (cls, false); +} + +extern "C" id _Nullable ARA_CALL ARAIPCAUProxyHostMessageChannelFor (NSString * _Nonnull channelName) +{ + if ([channelName isEqualToString:ARA_AUDIOUNIT_MAIN_THREAD_MESSAGES_UTI]) + { + if (!_mainMessageChannel) + createSharedMessageChannels (); + return _mainMessageChannel; + } + else if ([channelName isEqualToString:ARA_AUDIOUNIT_OTHER_THREADS_MESSAGES_UTI]) + { + if (!_otherMessageChannel) + createSharedMessageChannels (); + return _otherMessageChannel; + } + return nil; +} API_AVAILABLE_END +__attribute__((destructor)) +static void destroySharedMessageChannelsIfNeeded () +{ + if (@available(macOS 13.0, iOS 16.0, *)) + { + if (_proxyHost) + delete _proxyHost; + + _mainMessageChannel = nil; + _otherMessageChannel = nil; + } +} + +#endif // !ARA_AUDIOUNITV3_IPC_PROXY_PLUGIN_ONLY _Pragma ("GCC diagnostic pop") -} // extern "C" } // namespace IPC } // namespace ARA diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index 67a6c63..bfb1cd5 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -150,7 +150,6 @@ ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAModelUpdateControllerHostRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAPlaybackControllerHostRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCProxyHostRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCProxyPlugInRef) -ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCMessageChannelRef) ARA_IPC_SPECIALIZE_FOR_REF_TYPE (ARAIPCPlugInInstanceRef) #undef ARA_IPC_SPECIALIZE_FOR_REF_TYPE From 9590a17299abd575a0489b7749a3863661fcb0f9 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:15 +0200 Subject: [PATCH 33/42] Suppress unnecessary main thread wakeups when waiting for IPC messages --- IPC/ARAIPCConnection.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 00b028f..5e2853e 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -347,17 +347,20 @@ void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, std } _pendingMessageID = messageID; - _pendingMessageDecoder.store (decoder.release (), std::memory_order_release); - getConnection ()->signalMesssageReceived (); - if (processSynchronously) { + _pendingMessageDecoder.store (decoder.release (), std::memory_order_relaxed); + processPendingMessageIfNeeded (); } else { + _pendingMessageDecoder.store (decoder.release (), std::memory_order_release); + // only new transactions must be dispatched, otherwise the target thread is already waiting for the message received signal - if (!isResponse) + if (isResponse) + getConnection ()->signalMesssageReceived (); + else getConnection ()->dispatchToCreationThread (std::bind (&MainThreadMessageDispatcher::processPendingMessageIfNeeded, this)); } } From c273cbc4472c4bf6cd308602fbe7b04abc8fc6ef Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:15 +0200 Subject: [PATCH 34/42] waitForMessage...() must be implemented by IPC MEssageChannel instead of Connection to properly deal with IPC running on the main thread --- IPC/ARAIPCAudioUnit_v3.mm | 13 ++++ IPC/ARAIPCConnection.cpp | 142 ++++++++++++++++++++++---------------- IPC/ARAIPCConnection.h | 17 +++-- 3 files changed, 104 insertions(+), 68 deletions(-) diff --git a/IPC/ARAIPCAudioUnit_v3.mm b/IPC/ARAIPCAudioUnit_v3.mm index 7e2e3ab..df23dc0 100644 --- a/IPC/ARAIPCAudioUnit_v3.mm +++ b/IPC/ARAIPCAudioUnit_v3.mm @@ -100,10 +100,12 @@ constexpr size_t bit_width (size_t n) public: void routeReceivedDictionary (NSDictionary * _Nonnull message) { + _currentRoutingThread = [NSThread currentThread]; ARA_INTERNAL_ASSERT (![NSThread isMainThread]); const MessageID messageID { [(NSNumber *) [message objectForKey:_messageIDKey] intValue] }; auto decoder { std::make_unique ((__bridge CFDictionaryRef) message) }; routeReceivedMessage (messageID, std::move (decoder)); + _currentRoutingThread = nil; } void sendMessage (MessageID messageID, std::unique_ptr && encoder) override @@ -122,11 +124,22 @@ void sendMessage (MessageID messageID, std::unique_ptr && encode #endif } + bool receivesMessagesOnCurrentThread () override + { + return _currentRoutingThread == [NSThread currentThread]; + } + + bool waitForMessageOnCurrentThread () override + { + return CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.01, true) != kCFRunLoopRunTimedOut; + } + protected: virtual NSDictionary * _sendMessage (NSDictionary * message) = 0; protected: NSObject * __strong _Nonnull _audioUnitChannel; + NSThread * __strong _Nullable _currentRoutingThread { nil }; }; diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 5e2853e..e6a6770 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -238,18 +238,44 @@ class MainThreadMessageDispatcher : public MessageDispatcher { public: MainThreadMessageDispatcher (Connection* connection, std::unique_ptr && messageChannel) - : MessageDispatcher { connection, std::move (messageChannel) } + : MessageDispatcher { connection, std::move (messageChannel) }, +#if __cplusplus >= 202002L + _waitForMessageSemaphore { new std::binary_semaphore { 0 } } +#elif defined (_WIN32) + _waitForMessageSemaphore { ::CreateSemaphore (nullptr, 0, LONG_MAX, nullptr) } +#elif defined (__APPLE__) + _waitForMessageSemaphore { dispatch_semaphore_create (0) } +#else + #error "IPC not yet implemented for this platform" +#endif { getMessageChannel ()->_receivedMessageRouter = [this] (MessageID messageID, std::unique_ptr && decoder) { routeReceivedMessage (messageID, std::move (decoder)); }; } + ~MainThreadMessageDispatcher () override + { +#if __cplusplus >= 202002L + delete static_cast (_waitForMessageSemaphore); +#elif defined (_WIN32) + ::CloseHandle (_waitForMessageSemaphore); +#elif defined (__APPLE__) + dispatch_release (static_cast (_waitForMessageSemaphore)); +#else + #error "IPC not yet implemented for this platform" +#endif + } + void sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler && replyHandler); void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder); void processPendingMessageIfNeeded (); +private: + bool waitOnSemaphore (); + void signalSemaphore (); + private: // key to indicate whether an outgoing call is made in response (reply or callback) to a // currently handled incoming call, or a new call @@ -268,6 +294,8 @@ class MainThreadMessageDispatcher : public MessageDispatcher std::atomic _pendingMessageDecoder { reinterpret_cast (_noPendingMessageDecoder) }; const PendingReplyHandler* _pendingReplyHandler { nullptr }; + + void* const _waitForMessageSemaphore; // concrete type is platform-dependent }; @@ -289,8 +317,18 @@ void MainThreadMessageDispatcher::sendMessage (MessageID messageID, std::unique_ while (previousPendingReplyHandler != _pendingReplyHandler) { - if (getConnection ()->waitForMessageOnCreationThread ()) - processPendingMessageIfNeeded (); + if (getMessageChannel ()->receivesMessagesOnCurrentThread ()) + { + if (!getMessageChannel ()->waitForMessageOnCurrentThread ()) + getConnection ()->_callWaitForMessageDelegate (); + } + else + { + if (waitOnSemaphore ()) + processPendingMessageIfNeeded (); + else + getConnection ()->_callWaitForMessageDelegate (); + } } } @@ -359,12 +397,44 @@ void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, std // only new transactions must be dispatched, otherwise the target thread is already waiting for the message received signal if (isResponse) - getConnection ()->signalMesssageReceived (); + signalSemaphore (); else getConnection ()->dispatchToCreationThread (std::bind (&MainThreadMessageDispatcher::processPendingMessageIfNeeded, this)); } } +bool MainThreadMessageDispatcher::waitOnSemaphore () +{ + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + constexpr ARATimeDuration timeout { 0.010 }; + bool didReceiveMessage; +#if __cplusplus >= 202002L + didReceiveMessage = static_cast (_waitForMessageSemaphore)->try_acquire_for (std::chrono::duration { timeout }); +#elif defined (_WIN32) + didReceiveMessage = (::WaitForSingleObject (_waitForMessageSemaphore, static_cast (timeout * 1000.0 + 0.5)) == WAIT_OBJECT_0); +#elif defined (__APPLE__) + const auto deadline { dispatch_time (DISPATCH_TIME_NOW, static_cast (10e9 * timeout + 0.5)) }; + didReceiveMessage = (dispatch_semaphore_wait (static_cast (_waitForMessageSemaphore), deadline) == 0); +#else + #error "IPC not yet implemented for this platform" +#endif + return didReceiveMessage; +} + +void MainThreadMessageDispatcher::signalSemaphore () +{ +#if __cplusplus >= 202002L + static_cast (_waitForMessageSemaphore)->release (); +#elif defined (_WIN32) + ::ReleaseSemaphore (_waitForMessageSemaphore, 1, nullptr); +#elif defined (__APPLE__) + dispatch_semaphore_signal (static_cast (_waitForMessageSemaphore)); +#else + #error "IPC not yet implemented for this platform" +#endif +} + //------------------------------------------------------------------------------ // multi-threaded variant for all non-main thread communication @@ -413,6 +483,8 @@ thread_local const OtherThreadsMessageDispatcher::PendingReplyHandler* _pendingR void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler && replyHandler) { + ARA_INTERNAL_ASSERT (!getConnection ()->wasCreatedOnCurrentThread ()); + const auto currentThread { getCurrentThread () }; encoder->appendThreadRef (kSendThreadKey, currentThread); const auto isResponse { _remoteTargetThread != _invalidThread }; @@ -423,14 +495,16 @@ void OtherThreadsMessageDispatcher::sendMessage (MessageID messageID, std::uniqu _sendMessage (messageID, std::move (encoder), !isResponse); _sendLock.unlock (); - if (getConnection ()->wasCreatedOnCurrentThread ()) + + if (getMessageChannel ()->receivesMessagesOnCurrentThread ()) { const auto previousPendingReplyHandler { _pendingReplyHandler }; const PendingReplyHandler pendingReplyHandler { &replyHandler, previousPendingReplyHandler }; _pendingReplyHandler = &pendingReplyHandler; do { - getConnection ()->waitForMessageOnCreationThread (); + if (!getMessageChannel ()->waitForMessageOnCurrentThread ()) + getConnection ()->_callWaitForMessageDelegate (); } while (_pendingReplyHandler != previousPendingReplyHandler); } else @@ -597,15 +671,6 @@ Connection::Connection (MessageEncoderFactory && messageEncoderFactory, MessageH _messageHandler { std::move (messageHandler) }, _receiverEndianessMatches { receiverEndianessMatches }, _waitForMessageDelegate { std::move (waitForMessageDelegate) }, -#if __cplusplus >= 202002L - _waitForMessageSemaphore { new std::binary_semaphore { 0 } }, -#elif defined (_WIN32) - _waitForMessageSemaphore { ::CreateSemaphore (nullptr, 0, LONG_MAX, nullptr) }, -#elif defined (__APPLE__) - _waitForMessageSemaphore { dispatch_semaphore_create (0) }, -#else - #error "IPC not yet implemented for this platform" -#endif _creationThreadID { std::this_thread::get_id () }, #if defined (_WIN32) _creationThreadHandle { _GetRealCurrentThread () } @@ -625,15 +690,6 @@ Connection::~Connection () CFRunLoopRemoveSource (_creationThreadRunLoop, _runloopSource, kCFRunLoopCommonModes); CFRelease (_runloopSource); #endif -#if __cplusplus >= 202002L - delete static_cast (_waitForMessageSemaphore); -#elif defined (_WIN32) - ::CloseHandle (_waitForMessageSemaphore); -#elif defined (__APPLE__) - dispatch_release (static_cast (_waitForMessageSemaphore)); -#else - #error "IPC not yet implemented for this platform" -#endif } void Connection::setMainThreadChannel (std::unique_ptr && messageChannel) @@ -672,47 +728,15 @@ void Connection::dispatchToCreationThread (DispatchableFunction func) #endif } -bool Connection::waitForMessageOnCreationThread () -{ - ARA_INTERNAL_ASSERT (wasCreatedOnCurrentThread ()); - - constexpr ARATimeDuration timeout { 0.010 }; - bool didReceiveMessage; -#if __cplusplus >= 202002L - didReceiveMessage = static_cast (_waitForMessageSemaphore)->try_acquire_for (std::chrono::duration { timeout }); -#elif defined (_WIN32) - didReceiveMessage = (::WaitForSingleObject (_waitForMessageSemaphore, static_cast (timeout * 1000.0 + 0.5)) == WAIT_OBJECT_0); -#elif defined (__APPLE__) - const auto deadline { dispatch_time (DISPATCH_TIME_NOW, static_cast (10e9 * timeout + 0.5)) }; - didReceiveMessage = (dispatch_semaphore_wait (static_cast (_waitForMessageSemaphore), deadline) == 0); -#else - #error "IPC not yet implemented for this platform" -#endif - if (didReceiveMessage) - return true; - - if (_waitForMessageDelegate) - _waitForMessageDelegate (); - - return false; -} - void Connection::processPendingMessageOnCreationThreadIfNeeded () { _mainThreadDispatcher->processPendingMessageIfNeeded (); } -void Connection::signalMesssageReceived () +void Connection::_callWaitForMessageDelegate () { -#if __cplusplus >= 202002L - static_cast (_waitForMessageSemaphore)->release (); -#elif defined (_WIN32) - ::ReleaseSemaphore (_waitForMessageSemaphore, 1, nullptr); -#elif defined (__APPLE__) - dispatch_semaphore_signal (static_cast (_waitForMessageSemaphore)); -#else - #error "IPC not yet implemented for this platform" -#endif + if (_waitForMessageDelegate) + _waitForMessageDelegate (); } void Connection::_setDebugMessageHint (bool isHost) diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index 5d12b90..aae5857 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -73,6 +73,12 @@ class MessageChannel //! implemented by subclasses to perform the actual message (or reply) sending virtual void sendMessage (MessageID messageID, std::unique_ptr && encoder) = 0; + //! implemented by subclasses to inform the message dispatching code of the threading requirements + virtual bool receivesMessagesOnCurrentThread () = 0; + + //! implemented by subclasses to receive messages if receivesMessagesOnCurrentThread() returns true + virtual bool waitForMessageOnCurrentThread () = 0; + protected: //! called by subclasses when messages arrive void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder) @@ -133,17 +139,11 @@ class Connection using DispatchableFunction = std::function; void dispatchToCreationThread (DispatchableFunction func); - //! when a message dispatcher blocks the creation thread for some time, it needs to periodically - //! call this method to let other main thread tasks execute cooperatively - //! returns true if a message was received, false otherwise - bool waitForMessageOnCreationThread (); - //! spins message processing on the creation thread in case the thread is blocked by some outer loop void processPendingMessageOnCreationThreadIfNeeded (); - //! message dispatcher need to call this when routing a message to the creation thread - //! in order to wake it up - void signalMesssageReceived (); + // internal API: called by message dispatchers when blocking the current thread in a receive loop + void _callWaitForMessageDelegate (); // internal API: debug output helper static void _setDebugMessageHint (bool isHost); @@ -158,7 +158,6 @@ class Connection const MessageHandler _messageHandler; const bool _receiverEndianessMatches; const WaitForMessageDelegate _waitForMessageDelegate; - void* const _waitForMessageSemaphore; // concrete type is platform-dependent std::unique_ptr _mainThreadDispatcher {}; std::unique_ptr _otherThreadsDispatcher {}; std::thread::id const _creationThreadID; From 150880105bdf4c29e4c559d8196f977daf893620 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:15 +0200 Subject: [PATCH 35/42] Move semaphore from MainThreadMessageDispatcher to dedicated helper class and refactor dispatch accordingly --- IPC/ARAIPCConnection.cpp | 196 +++++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 91 deletions(-) diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index e6a6770..06dd81d 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -26,6 +26,7 @@ #include "ARA_Library/IPC/ARAIPCEncoding.h" #include +#include #if __cplusplus >= 202002L #include #include @@ -152,7 +153,6 @@ class MessageDispatcher public: explicit MessageDispatcher (Connection* connection, std::unique_ptr && messageChannel); - virtual ~MessageDispatcher () = default; // protected: this does not work with thread_local... struct PendingReplyHandler @@ -233,48 +233,109 @@ std::unique_ptr MessageDispatcher::_handleReceivedMessage (Messa //------------------------------------------------------------------------------ -// single-threaded variant for main thread communication only -class MainThreadMessageDispatcher : public MessageDispatcher +// helper for MainThreadMessageDispatcher: single-object message queue with semaphore to wait on +class WaitableSingleMessageQueue { -public: - MainThreadMessageDispatcher (Connection* connection, std::unique_ptr && messageChannel) - : MessageDispatcher { connection, std::move (messageChannel) }, + public: + WaitableSingleMessageQueue () #if __cplusplus >= 202002L - _waitForMessageSemaphore { new std::binary_semaphore { 0 } } + : _waitForMessageSemaphore { new std::binary_semaphore { 0 } } #elif defined (_WIN32) - _waitForMessageSemaphore { ::CreateSemaphore (nullptr, 0, LONG_MAX, nullptr) } + : _waitForMessageSemaphore { ::CreateSemaphore (nullptr, 0, LONG_MAX, nullptr) } #elif defined (__APPLE__) - _waitForMessageSemaphore { dispatch_semaphore_create (0) } + : _waitForMessageSemaphore { dispatch_semaphore_create (0) } #else - #error "IPC not yet implemented for this platform" +#error "IPC not yet implemented for this platform" #endif - { - getMessageChannel ()->_receivedMessageRouter = [this] (MessageID messageID, std::unique_ptr && decoder) - { routeReceivedMessage (messageID, std::move (decoder)); }; - } + {} - ~MainThreadMessageDispatcher () override - { + ~WaitableSingleMessageQueue () + { #if __cplusplus >= 202002L - delete static_cast (_waitForMessageSemaphore); + delete static_cast (_waitForMessageSemaphore); #elif defined (_WIN32) - ::CloseHandle (_waitForMessageSemaphore); + ::CloseHandle (_waitForMessageSemaphore); #elif defined (__APPLE__) - dispatch_release (static_cast (_waitForMessageSemaphore)); + dispatch_release (static_cast (_waitForMessageSemaphore)); #else - #error "IPC not yet implemented for this platform" +#error "IPC not yet implemented for this platform" #endif + } + + std::optional>> waitOnSemaphore (ARATimeDuration timeout) + { + bool didReceiveMessage; +#if __cplusplus >= 202002L + didReceiveMessage = static_cast (_waitForMessageSemaphore)->try_acquire_for (std::chrono::duration { timeout }); +#elif defined (_WIN32) + didReceiveMessage = (::WaitForSingleObject (_waitForMessageSemaphore, static_cast (timeout * 1000.0 + 0.5)) == WAIT_OBJECT_0); +#elif defined (__APPLE__) + const auto deadline { dispatch_time (DISPATCH_TIME_NOW, static_cast (10e9 * timeout + 0.5)) }; + didReceiveMessage = (dispatch_semaphore_wait (static_cast (_waitForMessageSemaphore), deadline) == 0); +#else +#error "IPC not yet implemented for this platform" +#endif + if (didReceiveMessage) + { + const auto messageID { _pendingMessageID }; + const auto messageDecoder { _pendingMessageDecoder.load (std::memory_order_acquire) }; + return std::make_pair (messageID, std::unique_ptr (messageDecoder)); + } + else + { + return {}; + } + } + + void signalSemaphore (MessageID messageID, std::unique_ptr && decoder) + { + _pendingMessageID = messageID; + _pendingMessageDecoder.store (decoder.release (), std::memory_order_release); +#if __cplusplus >= 202002L + static_cast (_waitForMessageSemaphore)->release (); +#elif defined (_WIN32) + ::ReleaseSemaphore (_waitForMessageSemaphore, 1, nullptr); +#elif defined (__APPLE__) + dispatch_semaphore_signal (static_cast (_waitForMessageSemaphore)); +#else +#error "IPC not yet implemented for this platform" +#endif + } + + private: + MessageID _pendingMessageID { 0 }; // read/write _pendingMessageDecoder with proper barrier before/after reading/writing this + std::atomic _pendingMessageDecoder { nullptr }; + void* const _waitForMessageSemaphore; // concrete type is platform-dependent +}; + +//------------------------------------------------------------------------------ + +// single-threaded variant for main thread communication only +class MainThreadMessageDispatcher : public MessageDispatcher +{ +public: + MainThreadMessageDispatcher (Connection* connection, std::unique_ptr && messageChannel) + : MessageDispatcher { connection, std::move (messageChannel) } + { + getMessageChannel ()->_receivedMessageRouter = [this] (MessageID messageID, std::unique_ptr && decoder) + { routeReceivedMessage (messageID, std::move (decoder)); }; } void sendMessage (MessageID messageID, std::unique_ptr && encoder, ReplyHandler && replyHandler); void routeReceivedMessage (MessageID messageID, std::unique_ptr && decoder); - void processPendingMessageIfNeeded (); + void processPendingMessageIfNeeded () + { + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); -private: - bool waitOnSemaphore (); - void signalSemaphore (); + auto receivedData { _messageQueue.waitOnSemaphore (0.0) }; + if (receivedData) + processMessage (receivedData->first, std::move (receivedData->second)); + } + +protected: + void processMessage (MessageID messageID, std::unique_ptr && decoder); private: // key to indicate whether an outgoing call is made in response (reply or callback) to a @@ -287,15 +348,9 @@ class MainThreadMessageDispatcher : public MessageDispatcher private: int32_t _processingMessagesCount { 0 }; -// \todo this is not allowed for some reason, so we must cast at every use of _noPendingMessageDecoder... -// static constexpr auto _noPendingMessageDecoder { reinterpret_cast (static_cast (-1)) }; - static constexpr auto _noPendingMessageDecoder { static_cast (-1) }; - MessageID _pendingMessageID { 0 }; // read/write _pendingMessageDecoder with proper barrier before/after reading/writing this - std::atomic _pendingMessageDecoder { reinterpret_cast (_noPendingMessageDecoder) }; - const PendingReplyHandler* _pendingReplyHandler { nullptr }; - void* const _waitForMessageSemaphore; // concrete type is platform-dependent + WaitableSingleMessageQueue _messageQueue {}; }; @@ -324,36 +379,31 @@ void MainThreadMessageDispatcher::sendMessage (MessageID messageID, std::unique_ } else { - if (waitOnSemaphore ()) - processPendingMessageIfNeeded (); + auto receivedData { _messageQueue.waitOnSemaphore (0.010) }; + if (receivedData ) + processMessage (receivedData->first, std::move (receivedData->second)); else getConnection ()->_callWaitForMessageDelegate (); } } } -void MainThreadMessageDispatcher::processPendingMessageIfNeeded () +void MainThreadMessageDispatcher::processMessage (MessageID messageID, std::unique_ptr && decoder) { ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - const auto pendingMessageDecoder { _pendingMessageDecoder.exchange (reinterpret_cast (_noPendingMessageDecoder), std::memory_order_acquire) }; - if (pendingMessageDecoder != reinterpret_cast (_noPendingMessageDecoder)) + if (isReply (messageID)) { - std::unique_ptr ownedPendingMessageDecoder { pendingMessageDecoder }; - const auto pendingMessageID { _pendingMessageID }; - if (isReply (pendingMessageID)) - { - ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); - _handleReply (std::move (ownedPendingMessageDecoder), std::move (*_pendingReplyHandler->_replyHandler)); - _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; - } - else - { - ++_processingMessagesCount; - auto replyEncoder { _handleReceivedMessage (pendingMessageID, std::move (ownedPendingMessageDecoder)) }; - --_processingMessagesCount; - _sendMessage (0, std::move (replyEncoder), false); - } + ARA_INTERNAL_ASSERT (_pendingReplyHandler != nullptr); + _handleReply (std::move (decoder), std::move (*_pendingReplyHandler->_replyHandler)); + _pendingReplyHandler = _pendingReplyHandler->_prevPendingReplyHandler; + } + else + { + ++_processingMessagesCount; + auto replyEncoder { _handleReceivedMessage (messageID, std::move (decoder)) }; + --_processingMessagesCount; + _sendMessage (0, std::move (replyEncoder), false); } } @@ -384,57 +434,21 @@ void MainThreadMessageDispatcher::routeReceivedMessage (MessageID messageID, std ARA_IPC_DECODE_RECEIVED_MESSAGE_ARGS (messageID), ARA_IPC_LABEL_THREAD_ARGS); } - _pendingMessageID = messageID; if (processSynchronously) { - _pendingMessageDecoder.store (decoder.release (), std::memory_order_relaxed); - - processPendingMessageIfNeeded (); + processMessage (messageID, std::move (decoder)); } else { - _pendingMessageDecoder.store (decoder.release (), std::memory_order_release); - // only new transactions must be dispatched, otherwise the target thread is already waiting for the message received signal if (isResponse) - signalSemaphore (); + _messageQueue.signalSemaphore (messageID, std::move (decoder)); else - getConnection ()->dispatchToCreationThread (std::bind (&MainThreadMessageDispatcher::processPendingMessageIfNeeded, this)); + getConnection ()->dispatchToCreationThread ([this, messageID, d = decoder.release ()] () + { processMessage (messageID, std::unique_ptr (d)); }); } } -bool MainThreadMessageDispatcher::waitOnSemaphore () -{ - ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); - - constexpr ARATimeDuration timeout { 0.010 }; - bool didReceiveMessage; -#if __cplusplus >= 202002L - didReceiveMessage = static_cast (_waitForMessageSemaphore)->try_acquire_for (std::chrono::duration { timeout }); -#elif defined (_WIN32) - didReceiveMessage = (::WaitForSingleObject (_waitForMessageSemaphore, static_cast (timeout * 1000.0 + 0.5)) == WAIT_OBJECT_0); -#elif defined (__APPLE__) - const auto deadline { dispatch_time (DISPATCH_TIME_NOW, static_cast (10e9 * timeout + 0.5)) }; - didReceiveMessage = (dispatch_semaphore_wait (static_cast (_waitForMessageSemaphore), deadline) == 0); -#else - #error "IPC not yet implemented for this platform" -#endif - return didReceiveMessage; -} - -void MainThreadMessageDispatcher::signalSemaphore () -{ -#if __cplusplus >= 202002L - static_cast (_waitForMessageSemaphore)->release (); -#elif defined (_WIN32) - ::ReleaseSemaphore (_waitForMessageSemaphore, 1, nullptr); -#elif defined (__APPLE__) - dispatch_semaphore_signal (static_cast (_waitForMessageSemaphore)); -#else - #error "IPC not yet implemented for this platform" -#endif -} - //------------------------------------------------------------------------------ // multi-threaded variant for all non-main thread communication From 3466e0ef72e04ac10c7c09572898b8ea6e2cedd5 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:16 +0200 Subject: [PATCH 36/42] Consistent spelling of timestrech --- ChangeLog.txt | 3 ++- PlugIn/ARAPlug.cpp | 2 +- PlugIn/ARAPlug.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 989d436..716d65e 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,7 +7,8 @@ Changes since previous releases: based on heavily refactored IPC Example from earlier SDK releases - dropped support for ARA 1 and 2.0 Draft APIs If previously optionally supporting obsolete ARA 1 persistency, now exclusively use ARA 2 persistency. -- C++14 is now consistently required on all platforms +- C++17 is now consistently required on all platforms +- fixed inconsistent spelling of "timestretch" in various identifiers === ARA SDK 2.3 release (aka 2.3.001) (2025/11/07) === diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index 2f000f1..987492c 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -157,7 +157,7 @@ std::ostream& operator<< (std::ostream& oss, const OptionalPropertygetStartInAudioModificationTime () << " to " << playbackRegion->getEndInAudioModificationTime () - << ", time-stretching:" << (playbackRegion->isTimestretchEnabled () ? (playbackRegion->isTimeStretchReflectingTempo () ? "musical" : "linear") : "off)") + << ", time-stretching:" << (playbackRegion->isTimestretchEnabled () ? (playbackRegion->isTimestretchReflectingTempo () ? "musical" : "linear") : "off)") << ", content based fades:" << (playbackRegion->hasContentBasedFadeAtHead () ? (playbackRegion->hasContentBasedFadeAtTail () ? "both" : "head only") : (playbackRegion->hasContentBasedFadeAtTail () ? "tail only" : "none")) << ", regionSequence:" << playbackRegion->getRegionSequence ()->getName () << ", color:" << playbackRegion->getColor (); diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index f907c32..9f46812 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -623,7 +623,7 @@ class PlaybackRegion ARASamplePosition getEndInPlaybackSamples (ARASampleRate playbackSampleRate) const noexcept; //!< Playback end time in samples, derived using underlying AudioSource sample rate. bool isTimestretchEnabled () const noexcept { return _timestretchEnabled; } //!< `ARAPlaybackRegionProperties::transformationFlags & ::kARAPlaybackTransformationTimestretch`. - bool isTimeStretchReflectingTempo () const noexcept { return _timestretchReflectingTempo; } //!< `ARAPlaybackRegionProperties::transformationFlags & ::kARAPlaybackTransformationTimestretchReflectingTempo`. + bool isTimestretchReflectingTempo () const noexcept { return _timestretchReflectingTempo; } //!< `ARAPlaybackRegionProperties::transformationFlags & ::kARAPlaybackTransformationTimestretchReflectingTempo`. bool hasContentBasedFadeAtHead () const noexcept { return _contentBasedFadeAtHead; } //!< `ARAPlaybackRegionProperties::transformationFlags & ::kARAPlaybackTransformationContentBasedFadeAtHead`. bool hasContentBasedFadeAtTail () const noexcept { return _contentBasedFadeAtTail; } //!< `ARAPlaybackRegionProperties::transformationFlags & ::kARAPlaybackTransformationContentBasedFadeAtTail`. From cbc43d5b235125989b3726c890cdeb2b854c09cb Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:23:16 +0200 Subject: [PATCH 37/42] Initial draft of region sequence persistency --- ChangeLog.txt | 1 + IPC/ARAIPCEncoding.h | 9 ++++++ PlugIn/ARAPlug.cpp | 68 ++++++++++++++++++++++++++++++++++++++++---- PlugIn/ARAPlug.h | 17 +++++++++-- 4 files changed, 87 insertions(+), 8 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 716d65e..4c5806c 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -3,6 +3,7 @@ This is a development build of the ARA Library 3.0. Changes since previous releases: +- initial draft of region sequence persistency - initial draft of generic ARA IPC library providing a proxy host and a proxy plug-in, based on heavily refactored IPC Example from earlier SDK releases - dropped support for ARA 1 and 2.0 Draft APIs diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index bfb1cd5..b387251 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -434,6 +434,9 @@ template<> struct _ValueEncoder : public _CompoundValueEncoderBasemusicalContextRef) }; ARA_VALIDATE_API_ARGUMENT (properties->musicalContextRef, getDocumentController ()->isValidMusicalContext (musicalContext)); setMusicalContext (musicalContext); + + if (properties.implements<&ARARegionSequenceProperties::persistentID> ()) + { + ARA_VALIDATE_API_ARGUMENT (properties->persistentID, properties->persistentID != nullptr); + ARA_VALIDATE_API_ARGUMENT (properties->persistentID, std::strlen (properties->persistentID) > 0); + _persistentID = properties->persistentID; + } + else + { + ARA_VALIDATE_API_ARGUMENT (properties, getDocumentController ()->getUsedApiGeneration () < kARAAPIGeneration_3_0_Draft); + _persistentID = nullptr; + } } void RegionSequence::setMusicalContext (MusicalContext* musicalContext) noexcept @@ -684,7 +696,7 @@ void PlaybackRegion::setRegionSequence (RegionSequence* regionSequence) noexcept /*******************************************************************************/ -RestoreObjectsFilter::RestoreObjectsFilter (const ARARestoreObjectsFilter* filter, Document* document) noexcept +RestoreObjectsFilter::RestoreObjectsFilter (const SizedStructPtr filter, Document* document) noexcept : _filter { filter } { for (const auto& audioSource : document->getAudioSources ()) @@ -701,6 +713,15 @@ RestoreObjectsFilter::RestoreObjectsFilter (const ARARestoreObjectsFilter* filte } } + for (const auto& regionSequence : document->getRegionSequences ()) + { + if (const auto& regionSequenceID { regionSequence->getPersistentID () }) + { + ARA_VALIDATE_API_STATE (_regionSequencesByID.count (regionSequenceID) == 0); // make sure all current region sequence persistentIDs are unique + _regionSequencesByID[regionSequenceID] = regionSequence; + } + } + if (filter) { decltype (_audioSourcesByID) audioSourcesByMappedIDs; @@ -728,6 +749,22 @@ RestoreObjectsFilter::RestoreObjectsFilter (const ARARestoreObjectsFilter* filte audioModificationsByMappedIDs[audioModificationArchiveID] = it->second; } _audioModificationsByID = std::move (audioModificationsByMappedIDs); + + if (filter.implements<&ARARestoreObjectsFilter::regionSequenceIDsCount> ()) + { + decltype (_regionSequencesByID) regionSequencesByMappedIDs; + for (ARASize i { 0 }; i < filter->regionSequenceIDsCount; ++i) + { + auto regionSequenceArchiveID { filter->regionSequenceArchiveIDs[i] }; + ARA_VALIDATE_API_STATE (regionSequencesByMappedIDs.count (regionSequenceArchiveID) == 0); // make sure audio Modification persistentIDs in filter are unique + auto regionSequenceCurrentID { (filter->regionSequenceCurrentIDs != nullptr) ? filter->regionSequenceCurrentIDs[i] : regionSequenceArchiveID }; + + const auto it { _regionSequencesByID.find (regionSequenceCurrentID) }; + if (it != _regionSequencesByID.end ()) + regionSequencesByMappedIDs[regionSequenceArchiveID] = it->second; + } + _regionSequencesByID = std::move (regionSequencesByMappedIDs); + } } } @@ -750,9 +787,15 @@ AudioModification* RestoreObjectsFilter::getAudioModificationToRestoreStateWithI return (it != _audioModificationsByID.end ()) ? it->second : nullptr; } +RegionSequence* RestoreObjectsFilter::getRegionSequenceToRestoreStateWithID (ARAPersistentID regionSequenceID) const noexcept +{ + const auto it { _regionSequencesByID.find (regionSequenceID) }; + return (it != _regionSequencesByID.end ()) ? it->second : nullptr; +} + /*******************************************************************************/ -StoreObjectsFilter::StoreObjectsFilter (const ARAStoreObjectsFilter* filter) noexcept +StoreObjectsFilter::StoreObjectsFilter (const SizedStructPtr filter) noexcept : _filter { filter } { ARA_INTERNAL_ASSERT (filter != nullptr); @@ -760,6 +803,12 @@ StoreObjectsFilter::StoreObjectsFilter (const ARAStoreObjectsFilter* filter) noe _audioSourcesToStore.push_back (fromRef (_filter->audioSourceRefs[i])); for (ARASize i { 0 }; i < _filter->audioModificationRefsCount; ++i) _audioModificationsToStore.push_back (fromRef (_filter->audioModificationRefs[i])); + + if (filter.implements<&ARAStoreObjectsFilter::regionSequenceRefs> ()) + { + for (ARASize i { 0 }; i < _filter->regionSequenceRefsCount; ++i) + _regionSequencesToStore.push_back (fromRef (_filter->regionSequenceRefs[i])); + } } StoreObjectsFilter::StoreObjectsFilter (const Document* document) noexcept @@ -769,6 +818,12 @@ StoreObjectsFilter::StoreObjectsFilter (const Document* document) noexcept _audioModificationsToStore.reserve (_audioSourcesToStore.size ()); for (const auto& audioSource : _audioSourcesToStore) _audioModificationsToStore.insert (_audioModificationsToStore.end (), audioSource->getAudioModifications ().begin (), audioSource->getAudioModifications ().end ()); + + for (const auto& regionSequence : document->getRegionSequences ()) + { + if (regionSequence->getPersistentID ()) + _regionSequencesToStore.emplace_back (regionSequence); + } } bool StoreObjectsFilter::shouldStoreDocumentData () const noexcept @@ -1189,10 +1244,11 @@ bool DocumentControllerDelegate::doStoreAudioSourceToAudioFileChunk (HostArchive *openAutomatically = false; ARAAudioSourceRef audioSourceRef { toRef (audioSource) }; - const SizedStruct<&ARAStoreObjectsFilter::audioModificationRefs> filter { kARATrue, - 1U, &audioSourceRef, - 0U, nullptr - }; + const SizedStruct<&ARAStoreObjectsFilter::regionSequenceRefs> filter { kARATrue, + 1U, &audioSourceRef, + 0U, nullptr, + 0U, nullptr + }; const StoreObjectsFilter storeObjectsFilter { &filter }; return doStoreObjectsToArchive (archiveWriter, &storeObjectsFilter); } diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index 9f46812..3524255 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -380,6 +380,7 @@ class RegionSequence const OptionalProperty& getName () const noexcept { return _name; } //!< See ARARegionSequenceProperties::name. ARAInt32 getOrderIndex () const noexcept { return _orderIndex; } //!< See ARARegionSequenceProperties::orderIndex. const OptionalProperty& getColor () const noexcept { return _color; } //!< See ARARegionSequenceProperties::color. + ARA_DRAFT const OptionalProperty& getPersistentID () const noexcept { return _persistentID; } //!< See ARARegionSequenceProperties::persistentID. //@} //! @name Region Sequence Relationships @@ -422,6 +423,7 @@ class RegionSequence OptionalProperty _name; ARAInt32 _orderIndex { 0 }; OptionalProperty _color; + OptionalProperty _persistentID; std::vector _playbackRegions; ARA_HOST_MANAGED_OBJECT (RegionSequence) @@ -732,7 +734,7 @@ class RestoreObjectsFilter }; public: - RestoreObjectsFilter (const ARARestoreObjectsFilter* filter, Document* document) noexcept; + RestoreObjectsFilter (const SizedStructPtr filter, Document* document) noexcept; //! @name Filter Queries //! Use these functions to filter and map the objects restored during DocumentController::doRestoreObjectsFromArchive(). @@ -746,12 +748,17 @@ class RestoreObjectsFilter AudioModification* getAudioModificationToRestoreStateWithID (ARAPersistentID archivedAudioModificationID) const noexcept; template AudioModification_t* getAudioModificationToRestoreStateWithID (ARAPersistentID archivedAudioModificationID) const noexcept { return static_cast (getAudioModificationToRestoreStateWithID (archivedAudioModificationID)); } + + ARA_DRAFT RegionSequence* getRegionSequenceToRestoreStateWithID (ARAPersistentID archivedRegionSequenceID) const noexcept; + template + ARA_DRAFT RegionSequence_t* getRegionSequenceToRestoreStateWithID (ARAPersistentID archivedRegionSequenceID) const noexcept { return static_cast (getRegionSequenceToRestoreStateWithID (archivedRegionSequenceID)); } //@} private: const ARARestoreObjectsFilter* _filter; std::map _audioSourcesByID; std::map _audioModificationsByID; + std::map _regionSequencesByID; }; @@ -760,7 +767,9 @@ class RestoreObjectsFilter class StoreObjectsFilter { public: - explicit StoreObjectsFilter (const ARAStoreObjectsFilter* filter) noexcept; + //! use this c'tor when host-provided filter is not a nullptr + explicit StoreObjectsFilter (const SizedStructPtr filter) noexcept; + //! use this c'tor when host-provided filter is a nullptr explicit StoreObjectsFilter (const Document* document) noexcept; //! @name Filter Queries @@ -772,12 +781,16 @@ class StoreObjectsFilter std::vector const& getAudioSourcesToStore () const noexcept { return vector_cast (_audioSourcesToStore); } template std::vector const& getAudioModificationsToStore () const noexcept { return vector_cast (_audioModificationsToStore); } + + template + ARA_DRAFT std::vector const& getRegionSequencesToStore () const noexcept { return vector_cast (_regionSequencesToStore); } //@} private: const ARAStoreObjectsFilter* _filter; std::vector _audioSourcesToStore; std::vector _audioModificationsToStore; + std::vector _regionSequencesToStore; }; //! @} ARA_Library_ARAPlug_Utility_Classes From 661c46a16c35fdae117fc46dc93d8ee6d4277b29 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Fri, 1 May 2026 12:01:43 +0200 Subject: [PATCH 38/42] Add notifyRegionSequenceDataChanged() to complement region sequence persistency --- Dispatch/ARAHostDispatch.cpp | 10 ++++++++-- Dispatch/ARAHostDispatch.h | 2 ++ Dispatch/ARAPlugInDispatch.cpp | 27 +++++++++++++++++++++++---- Dispatch/ARAPlugInDispatch.h | 8 ++++++++ IPC/ARAIPCEncoding.h | 1 + IPC/ARAIPCProxyHost.cpp | 8 ++++++++ IPC/ARAIPCProxyPlugIn.cpp | 11 +++++++++++ PlugIn/ARAPlug.cpp | 25 ++++++++++++++++++++++++- PlugIn/ARAPlug.h | 8 +++++++- 9 files changed, 92 insertions(+), 8 deletions(-) diff --git a/Dispatch/ARAHostDispatch.cpp b/Dispatch/ARAHostDispatch.cpp index 483ab27..fc204e3 100644 --- a/Dispatch/ARAHostDispatch.cpp +++ b/Dispatch/ARAHostDispatch.cpp @@ -218,15 +218,21 @@ namespace ModelUpdateControllerDispatcher fromHostRef (controllerHostRef)->notifyDocumentDataChanged (); } + static void ARA_CALL notifyRegionSequenceDataChanged (ARAModelUpdateControllerHostRef controllerHostRef, ARARegionSequenceHostRef regionSequenceHostRef) noexcept + { + fromHostRef (controllerHostRef)->notifyRegionSequenceDataChanged (regionSequenceHostRef); + } + static const ARAModelUpdateControllerInterface* getInterface () noexcept { - static const SizedStruct<&ARAModelUpdateControllerInterface::notifyDocumentDataChanged> ifc = + static const SizedStruct<&ARAModelUpdateControllerInterface::notifyRegionSequenceDataChanged> ifc = { ModelUpdateControllerDispatcher::notifyAudioSourceAnalysisProgress, ModelUpdateControllerDispatcher::notifyAudioSourceContentChanged, ModelUpdateControllerDispatcher::notifyAudioModificationContentChanged, ModelUpdateControllerDispatcher::notifyPlaybackRegionContentChanged, - ModelUpdateControllerDispatcher::notifyDocumentDataChanged + ModelUpdateControllerDispatcher::notifyDocumentDataChanged, + ModelUpdateControllerDispatcher::notifyRegionSequenceDataChanged }; return &ifc; } diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index 3bfb0a6..4b8fbdc 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -180,6 +180,8 @@ class ModelUpdateControllerInterface virtual void notifyPlaybackRegionContentChanged (ARAPlaybackRegionHostRef playbackRegionHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept = 0; //! \copydoc ARAModelUpdateControllerInterface::notifyDocumentDataChanged virtual void notifyDocumentDataChanged () noexcept = 0; + //! \copydoc ARAModelUpdateControllerInterface::notifyRegionSequenceDataChanged + ARA_DRAFT virtual void notifyRegionSequenceDataChanged (ARA::ARARegionSequenceHostRef regionSequenceHostRef) noexcept = 0; }; ARA_MAP_HOST_REF (ModelUpdateControllerInterface, ARAModelUpdateControllerHostRef) diff --git a/Dispatch/ARAPlugInDispatch.cpp b/Dispatch/ARAPlugInDispatch.cpp index 035a9f9..76b2d1e 100644 --- a/Dispatch/ARAPlugInDispatch.cpp +++ b/Dispatch/ARAPlugInDispatch.cpp @@ -645,21 +645,40 @@ void HostModelUpdateController::notifyAudioModificationContentChanged (ARAAudioM getInterface ()->notifyAudioModificationContentChanged (getRef (), audioModificationHostRef, range, scopeFlags); } +bool HostModelUpdateController::supportsNotifyPlaybackRegionContentChanged () noexcept +{ + return getInterface ().implements<&ARAModelUpdateControllerInterface::notifyPlaybackRegionContentChanged> (); +} + void HostModelUpdateController::notifyPlaybackRegionContentChanged (ARAPlaybackRegionHostRef playbackRegionHostRef, const ARAContentTimeRange* range, ContentUpdateScopes scopeFlags) noexcept { - // notifyPlaybackRegionContentChanged was optional in the ARA 2.0 draft, so check its presence here to be safe - if (getInterface ().implements<&ARAModelUpdateControllerInterface::notifyPlaybackRegionContentChanged> ()) + if (supportsNotifyPlaybackRegionContentChanged ()) getInterface ()->notifyPlaybackRegionContentChanged (getRef (), playbackRegionHostRef, range, scopeFlags); } +bool HostModelUpdateController::supportsNotifyDocumentDataChanged () noexcept +{ + return getInterface ().implements<&ARAModelUpdateControllerInterface::notifyDocumentDataChanged> (); +} + void HostModelUpdateController::notifyDocumentDataChanged () noexcept { - // notifyDocumentDataChanged was added in ARA 2.3 draft, so check its presence here - if (getInterface ().implements<&ARAModelUpdateControllerInterface::notifyDocumentDataChanged> ()) + if (supportsNotifyDocumentDataChanged ()) getInterface ()->notifyDocumentDataChanged (getRef ()); } +bool HostModelUpdateController::supportsNotifyRegionSequenceDataChanged () noexcept +{ + return getInterface ().implements<&ARAModelUpdateControllerInterface::notifyRegionSequenceDataChanged> (); +} + +void HostModelUpdateController::notifyRegionSequenceDataChanged (ARARegionSequenceHostRef regionSequenceHostRef) noexcept +{ + if (supportsNotifyRegionSequenceDataChanged ()) + getInterface ()->notifyRegionSequenceDataChanged (getRef (), regionSequenceHostRef); +} + /*******************************************************************************/ // PlaybackController /*******************************************************************************/ diff --git a/Dispatch/ARAPlugInDispatch.h b/Dispatch/ARAPlugInDispatch.h index 3470abe..39eb62c 100644 --- a/Dispatch/ARAPlugInDispatch.h +++ b/Dispatch/ARAPlugInDispatch.h @@ -452,11 +452,19 @@ class HostModelUpdateController : public InterfaceInstancewasCreatedOnCurrentThread ()); + remoteCall (ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyRegionSequenceDataChanged), + _remoteHostRef, regionSequenceHostRef); +} + /*******************************************************************************/ //! Implementation of PlaybackControllerInterface that channels all calls through IPC diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp index 24ee738..8d8bea5 100644 --- a/IPC/ARAIPCProxyPlugIn.cpp +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -1780,6 +1780,17 @@ void ProxyPlugIn::handleReceivedMessage (const MessageID messageID, const Messag documentController->getHostModelUpdateController ()->notifyDocumentDataChanged (); } + else if (messageID == ARA_IPC_METHOD_ID (ARAModelUpdateControllerInterface, notifyRegionSequenceDataChanged)) + { + ARAModelUpdateControllerHostRef controllerHostRef; + ARARegionSequenceHostRef regionSequenceHostRef; + decodeArguments (decoder, controllerHostRef, regionSequenceHostRef); + + auto documentController { fromHostRef (controllerHostRef) }; + ARA_VALIDATE_API_ARGUMENT (controllerHostRef, isValidInstance (documentController)); + + documentController->getHostModelUpdateController ()->notifyRegionSequenceDataChanged (regionSequenceHostRef); + } // ARAPlaybackControllerInterface else if (messageID == ARA_IPC_METHOD_ID (ARAPlaybackControllerInterface, requestStartPlayback)) diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index 028a931..50a65d3 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -1148,10 +1148,24 @@ void DocumentController::notifyModelUpdates () noexcept hostModelUpdateController->notifyPlaybackRegionContentChanged (playbackRegionUpdate.first->getHostRef (), nullptr, playbackRegionUpdate.second); _playbackRegionContentUpdates.clear (); + if (_regionSequenceDataUpdates.size () > 0) + { + if (hostModelUpdateController->supportsNotifyRegionSequenceDataChanged ()) + { + for (const auto& regionSequence : _regionSequenceDataUpdates) + hostModelUpdateController->notifyRegionSequenceDataChanged (regionSequence->getHostRef ()); + } + else + { + _documentDataChanged = true; // aka notifyDocumentDataChanged() + } + _regionSequenceDataUpdates.clear (); + } + if (_documentDataChanged) hostModelUpdateController->notifyDocumentDataChanged (); _documentDataChanged = false; - + didNotifyModelUpdates (); } @@ -1477,6 +1491,9 @@ void DocumentController::destroyRegionSequence (ARARegionSequenceRef regionSeque ARA_LOG_MODELOBJECT_LIFETIME ("will destroy region sequence", regionSequence); willDestroyRegionSequence (regionSequence); + + _regionSequenceDataUpdates.erase (regionSequence); + doDestroyRegionSequence (regionSequence); } @@ -2336,6 +2353,12 @@ void DocumentController::notifyPlaybackRegionContentChanged (PlaybackRegion* pla _playbackRegionContentUpdates[playbackRegion] += scopeFlags; } +void DocumentController::notifyRegionSequenceDataChanged (RegionSequence* regionSequence) noexcept +{ + if (getHostModelUpdateController ()) + _regionSequenceDataUpdates.insert (regionSequence); +} + void DocumentController::notifyDocumentDataChanged () noexcept { _documentDataChanged = true; diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index 3524255..7b89767 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -1273,15 +1273,20 @@ class DocumentController : public DocumentControllerInterface, //! @name Sending content updates to the host //! The implementation will internally enqueue the updates and later send them to the host -//! from notifyModelUpdates (). +//! from notifyModelUpdates(). //! Note that while the ARA API allows for specifying affected time ranges for content updates, //! this feature is not yet supported in our current plug-in implementation (since most hosts //! do not evaluate this either). +//! Since older ARA 2.x hosts will not yet support notifyRegionSequenceDataChanged(), the implementation +//! will eventually fall back to calling notifyDocumentDataChanged() instead if needed. This allows +//! plug-ins to consistently utilize the new update APIs (but they will still need to branch out for +//! old hosts/archives in their implementation of doStoreObjectsToArchive()/doRestoreObjectsFromArchive()). //@{ void notifyAudioSourceContentChanged (AudioSource* audioSource, ContentUpdateScopes scopeFlags) noexcept; void notifyAudioModificationContentChanged (AudioModification* audioModification, ContentUpdateScopes scopeFlags) noexcept; void notifyPlaybackRegionContentChanged (PlaybackRegion* playbackRegion, ContentUpdateScopes scopeFlags) noexcept; void notifyDocumentDataChanged () noexcept; + ARA_DRAFT void notifyRegionSequenceDataChanged (RegionSequence* regionSequence) noexcept; //@} // Helper for analysis requests. @@ -1356,6 +1361,7 @@ class DocumentController : public DocumentControllerInterface, std::map _audioSourceContentUpdates; std::map _audioModificationContentUpdates; std::map _playbackRegionContentUpdates; + std::set _regionSequenceDataUpdates; bool _documentDataChanged { false }; std::atomic_flag _analysisProgressIsSynced {}; // { true } would be better but C++ standard only allows for default-init to false From f60f3f07860ff3ac24087011c9604ced4671def0 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Fri, 1 May 2026 12:01:44 +0200 Subject: [PATCH 39/42] Add isPlaybackRegionPreservingAudioSourceSignal(), superseding isAudioModificationPreservingAudioSourceSignal() --- ChangeLog.txt | 1 + Dispatch/ARAHostDispatch.cpp | 12 ++++++++++++ Dispatch/ARAHostDispatch.h | 8 ++++++-- Dispatch/ARAPlugInDispatch.cpp | 14 ++++++++++---- Dispatch/ARAPlugInDispatch.h | 6 ++++-- IPC/ARAIPCEncoding.h | 1 + IPC/ARAIPCProxyHost.cpp | 8 ++++++++ IPC/ARAIPCProxyPlugIn.cpp | 12 ++++++++++++ PlugIn/ARAPlug.cpp | 11 +++++++++++ PlugIn/ARAPlug.h | 3 +++ 10 files changed, 68 insertions(+), 8 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 4c5806c..1bddb37 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -4,6 +4,7 @@ This is a development build of the ARA Library 3.0. Changes since previous releases: - initial draft of region sequence persistency +- added isPlaybackRegionPreservingAudioSourceSignal(), superseding isAudioModificationPreservingAudioSourceSignal() - initial draft of generic ARA IPC library providing a proxy host and a proxy plug-in, based on heavily refactored IPC Example from earlier SDK releases - dropped support for ARA 1 and 2.0 Draft APIs diff --git a/Dispatch/ARAHostDispatch.cpp b/Dispatch/ARAHostDispatch.cpp index fc204e3..ea62e79 100644 --- a/Dispatch/ARAHostDispatch.cpp +++ b/Dispatch/ARAHostDispatch.cpp @@ -510,6 +510,18 @@ void DocumentController::updatePlaybackRegionProperties (ARAPlaybackRegionRef pl getInterface ()->updatePlaybackRegionProperties (getRef (), playbackRegionRef, properties); } +bool DocumentController::supportsIsPlaybackRegionPreservingAudioSourceSignal () noexcept +{ + return getInterface ().implements<&ARADocumentControllerInterface::isPlaybackRegionPreservingAudioSourceSignal> (); +} + +bool DocumentController::isPlaybackRegionPreservingAudioSourceSignal (ARAPlaybackRegionRef playbackRegionRef) noexcept +{ + if (!supportsIsPlaybackRegionPreservingAudioSourceSignal ()) + return false; + return (getInterface ()->isPlaybackRegionPreservingAudioSourceSignal (getRef (), playbackRegionRef) != kARAFalse); +} + void DocumentController::getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept { return getInterface ()->getPlaybackRegionHeadAndTailTime (getRef (), playbackRegionRef, headTime, tailTime); diff --git a/Dispatch/ARAHostDispatch.h b/Dispatch/ARAHostDispatch.h index 4b8fbdc..0cf647f 100644 --- a/Dispatch/ARAHostDispatch.h +++ b/Dispatch/ARAHostDispatch.h @@ -349,7 +349,7 @@ class DocumentController : public InterfaceInstanceupdatePlaybackRegionProperties (playbackRegionRef, properties); } - static void ARA_CALL destroyPlaybackRegion (ARADocumentControllerRef controllerRef, ARAPlaybackRegionRef playbackRegionRef) noexcept + static ARABool ARA_CALL isPlaybackRegionPreservingAudioSourceSignal (ARADocumentControllerRef controllerRef, ARAPlaybackRegionRef playbackRegionRef) noexcept { - fromRef (controllerRef)->destroyPlaybackRegion (playbackRegionRef); + return fromRef (controllerRef)->isPlaybackRegionPreservingAudioSourceSignal (playbackRegionRef) ? kARATrue : kARAFalse; } static void ARA_CALL getPlaybackRegionHeadAndTailTime (ARADocumentControllerRef controllerRef, ARAPlaybackRegionRef playbackRegionRef, @@ -235,6 +235,11 @@ namespace DocumentControllerDispatcher fromRef (controllerRef)->getPlaybackRegionHeadAndTailTime (playbackRegionRef, headTime, tailTime); } + static void ARA_CALL destroyPlaybackRegion (ARADocumentControllerRef controllerRef, ARAPlaybackRegionRef playbackRegionRef) noexcept + { + fromRef (controllerRef)->destroyPlaybackRegion (playbackRegionRef); + } + // Content Reader Management static ARABool ARA_CALL isAudioSourceContentAvailable (ARADocumentControllerRef controllerRef, ARAAudioSourceRef audioSourceRef, ARAContentType type) noexcept @@ -342,7 +347,7 @@ namespace DocumentControllerDispatcher static const ARADocumentControllerInterface* getInterface () noexcept { - static const SizedStruct<&ARADocumentControllerInterface::isAudioModificationPreservingAudioSourceSignal> ifc = + static const SizedStruct<&ARADocumentControllerInterface::isPlaybackRegionPreservingAudioSourceSignal> ifc = { DocumentControllerDispatcher::destroyDocumentController, DocumentControllerDispatcher::getFactory, @@ -397,7 +402,8 @@ namespace DocumentControllerDispatcher DocumentControllerDispatcher::requestProcessingAlgorithmForAudioSource, DocumentControllerDispatcher::isLicensedForCapabilities, DocumentControllerDispatcher::storeAudioSourceToAudioFileChunk, - DocumentControllerDispatcher::isAudioModificationPreservingAudioSourceSignal + DocumentControllerDispatcher::isAudioModificationPreservingAudioSourceSignal, + DocumentControllerDispatcher::isPlaybackRegionPreservingAudioSourceSignal }; return &ifc; } diff --git a/Dispatch/ARAPlugInDispatch.h b/Dispatch/ARAPlugInDispatch.h index 39eb62c..4810d36 100644 --- a/Dispatch/ARAPlugInDispatch.h +++ b/Dispatch/ARAPlugInDispatch.h @@ -186,10 +186,12 @@ class DocumentControllerInterface virtual ARAPlaybackRegionRef createPlaybackRegion (ARAAudioModificationRef audioModificationRef, ARAPlaybackRegionHostRef hostRef, PropertiesPtr properties) noexcept = 0; //! \copydoc ARADocumentControllerInterface::updatePlaybackRegionProperties virtual void updatePlaybackRegionProperties (ARAPlaybackRegionRef playbackRegionRef, PropertiesPtr properties) noexcept = 0; - //! \copydoc ARADocumentControllerInterface::destroyPlaybackRegion - virtual void destroyPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept = 0; + //! \copydoc ARADocumentControllerInterface::isPlaybackRegionPreservingAudioSourceSignal + ARA_DRAFT virtual bool isPlaybackRegionPreservingAudioSourceSignal (ARAPlaybackRegionRef playbackRegionRef) noexcept = 0; //! \copydoc ARADocumentControllerInterface::getPlaybackRegionHeadAndTailTime virtual void getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept = 0; + //! \copydoc ARADocumentControllerInterface::destroyPlaybackRegion + virtual void destroyPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept = 0; //@} //! @name Content Reader Management diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index e47ea9f..ddcf5ee 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -1113,6 +1113,7 @@ inline const char* decodeHostMessageID (const MessageID messageID, const bool om ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isLicensedForCapabilities) ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, storeAudioSourceToAudioFileChunk) ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isAudioModificationPreservingAudioSourceSignal) + ARA_IPC_INTERFACE_MESSAGE_CASE (ARADocumentControllerInterface, isPlaybackRegionPreservingAudioSourceSignal) ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackRendererInterface, addPlaybackRegion) ARA_IPC_INTERFACE_MESSAGE_CASE (ARAPlaybackRendererInterface, removePlaybackRegion) ARA_IPC_INTERFACE_MESSAGE_CASE (ARAEditorRendererInterface, addPlaybackRegion) diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index d88b65b..d2bae8a 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -1137,6 +1137,14 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD fromRef (controllerRef)->updatePlaybackRegionProperties (playbackRegionRef, &properties); } + else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isPlaybackRegionPreservingAudioSourceSignal)) + { + ARADocumentControllerRef controllerRef; + ARAPlaybackRegionRef playbackRegionRef; + decodeArguments (decoder, controllerRef, playbackRegionRef); + + encodeReply (replyEncoder, (fromRef (controllerRef)->isPlaybackRegionPreservingAudioSourceSignal (playbackRegionRef)) ? kARATrue : kARAFalse); + } else if (messageID == ARA_IPC_METHOD_ID (ARADocumentControllerInterface, getPlaybackRegionHeadAndTailTime)) { ARADocumentControllerRef controllerRef; diff --git a/IPC/ARAIPCProxyPlugIn.cpp b/IPC/ARAIPCProxyPlugIn.cpp index 8d8bea5..6b71eb4 100644 --- a/IPC/ARAIPCProxyPlugIn.cpp +++ b/IPC/ARAIPCProxyPlugIn.cpp @@ -234,6 +234,7 @@ class DocumentController : public PlugIn::DocumentControllerInterface, public Re // Playback Region Management ARAPlaybackRegionRef createPlaybackRegion (ARAAudioModificationRef audioModificationRef, ARAPlaybackRegionHostRef hostRef, PropertiesPtr properties) noexcept override; void updatePlaybackRegionProperties (ARAPlaybackRegionRef playbackRegionRef, PropertiesPtr properties) noexcept override; + bool isPlaybackRegionPreservingAudioSourceSignal (ARAPlaybackRegionRef playbackRegionRef) noexcept override; void getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept override; void destroyPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override; @@ -784,6 +785,17 @@ void DocumentController::updatePlaybackRegionProperties (ARAPlaybackRegionRef pl remoteCall (ARA_IPC_METHOD_ID (ARADocumentControllerInterface, updatePlaybackRegionProperties), _remoteRef, playbackRegionRef, *properties); } +bool DocumentController::isPlaybackRegionPreservingAudioSourceSignal (ARAPlaybackRegionRef playbackRegionRef) noexcept +{ + ARA_LOG_HOST_ENTRY (playbackRegionRef); + ARA_INTERNAL_ASSERT (isValidInstance (this)); + ARA_INTERNAL_ASSERT (getConnection ()->wasCreatedOnCurrentThread ()); + + ARABool result; + remoteCall (result, ARA_IPC_METHOD_ID (ARADocumentControllerInterface, isPlaybackRegionPreservingAudioSourceSignal), _remoteRef, playbackRegionRef); + return (result != kARAFalse); +} + void DocumentController::getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept { ARA_LOG_HOST_ENTRY (playbackRegionRef); diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index 50a65d3..e76d383 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -1794,6 +1794,17 @@ void DocumentController::updatePlaybackRegionProperties (ARAPlaybackRegionRef pl ARA_LOG_PROPERTY_CHANGES ("did update properties of playback region", playbackRegion); } +bool DocumentController::isPlaybackRegionPreservingAudioSourceSignal (ARAPlaybackRegionRef playbackRegionRef) noexcept +{ + ARA_LOG_HOST_ENTRY (playbackRegionRef); + ARA_VALIDATE_API_ARGUMENT (this, isValidDocumentController (this)); + ARA_VALIDATE_API_THREAD (wasCreatedOnCurrentThread ()); + + auto playbackRegion { fromRef (playbackRegionRef) }; + ARA_VALIDATE_API_ARGUMENT (playbackRegionRef, isValidPlaybackRegion (playbackRegion)); + return doIsPlaybackRegionPreservingAudioSourceSignal (playbackRegion); +} + void DocumentController::getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept { ARA_LOG_HOST_ENTRY (playbackRegionRef); diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index 7b89767..b8ccae2 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -982,6 +982,8 @@ class DocumentControllerDelegate virtual void willUpdatePlaybackRegionProperties (PlaybackRegion* playbackRegion, PropertiesPtr newProperties) noexcept {} //! Override to customize post-update behavior of updatePlaybackRegionProperties(). virtual void didUpdatePlaybackRegionProperties (PlaybackRegion* playbackRegion) noexcept {} + //! Override to implement isPlaybackRegionPreservingAudioSourceSignal(). + ARA_DRAFT virtual bool doIsPlaybackRegionPreservingAudioSourceSignal (PlaybackRegion* playbackRegion) noexcept { return false; } //! Override to define a content based fade for \p playbackRegion by assigning positive values to \p headTime and/or \p tailTime - see getPlaybackRegionHeadAndTailTime(). virtual void doGetPlaybackRegionHeadAndTailTime (const PlaybackRegion* playbackRegion, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept { *headTime = 0.0; *tailTime = 0.0; } //! Override to customize behavior before \p playbackRegion is destroyed during destroyPlaybackRegion(). @@ -1177,6 +1179,7 @@ class DocumentController : public DocumentControllerInterface, // Playback Region Management ARAPlaybackRegionRef createPlaybackRegion (ARAAudioModificationRef audioModificationRef, ARAPlaybackRegionHostRef hostRef, PropertiesPtr properties) noexcept override; void updatePlaybackRegionProperties (ARAPlaybackRegionRef playbackRegionRef, PropertiesPtr properties) noexcept override; + ARA_DRAFT bool isPlaybackRegionPreservingAudioSourceSignal (ARAPlaybackRegionRef playbackRegionRef) noexcept override; void getPlaybackRegionHeadAndTailTime (ARAPlaybackRegionRef playbackRegionRef, ARATimeDuration* headTime, ARATimeDuration* tailTime) noexcept override; void destroyPlaybackRegion (ARAPlaybackRegionRef playbackRegionRef) noexcept override; From a4ab827e4c377f252abf2bce000fd84000d88704 Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Fri, 1 May 2026 12:01:44 +0200 Subject: [PATCH 40/42] Initial draft of lyrics-related content, tailored to support singing voice synthesis --- ChangeLog.txt | 1 + Debug/ARAContentLogger.h | 21 +++++++++++++++++++-- Debug/ARAContentValidator.h | 19 +++++++++++++++++++ Dispatch/ARAContentReader.h | 1 + Dispatch/ARADispatchBase.h | 10 +++++++--- IPC/ARAIPCEncoding.h | 19 +++++++++++++++++++ 6 files changed, 66 insertions(+), 5 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 1bddb37..4c306ad 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -3,6 +3,7 @@ This is a development build of the ARA Library 3.0. Changes since previous releases: +- initial draft of lyrics-related content types, tailored to support singing voice synthesis - initial draft of region sequence persistency - added isPlaybackRegionPreservingAudioSourceSignal(), superseding isAudioModificationPreservingAudioSourceSignal() - initial draft of generic ARA IPC library providing a proxy host and a proxy plug-in, diff --git a/Debug/ARAContentLogger.h b/Debug/ARAContentLogger.h index ff310bb..1c930e4 100644 --- a/Debug/ARAContentLogger.h +++ b/Debug/ARAContentLogger.h @@ -55,12 +55,13 @@ struct ContentLogger // array of all content types defined in the api - static inline constexpr std::array getAllContentTypes () noexcept + static inline constexpr std::array getAllContentTypes () noexcept { return { { kARAContentTypeNotes, kARAContentTypeTempoEntries, kARAContentTypeBarSignatures, kARAContentTypeStaticTuning, - kARAContentTypeKeySignatures, kARAContentTypeSheetChords + kARAContentTypeKeySignatures, kARAContentTypeSheetChords, + kARAContentTypeLyricEntries } }; } @@ -88,6 +89,7 @@ struct ContentLogger case kARAContentTypeStaticTuning: return ContentTypeMapper::enumName; case kARAContentTypeKeySignatures: return ContentTypeMapper::enumName; case kARAContentTypeSheetChords: return ContentTypeMapper::enumName; + case kARAContentTypeLyricEntries: return ContentTypeMapper::enumName; default: ARA_INTERNAL_ASSERT (false); return "kARAContentType???"; } } @@ -102,6 +104,7 @@ struct ContentLogger case kARAContentTypeStaticTuning: return ContentTypeMapper::typeName; case kARAContentTypeKeySignatures: return ContentTypeMapper::typeName; case kARAContentTypeSheetChords: return ContentTypeMapper::typeName; + case kARAContentTypeLyricEntries: return ContentTypeMapper::typeName; default: ARA_INTERNAL_ASSERT (false); return "ARAContent???"; } } @@ -188,6 +191,14 @@ struct ContentLogger (logGivenName && logParsedName) ? " aka " : "", (logGivenName && logParsedName) ? parsedChordName.c_str () : "", chordData.position); } + static inline void logEvent (ARAInt32 idx, const ARAContentLyricsEntry& lyricsEntry) + { + ARA_LOG ("%s[%i] %s%s, %i %s phonemes%s, position = %.3f", getTypeNameForContentType (kARAContentTypeLyricEntries), idx, + (lyricsEntry.continuesPreviousWord) ? "-" : "", lyricsEntry.lyrics, + lyricsEntry.phonemeCount, getNameForContentGrade (lyricsEntry.phonemesGrade), + (lyricsEntry.phonemeOffsets) ? " w/ offsets": "", lyricsEntry.position); + } + // internal helper for log () template = true> @@ -247,6 +258,7 @@ struct ContentLogger case kARAContentTypeStaticTuning: return log (controller, modelObjectRef, range, logIfNotAvailable); case kARAContentTypeKeySignatures: return log (controller, modelObjectRef, range, logIfNotAvailable); case kARAContentTypeSheetChords: return log (controller, modelObjectRef, range, logIfNotAvailable); + case kARAContentTypeLyricEntries: return log (controller, modelObjectRef, range, logIfNotAvailable); default: ARA_INTERNAL_ASSERT (false); return false; } } @@ -316,6 +328,11 @@ struct ContentLogger log (controller, modelObjectRef, range, false); log (controller, modelObjectRef, range, false); } + if (scopeFlags.affectLyrics ()) + { + ARA_LOG ("lyrics scope updated, related content is:"); + log (controller, modelObjectRef, range, false); + } } }; diff --git a/Debug/ARAContentValidator.h b/Debug/ARAContentValidator.h index e578922..666ba27 100644 --- a/Debug/ARAContentValidator.h +++ b/Debug/ARAContentValidator.h @@ -137,6 +137,25 @@ struct ContentReaderValidatorImplementation } }; +template <> +struct ContentReaderValidatorImplementation +{ + static inline void validateEventCount (ARAInt32 eventCount) { ARA_VALIDATE_API_CONDITION (eventCount >= 0); } + + static inline void validateEvent (const ARAContentLyricsEntry* event) + { + ARA_VALIDATE_API_CONDITION ((event->continuesPreviousWord == kARAFalse) || (event->lyrics != nullptr)); + ARA_VALIDATE_API_CONDITION ((event->phonemeCount == 0) || (event->phonemes != nullptr)); + ARA_VALIDATE_API_CONDITION ((event->phonemeCount != 0) || (event->phonemes == nullptr)); + ARA_VALIDATE_API_CONDITION ((event->phonemeCount != 0) || (event->phonemeOffsets == nullptr)); + } + + static inline void validateEventSequence (const ARAContentLyricsEntry* event, const ARAContentLyricsEntry* prevEvent) + { + ARA_VALIDATE_API_CONDITION (prevEvent->position < event->position); + } +}; + /*******************************************************************************/ // ContentReaderValidator diff --git a/Dispatch/ARAContentReader.h b/Dispatch/ARAContentReader.h index b852df6..a67edb5 100644 --- a/Dispatch/ARAContentReader.h +++ b/Dispatch/ARAContentReader.h @@ -51,6 +51,7 @@ struct ContentTypeMapper; ARA_SPECIALIZE_CONTENT_TYPE_MAPPER (kARAContentTypeStaticTuning, ARAContentTuning) ARA_SPECIALIZE_CONTENT_TYPE_MAPPER (kARAContentTypeKeySignatures, ARAContentKeySignature) ARA_SPECIALIZE_CONTENT_TYPE_MAPPER (kARAContentTypeSheetChords, ARAContentChord) + ARA_SPECIALIZE_CONTENT_TYPE_MAPPER (kARAContentTypeLyricEntries, ARAContentLyricsEntry) #undef ARA_SPECIALIZE_CONTENT_TYPE_MAPPER diff --git a/Dispatch/ARADispatchBase.h b/Dispatch/ARADispatchBase.h index 4b1da05..cb02043 100644 --- a/Dispatch/ARADispatchBase.h +++ b/Dispatch/ARADispatchBase.h @@ -334,6 +334,8 @@ class ContentUpdateScopes static constexpr ContentUpdateScopes tuningIsAffected () noexcept { return nothingIsAffected ()._flags & ~kARAContentUpdateTuningScopeRemainsUnchanged; } //! Content readers for key signatures, chords etc. are affected by the change. static constexpr ContentUpdateScopes harmoniesAreAffected () noexcept { return nothingIsAffected ()._flags & ~kARAContentUpdateHarmonicScopeRemainsUnchanged; } + //! Content readers for lyrics, phonemes etc. are affected by the change. + static constexpr ContentUpdateScopes lyricsAreAffected () noexcept { return nothingIsAffected ()._flags & ~kARAContentUpdateLyricsScopeRemainsUnchanged; } //! Everything is affected by the change. static constexpr ContentUpdateScopes everythingIsAffected () noexcept { return kARAContentUpdateEverythingChanged; } @@ -376,14 +378,16 @@ class ContentUpdateScopes constexpr bool affectTuning () const noexcept { return ((_flags & kARAContentUpdateTuningScopeRemainsUnchanged) == 0); } //! \copybrief harmoniesAreAffected constexpr bool affectHarmonies () const noexcept { return ((_flags & kARAContentUpdateHarmonicScopeRemainsUnchanged) == 0); } + //! \copybrief lyricsAreAffected + constexpr bool affectLyrics () const noexcept { return ((_flags & kARAContentUpdateLyricsScopeRemainsUnchanged) == 0); } //! \copybrief everythingIsAffected constexpr bool affectEverything () const noexcept { return ((_flags & _knownFlags) == 0); } //@} private: - static constexpr ARAContentUpdateFlags _knownFlags { (kARAContentUpdateSignalScopeRemainsUnchanged | - kARAContentUpdateNoteScopeRemainsUnchanged | kARAContentUpdateTimingScopeRemainsUnchanged | - kARAContentUpdateTuningScopeRemainsUnchanged | kARAContentUpdateHarmonicScopeRemainsUnchanged) }; + static constexpr ARAContentUpdateFlags _knownFlags { (kARAContentUpdateSignalScopeRemainsUnchanged | kARAContentUpdateNoteScopeRemainsUnchanged | + kARAContentUpdateTimingScopeRemainsUnchanged | kARAContentUpdateTuningScopeRemainsUnchanged | + kARAContentUpdateHarmonicScopeRemainsUnchanged | kARAContentUpdateLyricsScopeRemainsUnchanged) }; ARAContentUpdateFlags _flags; }; diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index ddcf5ee..c4f0567 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -742,6 +742,25 @@ ARA_IPC_BEGIN_DECODE (ARAContentChord) ARA_IPC_DECODE_MEMBER (position) ARA_IPC_END_DECODE +ARA_IPC_BEGIN_ENCODE (ARAContentLyricsEntry) + ARA_IPC_ENCODE_MEMBER (lyrics) + ARA_IPC_ENCODE_MEMBER (continuesPreviousWord) + ARA_IPC_ENCODE_MEMBER (language) + ARA_IPC_ENCODE_VARIABLE_ARRAY (phonemes, phonemeCount) + ARA_IPC_ENCODE_VARIABLE_ARRAY (phonemeOffsets, phonemeCount) + ARA_IPC_ENCODE_MEMBER (phonemesGrade) + ARA_IPC_ENCODE_MEMBER (position) +ARA_IPC_END_ENCODE +ARA_IPC_BEGIN_DECODE (ARAContentLyricsEntry) + ARA_IPC_DECODE_MEMBER (lyrics) + ARA_IPC_DECODE_MEMBER (continuesPreviousWord) + ARA_IPC_DECODE_MEMBER (language) + ARA_IPC_DECODE_VARIABLE_ARRAY (phonemes, phonemeCount, true) + ARA_IPC_DECODE_VARIABLE_ARRAY (phonemeOffsets, phonemeCount, false) + ARA_IPC_DECODE_MEMBER (phonemesGrade) + ARA_IPC_DECODE_MEMBER (position) +ARA_IPC_END_DECODE + ARA_IPC_BEGIN_ENCODE (ARARestoreObjectsFilter) ARA_IPC_ENCODE_MEMBER (documentData) ARA_IPC_ENCODE_VARIABLE_ARRAY (audioSourceArchiveIDs, audioSourceIDsCount) From 5fc1e83385f3af299f91e3f3ef51e4d06ca593ec Mon Sep 17 00:00:00 2001 From: sgretscher <41306803+sgretscher@users.noreply.github.com> Date: Fri, 1 May 2026 12:01:45 +0200 Subject: [PATCH 41/42] Initial draft of ARA generator plug-ins that operate merely based on content descriptions (no sample input) --- ChangeLog.txt | 1 + IPC/ARAIPCEncoding.h | 6 ++++++ PlugIn/ARAPlug.cpp | 9 ++++++++- PlugIn/ARAPlug.h | 18 ++++++++++++++---- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 4c306ad..b5f1c63 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -3,6 +3,7 @@ This is a development build of the ARA Library 3.0. Changes since previous releases: +- initial draft of ARA generator plug-ins that operate merely based on content descriptions (no sample input) - initial draft of lyrics-related content types, tailored to support singing voice synthesis - initial draft of region sequence persistency - added isPlaybackRegionPreservingAudioSourceSignal(), superseding isAudioModificationPreservingAudioSourceSignal() diff --git a/IPC/ARAIPCEncoding.h b/IPC/ARAIPCEncoding.h index c4f0567..0136536 100644 --- a/IPC/ARAIPCEncoding.h +++ b/IPC/ARAIPCEncoding.h @@ -824,6 +824,9 @@ ARA_IPC_BEGIN_ENCODE (ARAFactory) ARA_IPC_ENCODE_VARIABLE_ARRAY (analyzeableContentTypes, analyzeableContentTypesCount) ARA_IPC_ENCODE_MEMBER (supportedPlaybackTransformationFlags) ARA_IPC_ENCODE_ADDENDUM_MEMBER (supportsStoringAudioFileChunks) + ARA_IPC_ENCODE_ADDENDUM_MEMBER (supportsSampleBasedAudioSources) + ARA_IPC_ENCODE_ADDENDUM_MEMBER (supportsContentOnlyAudioSources) + ARA_IPC_ENCODE_ADDENDUM_MEMBER (requiresPresetAudioSources) ARA_IPC_END_ENCODE ARA_IPC_BEGIN_DECODE_SIZED (ARAFactory) ARA_IPC_DECODE_MEMBER (lowestSupportedApiGeneration) @@ -841,6 +844,9 @@ ARA_IPC_BEGIN_DECODE_SIZED (ARAFactory) ARA_IPC_DECODE_VARIABLE_ARRAY (analyzeableContentTypes, analyzeableContentTypesCount, true) ARA_IPC_DECODE_MEMBER (supportedPlaybackTransformationFlags) ARA_IPC_DECODE_ADDENDUM_MEMBER (supportsStoringAudioFileChunks) + ARA_IPC_DECODE_ADDENDUM_MEMBER (supportsSampleBasedAudioSources) + ARA_IPC_DECODE_ADDENDUM_MEMBER (supportsContentOnlyAudioSources) + ARA_IPC_DECODE_ADDENDUM_MEMBER (requiresPresetAudioSources) ARA_IPC_END_DECODE diff --git a/PlugIn/ARAPlug.cpp b/PlugIn/ARAPlug.cpp index e76d383..f0b020c 100644 --- a/PlugIn/ARAPlug.cpp +++ b/PlugIn/ARAPlug.cpp @@ -527,10 +527,14 @@ void AudioSource::updateProperties (PropertiesPtr prop ARA_VALIDATE_API_ARGUMENT (properties->persistentID, std::strlen (properties->persistentID) > 0); _persistentID = properties->persistentID; + [[maybe_unused]] const auto supportsContentOnlyAudioSources { getDocumentController ()->getFactory ()->supportsContentOnlyAudioSources != kARAFalse }; + ARA_VALIDATE_API_ARGUMENT (properties, properties->sampleCount >= ((supportsContentOnlyAudioSources) ? 0 : 1)); _sampleCount = properties->sampleCount; + ARA_VALIDATE_API_ARGUMENT (properties, properties->sampleRate > 0.0); _sampleRate = properties->sampleRate; _merits64BitSamples = (properties->merits64BitSamples != kARAFalse); + ARA_VALIDATE_API_ARGUMENT (properties, properties->channelCount >= ((supportsContentOnlyAudioSources) ? 0 : 1)); if (properties.implements<&ARAAudioSourceProperties::channelArrangement> ()) _channelFormat.update (properties->channelCount, properties->channelArrangementDataType, properties->channelArrangement); else @@ -2847,7 +2851,10 @@ PlugInEntry::PlugInEntry (const FactoryConfig* factoryConfig, factoryConfig->getDocumentArchiveID (), factoryConfig->getCompatibleDocumentArchiveIDsCount (), factoryConfig->getCompatibleDocumentArchiveIDs (), factoryConfig->getAnalyzeableContentTypesCount (), factoryConfig->getAnalyzeableContentTypes (), factoryConfig->getSupportedPlaybackTransformationFlags (), - (factoryConfig->supportsStoringAudioFileChunks ()) ? kARATrue : kARAFalse + (factoryConfig->supportsStoringAudioFileChunks ()) ? kARATrue : kARAFalse, + (factoryConfig->supportsSampleBasedAudioSources ()) ? kARATrue : kARAFalse, + (factoryConfig->supportsContentOnlyAudioSources ()) ? kARATrue : kARAFalse, + (factoryConfig->requiresPresetAudioSources ()) ? kARATrue : kARAFalse } { #if ARA_CPU_ARM diff --git a/PlugIn/ARAPlug.h b/PlugIn/ARAPlug.h index b8ccae2..df95120 100644 --- a/PlugIn/ARAPlug.h +++ b/PlugIn/ARAPlug.h @@ -1808,12 +1808,13 @@ class FactoryConfig virtual ~FactoryConfig () = default; //! \copydoc ARAFactory::lowestSupportedApiGeneration - virtual ARAAPIGeneration getLowestSupportedApiGeneration () const noexcept + virtual ARAAPIGeneration getLowestSupportedApiGeneration () const noexcept { return (supportsSampleBasedAudioSources ()) ? #if ARA_CPU_ARM - { return kARAAPIGeneration_2_0_Final; } + kARAAPIGeneration_2_0_Final : #else - { return kARAAPIGeneration_2_0_Draft; } + kARAAPIGeneration_2_0_Draft : #endif + kARAAPIGeneration_3_0_Draft; } //! \copydoc ARAFactory::highestSupportedApiGeneration virtual ARAAPIGeneration getHighestSupportedApiGeneration () const noexcept { return kARAAPIGeneration_3_0_Draft; } @@ -1848,6 +1849,15 @@ class FactoryConfig //! \copydoc ARAFactory::supportsStoringAudioFileChunks virtual bool supportsStoringAudioFileChunks () const noexcept { return false; } + + //! \copydoc ARAFactory::supportsSampleBasedAudioSources + virtual bool supportsSampleBasedAudioSources () const noexcept { return true; } + + //! \copydoc ARAFactory::supportsContentOnlyAudioSources + virtual bool supportsContentOnlyAudioSources () const noexcept { return false; } + + //! \copydoc ARAFactory::requiresPresetAudioSources + virtual bool requiresPresetAudioSources () const noexcept { return false; } }; @@ -1965,7 +1975,7 @@ class PlugInEntry private: const FactoryConfig* const _factoryConfig; - const SizedStruct<&ARAFactory::supportsStoringAudioFileChunks> _factory; + const SizedStruct<&ARAFactory::requiresPresetAudioSources> _factory; ARAAPIGeneration _usedApiGeneration { 0 }; ARA_DISABLE_COPY_AND_MOVE (PlugInEntry) From 057987bb1f19af5b5f04b90162f8e71760582a09 Mon Sep 17 00:00:00 2001 From: samuel-asleep Date: Fri, 19 Jun 2026 15:23:07 +0100 Subject: [PATCH 42/42] feat(IPC): Add Linux IPC support (with Wine compatibility) - Remove #error on non-Apple/non-Windows platforms - Add ThreadRef = size_t for Linux (matches std::thread::id on 64-bit POSIX) - Add Connection members: std::queue + std::mutex + std::condition_variable - Implement dispatchToCreationThread() for Linux - Implement processPendingMessageOnCreationThreadIfNeeded() for Linux - Add Wine-compatible WaitableSingleMessageQueue using POSIX sem_t (std::binary_semaphore hangs under Wine due to broken futex mapping) --- IPC/ARAIPCConnection.cpp | 102 ++++++++++++++++++++++++++++++++---- IPC/ARAIPCConnection.h | 12 +++-- IPC/ARAIPCProxyHost.cpp | 108 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 207 insertions(+), 15 deletions(-) diff --git a/IPC/ARAIPCConnection.cpp b/IPC/ARAIPCConnection.cpp index 06dd81d..aafc5a7 100644 --- a/IPC/ARAIPCConnection.cpp +++ b/IPC/ARAIPCConnection.cpp @@ -127,7 +127,7 @@ namespace ARA { namespace IPC { -#if defined (_WIN32) +#if defined (_WIN32) && !defined (__WINE__) #define readThreadRef readInt32 #define appendThreadRef appendInt32 #else @@ -142,12 +142,11 @@ static bool _debugAsHost {}; class MessageDispatcher { public: // needs to be public for thread-local variables (which cannot be class members) -#if defined (_WIN32) +#if defined (_WIN32) && !defined (__WINE__) using ThreadRef = int32_t; -#elif defined (__APPLE__) - using ThreadRef = size_t; #else - #error "not yet implemented on this platform" + // Apple, Linux, and Wine: std::thread::id is pointer-sized. + using ThreadRef = size_t; #endif static constexpr ThreadRef _invalidThread { 0 }; @@ -234,6 +233,56 @@ std::unique_ptr MessageDispatcher::_handleReceivedMessage (Messa //------------------------------------------------------------------------------ // helper for MainThreadMessageDispatcher: single-object message queue with semaphore to wait on +// Wine: std::binary_semaphore's try_acquire_for hangs (broken futex mapping). +// Use POSIX sem_t with sem_timedwait instead. +#if defined (__WINE__) +#include +#include +class WaitableSingleMessageQueue +{ + public: + WaitableSingleMessageQueue () { sem_init (&_sem, 0, 0); } + ~WaitableSingleMessageQueue () { sem_destroy (&_sem); } + + std::optional>> waitOnSemaphore (ARATimeDuration timeout) + { + bool didReceiveMessage; + if (timeout <= 0.0) + { + didReceiveMessage = (sem_trywait (&_sem) == 0); + } + else + { + struct timespec ts {}; + clock_gettime (CLOCK_REALTIME, &ts); + long long ns = static_cast (timeout * 1e9); + ts.tv_sec += ns / 1000000000LL; + ts.tv_nsec += ns % 1000000000LL; + if (ts.tv_nsec >= 1000000000LL) { ts.tv_sec++; ts.tv_nsec -= 1000000000LL; } + didReceiveMessage = (sem_timedwait (&_sem, &ts) == 0); + } + if (didReceiveMessage) + { + const auto messageID { _pendingMessageID }; + const auto messageDecoder { _pendingMessageDecoder.load (std::memory_order_acquire) }; + return std::make_pair (messageID, std::unique_ptr (messageDecoder)); + } + return {}; + } + + void signalSemaphore (MessageID messageID, std::unique_ptr && decoder) + { + _pendingMessageID = messageID; + _pendingMessageDecoder.store (decoder.release (), std::memory_order_release); + sem_post (&_sem); + } + + private: + MessageID _pendingMessageID { 0 }; + std::atomic _pendingMessageDecoder { nullptr }; + sem_t _sem; +}; +#else class WaitableSingleMessageQueue { public: @@ -307,6 +356,7 @@ class WaitableSingleMessageQueue std::atomic _pendingMessageDecoder { nullptr }; void* const _waitForMessageSemaphore; // concrete type is platform-dependent }; +#endif // !defined(__WINE__) — close the #else block for the original WaitableSingleMessageQueue //------------------------------------------------------------------------------ @@ -630,7 +680,7 @@ void OtherThreadsMessageDispatcher::_processReceivedMessage (MessageID messageID //------------------------------------------------------------------------------ -#if defined (_WIN32) +#if defined (_WIN32) && !defined (__WINE__) // from https://devblogs.microsoft.com/oldnewthing/20141015-00/?p=43843 BOOL ConvertToRealHandle(HANDLE h, @@ -685,17 +735,20 @@ Connection::Connection (MessageEncoderFactory && messageEncoderFactory, MessageH _messageHandler { std::move (messageHandler) }, _receiverEndianessMatches { receiverEndianessMatches }, _waitForMessageDelegate { std::move (waitForMessageDelegate) }, - _creationThreadID { std::this_thread::get_id () }, -#if defined (_WIN32) - _creationThreadHandle { _GetRealCurrentThread () } + _creationThreadID { std::this_thread::get_id () } +#if defined (_WIN32) && !defined (__WINE__) + , _creationThreadHandle { _GetRealCurrentThread () } {} #elif defined (__APPLE__) - _creationThreadRunLoop { CFRunLoopGetCurrent () } + , _creationThreadRunLoop { CFRunLoopGetCurrent () } { CFRunLoopSourceContext context { 0, this, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, _performRunLoopSource }; _runloopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 0, &context); CFRunLoopAddSource (_creationThreadRunLoop, _runloopSource, kCFRunLoopCommonModes); } +#else + // Linux: _queue, _mutex, and _condition all default-construct. +{} #endif Connection::~Connection () @@ -729,7 +782,7 @@ void Connection::sendMessage (MessageID messageID, std::unique_ptr (funcPtr)) }; ARA_INTERNAL_ASSERT (result != 0); @@ -739,11 +792,38 @@ void Connection::dispatchToCreationThread (DispatchableFunction func) _mutex.unlock (); CFRunLoopSourceSignal (_runloopSource); CFRunLoopWakeUp (_creationThreadRunLoop); +#else + // Linux: push onto the queue and wake the creation thread. + { + std::lock_guard lock { _mutex }; + _queue.emplace (std::move (func)); + } + _condition.notify_one (); #endif } void Connection::processPendingMessageOnCreationThreadIfNeeded () { +#if (!defined (_WIN32) || defined (__WINE__)) && !defined (__APPLE__) + // On Linux there is no run loop to fire dispatch functions automatically, + // so we must drain Connection::_queue here before letting the main thread + // dispatcher check for a pending IPC message. + // We hold the lock only while popping each item, then release it before + // executing so that dispatchToCreationThread() can push more items + // concurrently without deadlocking. + while (true) + { + DispatchableFunction func; + { + std::lock_guard lock { _mutex }; + if (_queue.empty ()) + break; + func = std::move (_queue.front ()); + _queue.pop (); + } + func (); + } +#endif _mainThreadDispatcher->processPendingMessageIfNeeded (); } diff --git a/IPC/ARAIPCConnection.h b/IPC/ARAIPCConnection.h index aae5857..64be8d1 100644 --- a/IPC/ARAIPCConnection.h +++ b/IPC/ARAIPCConnection.h @@ -25,7 +25,7 @@ #if ARA_ENABLE_IPC -#if defined (_WIN32) +#if defined (_WIN32) && !defined (__WINE__) #include #elif defined (__APPLE__) #include @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -161,7 +162,7 @@ class Connection std::unique_ptr _mainThreadDispatcher {}; std::unique_ptr _otherThreadsDispatcher {}; std::thread::id const _creationThreadID; -#if defined (_WIN32) +#if defined (_WIN32) && !defined (__WINE__) HANDLE const _creationThreadHandle; #elif defined (__APPLE__) CFRunLoopRef const _creationThreadRunLoop; @@ -169,7 +170,12 @@ class Connection std::queue _queue; // \todo instead of locking, use a lockless concurrent queue, std::recursive_mutex _mutex; // eg this one: https://github.com/hogliux/farbot #else - #error "not yet implemented on this platform" + // Linux: dispatch queue protected by mutex + condition variable. + // dispatchToCreationThread() pushes a function and notifies; + // processPendingMessageOnCreationThreadIfNeeded() drains it. + std::queue _queue; + std::mutex _mutex; + std::condition_variable _condition; #endif }; diff --git a/IPC/ARAIPCProxyHost.cpp b/IPC/ARAIPCProxyHost.cpp index d2bae8a..68eaa98 100644 --- a/IPC/ARAIPCProxyHost.cpp +++ b/IPC/ARAIPCProxyHost.cpp @@ -27,12 +27,46 @@ #include "ARA_Library/Utilities/ARAStdVectorUtilities.h" #include +#include #include #include +#if defined (__WINE__) + // Wine: need Win32 APIs for the initializeARAWithConfiguration thread trick + #ifndef NOMINMAX + #define NOMINMAX + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #include + #include + #include + #include + #include + #include +#endif + + +// (assert logger defined inside ARA::IPC namespace below) namespace ARA { namespace IPC { + +// Logging assert function passed to initializeARAWithConfiguration. +// Melodyne fires ARA assertions even in Release builds — logging them +// reveals threading violations and other setup errors (per Stefan Gretscher). +// Must be ms_abi since Melodyne (MSVC PE) calls it with Windows calling convention. +static void __attribute__((ms_abi)) _araAssertLogger (ARA::ARAAssertCategory category, + const void* /*problematicArgument*/, + const char* diagnosis) noexcept +{ + std::fprintf (stderr, "[ARA_ASSERT] category=%d: %s\n", + (int)category, diagnosis ? diagnosis : "(null)"); + std::fflush (stderr); +} + namespace ProxyHostImpl { class AudioAccessController; @@ -685,12 +719,84 @@ void ProxyHost::handleReceivedMessage (const MessageID messageID, const MessageD else if (messageID == kInitializeARAMethodID) { ARAPersistentID factoryID; - ARA::SizedStruct<&ARA::ARAInterfaceConfiguration::assertFunctionAddress> interfaceConfig = { kARAAPIGeneration_2_0_Final, nullptr }; + // Use a logging assert so Melodyne's threading checks produce output. + // Cast to ARAAssertFunction (which has no ABI annotation under wineg++) + // — Melodyne will call it via ms_abi, which our function provides. + static ARAAssertFunction _assertFn = + reinterpret_cast(_araAssertLogger); + static ARA::SizedStruct<&ARA::ARAInterfaceConfiguration::assertFunctionAddress> interfaceConfig; + interfaceConfig = { kARAAPIGeneration_2_0_Final, &_assertFn }; decodeArguments (decoder, factoryID, interfaceConfig.desiredApiGeneration); ARA_INTERNAL_ASSERT (interfaceConfig.desiredApiGeneration >= kARAAPIGeneration_2_0_Final); if (const ARAFactory* const factory { getFactoryWithID (factoryID) }) + { +#if defined (__WINE__) + // Under wineg++, ARA_CALL is empty (no ms_abi attribute), but + // Melodyne.vst3 is a real Windows PE compiled with MSVC that + // expects its arguments in %rcx (ms_abi). Cast explicitly. + // + // The creation thread (pluginMainLoop) already has: + // - OleInitialize (STA) + // - Win32 message queue (PeekMessageW) + // - 32MB stack (STACK_SIZE_PARAM_IS_A_RESERVATION) + // Call initFn directly here — adding another thread creates a second + // STA which causes COM cross-apartment failures under Wine. + // + // Melodyne's std::thread objects may call their destructor from + // within the thread they represent, causing join()-on-self → + // EINVAL → std::system_error → std::terminate. Install a terminate + // handler that exits the crashing thread silently instead of + // aborting the whole process. The terminate handler must be set + // process-wide before initFn runs so Melodyne's threads use it. + auto oldTerminate = std::set_terminate([] () noexcept { + // A Melodyne thread (or our std::thread) has an unhandled + // exception. We can't continue safely, so exit cleanly with + // status 0 so the host doesn't treat it as a crash. + // Use _exit() to skip destructors and avoid re-entrancy. + std::fprintf(stderr, "[ProxyHost] terminate intercepted — exiting cleanly\n"); + std::fflush(stderr); + _exit(0); + }); + + // Also handle SIGSEGV from Wine's access violation handler — + // Melodyne's background threads may fault accessing stale data. + // The signal handler must use siglongjmp or just return to let + // the faulting thread exit. + struct sigaction sa_segv{}, old_segv{}; + sa_segv.sa_handler = [](int){ pthread_exit(nullptr); }; + sa_segv.sa_flags = SA_RESETHAND; + sigaction(SIGSEGV, &sa_segv, &old_segv); + + struct sigaction sa_abrt{}, old_abrt{}; + sa_abrt.sa_handler = [](int){ pthread_exit(nullptr); }; + sa_abrt.sa_flags = SA_RESETHAND; + sigaction(SIGABRT, &sa_abrt, &old_abrt); + + std::fprintf (stderr, "[ProxyHost] calling initializeARAWithConfiguration on creation thread\n"); + std::fflush (stderr); + using InitFn = void (__attribute__((ms_abi)) *) (const ARAInterfaceConfiguration*); + auto initFn = reinterpret_cast( + reinterpret_cast(factory->initializeARAWithConfiguration)); + try { + initFn (&interfaceConfig); + } catch (...) { + std::fprintf (stderr, "[ProxyHost] initFn exception swallowed\n"); + std::fflush (stderr); + } + std::fprintf (stderr, "[ProxyHost] initializeARAWithConfiguration returned\n"); + std::fflush (stderr); + + // Restore original terminate handler and signal handlers + std::set_terminate(oldTerminate); + sigaction(SIGSEGV, &old_segv, nullptr); + sigaction(SIGABRT, &old_abrt, nullptr); +#elif defined (_WIN32) + factory->initializeARAWithConfiguration (&interfaceConfig); +#else factory->initializeARAWithConfiguration (&interfaceConfig); +#endif + } } else if (messageID == kCreateDocumentControllerMethodID) {