Browse Source

Implement MIDI input for AU; cleanup

Signed-off-by: falkTX <falktx@falktx.com>
pull/452/head
falkTX 1 year ago
parent
commit
475c2125c9
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
6 changed files with 205 additions and 27 deletions
  1. +187
    -20
      distrho/src/DistrhoPluginAU.cpp
  2. +1
    -1
      distrho/src/DistrhoPluginVST2.cpp
  3. +1
    -1
      distrho/src/DistrhoPluginVST3.cpp
  4. +11
    -4
      distrho/src/DistrhoUIAU.mm
  5. +4
    -1
      examples/SendNote/DistrhoPluginInfo.h
  6. +1
    -0
      examples/SendNote/Makefile

+ 187
- 20
distrho/src/DistrhoPluginAU.cpp View File

@@ -21,6 +21,10 @@
#include "DistrhoPluginInternal.hpp" #include "DistrhoPluginInternal.hpp"
#include "../DistrhoPluginUtils.hpp" #include "../DistrhoPluginUtils.hpp"


#if DISTRHO_PLUGIN_HAS_UI
# include "../extra/RingBuffer.hpp"
#endif

#include <AudioUnit/AudioUnit.h> #include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioUnitUtilities.h> #include <AudioToolbox/AudioUnitUtilities.h>
#include <vector> #include <vector>
@@ -221,6 +225,9 @@ public:
fParameterCount(fPlugin.getParameterCount()), fParameterCount(fPlugin.getParameterCount()),
fLastParameterValues(nullptr), fLastParameterValues(nullptr),
fBypassParameterIndex(UINT32_MAX) fBypassParameterIndex(UINT32_MAX)
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
, fMidiEventCount(0)
#endif
{ {
if (fParameterCount != 0) if (fParameterCount != 0)
{ {
@@ -245,6 +252,9 @@ public:
OSStatus auInitialize() OSStatus auInitialize()
{ {
fPlugin.activate(); fPlugin.activate();
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
fMidiEventCount = 0;
#endif
return noErr; return noErr;
} }


@@ -306,17 +316,24 @@ public:


#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
case kAudioUnitProperty_StreamFormat: case kAudioUnitProperty_StreamFormat:
#if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
#elif DISTRHO_PLUGIN_NUM_INPUTS != 0
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope);
#else
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope);
#endif
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
outDataSize = sizeof(AudioStreamBasicDescription);
outWritable = true;
return noErr;
#if DISTRHO_PLUGIN_NUM_INPUTS != 0
if (inScope == kAudioUnitScope_Input)
{
outDataSize = sizeof(AudioStreamBasicDescription);
outWritable = true;
return noErr;
}
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
if (inScope == kAudioUnitScope_Output)
{
outDataSize = sizeof(AudioStreamBasicDescription);
outWritable = true;
return noErr;
}
#endif
return kAudioUnitErr_InvalidScope;
#endif #endif


case kAudioUnitProperty_ElementCount: case kAudioUnitProperty_ElementCount:
@@ -375,6 +392,13 @@ public:
outWritable = true; outWritable = true;
return noErr; return noErr;


case kAudioUnitProperty_InPlaceProcessing:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
outDataSize = sizeof(UInt32);
outWritable = false;
return noErr;

case kAudioUnitProperty_PresentPreset: case kAudioUnitProperty_PresentPreset:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
@@ -393,6 +417,17 @@ public:
return kAudioUnitErr_InvalidProperty; return kAudioUnitErr_InvalidProperty;
#endif #endif


case kAudioUnitProperty_AudioUnitMIDIProtocol:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
outDataSize = sizeof(SInt32);
outWritable = false;
return noErr;
#else
return kAudioUnitErr_InvalidProperty;
#endif

#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
case 'DPFa': case 'DPFa':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
@@ -416,6 +451,15 @@ public:
outWritable = true; outWritable = true;
return noErr; return noErr;


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI
case 'DPFn':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
outDataSize = sizeof(uint8_t) * 3;
outWritable = true;
return noErr;
#endif

#if DISTRHO_PLUGIN_WANT_STATE #if DISTRHO_PLUGIN_WANT_STATE
case 'DPFs': case 'DPFs':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
@@ -670,6 +714,10 @@ public:
// TODO // TODO
break; break;


case kAudioUnitProperty_InPlaceProcessing:
*static_cast<UInt32*>(outData) = 1;
return noErr;

case kAudioUnitProperty_PresentPreset: case kAudioUnitProperty_PresentPreset:
{ {
AUPreset* const preset = static_cast<AUPreset*>(outData); AUPreset* const preset = static_cast<AUPreset*>(outData);
@@ -709,6 +757,12 @@ public:
return noErr; return noErr;
#endif #endif


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
case kAudioUnitProperty_AudioUnitMIDIProtocol:
*static_cast<SInt32*>(outData) = kMIDIProtocol_1_0;
return noErr;
#endif

#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
case 'DPFa': case 'DPFa':
*static_cast<void**>(outData) = fPlugin.getInstancePointer(); *static_cast<void**>(outData) = fPlugin.getInstancePointer();
@@ -967,6 +1021,20 @@ public:
} }
return noErr; return noErr;


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI
case 'DPFn':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t) * 3, inDataSize, kAudioUnitErr_InvalidPropertyValue);
{
const uint8_t* const midiData = static_cast<const uint8_t*>(inData);

fNotesRingBuffer.writeCustomData(midiData, 3);
fNotesRingBuffer.commitWrite();
}
return noErr;
#endif

