| @@ -6,9 +6,11 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| Changelist for version 1.45 | Changelist for version 1.45 | ||||
| - big new project in the "extras" folder - a basic audio plugin host! Currently it loads VSTs on PC/Mac, and lets you put them together in a filter graph, which it plays. Hosting functionality is very basic at the moment, but I'm laying down a good architecture to hopefully build out into cross-platform plugin loading. | |||||
| - audio plugins: I've simplified the processBlock() call in AudioFilterBase. It now just takes a single buffer for all input and output channels, and the accumulate parameter has gone. This will mean tweaking your plugin code, but the result will probably make it much less complex and less messy. | |||||
| - big new project in the "extras" folder - a basic audio plugin host! Currently it loads VSTs on PC/Mac, and lets you put them together in a filter graph, which it plays. Hosting functionality is very basic at the moment, but I'm laying down a good architecture to hopefully develop into a full cross-platform plugin host. | |||||
| - audio plugins: I've simplified the processBlock() call in AudioFilterBase. It now just takes a single buffer for all input and output channels, and the accumulate parameter has gone. This will mean tweaking your plugin code, but will probably make it much less complicated. | |||||
| - audio plugins: AudioFilterBase now requires a few extra methods to be implemented by your plugin: getInputChannelName, getOutputChannelName, isInputChannelStereoPair, isOutputChannelStereoPair. | - audio plugins: AudioFilterBase now requires a few extra methods to be implemented by your plugin: getInputChannelName, getOutputChannelName, isInputChannelStereoPair, isOutputChannelStereoPair. | ||||
| - audio plugins: new method AudioFilterBase::updateHostDisplay() to tell the host that something about your plugin has changed and that it should refresh its display. | |||||
| - audio plugins: new methods AudioFilterBase::beginParameterChangeGesture() and endParameterChangeGesture() let you tell the host when a parameter-change action starts and finishes. | |||||
| - new class: FileSearchPathListComponent, for letting the user edit a FileSearchPath. | - new class: FileSearchPathListComponent, for letting the user edit a FileSearchPath. | ||||
| @@ -43,7 +43,7 @@ | |||||
| 20286C33FDCF999611CA2CEA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; }; | 20286C33FDCF999611CA2CEA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; }; | ||||
| 4A9504C8FFE6A3BC11CA0CBA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; }; | 4A9504C8FFE6A3BC11CA0CBA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = "<absolute>"; }; | ||||
| 4A9504CAFFE6A41611CA0CBA /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = "<absolute>"; }; | 4A9504CAFFE6A41611CA0CBA /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = "<absolute>"; }; | ||||
| 508344B209E5C41E0093A071 /* Juce Plugin Host.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Juce Plugin Host.app"; sourceTree = BUILT_PRODUCTS_DIR; }; | |||||
| 508344B209E5C41E0093A071 /* Juce Plugin Host.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = "Juce Plugin Host.app"; sourceTree = BUILT_PRODUCTS_DIR; }; | |||||
| 84E5BB950C7203B70088E799 /* juce_GenericAudioFilterEditor.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_GenericAudioFilterEditor.cpp; sourceTree = "<group>"; }; | 84E5BB950C7203B70088E799 /* juce_GenericAudioFilterEditor.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_GenericAudioFilterEditor.cpp; sourceTree = "<group>"; }; | ||||
| 84E5BB960C7203B70088E799 /* juce_GenericAudioFilterEditor.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_GenericAudioFilterEditor.h; sourceTree = "<group>"; }; | 84E5BB960C7203B70088E799 /* juce_GenericAudioFilterEditor.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_GenericAudioFilterEditor.h; sourceTree = "<group>"; }; | ||||
| 84FFAE920C6C8A6F009F6E72 /* FilterGraph.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = FilterGraph.cpp; path = ../../src/host/FilterGraph.cpp; sourceTree = SOURCE_ROOT; }; | 84FFAE920C6C8A6F009F6E72 /* FilterGraph.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = FilterGraph.cpp; path = ../../src/host/FilterGraph.cpp; sourceTree = SOURCE_ROOT; }; | ||||
| @@ -233,9 +233,11 @@ XmlElement* FilterInGraph::createXml() const | |||||
| return e; | return e; | ||||
| } | } | ||||
| FilterInGraph* FilterInGraph::createForDescription (FilterGraph& owner, const PluginDescription& desc) | |||||
| FilterInGraph* FilterInGraph::createForDescription (FilterGraph& owner, | |||||
| const PluginDescription& desc, | |||||
| String& errorMessage) | |||||
| { | { | ||||
| AudioPluginInstance* instance = desc.createInstance(); | |||||
| AudioPluginInstance* instance = desc.createInstance (errorMessage); | |||||
| if (instance != 0) | if (instance != 0) | ||||
| return new FilterInGraph (owner, instance); | return new FilterInGraph (owner, instance); | ||||
| @@ -253,7 +255,8 @@ FilterInGraph* FilterInGraph::createFromXml (FilterGraph& owner, const XmlElemen | |||||
| break; | break; | ||||
| } | } | ||||
| FilterInGraph* const c = createForDescription (owner, pd); | |||||
| String errorMessage; | |||||
| FilterInGraph* const c = createForDescription (owner, pd, errorMessage); | |||||
| if (c == 0) | if (c == 0) | ||||
| return 0; | return 0; | ||||
| @@ -398,13 +401,20 @@ void FilterGraph::addFilter (const PluginDescription* desc, double x, double y) | |||||
| { | { | ||||
| if (desc != 0) | if (desc != 0) | ||||
| { | { | ||||
| FilterInGraph* cf = FilterInGraph::createForDescription (*this, *desc); | |||||
| String errorMessage; | |||||
| FilterInGraph* cf = FilterInGraph::createForDescription (*this, *desc, errorMessage); | |||||
| if (cf != 0) | if (cf != 0) | ||||
| { | { | ||||
| cf->setPosition (x, y); | cf->setPosition (x, y); | ||||
| addFilter (cf); | addFilter (cf); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||||
| TRANS("Couldn't create filter"), | |||||
| errorMessage); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -95,7 +95,10 @@ public: | |||||
| XmlElement* createXml() const; | XmlElement* createXml() const; | ||||
| static FilterInGraph* createForDescription (FilterGraph& owner, const PluginDescription& desc); | |||||
| static FilterInGraph* createForDescription (FilterGraph& owner, | |||||
| const PluginDescription& desc, | |||||
| String& errorMessage); | |||||
| static FilterInGraph* createFromXml (FilterGraph& owner, const XmlElement& xml); | static FilterInGraph* createFromXml (FilterGraph& owner, const XmlElement& xml); | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -67,6 +67,8 @@ void AudioPluginInstance::internalAsyncCallback() | |||||
| changedParamLock.enter(); | changedParamLock.enter(); | ||||
| Array <int> changed; | Array <int> changed; | ||||
| changed.swapWithArray (changedParams); | changed.swapWithArray (changedParams); | ||||
| Array <float> changedValues; | |||||
| changedValues.swapWithArray (changedParamValues); | |||||
| changedParamLock.exit(); | changedParamLock.exit(); | ||||
| for (int j = 0; j < changed.size(); ++j) | for (int j = 0; j < changed.size(); ++j) | ||||
| @@ -78,9 +80,13 @@ void AudioPluginInstance::internalAsyncCallback() | |||||
| AudioPluginParameterListener* const l = (AudioPluginParameterListener*) listeners.getUnchecked(i); | AudioPluginParameterListener* const l = (AudioPluginParameterListener*) listeners.getUnchecked(i); | ||||
| if (paramIndex >= 0) | if (paramIndex >= 0) | ||||
| l->audioPluginParameterChanged (this, paramIndex); | |||||
| else | |||||
| l->audioPluginParameterChanged (this, paramIndex, changedValues.getUnchecked(j)); | |||||
| else if (paramIndex == -1) | |||||
| l->audioPluginChanged (this); | l->audioPluginChanged (this); | ||||
| else if ((paramIndex & 0xc0000000) == 0xc0000000) | |||||
| l->audioPluginParameterChangeGestureBegin (this, paramIndex & 0x3fffffff); | |||||
| else if ((paramIndex & 0xc0000000) == 0x80000000) | |||||
| l->audioPluginParameterChangeGestureEnd (this, paramIndex & 0x3fffffff); | |||||
| i = jmin (i, listeners.size()); | i = jmin (i, listeners.size()); | ||||
| } | } | ||||
| @@ -122,20 +128,31 @@ bool JUCE_CALLTYPE AudioPluginInstance::getCurrentPositionInfo (AudioFilterBase: | |||||
| return true; | return true; | ||||
| } | } | ||||
| void JUCE_CALLTYPE AudioPluginInstance::informHostOfParameterChange (int index, float /*newValue*/) | |||||
| void JUCE_CALLTYPE AudioPluginInstance::informHostOfParameterChange (int index, float newValue) | |||||
| { | { | ||||
| queueChangeMessage (index); | |||||
| queueChangeMessage (index, newValue); | |||||
| } | |||||
| void JUCE_CALLTYPE AudioPluginInstance::informHostOfParameterGestureBegin (int index) | |||||
| { | |||||
| queueChangeMessage (0xc0000000 | index, 0); | |||||
| } | |||||
| void JUCE_CALLTYPE AudioPluginInstance::informHostOfParameterGestureEnd (int index) | |||||
| { | |||||
| queueChangeMessage (0x80000000 | index, 0); | |||||
| } | } | ||||
| void JUCE_CALLTYPE AudioPluginInstance::informHostOfStateChange() | void JUCE_CALLTYPE AudioPluginInstance::informHostOfStateChange() | ||||
| { | { | ||||
| queueChangeMessage (-1); | |||||
| queueChangeMessage (-1, 0); | |||||
| } | } | ||||
| void AudioPluginInstance::queueChangeMessage (const int index) throw() | |||||
| void AudioPluginInstance::queueChangeMessage (const int index, const float value) throw() | |||||
| { | { | ||||
| const ScopedLock sl (changedParamLock); | const ScopedLock sl (changedParamLock); | ||||
| changedParams.addIfNotAlreadyThere (index); | |||||
| changedParams.add (index); | |||||
| changedParamValues.add (value); | |||||
| if (! internalAsyncUpdater->isTimerRunning()) | if (! internalAsyncUpdater->isTimerRunning()) | ||||
| internalAsyncUpdater->startTimer (1); | internalAsyncUpdater->startTimer (1); | ||||
| @@ -153,3 +170,12 @@ void AudioPluginInstance::InternalAsyncUpdater::timerCallback() | |||||
| owner.internalAsyncCallback(); | owner.internalAsyncCallback(); | ||||
| } | } | ||||
| //============================================================================== | |||||
| void AudioPluginParameterListener::audioPluginParameterChangeGestureBegin (AudioPluginInstance*, int) | |||||
| { | |||||
| } | |||||
| void AudioPluginParameterListener::audioPluginParameterChangeGestureEnd (AudioPluginInstance*, int) | |||||
| { | |||||
| } | |||||
| @@ -47,12 +47,33 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Receives a callback when a parameter is changed. */ | /** Receives a callback when a parameter is changed. */ | ||||
| virtual void audioPluginParameterChanged (AudioPluginInstance* plugin, | virtual void audioPluginParameterChanged (AudioPluginInstance* plugin, | ||||
| int parameterIndex) = 0; | |||||
| int parameterIndex, | |||||
| float newValue) = 0; | |||||
| /** Called to indicate that something else in the plugin has changed, like its | /** Called to indicate that something else in the plugin has changed, like its | ||||
| program, number of parameters, etc. | program, number of parameters, etc. | ||||
| */ | */ | ||||
| virtual void audioPluginChanged (AudioPluginInstance* plugin) = 0; | virtual void audioPluginChanged (AudioPluginInstance* plugin) = 0; | ||||
| /** Indicates that a parameter change gesture has started. | |||||
| E.g. if the user is dragging a slider, this would be called when they first | |||||
| press the mouse button, and audioPluginParameterChangeGestureEnd would be | |||||
| called when they release it. | |||||
| @see audioPluginParameterChangeGestureEnd | |||||
| */ | |||||
| virtual void audioPluginParameterChangeGestureBegin (AudioPluginInstance* plugin, | |||||
| int parameterIndex); | |||||
| /** Indicates that a parameter change gesture has finished. | |||||
| E.g. if the user is dragging a slider, this would be called when they release | |||||
| the mouse button. | |||||
| @see audioPluginParameterChangeGestureStart | |||||
| */ | |||||
| virtual void audioPluginParameterChangeGestureEnd (AudioPluginInstance* plugin, | |||||
| int parameterIndex); | |||||
| }; | }; | ||||
| @@ -145,6 +166,7 @@ protected: | |||||
| VoidArray listeners; | VoidArray listeners; | ||||
| CriticalSection changedParamLock; | CriticalSection changedParamLock; | ||||
| Array <int> changedParams; | Array <int> changedParams; | ||||
| Array <float> changedParamValues; | |||||
| class InternalAsyncUpdater : public Timer | class InternalAsyncUpdater : public Timer | ||||
| { | { | ||||
| @@ -162,12 +184,14 @@ protected: | |||||
| InternalAsyncUpdater* internalAsyncUpdater; | InternalAsyncUpdater* internalAsyncUpdater; | ||||
| void internalAsyncCallback(); | void internalAsyncCallback(); | ||||
| void queueChangeMessage (const int index) throw(); | |||||
| void queueChangeMessage (const int index, const float value) throw(); | |||||
| AudioPluginInstance(); | AudioPluginInstance(); | ||||
| bool JUCE_CALLTYPE getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info); | bool JUCE_CALLTYPE getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info); | ||||
| void JUCE_CALLTYPE informHostOfParameterChange (int index, float newValue); | void JUCE_CALLTYPE informHostOfParameterChange (int index, float newValue); | ||||
| void JUCE_CALLTYPE informHostOfParameterGestureBegin (int index); | |||||
| void JUCE_CALLTYPE informHostOfParameterGestureEnd (int index); | |||||
| void JUCE_CALLTYPE informHostOfStateChange(); | void JUCE_CALLTYPE informHostOfStateChange(); | ||||
| }; | }; | ||||
| @@ -64,7 +64,7 @@ public: | |||||
| { | { | ||||
| } | } | ||||
| void audioPluginParameterChanged (AudioPluginInstance*, int parameterIndex) | |||||
| void audioPluginParameterChanged (AudioPluginInstance*, int parameterIndex, float) | |||||
| { | { | ||||
| if (parameterIndex == index) | if (parameterIndex == index) | ||||
| refresh(); | refresh(); | ||||
| @@ -100,7 +100,7 @@ void PluginDescription::fillInFromInstance (AudioPluginInstance& instance) throw | |||||
| isInstrument = instance.isInstrument(); | isInstrument = instance.isInstrument(); | ||||
| } | } | ||||
| AudioPluginInstance* PluginDescription::createInstance() const | |||||
| AudioPluginInstance* PluginDescription::createInstance (String& errorMessage) const | |||||
| { | { | ||||
| AudioPluginInstance* result = 0; | AudioPluginInstance* result = 0; | ||||
| @@ -114,6 +114,14 @@ AudioPluginInstance* PluginDescription::createInstance() const | |||||
| break; | break; | ||||
| } | } | ||||
| if (result == 0) | |||||
| { | |||||
| if (file != File::nonexistent && ! file.exists()) | |||||
| errorMessage = TRANS ("This plug-in file no longer exists"); | |||||
| else | |||||
| errorMessage = TRANS ("This plug-in failed to load correctly"); | |||||
| } | |||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -117,9 +117,10 @@ public: | |||||
| The caller is responsible for deleting the object that is returned. | The caller is responsible for deleting the object that is returned. | ||||
| Returns 0 if it can't load the plugin for some reason. | |||||
| If it can't load the plugin, it returns 0 and leaves a message in the | |||||
| errorMessage string. | |||||
| */ | */ | ||||
| AudioPluginInstance* createInstance() const; | |||||
| AudioPluginInstance* createInstance (String& errorMessage) const; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates an XML object containing these details. | /** Creates an XML object containing these details. | ||||
| @@ -135,6 +135,7 @@ void PluginListComponent::buttonClicked (Button* b) | |||||
| menu.addItem (1, TRANS("Clear list")); | menu.addItem (1, TRANS("Clear list")); | ||||
| menu.addItem (5, TRANS("Remove selected plugin from list"), listBox->getNumSelectedRows() > 0); | menu.addItem (5, TRANS("Remove selected plugin from list"), listBox->getNumSelectedRows() > 0); | ||||
| menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox->getNumSelectedRows() > 0); | menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox->getNumSelectedRows() > 0); | ||||
| menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); | |||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addItem (2, TRANS("Sort alphabetically")); | menu.addItem (2, TRANS("Sort alphabetically")); | ||||
| menu.addItem (3, TRANS("Sort by category")); | menu.addItem (3, TRANS("Sort by category")); | ||||
| @@ -182,6 +183,17 @@ void PluginListComponent::buttonClicked (Button* b) | |||||
| if (desc != 0) | if (desc != 0) | ||||
| desc->file.getParentDirectory().startAsProcess(); | desc->file.getParentDirectory().startAsProcess(); | ||||
| } | } | ||||
| else if (r == 7) | |||||
| { | |||||
| for (int i = list.getNumTypes(); --i >= 0;) | |||||
| { | |||||
| if (list.getType (i)->file != File::nonexistent | |||||
| && ! list.getType (i)->file.exists()) | |||||
| { | |||||
| list.removeType (i); | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (r != 0) | else if (r != 0) | ||||
| { | { | ||||
| scanFor (AudioPluginFormatManager::getInstance()->getFormat (r - 10)); | scanFor (AudioPluginFormatManager::getInstance()->getFormat (r - 10)); | ||||
| @@ -81,6 +81,11 @@ public: | |||||
| Globals()->UseIndexedParameters (juceFilter->getNumParameters()); | Globals()->UseIndexedParameters (juceFilter->getNumParameters()); | ||||
| activePlugins.add (this); | activePlugins.add (this); | ||||
| zerostruct (auEvent); | |||||
| auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance(); | |||||
| auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global; | |||||
| auEvent.mArgument.mParameter.mElement = 0; | |||||
| } | } | ||||
| ~JuceAU() | ~JuceAU() | ||||
| @@ -219,14 +224,27 @@ public: | |||||
| AudioUnitParameterID inParameterID, | AudioUnitParameterID inParameterID, | ||||
| AudioUnitParameterInfo& outParameterInfo) | AudioUnitParameterInfo& outParameterInfo) | ||||
| { | { | ||||
| if (inScope == kAudioUnitScope_Global && juceFilter != 0) | |||||
| const int index = (int) inParameterID; | |||||
| if (inScope == kAudioUnitScope_Global | |||||
| && juceFilter != 0 | |||||
| && index < juceFilter->getNumParameters()) | |||||
| { | { | ||||
| outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable | outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable | ||||
| | kAudioUnitParameterFlag_IsReadable | | kAudioUnitParameterFlag_IsReadable | ||||
| | kAudioUnitParameterFlag_HasCFNameString; | | kAudioUnitParameterFlag_HasCFNameString; | ||||
| outParameterInfo.name[0] = 0; | |||||
| outParameterInfo.cfNameString = PlatformUtilities::juceStringToCFString (juceFilter->getParameterName ((int) inParameterID)); | |||||
| const String name (juceFilter->getParameterName (index)); | |||||
| CharacterFunctions::copy ((char*) outParameterInfo.name, | |||||
| (const char*) name.toUTF8(), | |||||
| sizeof (outParameterInfo.name) - 1); | |||||
| // set whether the param is automatable (unnamed parameters aren't allowed to be automated) | |||||
| if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index)) | |||||
| outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime; | |||||
| outParameterInfo.cfNameString = PlatformUtilities::juceStringToCFString (name); | |||||
| outParameterInfo.minValue = 0.0f; | outParameterInfo.minValue = 0.0f; | ||||
| outParameterInfo.maxValue = 1.0f; | outParameterInfo.maxValue = 1.0f; | ||||
| outParameterInfo.defaultValue = 0.0f; | outParameterInfo.defaultValue = 0.0f; | ||||
| @@ -377,25 +395,37 @@ public: | |||||
| return true; | return true; | ||||
| } | } | ||||
| void sendAUEvent (const AudioUnitEventType type, const int index) throw() | |||||
| { | |||||
| if (AUEventListenerNotify != 0) | |||||
| { | |||||
| auEvent.mEventType = type; | |||||
| auEvent.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index; | |||||
| AUEventListenerNotify (0, 0, &auEvent); | |||||
| } | |||||
| } | |||||
| void informHostOfParameterChange (int index, float newValue) | void informHostOfParameterChange (int index, float newValue) | ||||
| { | { | ||||
| if (juceFilter != 0) | if (juceFilter != 0) | ||||
| { | { | ||||
| juceFilter->setParameter (index, newValue); | juceFilter->setParameter (index, newValue); | ||||
| if (AUEventListenerNotify != 0) | |||||
| { | |||||
| AudioUnitEvent e; | |||||
| e.mEventType = kAudioUnitEvent_ParameterValueChange; | |||||
| e.mArgument.mParameter.mAudioUnit = GetComponentInstance(); | |||||
| e.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index; | |||||
| e.mArgument.mParameter.mScope = kAudioUnitScope_Global; | |||||
| e.mArgument.mParameter.mElement = 0; | |||||
| AUEventListenerNotify (0, 0, &e); | |||||
| } | |||||
| sendAUEvent (kAudioUnitEvent_ParameterValueChange, index); | |||||
| } | } | ||||
| } | } | ||||
| void informHostOfParameterGestureBegin (int index) | |||||
| { | |||||
| if (juceFilter != 0) | |||||
| sendAUEvent (kAudioUnitEvent_BeginParameterChangeGesture, index); | |||||
| } | |||||
| void informHostOfParameterGestureEnd (int index) | |||||
| { | |||||
| if (juceFilter != 0) | |||||
| sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index); | |||||
| } | |||||
| void informHostOfStateChange() | void informHostOfStateChange() | ||||
| { | { | ||||
| // xxx is there an AU equivalent? | // xxx is there an AU equivalent? | ||||
| @@ -645,6 +675,7 @@ private: | |||||
| bool prepared; | bool prepared; | ||||
| SMPTETime lastSMPTETime; | SMPTETime lastSMPTETime; | ||||
| AUChannelInfo channelInfo [numChannelConfigs]; | AUChannelInfo channelInfo [numChannelConfigs]; | ||||
| AudioUnitEvent auEvent; | |||||
| }; | }; | ||||
| @@ -848,6 +848,16 @@ protected: | |||||
| SetControlValue (index + 2, floatToLong (newValue)); | SetControlValue (index + 2, floatToLong (newValue)); | ||||
| } | } | ||||
| void JUCE_CALLTYPE informHostOfParameterGestureBegin (int index) | |||||
| { | |||||
| TouchControl (index + 2); | |||||
| } | |||||
| void JUCE_CALLTYPE informHostOfParameterGestureEnd (int index) | |||||
| { | |||||
| ReleaseControl (index + 2); | |||||
| } | |||||
| void JUCE_CALLTYPE informHostOfStateChange() | void JUCE_CALLTYPE informHostOfStateChange() | ||||
| { | { | ||||
| // xxx is there an RTAS equivalent? | // xxx is there an RTAS equivalent? | ||||
| @@ -142,6 +142,13 @@ void AudioFilterStreamer::informHostOfParameterChange (int index, float newValue | |||||
| filter.setParameter (index, newValue); | filter.setParameter (index, newValue); | ||||
| } | } | ||||
| void JUCE_CALLTYPE AudioFilterStreamer::informHostOfParameterGestureBegin (int index) | |||||
| { | |||||
| } | |||||
| void JUCE_CALLTYPE AudioFilterStreamer::informHostOfParameterGestureEnd (int index) | |||||
| { | |||||
| } | |||||
| void JUCE_CALLTYPE AudioFilterStreamer::informHostOfStateChange() | void JUCE_CALLTYPE AudioFilterStreamer::informHostOfStateChange() | ||||
| { | { | ||||
| @@ -73,6 +73,8 @@ public: | |||||
| bool JUCE_CALLTYPE getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info); | bool JUCE_CALLTYPE getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info); | ||||
| void JUCE_CALLTYPE informHostOfParameterChange (int index, float newValue); | void JUCE_CALLTYPE informHostOfParameterChange (int index, float newValue); | ||||
| void JUCE_CALLTYPE informHostOfParameterGestureBegin (int index); | |||||
| void JUCE_CALLTYPE informHostOfParameterGestureEnd (int index); | |||||
| void JUCE_CALLTYPE informHostOfStateChange(); | void JUCE_CALLTYPE informHostOfStateChange(); | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| @@ -814,6 +814,16 @@ public: | |||||
| setParameterAutomated (index, newValue); | setParameterAutomated (index, newValue); | ||||
| } | } | ||||
| void JUCE_CALLTYPE informHostOfParameterGestureBegin (int index) | |||||
| { | |||||
| beginEdit (index); | |||||
| } | |||||
| void JUCE_CALLTYPE informHostOfParameterGestureEnd (int index) | |||||
| { | |||||
| endEdit (index); | |||||
| } | |||||
| void JUCE_CALLTYPE informHostOfStateChange() | void JUCE_CALLTYPE informHostOfStateChange() | ||||
| { | { | ||||
| updateDisplay(); | updateDisplay(); | ||||
| @@ -49,9 +49,13 @@ AudioFilterBase::~AudioFilterBase() | |||||
| // ooh, nasty - the editor should have been deleted before the filter | // ooh, nasty - the editor should have been deleted before the filter | ||||
| // that it refers to is deleted.. | // that it refers to is deleted.. | ||||
| jassert (activeEditor == 0); | jassert (activeEditor == 0); | ||||
| // This will fail if you've called beginParameterChangeGesture() for one | |||||
| // or more parameters without having made a corresponding call to endParameterChangeGesture... | |||||
| jassert (changingParams.countNumberOfSetBits() == 0); | |||||
| } | } | ||||
| void AudioFilterBase::setHostCallbacks (HostCallbacks* const callbacks_) | |||||
| void AudioFilterBase::setHostCallbacks (HostCallbacks* const callbacks_) throw() | |||||
| { | { | ||||
| callbacks = callbacks_; | callbacks = callbacks_; | ||||
| } | } | ||||
| @@ -78,6 +82,31 @@ void AudioFilterBase::setParameterNotifyingHost (const int parameterIndex, | |||||
| setParameter (parameterIndex, newValue); | setParameter (parameterIndex, newValue); | ||||
| } | } | ||||
| void AudioFilterBase::beginParameterChangeGesture (int parameterIndex) | |||||
| { | |||||
| jassert (parameterIndex >= 0 && parameterIndex < getNumParameters()); | |||||
| // This means you've called beginParameterChangeGesture twice in succession without a matching | |||||
| // call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. | |||||
| jassert (! changingParams [parameterIndex]); | |||||
| if (callbacks != 0) | |||||
| callbacks->informHostOfParameterGestureBegin (parameterIndex); | |||||
| } | |||||
| void AudioFilterBase::endParameterChangeGesture (int parameterIndex) | |||||
| { | |||||
| jassert (parameterIndex >= 0 && parameterIndex < getNumParameters()); | |||||
| // This means you've called endParameterChangeGesture without having previously called | |||||
| // endParameterChangeGesture. That might be fine in most hosts, but better to keep the | |||||
| // calls matched correctly. | |||||
| jassert (changingParams [parameterIndex]); | |||||
| if (callbacks != 0) | |||||
| callbacks->informHostOfParameterGestureEnd (parameterIndex); | |||||
| } | |||||
| void JUCE_CALLTYPE AudioFilterBase::updateHostDisplay() | void JUCE_CALLTYPE AudioFilterBase::updateHostDisplay() | ||||
| { | { | ||||
| if (callbacks != 0) | if (callbacks != 0) | ||||
| @@ -103,7 +132,7 @@ bool AudioFilterBase::getCurrentPositionInfo (CurrentPositionInfo& info) | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| void AudioFilterBase::editorBeingDeleted (AudioFilterEditor* const editor) | |||||
| void AudioFilterBase::editorBeingDeleted (AudioFilterEditor* const editor) throw() | |||||
| { | { | ||||
| const ScopedLock sl (callbackLock); | const ScopedLock sl (callbackLock); | ||||
| @@ -403,6 +403,10 @@ public: | |||||
| This could happen when the editor or some other internal operation changes | This could happen when the editor or some other internal operation changes | ||||
| a parameter. This method will call the setParameter() method to change the | a parameter. This method will call the setParameter() method to change the | ||||
| value, and will then send a message to the host telling it about the change. | value, and will then send a message to the host telling it about the change. | ||||
| Note that to make sure the host correctly handles automation, you should call | |||||
| the beginParameterChangeGesture() and endParameterChangeGesture() methods to | |||||
| tell the host when the user has started and stopped changing the parameter. | |||||
| */ | */ | ||||
| void JUCE_CALLTYPE setParameterNotifyingHost (int parameterIndex, | void JUCE_CALLTYPE setParameterNotifyingHost (int parameterIndex, | ||||
| float newValue); | float newValue); | ||||
| @@ -411,7 +415,26 @@ public: | |||||
| By default, this returns true for all parameters. | By default, this returns true for all parameters. | ||||
| */ | */ | ||||
| virtual bool isParameterAutomatable (int index) const; | |||||
| virtual bool isParameterAutomatable (int parameterIndex) const; | |||||
| /** Sends a signal to the host to tell it that the user is about to start changing this | |||||
| parameter. | |||||
| This allows the host to know when a parameter is actively being held by the user, and | |||||
| it may use this information to help it record automation. | |||||
| If you call this, it must be matched by a later call to endParameterChangeGesture(). | |||||
| */ | |||||
| void beginParameterChangeGesture (int parameterIndex); | |||||
| /** Tells the host that the user has finished changing this parameter. | |||||
| This allows the host to know when a parameter is actively being held by the user, and | |||||
| it may use this information to help it record automation. | |||||
| A call to this method must follow a call to beginParameterChangeGesture(). | |||||
| */ | |||||
| void endParameterChangeGesture (int parameterIndex); | |||||
| /** The filter can call this when something (apart from a parameter value) has changed. | /** The filter can call this when something (apart from a parameter value) has changed. | ||||
| @@ -506,6 +529,8 @@ public: | |||||
| virtual bool JUCE_CALLTYPE getCurrentPositionInfo (CurrentPositionInfo& info) = 0; | virtual bool JUCE_CALLTYPE getCurrentPositionInfo (CurrentPositionInfo& info) = 0; | ||||
| virtual void JUCE_CALLTYPE informHostOfParameterChange (int index, float newValue) = 0; | virtual void JUCE_CALLTYPE informHostOfParameterChange (int index, float newValue) = 0; | ||||
| virtual void JUCE_CALLTYPE informHostOfParameterGestureBegin (int index) = 0; | |||||
| virtual void JUCE_CALLTYPE informHostOfParameterGestureEnd (int index) = 0; | |||||
| /** Callback to indicate that something (other than a parameter) has changed in the | /** Callback to indicate that something (other than a parameter) has changed in the | ||||
| filter, such as its current program, parameter list, etc. */ | filter, such as its current program, parameter list, etc. */ | ||||
| @@ -517,12 +542,12 @@ public: | |||||
| /** Not for public use - this is called by the wrapper code before deleting an | /** Not for public use - this is called by the wrapper code before deleting an | ||||
| editor component. | editor component. | ||||
| */ | */ | ||||
| void JUCE_CALLTYPE editorBeingDeleted (AudioFilterEditor* const editor); | |||||
| void JUCE_CALLTYPE editorBeingDeleted (AudioFilterEditor* const editor) throw(); | |||||
| /** Not for public use - this is called by the wrapper code to initialise the | /** Not for public use - this is called by the wrapper code to initialise the | ||||
| filter. | filter. | ||||
| */ | */ | ||||
| void JUCE_CALLTYPE setHostCallbacks (HostCallbacks* const); | |||||
| void JUCE_CALLTYPE setHostCallbacks (HostCallbacks* const) throw(); | |||||
| /** Not for public use - this is called by the wrapper code to initialise the | /** Not for public use - this is called by the wrapper code to initialise the | ||||
| filter. | filter. | ||||
| @@ -564,6 +589,10 @@ private: | |||||
| int blockSize, numInputChannels, numOutputChannels; | int blockSize, numInputChannels, numOutputChannels; | ||||
| bool suspended; | bool suspended; | ||||
| CriticalSection callbackLock; | CriticalSection callbackLock; | ||||
| #ifdef JUCE_DEBUG | |||||
| BitArray changingParams; | |||||
| #endif | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||