diff --git a/examples/audio plugin demo/Source/PluginEditor.cpp b/examples/audio plugin demo/Source/PluginEditor.cpp index 0cb26334cd..b784080a2b 100644 --- a/examples/audio plugin demo/Source/PluginEditor.cpp +++ b/examples/audio plugin demo/Source/PluginEditor.cpp @@ -107,6 +107,8 @@ JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemo setSize (owner.lastUIWidth, owner.lastUIHeight); + updateTrackProperties(); + // start a timer which will keep our timecode display updated startTimerHz (30); } @@ -118,7 +120,7 @@ JuceDemoPluginAudioProcessorEditor::~JuceDemoPluginAudioProcessorEditor() //============================================================================== void JuceDemoPluginAudioProcessorEditor::paint (Graphics& g) { - g.setColour (getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); + g.setColour (backgroundColour); g.fillAll(); } @@ -151,6 +153,16 @@ void JuceDemoPluginAudioProcessorEditor::hostMIDIControllerIsAvailable (bool con midiKeyboard.setVisible (! controllerIsAvailable); } +void JuceDemoPluginAudioProcessorEditor::updateTrackProperties () +{ + auto trackColour = getProcessor().trackProperties.colour; + auto& lf = getLookAndFeel(); + + backgroundColour = (trackColour == Colour() ? lf.findColour (ResizableWindow::backgroundColourId) + : trackColour.withAlpha (1.0f).withBrightness (0.266f)); + repaint(); +} + //============================================================================== // quick-and-dirty function to format a timecode string static String timeToTimecodeString (double seconds) diff --git a/examples/audio plugin demo/Source/PluginEditor.h b/examples/audio plugin demo/Source/PluginEditor.h index 490875cd6a..a012968176 100644 --- a/examples/audio plugin demo/Source/PluginEditor.h +++ b/examples/audio plugin demo/Source/PluginEditor.h @@ -45,6 +45,7 @@ public: void resized() override; void timerCallback() override; void hostMIDIControllerIsAvailable (bool) override; + void updateTrackProperties(); private: class ParameterSlider; @@ -52,6 +53,7 @@ private: MidiKeyboardComponent midiKeyboard; Label timecodeDisplayLabel, gainLabel, delayLabel; ScopedPointer gainSlider, delaySlider; + Colour backgroundColour; //============================================================================== JuceDemoPluginAudioProcessor& getProcessor() const diff --git a/examples/audio plugin demo/Source/PluginProcessor.cpp b/examples/audio plugin demo/Source/PluginProcessor.cpp index 33200af2ae..f8c06d9de8 100644 --- a/examples/audio plugin demo/Source/PluginProcessor.cpp +++ b/examples/audio plugin demo/Source/PluginProcessor.cpp @@ -390,6 +390,14 @@ void JuceDemoPluginAudioProcessor::setStateInformation (const void* data, int si } } +void JuceDemoPluginAudioProcessor::updateTrackProperties (const TrackProperties& properties) +{ + trackProperties = properties; + + if (auto* editor = dynamic_cast (getActiveEditor())) + editor->updateTrackProperties (); +} + //============================================================================== // This creates new instances of the plugin.. AudioProcessor* JUCE_CALLTYPE createPluginFilter() diff --git a/examples/audio plugin demo/Source/PluginProcessor.h b/examples/audio plugin demo/Source/PluginProcessor.h index 430815a319..131c8a0c84 100644 --- a/examples/audio plugin demo/Source/PluginProcessor.h +++ b/examples/audio plugin demo/Source/PluginProcessor.h @@ -82,6 +82,9 @@ public: void getStateInformation (MemoryBlock&) override; void setStateInformation (const void* data, int sizeInBytes) override; + //============================================================================== + void updateTrackProperties (const TrackProperties& properties) override; + //============================================================================== // These properties are public so that our editor component can access them // A bit of a hacky way to do it, but it's only a demo! Obviously in your own @@ -104,6 +107,9 @@ public: AudioParameterFloat* gainParam = nullptr; AudioParameterFloat* delayParam = nullptr; + // Current track colour and name + TrackProperties trackProperties; + private: //============================================================================== template diff --git a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp index b0f80cce43..6f390c9f5d 100644 --- a/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp @@ -942,6 +942,13 @@ namespace AAXClasses { if (type == AAX_eNotificationEvent_EnteringOfflineMode) pluginInstance->setNonRealtime (true); if (type == AAX_eNotificationEvent_ExitingOfflineMode) pluginInstance->setNonRealtime (false); + if (type == AAX_eNotificationEvent_TrackNameChanged && data != nullptr) + { + AudioProcessor::TrackProperties props; + props.name = static_cast (data)->Get(); + + pluginInstance->updateTrackProperties (props); + } return AAX_CEffectParameters::NotificationReceived (type, data, size); } diff --git a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm index d7092aadb7..f3d9a0e8e9 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm @@ -147,6 +147,8 @@ public: channelInfo = AudioUnitHelpers::getAUChannelInfo (*juceFilter); #endif + AddPropertyListener (kAudioUnitProperty_ContextName, auPropertyListenerDispatcher, this); + totalInChannels = juceFilter->getTotalNumInputChannels(); totalOutChannels = juceFilter->getTotalNumOutputChannels(); @@ -1956,6 +1958,25 @@ private: return (getHostType().isLogic() ? 8 : 64); } + //============================================================================== + void auPropertyListener (AudioUnitPropertyID propId, AudioUnitScope scope, AudioUnitElement) + { + if (scope == kAudioUnitScope_Global && propId == kAudioUnitProperty_ContextName + && juceFilter != nullptr && mContextName != nullptr) + { + AudioProcessor::TrackProperties props; + props.name = String::fromCFString (mContextName); + + juceFilter->updateTrackProperties (props); + } + } + + static void auPropertyListenerDispatcher (void* inRefCon, AudioUnit, AudioUnitPropertyID propId, + AudioUnitScope scope, AudioUnitElement element) + { + static_cast (inRefCon)->auPropertyListener (propId, scope, element); + } + JUCE_DECLARE_NON_COPYABLE (JuceAU) }; diff --git a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm index 2fe29fa646..56afdb4c0f 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm @@ -195,6 +195,10 @@ public: virtual bool getRenderingOffline() = 0; virtual void setRenderingOffline (bool offline) = 0; + //============================================================================== + virtual NSString* getContextName() const = 0; + virtual void setContextName (NSString*) = 0; + virtual bool allocateRenderResourcesAndReturnError (NSError **outError) { objc_super s = { getAudioUnit(), [AUAudioUnit class] }; @@ -273,6 +277,10 @@ private: addMethod (@selector (allocateRenderResourcesAndReturnError:), allocateRenderResourcesAndReturnError, "B@:^@"); addMethod (@selector (deallocateRenderResources), deallocateRenderResources, "v@:"); + //============================================================================== + addMethod (@selector (contextName), getContextName, "@@:"); + addMethod (@selector (setContextName:), setContextName, "v@:@"); + //============================================================================== #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED addMethod (@selector (supportedViewConfigurations:), getSupportedViewConfigurations, "@@:@"); @@ -351,6 +359,10 @@ private: static BOOL allocateRenderResourcesAndReturnError (id self, SEL, NSError** error) { return _this (self)->allocateRenderResourcesAndReturnError (error) ? YES : NO; } static void deallocateRenderResources (id self, SEL) { _this (self)->deallocateRenderResources(); } + //============================================================================== + static NSString* getContextName (id self, SEL) { return _this (self)->getContextName(); } + static void setContextName (id self, SEL, NSString* str) { return _this (self)->setContextName (str); } + //============================================================================== #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED static NSIndexSet* getSupportedViewConfigurations (id self, SEL, NSArray* configs) { return _this (self)->getSupportedViewConfigurations (configs); } @@ -679,6 +691,20 @@ public: bool getRenderingOffline() override { return getAudioProcessor().isNonRealtime(); } void setRenderingOffline (bool offline) override { getAudioProcessor().setNonRealtime (offline); } + //============================================================================== + NSString* getContextName() const override { return juceStringToNS (contextName); } + void setContextName (NSString* str) override + { + if (str != nullptr) + { + AudioProcessor::TrackProperties props; + props.name = nsStringToJuce (str); + + getAudioProcessor().updateTrackProperties (props); + } + } + + //============================================================================== bool allocateRenderResourcesAndReturnError (NSError **outError) override { AudioProcessor& processor = getAudioProcessor(); @@ -1436,6 +1462,8 @@ private: AudioTimeStamp lastTimeStamp; CurrentPositionInfo lastAudioHead; + + String contextName; }; const double JuceAudioUnitv3::kDefaultSampleRate = 44100.0; diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index 8e65d8b732..f68244ca0e 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -115,6 +115,7 @@ class JuceVST3Component; //============================================================================== class JuceVST3EditController : public Vst::EditController, public Vst::IMidiMapping, + public Vst::ChannelContext::IInfoListener, public AudioProcessorListener { public: @@ -147,6 +148,7 @@ public: TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IEditController2) TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IConnectionPoint) TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IMidiMapping) + TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::ChannelContext::IInfoListener) TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, IPluginBase, Vst::IEditController) TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, IDependent, Vst::IEditController) TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, FUnknown, Vst::IEditController) @@ -429,6 +431,41 @@ public: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProgramChangeParameter) }; + //============================================================================== + tresult PLUGIN_API setChannelContextInfos (Vst::IAttributeList* list) override + { + if (auto* instance = getPluginInstance()) + { + if (list != nullptr) + { + AudioProcessor::TrackProperties trackProperties; + + { + Vst::String128 channelName; + if (list->getString (Vst::ChannelContext::kChannelNameKey, channelName, sizeof (channelName)) == kResultTrue) + trackProperties.name = toString (channelName); + } + + { + int64 colour; + if (list->getInt (Vst::ChannelContext::kChannelColorKey, colour) == kResultTrue) + trackProperties.colour = Colour (Vst::ChannelContext::GetRed ((uint32) colour), Vst::ChannelContext::GetGreen ((uint32) colour), + Vst::ChannelContext::GetBlue ((uint32) colour), Vst::ChannelContext::GetAlpha ((uint32) colour)); + } + + + + if (MessageManager::getInstance()->isThisTheMessageThread()) + instance->updateTrackProperties (trackProperties); + else + MessageManager::callAsync ([trackProperties, instance] () + { instance->updateTrackProperties (trackProperties); }); + } + } + + return kResultOk; + } + //============================================================================== tresult PLUGIN_API setComponentState (IBStream* stream) override { @@ -1120,6 +1157,7 @@ public: TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IAudioProcessor) TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IUnitInfo) TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IConnectionPoint) + TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::ChannelContext::IInfoListener) TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, FUnknown, Vst::IComponent) if (doUIDsMatch (targetIID, JuceAudioProcessor::iid)) diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index 457d10835b..d2dffa09fc 100644 --- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -1065,6 +1065,19 @@ public: jassertfalse; // xxx not implemented! } + //============================================================================== + void updateTrackProperties (const TrackProperties& properties) override + { + if (properties.name.isNotEmpty()) + { + CFStringRef contextName = properties.name.toCFString(); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ContextName, kAudioUnitScope_Global, + 0, &contextName, sizeof (CFStringRef)); + + CFRelease (contextName); + } + } + //============================================================================== void getStateInformation (MemoryBlock& destData) override { diff --git a/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/modules/juce_audio_processors/format_types/juce_VST3Headers.h index 8b64adabaf..57a47752af 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Headers.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -84,6 +84,7 @@ #include #include #include + #include #include #include #else @@ -102,6 +103,7 @@ #include #include #include + #include #include #include #include diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 88d8f49cf1..91f7b666e3 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -2100,6 +2100,67 @@ struct VST3PluginInstance : public AudioPluginInstance return result; } + //============================================================================== + void updateTrackProperties (const TrackProperties& properties) override + { + if (trackInfoListener != nullptr) + { + ComSmartPtr l (new TrackPropertiesAttributeList (properties)); + trackInfoListener->setChannelContextInfos (l); + } + } + + struct TrackPropertiesAttributeList : public Vst::IAttributeList + { + TrackPropertiesAttributeList (const TrackProperties& properties) : props (properties) {} + virtual ~TrackPropertiesAttributeList() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + + tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override + { + TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IAttributeList) + TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (iid, FUnknown, Vst::IAttributeList) + + *obj = nullptr; + return kNotImplemented; + } + + tresult PLUGIN_API setInt (AttrID, int64) override { return kOutOfMemory; } + tresult PLUGIN_API setFloat (AttrID, double) override { return kOutOfMemory; } + tresult PLUGIN_API setString (AttrID, const Vst::TChar*) override { return kOutOfMemory; } + tresult PLUGIN_API setBinary (AttrID, const void*, uint32) override { return kOutOfMemory; } + tresult PLUGIN_API getFloat (AttrID, double&) override { return kResultFalse; } + tresult PLUGIN_API getBinary (AttrID, const void*&, uint32&) override { return kResultFalse; } + + tresult PLUGIN_API getString (AttrID id, Vst::TChar* string, uint32 size) override + { + if (! std::strcmp (id, Vst::ChannelContext::kChannelNameKey)) + { + Steinberg::String str (props.name.toRawUTF8()); + str.copyTo (string, 0, (Steinberg::int32) jmin (size, (Steinberg::uint32) std::numeric_limits::max())); + + return kResultTrue; + } + + return kResultFalse; + } + + tresult PLUGIN_API getInt (AttrID id, int64& value) override + { + if (! std::strcmp (Vst::ChannelContext::kChannelNameLengthKey, id)) value = props.name.length(); + else if (! std::strcmp (Vst::ChannelContext::kChannelColorKey, id)) value = static_cast (props.colour.getARGB()); + else return kResultFalse; + + return kResultTrue; + } + + Atomic refCount; + TrackProperties props; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TrackPropertiesAttributeList) + }; + //============================================================================== String getChannelName (int channelIndex, bool forInput, bool forAudioChannel) const { @@ -2449,6 +2510,7 @@ private: ComSmartPtr unitData; ComSmartPtr programListData; ComSmartPtr componentConnection, editControllerConnection; + ComSmartPtr trackInfoListener; /** The number of IO buses MUST match that of the plugin, even if there aren't enough channels to process, @@ -2533,6 +2595,7 @@ private: editController2.loadFrom (holder->component); componentHandler.loadFrom (holder->component); componentHandler2.loadFrom (holder->component); + trackInfoListener.loadFrom (holder->component); if (processor == nullptr) processor.loadFrom (editController); if (unitInfo == nullptr) unitInfo.loadFrom (editController); @@ -2541,6 +2604,7 @@ private: if (editController2 == nullptr) editController2.loadFrom (editController); if (componentHandler == nullptr) componentHandler.loadFrom (editController); if (componentHandler2 == nullptr) componentHandler2.loadFrom (editController); + if (trackInfoListener == nullptr) trackInfoListener.loadFrom (editController); } void setStateForAllMidiBuses (bool newState) diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 321060ba7b..b19d9bb572 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -1025,6 +1025,9 @@ void AudioProcessor::setCurrentProgramStateInformation (const void* data, int si setStateInformation (data, sizeInBytes); } +//============================================================================== +void AudioProcessor::updateTrackProperties (const AudioProcessor::TrackProperties&) {} + //============================================================================== // magic number to identify memory blocks that we've stored as XML const uint32 magicXmlNumber = 0x21324356; diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h index 3aea53ae7a..66fb89d9fe 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -1310,6 +1310,32 @@ public: */ WrapperType wrapperType; + /** A struct containing information about the DAW track inside which your + AudioProcessor is loaded. */ + struct TrackProperties + { + String name; // The name of the track - this will be empty if the track name is not known + Colour colour; // The colour of the track - this will be transparentBlack if the colour is not known + + // other properties may be added in the future + }; + + /** Informs the AudioProcessor that track properties such as the track's name or + colour has been changed. + + If you are hosting this AudioProcessor then use this method to inform the + AudioProcessor about which track the AudioProcessor is loaded on. This method + may only be called on the message thread. + + If you are implemeting an AudioProcessor then you can override this callback + to do something useful with the track properties such as changing the colour + of your AudioProcessor's editor. It's entirely up to the host when and how + often this callback will be called. + + The default implementation of this callback will do nothing. + */ + virtual void updateTrackProperties (const TrackProperties& properties); + //============================================================================== #ifndef DOXYGEN /** Deprecated: use getTotalNumInputChannels instead. */