#if DISTRHO_PLUGIN_WANT_STATE #if DISTRHO_PLUGIN_WANT_STATE
case 'DPFs': case 'DPFs':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
@@ -1108,6 +1176,23 @@ public:
return noErr; return noErr;
} }


OSStatus auReset(const AudioUnitScope scope, const AudioUnitElement elem)
{
DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global || scope == kAudioUnitScope_Input || scope == kAudioUnitScope_Output, scope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);

if (fPlugin.isActive())
{
fPlugin.deactivate();
fPlugin.activate();
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
fMidiEventCount = 0;
#endif
return noErr;
}

OSStatus auRender(AudioUnitRenderActionFlags& ioActionFlags, OSStatus auRender(AudioUnitRenderActionFlags& ioActionFlags,
const AudioTimeStamp& inTimeStamp, const AudioTimeStamp& inTimeStamp,
const UInt32 inBusNumber, const UInt32 inBusNumber,
@@ -1156,12 +1241,35 @@ public:
constexpr float** outputs = nullptr; constexpr float** outputs = nullptr;
#endif #endif


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
fPlugin.run(inputs, outputs, inFramesToProcess, nullptr, 0);
#else
fPlugin.run(inputs, outputs, inFramesToProcess);
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_HAS_UI
if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading())
{
uint8_t midiData[3];
const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount-1].frame : 0;

while (fNotesRingBuffer.isDataAvailableForReading())
{
if (! fNotesRingBuffer.readCustomData(midiData, 3))
break;

MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
midiEvent.frame = frame;
midiEvent.size = 3;
std::memcpy(midiEvent.data, midiData, 3);

if (fMidiEventCount == kMaxMidiEvents)
break;
}
}
#endif #endif


fPlugin.run(inputs, outputs, inFramesToProcess, fMidiEvents, fMidiEventCount);
fMidiEventCount = 0;
#else
fPlugin.run(inputs, outputs, inFramesToProcess);
#endif

float value; float value;
AudioUnitEvent event; AudioUnitEvent event;
std::memset(&event, 0, sizeof(event)); std::memset(&event, 0, sizeof(event));
@@ -1190,20 +1298,49 @@ public:
return noErr; return noErr;
} }


OSStatus auReset(const AudioUnitScope scope, const AudioUnitElement elem)
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
OSStatus auMIDIEvent(const UInt32 inStatus,
const UInt32 inData1,
const UInt32 inData2,
const UInt32 inOffsetSampleFrame)
{ {
DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global || scope == kAudioUnitScope_Input || scope == kAudioUnitScope_Output, scope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);
if (fMidiEventCount >= kMaxMidiEvents)
return noErr;


if (fPlugin.isActive())
MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
midiEvent.frame = inOffsetSampleFrame;
midiEvent.data[0] = inStatus;
midiEvent.data[1] = inData1;
midiEvent.data[2] = inData2;

switch (inStatus)
{ {
fPlugin.deactivate();
fPlugin.activate();
case 0x80:
case 0x90:
case 0xA0:
case 0xB0:
case 0xD0:
case 0xE0:
midiEvent.size = 3;
break;
case 0xC0:
midiEvent.size = 2;
break;
default:
midiEvent.size = 1;
break;
} }


return noErr; return noErr;
} }


OSStatus auSysEx(const UInt8* const inData, const UInt32 inLength)
{
// TODO
return kAudioUnitErr_PropertyNotInUse;
}
#endif

// ---------------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------------


private: private:
@@ -1227,6 +1364,14 @@ private:
float* fLastParameterValues; float* fLastParameterValues;
uint32_t fBypassParameterIndex; uint32_t fBypassParameterIndex;


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
uint32_t fMidiEventCount;
MidiEvent fMidiEvents[kMaxMidiEvents];
#if DISTRHO_PLUGIN_HAS_UI
SmallStackRingBuffer fNotesRingBuffer;
#endif
#endif

// ---------------------------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------------------------


void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem)
@@ -1371,6 +1516,12 @@ struct AudioComponentPlugInInstance {
case kAudioUnitProcessMultipleSelect: case kAudioUnitProcessMultipleSelect:
return reinterpret_cast<AudioComponentMethod>(ProcessMultiple); return reinterpret_cast<AudioComponentMethod>(ProcessMultiple);
*/ */
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
case kMusicDeviceMIDIEventSelect:
return reinterpret_cast<AudioComponentMethod>(MIDIEvent);
case kMusicDeviceSysExSelect:
return reinterpret_cast<AudioComponentMethod>(SysEx);
#endif
} }


d_stdout("TODO Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector)); d_stdout("TODO Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector));
@@ -1594,6 +1745,22 @@ struct AudioComponentPlugInInstance {
return self->plugin->auRender(*ioActionFlags, *inTimeStamp, inOutputBusNumber, inNumberFrames, *ioData); return self->plugin->auRender(*ioActionFlags, *inTimeStamp, inOutputBusNumber, inNumberFrames, *ioData);
} }


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
static OSStatus MIDIEvent(AudioComponentPlugInInstance* const self,
const UInt32 inStatus,
const UInt32 inData1,
const UInt32 inData2,
const UInt32 inOffsetSampleFrame)
{
return self->plugin->auMIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame);
}

static OSStatus SysEx(AudioComponentPlugInInstance* const self, const UInt8* const inData, const UInt32 inLength)
{
return self->plugin->auSysEx(inData, inLength);
}
#endif

DISTRHO_DECLARE_NON_COPYABLE(AudioComponentPlugInInstance) DISTRHO_DECLARE_NON_COPYABLE(AudioComponentPlugInInstance)
}; };




+ 1
- 1
distrho/src/DistrhoPluginVST2.cpp View File

@@ -1084,7 +1084,7 @@ public:
if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading()) if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading())
{ {
uint8_t midiData[3]; uint8_t midiData[3];
uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount-1].frame : 0;
const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount-1].frame : 0;


