1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/modules/juce_midi_ci/ci/juce_CISubscriptionManager.h
reuk 60757de2f2
CIDevice: Improve robustness of subscription API
The old API only allowed cancelling property "get" inquiries and
subscription updates. However, there are use-cases for cancelling other
requests too. e.g. switching between views in a JUCE app might mean that
it's no longer necessary to subscribe to a particular property.

Cancelling subscriptions ends up being quite involved. Different
handling is needed depending on whether the subscription is cancelled
before or after the responder replies to the initial request.
In addition, the responder may ask the initiator to retry a subscription
begin request.
2024-01-18 10:37:17 +00:00

167 lines
5.8 KiB
C++

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce::midi_ci
{
/**
A key used to uniquely identify ongoing property subscriptions initiated by a ci::Device.
@tags{Audio}
*/
class SubscriptionKey
{
auto tie() const { return std::tuple (m, v); }
public:
/** Constructor */
SubscriptionKey() = default;
/** Constructor */
SubscriptionKey (MUID muid, Token64 key) : m (muid), v (key) {}
/** Returns the muid of the device to which we are subscribed. */
MUID getMuid() const { return m; }
/** Returns an identifier unique to this subscription. */
Token64 getKey() const { return v; }
/** Equality operator. */
bool operator== (const SubscriptionKey& other) const { return tie() == other.tie(); }
/** Inequality operator. */
bool operator!= (const SubscriptionKey& other) const { return tie() != other.tie(); }
/** Less-than operator. */
bool operator< (const SubscriptionKey& other) const { return tie() < other.tie(); }
private:
MUID m = MUID::getBroadcast();
Token64 v{};
};
/**
Functions used by a SubscriptionManager to negotiate subscriptions.
@tags{Audio}
*/
struct SubscriptionManagerDelegate
{
virtual ~SubscriptionManagerDelegate() = default;
/** Called when the manager wants to send an update. */
virtual std::optional<RequestKey> sendPropertySubscribe (MUID m,
const PropertySubscriptionHeader& header,
std::function<void (const PropertyExchangeResult&)> onResult) = 0;
/** Called by the manager to cancel a previous request. */
virtual void abortPropertyRequest (RequestKey) = 0;
/** Called by the manager when the remote device provides a subscribeId, or when it
terminates a subscription.
*/
virtual void propertySubscriptionChanged (SubscriptionKey, const std::optional<String>&) = 0;
};
/**
Manages subscriptions to properties on remote devices.
Occasionally, sending a subscription-begin request may fail, in which case the request will be
cached. Cached requests will be sent during a future call to sendPendingMessages().
To use this:
- pass a SubscriptionManagerDelegate (such as a ci::Device) to the constructor
- call sendPendingMessages() periodically, e.g. in a timer callback
@tags{Audio}
*/
class SubscriptionManager
{
public:
/** Constructor.
The delegate functions will be called when necessary to start and cancel property requests.
*/
explicit SubscriptionManager (SubscriptionManagerDelegate& delegate);
/** Attempts to begin a subscription using the provided details.
@returns a token that uniquely identifies this subscription. This token can be passed to
endSubscription to terminate an ongoing subscription.
*/
SubscriptionKey beginSubscription (MUID m, const PropertySubscriptionHeader& header);
/** Ends an ongoing subscription by us.
If the subscription begin request hasn't been sent yet, then this will just cancel the cached request.
If a subscription begin request has been sent, but no response has been received, this will
send a notification cancelling the initial request via SubscriptionManagerDelegate::abortPropertyRequest().
If the subscription has started successfully, then this will send a subscription end request
via SubscriptionManagerDelegate::sendPropertySubscribe().
*/
void endSubscription (SubscriptionKey);
/** Ends an ongoing subscription as requested from the remote device.
Unlike the other overload of endSubscription, this won't notify the delegate. It will only
update the internal record of active subscriptions.
Calls Delegate::propertySubscriptionChanged().
*/
void endSubscriptionFromResponder (MUID, String);
/** Ends all ongoing subscriptions as requested from a remote device.
Calls Delegate::propertySubscriptionChanged().
*/
void endSubscriptionsFromResponder (MUID);
/** Returns all of the subscriptions that have been initiated by this manager. */
std::vector<SubscriptionKey> getOngoingSubscriptions() const;
/** If the provided subscription has started successfully, this returns the subscribeId assigned
to the subscription by the remote device.
*/
std::optional<String> getSubscribeIdForKey (SubscriptionKey key) const;
/** If the provided subscription has not been cancelled, this returns the name of the
subscribed resource.
*/
std::optional<String> getResourceForKey (SubscriptionKey key) const;
/** Sends any cached messages that need retrying.
@returns true if there are no more messages to send, or false otherwise
*/
bool sendPendingMessages();
private:
class Impl;
std::shared_ptr<Impl> pimpl;
};
} // namespace juce::midi_ci