commit 475c2125c974f85ab41e4b12acf834694de6837b
parent c864255471d1ac85163596c02e6bf0b78c665d5e
Author: falkTX <falktx@falktx.com>
Date: Fri, 23 Feb 2024 20:16:04 +0100
Implement MIDI input for AU; cleanup
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
6 files changed, 205 insertions(+), 27 deletions(-)
diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp
@@ -21,6 +21,10 @@
#include "DistrhoPluginInternal.hpp"
#include "../DistrhoPluginUtils.hpp"
+#if DISTRHO_PLUGIN_HAS_UI
+# include "../extra/RingBuffer.hpp"
+#endif
+
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioUnitUtilities.h>
#include <vector>
@@ -221,6 +225,9 @@ public:
fParameterCount(fPlugin.getParameterCount()),
fLastParameterValues(nullptr),
fBypassParameterIndex(UINT32_MAX)
+ #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
+ , fMidiEventCount(0)
+ #endif
{
if (fParameterCount != 0)
{
@@ -245,6 +252,9 @@ public:
OSStatus auInitialize()
{
fPlugin.activate();
+ #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
+ fMidiEventCount = 0;
+ #endif
return noErr;
}
@@ -306,17 +316,24 @@ public:
#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
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);
- 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
case kAudioUnitProperty_ElementCount:
@@ -375,6 +392,13 @@ public:
outWritable = true;
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:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
@@ -393,6 +417,17 @@ public:
return kAudioUnitErr_InvalidProperty;
#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
case 'DPFa':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
@@ -416,6 +451,15 @@ public:
outWritable = true;
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
case 'DPFs':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
@@ -670,6 +714,10 @@ public:
// TODO
break;
+ case kAudioUnitProperty_InPlaceProcessing:
+ *static_cast<UInt32*>(outData) = 1;
+ return noErr;
+
case kAudioUnitProperty_PresentPreset:
{
AUPreset* const preset = static_cast<AUPreset*>(outData);
@@ -709,6 +757,12 @@ public:
return noErr;
#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
case 'DPFa':
*static_cast<void**>(outData) = fPlugin.getInstancePointer();
@@ -967,6 +1021,20 @@ public:
}
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
case 'DPFs':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
@@ -1108,6 +1176,23 @@ public:
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,
const AudioTimeStamp& inTimeStamp,
const UInt32 inBusNumber,
@@ -1156,12 +1241,35 @@ public:
constexpr float** outputs = nullptr;
#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
+ fPlugin.run(inputs, outputs, inFramesToProcess, fMidiEvents, fMidiEventCount);
+ fMidiEventCount = 0;
+ #else
+ fPlugin.run(inputs, outputs, inFramesToProcess);
+ #endif
+
float value;
AudioUnitEvent event;
std::memset(&event, 0, sizeof(event));
@@ -1190,20 +1298,49 @@ public:
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;
}
+ OSStatus auSysEx(const UInt8* const inData, const UInt32 inLength)
+ {
+ // TODO
+ return kAudioUnitErr_PropertyNotInUse;
+ }
+ #endif
+
// ----------------------------------------------------------------------------------------------------------------
private:
@@ -1227,6 +1364,14 @@ private:
float* fLastParameterValues;
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)
@@ -1371,6 +1516,12 @@ struct AudioComponentPlugInInstance {
case kAudioUnitProcessMultipleSelect:
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));
@@ -1594,6 +1745,22 @@ struct AudioComponentPlugInInstance {
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)
};
diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp
@@ -1084,7 +1084,7 @@ public:
if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading())
{
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())
{
diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp
@@ -3103,7 +3103,7 @@ private:
event.midi_cc_out.cc_number = data[1];
event.midi_cc_out.value = data[2];
if (midiEvent.size == 4)
- event.midi_cc_out.value2 = midiEvent.size == 4;
+ event.midi_cc_out.value2 = data[3];
break;
/* TODO how do we deal with program changes??
case 0xC0:
diff --git a/distrho/src/DistrhoUIAU.mm b/distrho/src/DistrhoUIAU.mm
@@ -133,6 +133,8 @@ private:
fUI.parameterChanged(elem, value);
}
break;
+ case 'DPFS':
+ break;
}
}
@@ -180,14 +182,16 @@ private:
const size_t len_key = std::strlen(key);
const size_t len_value = std::strlen(value);
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 + len_key + 1, value, len_value);
data[len_key] = data[len_key + len_value + 1] = '\0';
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)
@@ -197,8 +201,10 @@ private:
#endif
#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)
@@ -226,7 +232,8 @@ END_NAMESPACE_DISTRHO
#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 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>
{
diff --git a/examples/SendNote/DistrhoPluginInfo.h b/examples/SendNote/DistrhoPluginInfo.h
@@ -1,6 +1,6 @@
/*
* 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
* 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_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_EMBED_UI 1
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
diff --git a/examples/SendNote/Makefile b/examples/SendNote/Makefile
@@ -35,6 +35,7 @@ TARGETS += lv2_sep
TARGETS += vst2
TARGETS += vst3
TARGETS += clap
+TARGETS += au
all: $(TARGETS)