mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-24 01:54:22 +00:00
AUv3: Added support for MIDI output, host MIDI controller visibility and MPE
This commit is contained in:
parent
1089f3837c
commit
621fcf31d2
5 changed files with 167 additions and 29 deletions
|
|
@ -101,7 +101,7 @@ JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemo
|
|||
timecodeDisplayLabel.setFont (Font (Font::getDefaultMonospacedFontName(), 15.0f, Font::plain));
|
||||
|
||||
// set resize limits for this plug-in
|
||||
setResizeLimits (400, 200, 800, 300);
|
||||
setResizeLimits (400, 200, 1024, 700);
|
||||
|
||||
// set our component's initial size to be the last one that was stored in the filter's settings
|
||||
setSize (owner.lastUIWidth,
|
||||
|
|
@ -146,6 +146,11 @@ void JuceDemoPluginAudioProcessorEditor::timerCallback()
|
|||
updateTimecodeDisplay (getProcessor().lastPosInfo);
|
||||
}
|
||||
|
||||
void JuceDemoPluginAudioProcessorEditor::hostMIDIControllerIsAvailable (bool controllerIsAvailable)
|
||||
{
|
||||
midiKeyboard.setVisible (! controllerIsAvailable);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// quick-and-dirty function to format a timecode string
|
||||
static String timeToTimecodeString (double seconds)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ public:
|
|||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
void timerCallback() override;
|
||||
void hostMIDIControllerIsAvailable (bool) override;
|
||||
|
||||
private:
|
||||
class ParameterSlider;
|
||||
|
|
|
|||
|
|
@ -37,12 +37,20 @@
|
|||
#if (! defined MAC_OS_X_VERSION_MIN_REQUIRED) || (! defined MAC_OS_X_VERSION_10_11) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11)
|
||||
#error AUv3 needs Deployment Target OS X 10.11 or higher to compile
|
||||
#endif
|
||||
#if (defined MAC_OS_X_VERSION_10_13) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13)
|
||||
#define JUCE_AUV3_MIDI_OUTPUT_SUPPORTED 1
|
||||
#define JUCE_AUV3_VIEW_CONFIG_SUPPORTED 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
#if (! defined __IPHONE_OS_VERSION_MIN_REQUIRED) || (! defined __IPHONE_9_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
|
||||
#error AUv3 needs Deployment Target iOS 9.0 or higher to compile
|
||||
#endif
|
||||
#if (defined __IPHONE_11_0) && (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0)
|
||||
#define JUCE_AUV3_MIDI_OUTPUT_SUPPORTED 1
|
||||
#define JUCE_AUV3_VIEW_CONFIG_SUPPORTED 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __OBJC2__
|
||||
|
|
@ -97,6 +105,15 @@ struct AudioProcessorHolder : public ReferenceCountedObject
|
|||
AudioProcessor* operator->() noexcept { return processor; }
|
||||
AudioProcessor* get() noexcept { return processor; }
|
||||
|
||||
struct ViewConfig
|
||||
{
|
||||
double width;
|
||||
double height;
|
||||
bool hostHasMIDIController;
|
||||
};
|
||||
|
||||
ScopedPointer<ViewConfig> viewConfiguration;
|
||||
|
||||
typedef ReferenceCountedObjectPtr<AudioProcessorHolder> Ptr;
|
||||
|
||||
private:
|
||||
|
|
@ -155,7 +172,7 @@ public:
|
|||
virtual AUParameterTree* getParameterTree() = 0;
|
||||
virtual AUInternalRenderBlock getInternalRenderBlock() = 0;
|
||||
virtual void setRenderingOffline (bool offline) = 0;
|
||||
virtual NSArray<NSNumber*> *getChannelCapabilities() = 0;
|
||||
virtual NSArray<NSNumber*>* getChannelCapabilities() = 0;
|
||||
|
||||
//==============================================================================
|
||||
virtual NSArray<NSNumber*>* parametersForOverviewWithCount (int)
|
||||
|
|
@ -192,6 +209,14 @@ public:
|
|||
ObjCMsgSendSuper<void> (&s, @selector (deallocateRenderResources));
|
||||
}
|
||||
|
||||
virtual bool getSupportsMPE() = 0;
|
||||
virtual NSArray<NSString*>* getMIDIOutputNames() = 0;
|
||||
|
||||
#if JUCE_AUV3_VIEW_CONFIG_SUPPORTED
|
||||
virtual NSIndexSet* getSupportedViewConfigurations (NSArray<AUAudioUnitViewConfiguration*>*) = 0;
|
||||
virtual void selectViewConfiguration (AUAudioUnitViewConfiguration*) = 0;
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct Class : public ObjCClass<AUAudioUnit>
|
||||
{
|
||||
|
|
@ -230,13 +255,22 @@ private:
|
|||
addMethod (@selector (parametersForOverviewWithCount:), parametersForOverviewWithCount, "@@:", @encode (NSInteger));
|
||||
addMethod (@selector (setRenderingOffline:), setRenderingOffline, "v@:", @encode (BOOL));
|
||||
|
||||
|
||||
addMethod (@selector (internalRenderBlock), getInternalRenderBlock, @encode (AUInternalRenderBlock), "@:");
|
||||
addMethod (@selector (virtualMIDICableCount), getVirtualMIDICableCount, @encode (NSInteger), "@:");
|
||||
addMethod (@selector (latency), getLatency, @encode (NSTimeInterval), "@:");
|
||||
addMethod (@selector (tailTime), getTailTime, @encode (NSTimeInterval), "@:");
|
||||
addMethod (@selector (canProcessInPlace), getCanProcessInPlace, @encode (BOOL), "@:");
|
||||
addMethod (@selector (isRenderingOffline), getRenderingOffline, @encode (BOOL), "@:");
|
||||
addMethod (@selector (supportsMPE), getSupportsMPE, @encode (BOOL), "@:");
|
||||
|
||||
#if JUCE_AUV3_VIEW_CONFIG_SUPPORTED
|
||||
addMethod (@selector (supportedViewConfigurations:), getSupportedViewConfigurations, "@@:@");
|
||||
addMethod (@selector (selectViewConfiguration:), selectViewConfiguration, "v@:@");
|
||||
#endif
|
||||
|
||||
#if JUCE_AUV3_MIDI_OUTPUT_SUPPORTED
|
||||
addMethod (@selector (MIDIOutputNames), getMIDIOutputNames, "@@:");
|
||||
#endif
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
|
@ -273,28 +307,34 @@ private:
|
|||
return self;
|
||||
}
|
||||
|
||||
static void dealloc (id self, SEL) { delete _this (self); }
|
||||
static AUAudioUnitBusArray* getInputBusses (id self, SEL) { return _this (self)->getInputBusses(); }
|
||||
static AUAudioUnitBusArray* getOutputBusses (id self, SEL) { return _this (self)->getOutputBusses(); }
|
||||
static AUParameterTree* getParameterTree (id self, SEL) { return _this (self)->getParameterTree(); }
|
||||
static AUInternalRenderBlock getInternalRenderBlock (id self, SEL) { return _this (self)->getInternalRenderBlock(); }
|
||||
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 void reset (id self, SEL) { _this (self)->reset(); }
|
||||
static NSInteger getVirtualMIDICableCount (id self, SEL) { return _this (self)->getVirtualMIDICableCount(); }
|
||||
static BOOL shouldChangeToFormat (id self, SEL, AVAudioFormat* format, AUAudioUnitBus* bus) { return _this (self)->shouldChangeToFormat (format, bus) ? YES : NO; }
|
||||
static NSArray<NSNumber*>* parametersForOverviewWithCount (id self, SEL, NSInteger count) { return _this (self)->parametersForOverviewWithCount (static_cast<int> (count)); }
|
||||
static NSArray<AUAudioUnitPreset*>* getFactoryPresets (id self, SEL) { return _this (self)->getFactoryPresets(); }
|
||||
static AUAudioUnitPreset* getCurrentPreset (id self, SEL) { return _this (self)->getCurrentPreset(); }
|
||||
static void setCurrentPreset (id self, SEL, AUAudioUnitPreset* preset) { return _this (self)->setCurrentPreset (preset); }
|
||||
static NSDictionary<NSString*, id>* getFullState (id self, SEL) { return _this (self)->getFullState(); }
|
||||
static void setFullState (id self, SEL, NSDictionary<NSString *, id>* state) { return _this (self)->setFullState (state); }
|
||||
static NSTimeInterval getLatency (id self, SEL) { return _this (self)->getLatency(); }
|
||||
static NSTimeInterval getTailTime (id self, SEL) { return _this (self)->getTailTime(); }
|
||||
static BOOL getCanProcessInPlace (id self, SEL) { return _this (self)->getCanProcessInPlace() ? YES : NO; }
|
||||
static BOOL getRenderingOffline (id self, SEL) { return _this (self)->getRenderingOffline() ? YES : NO; }
|
||||
static void setRenderingOffline (id self, SEL, BOOL renderingOffline) { _this (self)->setRenderingOffline (renderingOffline); }
|
||||
static NSArray<NSNumber*>* getChannelCapabilities (id self, SEL) { return _this (self)->getChannelCapabilities(); }
|
||||
static void dealloc (id self, SEL) { delete _this (self); }
|
||||
static AUAudioUnitBusArray* getInputBusses (id self, SEL) { return _this (self)->getInputBusses(); }
|
||||
static AUAudioUnitBusArray* getOutputBusses (id self, SEL) { return _this (self)->getOutputBusses(); }
|
||||
static AUParameterTree* getParameterTree (id self, SEL) { return _this (self)->getParameterTree(); }
|
||||
static AUInternalRenderBlock getInternalRenderBlock (id self, SEL) { return _this (self)->getInternalRenderBlock(); }
|
||||
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 void reset (id self, SEL) { _this (self)->reset(); }
|
||||
static NSInteger getVirtualMIDICableCount (id self, SEL) { return _this (self)->getVirtualMIDICableCount(); }
|
||||
static BOOL shouldChangeToFormat (id self, SEL, AVAudioFormat* format, AUAudioUnitBus* bus) { return _this (self)->shouldChangeToFormat (format, bus) ? YES : NO; }
|
||||
static NSArray<NSNumber*>* parametersForOverviewWithCount (id self, SEL, NSInteger count) { return _this (self)->parametersForOverviewWithCount (static_cast<int> (count)); }
|
||||
static NSArray<AUAudioUnitPreset*>* getFactoryPresets (id self, SEL) { return _this (self)->getFactoryPresets(); }
|
||||
static AUAudioUnitPreset* getCurrentPreset (id self, SEL) { return _this (self)->getCurrentPreset(); }
|
||||
static void setCurrentPreset (id self, SEL, AUAudioUnitPreset* preset) { return _this (self)->setCurrentPreset (preset); }
|
||||
static NSDictionary<NSString*, id>* getFullState (id self, SEL) { return _this (self)->getFullState(); }
|
||||
static void setFullState (id self, SEL, NSDictionary<NSString *, id>* state) { return _this (self)->setFullState (state); }
|
||||
static NSTimeInterval getLatency (id self, SEL) { return _this (self)->getLatency(); }
|
||||
static NSTimeInterval getTailTime (id self, SEL) { return _this (self)->getTailTime(); }
|
||||
static BOOL getCanProcessInPlace (id self, SEL) { return _this (self)->getCanProcessInPlace() ? YES : NO; }
|
||||
static BOOL getRenderingOffline (id self, SEL) { return _this (self)->getRenderingOffline() ? YES : NO; }
|
||||
static void setRenderingOffline (id self, SEL, BOOL renderingOffline) { _this (self)->setRenderingOffline (renderingOffline); }
|
||||
static NSArray<NSNumber*>* getChannelCapabilities (id self, SEL) { return _this (self)->getChannelCapabilities(); }
|
||||
static BOOL getSupportsMPE (id self, SEL) { return _this (self)->getSupportsMPE() ? YES : NO; }
|
||||
static NSArray<NSString*>* getMIDIOutputNames (id self, SEL) { return _this (self)->getMIDIOutputNames(); }
|
||||
#if JUCE_AUV3_VIEW_CONFIG_SUPPORTED
|
||||
static NSIndexSet* getSupportedViewConfigurations (id self, SEL, NSArray<AUAudioUnitViewConfiguration*>* configs) { return _this (self)->getSupportedViewConfigurations (configs); }
|
||||
static void selectViewConfiguration (id self, SEL, AUAudioUnitViewConfiguration* config) { _this (self)->selectViewConfiguration (config); }
|
||||
#endif
|
||||
};
|
||||
|
||||
static JuceAudioUnitv3Base* create (AUAudioUnit*, AudioComponentDescription, AudioComponentInstantiationOptions, NSError**);
|
||||
|
|
@ -647,6 +687,60 @@ public:
|
|||
lastTimeStamp.mSampleTime = std::numeric_limits<Float64>::max();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_AUV3_VIEW_CONFIG_SUPPORTED
|
||||
NSIndexSet* getSupportedViewConfigurations (NSArray<AUAudioUnitViewConfiguration*>* configs) override
|
||||
{
|
||||
auto supportedViewIndecies = [[NSMutableIndexSet alloc] init];
|
||||
auto n = [configs count];
|
||||
|
||||
if (auto* editor = getAudioProcessor().createEditorIfNeeded())
|
||||
{
|
||||
// If you hit this assertion then your plug-in's editor is reporting that it doesn't support
|
||||
// any host MIDI controller configurations!
|
||||
jassert (editor->supportsHostMIDIControllerPresence (true) || editor->supportsHostMIDIControllerPresence (false));
|
||||
|
||||
for (auto i = 0u; i < n; ++i)
|
||||
{
|
||||
if (auto* viewConfiguration = [configs objectAtIndex:i])
|
||||
{
|
||||
if (editor->supportsHostMIDIControllerPresence ([viewConfiguration hostHasController] == YES))
|
||||
{
|
||||
auto* constrainer = editor->getConstrainer();
|
||||
auto height = (int) [viewConfiguration height];
|
||||
auto width = (int) [viewConfiguration width];
|
||||
|
||||
if (height <= constrainer->getMaximumHeight() && height >= constrainer->getMinimumHeight()
|
||||
&& width <= constrainer->getMaximumWidth() && width >= constrainer->getMinimumWidth())
|
||||
[supportedViewIndecies addIndex: i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [supportedViewIndecies autorelease];
|
||||
}
|
||||
|
||||
void selectViewConfiguration (AUAudioUnitViewConfiguration* config) override
|
||||
{
|
||||
processorHolder->viewConfiguration = new AudioProcessorHolder::ViewConfig { [config width], [config height], [config hostHasController] == YES };
|
||||
}
|
||||
#endif
|
||||
|
||||
bool getSupportsMPE() override
|
||||
{
|
||||
return getAudioProcessor().supportsMPE();
|
||||
}
|
||||
|
||||
NSArray<NSString*>* getMIDIOutputNames() override
|
||||
{
|
||||
#if JucePlugin_ProducesMidiOutput
|
||||
return @[@"MIDI Out"];
|
||||
#else
|
||||
return @[];
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool shouldChangeToFormat (AVAudioFormat* format, AUAudioUnitBus* auBus) override
|
||||
{
|
||||
|
|
@ -1175,6 +1269,17 @@ private:
|
|||
|
||||
// process audio
|
||||
processBlock (audioBuffer.getBuffer (frameCount), midiMessages);
|
||||
|
||||
// send MIDI
|
||||
#if JucePlugin_ProducesMidiOutput && JUCE_AUV3_MIDI_OUTPUT_SUPPORTED
|
||||
auto midiOut = [au MIDIOutputEventBlock];
|
||||
MidiMessage msg;
|
||||
int samplePosition;
|
||||
|
||||
for (MidiBuffer::Iterator it (midiMessages); it.getNextEvent (msg, samplePosition);)
|
||||
midiOut (samplePosition, 0, msg.getRawDataSize(), msg.getRawData());
|
||||
#endif
|
||||
|
||||
midiMessages.clear();
|
||||
}
|
||||
|
||||
|
|
@ -1288,8 +1393,8 @@ private:
|
|||
AUParameterObserverToken editorObserverToken;
|
||||
|
||||
ScopedPointer<AUParameterTree> paramTree;
|
||||
ScopedPointer<NSMutableArray<NSNumber*> > overviewParams;
|
||||
ScopedPointer<NSMutableArray<NSNumber*> > channelCapabilities;
|
||||
ScopedPointer<NSMutableArray<NSNumber*>> overviewParams;
|
||||
ScopedPointer<NSMutableArray<NSNumber*>> channelCapabilities;
|
||||
|
||||
ScopedPointer<NSMutableArray<AUAudioUnitPreset*> > factoryPresets;
|
||||
|
||||
|
|
@ -1320,7 +1425,6 @@ JuceAudioUnitv3Base* JuceAudioUnitv3Base::create (AUAudioUnit* audioUnit, AudioC
|
|||
class JuceAUViewController
|
||||
{
|
||||
public:
|
||||
|
||||
JuceAUViewController (AUViewController<AUAudioUnitFactory>* p)
|
||||
: myself (p), processorHolder (nullptr), preferredSize (1.0f, 1.0f)
|
||||
{
|
||||
|
|
@ -1370,6 +1474,9 @@ public:
|
|||
{
|
||||
if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor())
|
||||
{
|
||||
if (processorHolder->viewConfiguration != nullptr)
|
||||
editor->hostMIDIControllerIsAvailable (processorHolder->viewConfiguration->hostHasMIDIController);
|
||||
|
||||
editor->setBounds (convertToRectInt ([[myself view] bounds]));
|
||||
|
||||
if (JUCE_IOS_MAC_VIEW* peerView = [[[myself view] subviews] objectAtIndex: 0])
|
||||
|
|
|
|||
|
|
@ -47,7 +47,10 @@ AudioProcessorEditor::~AudioProcessorEditor()
|
|||
}
|
||||
|
||||
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {}
|
||||
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
|
||||
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
|
||||
|
||||
bool AudioProcessorEditor::supportsHostMIDIControllerPresence (bool) { return true; }
|
||||
void AudioProcessorEditor::hostMIDIControllerIsAvailable (bool) {}
|
||||
|
||||
void AudioProcessorEditor::initialise()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -85,6 +85,28 @@ public:
|
|||
*/
|
||||
virtual int getControlParameterIndex (Component&);
|
||||
|
||||
/** Override this method to indicate if your editor supports the presence or
|
||||
absence of a host-provided MIDI controller.
|
||||
|
||||
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later)
|
||||
support this functionality, and even then the host may choose to ignore this
|
||||
information.
|
||||
|
||||
The default behaviour is to report support for both cases.
|
||||
*/
|
||||
virtual bool supportsHostMIDIControllerPresence (bool hostMIDIControllerIsAvailable);
|
||||
|
||||
/** Called to indicate if a host is providing a MIDI controller when the host
|
||||
reconfigures its layout.
|
||||
|
||||
Use this as an opportunity to hide or display your own onscreen keyboard or
|
||||
other input component.
|
||||
|
||||
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later)
|
||||
support this functionality.
|
||||
*/
|
||||
virtual void hostMIDIControllerIsAvailable (bool controllerIsAvailable);
|
||||
|
||||
/** Can be called by a host to tell the editor that it should use a non-unity
|
||||
GUI scale.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue