diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index c9b3fd41fd..aca7ef9474 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -2,6 +2,26 @@ # develop +## Change + +ProfileHost::enableProfile and ProfileHost::disableProfile have been combined +into a single function, ProfileHost::setProfileEnablement. + +**Possible Issues** + +Code that calls this function will fail to compile until it is updated. + +**Workaround** + +To enable a profile, call setProfileEnablement with a positive number of +channels. To disable a profile, call setProfileEnablement with zero channels. + +**Rationale** + +The new API is simpler, more compact, and more consistent, as it now mirrors +the signature of Device::sendProfileEnablement. + + ## Change OpenGLContext::getRenderingScale() has been changed to include the effects of @@ -98,12 +118,12 @@ and FormatOptions::getMaxDecimalPlaces() as necessary. To find whether the outpu should be multi-line, compare the result of FormatOptions::getSpacing() with JSON::Spacing::multiLine. -Callers of the function can construct the new argument type using the old +Callers of the function can construct the new argument type using the old arguments accordingly ``` JSON::FormatOptions{}.withIndentLevel (indentLevel) - .withSpacing (allOnOneLine ? JSON::Spacing::singleLine + .withSpacing (allOnOneLine ? JSON::Spacing::singleLine : JSON::Spacing::multiLine) .withMaxDecimalPlaces (maximumDecimalPlaces); ``` @@ -119,7 +139,7 @@ FormatOptions type, which will not be a breaking change. ## Change -CachedValue::operator==() will now emit floating point comparison warnings if +CachedValue::operator==() will now emit floating point comparison warnings if they are enabled for the project. **Possible Issues** @@ -135,7 +155,7 @@ CachedValue::get(). **Rationale** -The JUCE Framework now offers the free-standing exactlyEqual() and +The JUCE Framework now offers the free-standing exactlyEqual() and approximatelyEqual() functions to clearly express the desired semantics when comparing floating point values. These functions are intended to eliminate the ambiguity in code-bases regarding these types. However, when such a value diff --git a/examples/Audio/CapabilityInquiryDemo.h b/examples/Audio/CapabilityInquiryDemo.h index 46ed2e3a3d..9fa4f6ab40 100644 --- a/examples/Audio/CapabilityInquiryDemo.h +++ b/examples/Audio/CapabilityInquiryDemo.h @@ -4088,10 +4088,7 @@ private: else h->addProfile (profileAtAddress, state.supported); - if (state.active == 0) - h->disableProfile (profileAtAddress); - else - h->enableProfile (profileAtAddress, state.active); + h->setProfileEnablement (profileAtAddress, state.active); } } @@ -4589,12 +4586,9 @@ private: if (auto* host = demo.getProfileHost()) { - if (enabled) - host->enableProfile (profileAtAddress, numChannels); - else - host->disableProfile (profileAtAddress); - - profiles.channels[profileAtAddress].active = (uint16_t) numChannels; + const auto count = enabled ? jmax (1, numChannels) : 0; + host->setProfileEnablement (profileAtAddress, count); + profiles.channels[profileAtAddress].active = (uint16_t) count; state = profiles; } diff --git a/modules/juce_midi_ci/ci/juce_CIDevice.cpp b/modules/juce_midi_ci/ci/juce_CIDevice.cpp index 4f3c4f0db1..2a4f685b98 100644 --- a/modules/juce_midi_ci/ci/juce_CIDevice.cpp +++ b/modules/juce_midi_ci/ci/juce_CIDevice.cpp @@ -151,11 +151,15 @@ public: if (numChannels > 0) { + const auto channelsToSend = address == ChannelInGroup::wholeBlock || address == ChannelInGroup::wholeGroup + ? 0 + : numChannels; + detail::MessageTypeUtils::send (concreteBufferOutput, options.getFunctionBlock().firstGroup, m, address, - Message::ProfileOn { profile, (uint16_t) numChannels }); + Message::ProfileOn { profile, (uint16_t) channelsToSend }); } else { @@ -1045,7 +1049,7 @@ private: return iter != device.discovered.end() ? jmin (defaultResult, (int) iter->second.discovery.maximumSysexSize) : defaultResult; } - public: + private: Impl& device; }; @@ -1062,13 +1066,10 @@ private: if (! device.profileHost.has_value()) return; - if (enabled) - device.profileHost->enableProfile (profileAtAddress, numChannels); - else - device.profileHost->disableProfile (profileAtAddress); + device.profileHost->setProfileEnablement (profileAtAddress, enabled ? jmax (1, numChannels) : 0); } - public: + private: Impl& device; }; @@ -1127,7 +1128,7 @@ private: d->subscriptionWillEnd (m, subscription); } - public: + private: Impl& device; }; @@ -1755,8 +1756,7 @@ public: beginTest ("If a device has previously acted as a responder to profile inquiry, then modifying profiles emits events"); { - const auto numChannels = 0; - device.getProfileHost()->enableProfile ({ profile, ChannelAddress{}.withChannel (ChannelInGroup::wholeBlock) }, numChannels); + device.getProfileHost()->setProfileEnablement ({ profile, ChannelAddress{}.withChannel (ChannelInGroup::wholeBlock) }, 1); expect (output.messages.size() == 2); expect (output.messages.back().bytes == getMessageBytes ({ ChannelInGroup::wholeBlock, @@ -1764,7 +1764,7 @@ public: detail::MessageMeta::implementationVersion, device.getMuid(), MUID::getBroadcast() }, - Message::ProfileEnabledReport { profile, numChannels })); + Message::ProfileEnabledReport { profile, 0 })); } } @@ -1834,7 +1834,7 @@ public: detail::MessageMeta::implementationVersion, inquiryMUID, device.getMuid() }, - Message::ProfileOn { profile, 1 }) }); + Message::ProfileOn { profile, 0 }) }); expect (output.messages.size() == 1); expect (output.messages.back().bytes == getMessageBytes ({ ChannelInGroup::wholeBlock, @@ -1842,7 +1842,7 @@ public: detail::MessageMeta::implementationVersion, device.getMuid(), MUID::getBroadcast() }, - Message::ProfileEnabledReport { profile, 1 })); + Message::ProfileEnabledReport { profile, 0 })); } struct DoNothingProfileDelegate : public ProfileDelegate @@ -1950,7 +1950,7 @@ public: std::byte { 0x05 } }; device.getProfileHost()->addProfile ({ profile, ChannelAddress{}.withChannel (ChannelInGroup::wholeBlock) }); - device.getProfileHost()->enableProfile ({ profile, ChannelAddress{}.withChannel (ChannelInGroup::wholeBlock) }, 0); + device.getProfileHost()->setProfileEnablement ({ profile, ChannelAddress{}.withChannel (ChannelInGroup::wholeBlock) }, 0); const auto inquiryMUID = MUID::makeRandom (random); @@ -1991,7 +1991,7 @@ public: std::byte { 0x05 } }; device.getProfileHost()->addProfile ({ profile, ChannelAddress{}.withChannel (ChannelInGroup::wholeBlock) }); - device.getProfileHost()->enableProfile ({ profile, ChannelAddress{}.withChannel (ChannelInGroup::wholeBlock) }, 1); + device.getProfileHost()->setProfileEnablement ({ profile, ChannelAddress{}.withChannel (ChannelInGroup::wholeBlock) }, 1); const auto inquiryMUID = MUID::makeRandom (random); @@ -2008,7 +2008,7 @@ public: detail::MessageMeta::implementationVersion, device.getMuid(), MUID::getBroadcast() }, - Message::ProfileEnabledReport { profile, 1 })); + Message::ProfileEnabledReport { profile, 0 })); } beginTest ("If a device receives a set profile off for an unsupported profile, NAK is emitted"); diff --git a/modules/juce_midi_ci/ci/juce_CIDevice.h b/modules/juce_midi_ci/ci/juce_CIDevice.h index c9ac6a4a9c..aa5b9cce2b 100644 --- a/modules/juce_midi_ci/ci/juce_CIDevice.h +++ b/modules/juce_midi_ci/ci/juce_CIDevice.h @@ -127,6 +127,12 @@ public: void sendProfileSpecificData (MUID muid, ChannelInGroup address, Profile profile, Span); /** Sets a profile on or off. Pass 0 or less to disable the profile, or a positive number to enable it. + + This also goes for group/block profiles. If the request is addressed to a group/block, then + a positive number will cause a "profile on" message to be sent, and a non-positive number + will cause a "profile off" message to be sent. The channel count of the sent message will + always be zero for messages addressed to groups/blocks.regardless of the value of the + numChannels argument. */ void sendProfileEnablement (MUID muid, ChannelInGroup address, Profile profile, int numChannels); diff --git a/modules/juce_midi_ci/ci/juce_CIProfileDelegate.h b/modules/juce_midi_ci/ci/juce_CIProfileDelegate.h index d33f5246f0..b7c5a7d81d 100644 --- a/modules/juce_midi_ci/ci/juce_CIProfileDelegate.h +++ b/modules/juce_midi_ci/ci/juce_CIProfileDelegate.h @@ -47,6 +47,10 @@ struct ProfileDelegate is enabled with zero channels active - in this situation, it is recommended that you use ProfileHost::enableProfile to enable the default number of channels for that profile. + + Additionally, profiles for entire groups or function blocks may be enabled with zero + active channels. In this case, the profile should be enabled on the entire group or + function block. */ virtual void profileEnablementRequested ([[maybe_unused]] MUID x, [[maybe_unused]] ProfileAtAddress profileAtAddress, diff --git a/modules/juce_midi_ci/ci/juce_CIProfileHost.cpp b/modules/juce_midi_ci/ci/juce_CIProfileHost.cpp index 74a2e1dce7..a773b1be5c 100644 --- a/modules/juce_midi_ci/ci/juce_CIProfileHost.cpp +++ b/modules/juce_midi_ci/ci/juce_CIProfileHost.cpp @@ -136,11 +136,11 @@ private: .withChannel (output->getIncomingHeader().deviceID); if (auto* state = host->states.getStateForDestination (destination)) { - if (state->get (request.profile).isSupported()) + const auto previousState = state->get (request.profile); + + if (previousState.isSupported()) { - const auto address = ChannelAddress{}.withGroup (output->getIncomingGroup()) - .withChannel (output->getIncomingHeader().deviceID); - const ProfileAtAddress profileAtAddress { request.profile, address }; + const ProfileAtAddress profileAtAddress { request.profile, destination }; { const ScopedValueSetter scope { host->currentEnablementMessage, @@ -167,10 +167,16 @@ private: detail::MessageTypeUtils::send (*output, profileAtAddress.address.getGroup(), header, response); }; + const auto numIndividualChannels = (std::is_same_v ? currentState : previousState).active; + + const auto numChannelsToSend = destination.isSingleChannel() + ? numIndividualChannels + : uint16_t{}; + if (currentState.isActive()) - sendResponse (Message::ProfileEnabledReport { profileAtAddress.profile, currentState.active }); + sendResponse (Message::ProfileEnabledReport { profileAtAddress.profile, numChannelsToSend }); else - sendResponse (Message::ProfileDisabledReport { profileAtAddress.profile, 0 }); + sendResponse (Message::ProfileDisabledReport { profileAtAddress.profile, numChannelsToSend }); host->isResponder = true; return true; @@ -196,6 +202,14 @@ private: bool* handled = nullptr; }; +void ProfileHost::setProfileEnablement (ProfileAtAddress profileAtAddress, int numChannels) +{ + if (numChannels > 0) + enableProfileImpl (profileAtAddress, numChannels); + else + disableProfileImpl (profileAtAddress); +} + void ProfileHost::addProfile (ProfileAtAddress profileAtAddress, int maxNumChannels) { auto* state = states.getStateForDestination (profileAtAddress.address); @@ -206,7 +220,7 @@ void ProfileHost::addProfile (ProfileAtAddress profileAtAddress, int maxNumChann // There are only 256 channels on a UMP endpoint, so requesting more probably doesn't make sense! jassert (maxNumChannels <= 256); - state->set (profileAtAddress.profile, { (uint16_t) maxNumChannels, 0 }); + state->set (profileAtAddress.profile, { (uint16_t) jmax (1, maxNumChannels), 0 }); if (! isResponder || profileAtAddress == currentEnablementMessage) return; @@ -233,7 +247,7 @@ void ProfileHost::removeProfile (ProfileAtAddress profileAtAddress) if (state == nullptr) return; - disableProfile (profileAtAddress); + setProfileEnablement (profileAtAddress, 0); if (! state->get (profileAtAddress.profile).isSupported()) return; @@ -258,7 +272,7 @@ void ProfileHost::removeProfile (ProfileAtAddress profileAtAddress) Message::ProfileRemoved { profileAtAddress.profile }); } -void ProfileHost::enableProfile (ProfileAtAddress profileAtAddress, int numChannels) +void ProfileHost::enableProfileImpl (ProfileAtAddress profileAtAddress, int numChannels) { auto* state = states.getStateForDestination (profileAtAddress.address); @@ -273,7 +287,7 @@ void ProfileHost::enableProfile (ProfileAtAddress profileAtAddress, int numChann // There are only 256 channels on a UMP endpoint, so requesting more probably doesn't make sense! jassert (numChannels <= 256); - const auto enabledChannels = jmin (old.supported, (uint16_t) numChannels); + const auto enabledChannels = jmax ((uint16_t) 1, jmin (old.supported, (uint16_t) numChannels)); state->set (profileAtAddress.profile, { old.supported, enabledChannels }); if (! isResponder || profileAtAddress == currentEnablementMessage) @@ -288,13 +302,15 @@ void ProfileHost::enableProfile (ProfileAtAddress profileAtAddress, int numChann MUID::getBroadcast(), }; + const auto numChannelsToSend = profileAtAddress.address.isSingleChannel() ? enabledChannels : uint16_t{}; + detail::MessageTypeUtils::send (output, profileAtAddress.address.getGroup(), header, - Message::ProfileEnabledReport { profileAtAddress.profile, enabledChannels }); + Message::ProfileEnabledReport { profileAtAddress.profile, numChannelsToSend }); } -void ProfileHost::disableProfile (ProfileAtAddress profileAtAddress) +void ProfileHost::disableProfileImpl (ProfileAtAddress profileAtAddress) { auto* state = states.getStateForDestination (profileAtAddress.address); @@ -320,10 +336,12 @@ void ProfileHost::disableProfile (ProfileAtAddress profileAtAddress) MUID::getBroadcast(), }; + const auto numChannelsToSend = profileAtAddress.address.isSingleChannel() ? old.active : uint16_t{}; + detail::MessageTypeUtils::send (output, profileAtAddress.address.getGroup(), header, - Message::ProfileDisabledReport { profileAtAddress.profile, old.active }); + Message::ProfileDisabledReport { profileAtAddress.profile, numChannelsToSend }); } bool ProfileHost::tryRespond (ResponderOutput& responderOutput, const Message::Parsed& message) diff --git a/modules/juce_midi_ci/ci/juce_CIProfileHost.h b/modules/juce_midi_ci/ci/juce_CIProfileHost.h index dffe7412d8..7177dec724 100644 --- a/modules/juce_midi_ci/ci/juce_CIProfileHost.h +++ b/modules/juce_midi_ci/ci/juce_CIProfileHost.h @@ -58,18 +58,14 @@ public: */ void removeProfile (ProfileAtAddress); - /** Activates a profile on the specified group/channel with the provided - number of channels. + /** Activates or deactivates a profile on the specified group/channel. - The profile should previously have been added with addProfile(), and - numChannels should be in the closed range between 1 and the maximum - number of channels allowed for that profile. + The profile should previously have been added with addProfile(). + A positive value of numChannels will enable the profile, and a non-positive value + will disable it. This includes group and function-block profiles; passing any positive + value will enable the profile on the entire group or block. */ - void enableProfile (ProfileAtAddress, int numChannels); - - /** Deactivates a profile on the specified group/channel. - */ - void disableProfile (ProfileAtAddress); + void setProfileEnablement (ProfileAtAddress, int numChannels); /** Returns the profile states (supported/active) for all groups and channels. */ @@ -98,6 +94,9 @@ public: private: class Visitor; + void enableProfileImpl (ProfileAtAddress, int); + void disableProfileImpl (ProfileAtAddress); + template bool profileEnablementReceived (ResponderOutput&, const Body&); diff --git a/modules/juce_midi_ci/ci/juce_CISupportedAndActive.h b/modules/juce_midi_ci/ci/juce_CISupportedAndActive.h index d4c283b751..343b573b9a 100644 --- a/modules/juce_midi_ci/ci/juce_CISupportedAndActive.h +++ b/modules/juce_midi_ci/ci/juce_CISupportedAndActive.h @@ -34,8 +34,15 @@ namespace juce::midi_ci */ struct SupportedAndActive { - uint16_t supported{}; ///< The maximum number of member channels for a profile. 0 indicates that the profile is unsupported. - uint16_t active{}; ///< The number of member channels currently active for a profile. 0 indicates that the profile is inactive. + uint16_t supported{}; ///< The maximum number of member channels for a profile. + ///< 0 indicates that the profile is unsupported. + ///< For group/block profiles, 1/0 indicates that the + ///< profile is supported/unsupported respectively. + + uint16_t active{}; ///< The number of member channels currently active for a profile. + ///< 0 indicates that the profile is inactive. + ///< For group/block profiles, 1/0 indicates that the + ///< profile is supported/unsupported respectively. /** Returns true if supported is non-zero. */ bool isSupported() const { return supported != 0; }