while (fNotesRingBuffer.isDataAvailableForReading()) while (fNotesRingBuffer.isDataAvailableForReading())
{ {


+ 1
- 1
distrho/src/DistrhoPluginVST3.cpp View File

@@ -3103,7 +3103,7 @@ private:
event.midi_cc_out.cc_number = data[1]; event.midi_cc_out.cc_number = data[1];
event.midi_cc_out.value = data[2]; event.midi_cc_out.value = data[2];
if (midiEvent.size == 4) if (midiEvent.size == 4)
event.midi_cc_out.value2 = midiEvent.size == 4;
event.midi_cc_out.value2 = data[3];
break; break;
/* TODO how do we deal with program changes?? /* TODO how do we deal with program changes??
case 0xC0: case 0xC0:


+ 11
- 4
distrho/src/DistrhoUIAU.mm View File

@@ -133,6 +133,8 @@ private:
fUI.parameterChanged(elem, value); fUI.parameterChanged(elem, value);
} }
break; break;
case 'DPFS':
break;
} }
} }


@@ -180,14 +182,16 @@ private:
const size_t len_key = std::strlen(key); const size_t len_key = std::strlen(key);
const size_t len_value = std::strlen(value); const size_t len_value = std::strlen(value);
const size_t len_combined = len_key + len_value + 2; const size_t len_combined = len_key + len_value + 2;
char* const data = new char[len_combined];
char* const data = static_cast<char*>(std::malloc(len_combined));
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr,);

std::memcpy(data, key, len_key); std::memcpy(data, key, len_key);
std::memcpy(data + len_key + 1, value, len_value); std::memcpy(data + len_key + 1, value, len_value);
data[len_key] = data[len_key + len_value + 1] = '\0'; data[len_key] = data[len_key + len_value + 1] = '\0';


AudioUnitSetProperty(fComponent, 'DPFs', kAudioUnitScope_Global, len_combined, data, len_combined); AudioUnitSetProperty(fComponent, 'DPFs', kAudioUnitScope_Global, len_combined, data, len_combined);


delete[] data;
std::free(data);
} }


static void setStateCallback(void* const ptr, const char* const key, const char* const value) static void setStateCallback(void* const ptr, const char* const key, const char* const value)
@@ -197,8 +201,10 @@ private:
#endif #endif


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void sendNote(uint8_t, uint8_t, uint8_t)
void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
{ {
const uint8_t data[3] = { static_cast<uint8_t>((velocity != 0 ? 0x90 : 0x80) | channel), note, velocity };
AudioUnitSetProperty(fComponent, 'DPFn', kAudioUnitScope_Global, 0, data, sizeof(data));
} }


static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity)
@@ -226,7 +232,8 @@ END_NAMESPACE_DISTRHO
#define MACRO_NAME2(a, b, c, d, e, f) a ## b ## c ## d ## e ## f #define MACRO_NAME2(a, b, c, d, e, f) a ## b ## c ## d ## e ## f
#define MACRO_NAME(a, b, c, d, e, f) MACRO_NAME2(a, b, c, d, e, f) #define MACRO_NAME(a, b, c, d, e, f) MACRO_NAME2(a, b, c, d, e, f)


#define COCOA_VIEW_CLASS_NAME MACRO_NAME(CocoaAUView_, DISTRHO_PLUGIN_AU_TYPE, _, DISTRHO_PLUGIN_AU_SUBTYPE, _, DISTRHO_PLUGIN_AU_MANUFACTURER)
#define COCOA_VIEW_CLASS_NAME \
MACRO_NAME(CocoaAUView_, DISTRHO_PLUGIN_AU_TYPE, _, DISTRHO_PLUGIN_AU_SUBTYPE, _, DISTRHO_PLUGIN_AU_MANUFACTURER)


@interface COCOA_VIEW_CLASS_NAME : NSObject<AUCocoaUIBase> @interface COCOA_VIEW_CLASS_NAME : NSObject<AUCocoaUIBase>
{ {


+ 4
- 1
examples/SendNote/DistrhoPluginInfo.h View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* *
* Permission to use, copy, modify, and/or distribute this software for any purpose with * Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this * or without fee is hereby granted, provided that the above copyright notice and this
@@ -22,6 +22,9 @@
#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/SendNote" #define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/SendNote"
#define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.examples.send-note" #define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.examples.send-note"


#define DISTRHO_PLUGIN_AU_SUBTYPE note
#define DISTRHO_PLUGIN_AU_MANUFACTURER Dstr

#define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_HAS_UI 1
#define DISTRHO_PLUGIN_HAS_EMBED_UI 1 #define DISTRHO_PLUGIN_HAS_EMBED_UI 1
#define DISTRHO_PLUGIN_IS_RT_SAFE 1 #define DISTRHO_PLUGIN_IS_RT_SAFE 1


+ 1
- 0
examples/SendNote/Makefile View File

@@ -35,6 +35,7 @@ TARGETS += lv2_sep
TARGETS += vst2 TARGETS += vst2
TARGETS += vst3 TARGETS += vst3
TARGETS += clap TARGETS += clap
TARGETS += au


all: $(TARGETS) all: $(TARGETS)




Loading…
Cancel
Save