Skip to content

Commit

Permalink
Implement MIDI input for AU; cleanup
Browse files Browse the repository at this point in the history
Signed-off-by: falkTX <falktx@falktx.com>
  • Loading branch information
falkTX committed Feb 23, 2024
1 parent d588b42 commit 1dd805b
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 27 deletions.
207 changes: 187 additions & 20 deletions distrho/src/DistrhoPluginAU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -221,6 +225,9 @@ class PluginAU
fParameterCount(fPlugin.getParameterCount()),
fLastParameterValues(nullptr),
fBypassParameterIndex(UINT32_MAX)
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
, fMidiEventCount(0)
#endif
{
if (fParameterCount != 0)
{
Expand All @@ -245,6 +252,9 @@ class PluginAU
OSStatus auInitialize()
{
fPlugin.activate();
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
fMidiEventCount = 0;
#endif
return noErr;
}

Expand Down Expand Up @@ -306,17 +316,24 @@ class PluginAU

#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:
Expand Down Expand Up @@ -375,6 +392,13 @@ class PluginAU
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);
Expand All @@ -393,6 +417,17 @@ class PluginAU
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);
Expand All @@ -416,6 +451,15 @@ class PluginAU
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);
Expand Down Expand Up @@ -670,6 +714,10 @@ class PluginAU
// TODO
break;

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

case kAudioUnitProperty_PresentPreset:
{
AUPreset* const preset = static_cast<AUPreset*>(outData);
Expand Down Expand Up @@ -709,6 +757,12 @@ class PluginAU
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();
Expand Down Expand Up @@ -967,6 +1021,20 @@ class PluginAU
}
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);
Expand Down Expand Up @@ -1108,6 +1176,23 @@ class PluginAU
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,
Expand Down Expand Up @@ -1156,12 +1241,35 @@ class PluginAU
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));
Expand Down Expand Up @@ -1190,20 +1298,49 @@ class PluginAU
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:
Expand All @@ -1227,6 +1364,14 @@ class PluginAU
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)
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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)
};

Expand Down
2 changes: 1 addition & 1 deletion distrho/src/DistrhoPluginVST2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,7 @@ class PluginVst : public ParameterAndNotesHelper
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())
{
Expand Down
2 changes: 1 addition & 1 deletion distrho/src/DistrhoPluginVST3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3103,7 +3103,7 @@ class PluginVst3
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:
Expand Down
15 changes: 11 additions & 4 deletions distrho/src/DistrhoUIAU.mm
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ void auPropertyChanged(const AudioUnitPropertyID prop, const AudioUnitElement el
fUI.parameterChanged(elem, value);
}
break;
case 'DPFS':
break;
}
}

Expand Down Expand Up @@ -180,14 +182,16 @@ void setState(const char* const key, const char* const value)
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)
Expand All @@ -197,8 +201,10 @@ static void setStateCallback(void* const ptr, const char* const key, const char*
#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)
Expand Down Expand Up @@ -226,7 +232,8 @@ static void setSizeCallback(void* const ptr, const uint width, const uint height
#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>
{
Expand Down
Loading

0 comments on commit 1dd805b

Please sign in to comment.