diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index 8a63f65f..9430ce9a 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -198,6 +198,13 @@ typedef std::vector PropertyListeners; // -------------------------------------------------------------------------------------------------------------------- +typedef struct { + UInt32 numPackets; + MIDIPacket packets[kMaxMidiEvents]; +} MIDIPacketList; + +// -------------------------------------------------------------------------------------------------------------------- + #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT static constexpr const writeMidiFunc writeMidiCallback = nullptr; #endif @@ -242,6 +249,15 @@ public: fBypassParameterIndex = i; } } + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + std::memset(&fMidiEvents, 0, sizeof(fMidiEvents)); + #endif + + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + std::memset(&fMidiOutput, 0, sizeof(fMidiOutput)); + std::memset(&fMidiOutputPackets, 0, sizeof(fMidiOutputPackets)); + #endif } ~PluginAU() @@ -254,6 +270,9 @@ public: fPlugin.activate(); #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fMidiEventCount = 0; + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fMidiOutputPackets.numPackets = 0; #endif return noErr; } @@ -417,10 +436,33 @@ public: return kAudioUnitErr_InvalidProperty; #endif + case kAudioUnitProperty_MIDIOutputCallbackInfo: + 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_OUTPUT + outDataSize = sizeof(CFArrayRef); + outWritable = false; + return noErr; + #else + return kAudioUnitErr_InvalidProperty; + #endif + + case kAudioUnitProperty_MIDIOutputCallback: + 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_OUTPUT + outDataSize = sizeof(AUMIDIOutputCallbackStruct); + outWritable = true; + return noErr; + #else + 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 + // FIXME implement the event list stuff + #if 0 && (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) outDataSize = sizeof(SInt32); outWritable = false; return noErr; @@ -757,10 +799,26 @@ public: return noErr; #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + case kAudioUnitProperty_MIDIOutputCallbackInfo: + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + { + CFStringRef refs[1] = { CFSTR("MIDI Callback") }; + *static_cast(outData) = CFArrayCreate(nullptr, + reinterpret_cast(refs), + 1, + &kCFTypeArrayCallBacks); + } + return noErr; + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + /* FIXME implement the event list stuff case kAudioUnitProperty_AudioUnitMIDIProtocol: *static_cast(outData) = kMIDIProtocol_1_0; return noErr; + */ #endif #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS @@ -980,6 +1038,15 @@ public: // TODO return noErr; + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + case kAudioUnitProperty_MIDIOutputCallback: + 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(AUMIDIOutputCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue); + std::memcpy(&fMidiOutput, inData, sizeof(AUMIDIOutputCallbackStruct)); + return noErr; + #endif + case 'DPFe': DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); @@ -1189,6 +1256,9 @@ public: #if DISTRHO_PLUGIN_WANT_MIDI_INPUT fMidiEventCount = 0; + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fMidiOutputPackets.numPackets = 0; #endif return noErr; } @@ -1241,27 +1311,13 @@ public: constexpr float** outputs = nullptr; #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fMidiOutputPackets.numPackets = 0; + #endif + #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; - } - } + importRingBufferNotes(); #endif fPlugin.run(inputs, outputs, inFramesToProcess, fMidiEvents, fMidiEventCount); @@ -1270,6 +1326,16 @@ public: fPlugin.run(inputs, outputs, inFramesToProcess); #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (fMidiOutputPackets.numPackets != 0 && fMidiOutput.midiOutputCallback != nullptr) + { + fMidiOutput.midiOutputCallback(fMidiOutput.userData, + &inTimeStamp, + 0, + reinterpret_cast(&fMidiOutputPackets)); + } + #endif + float value; AudioUnitEvent event; std::memset(&event, 0, sizeof(event)); @@ -1296,6 +1362,11 @@ public: } return noErr; + + #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + // unused + (void)inTimeStamp; + #endif } #if DISTRHO_PLUGIN_WANT_MIDI_INPUT @@ -1331,6 +1402,28 @@ public: break; } + // if plugin has no audio, assume render function is not going to be called + #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 + #if DISTRHO_PLUGIN_HAS_UI + importRingBufferNotes(); + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fMidiOutputPackets.numPackets = 0; + #endif + + fPlugin.run(nullptr, nullptr, std::max(1u, inOffsetSampleFrame), fMidiEvents, fMidiEventCount); + fMidiEventCount = 0; + + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + if (fMidiOutputPackets.numPackets != 0 && fMidiOutput.midiOutputCallback != nullptr) + { + fMidiOutput.midiOutputCallback(fMidiOutput.userData, + nullptr, 0, // FIXME do we need a valid timestamp? + reinterpret_cast(&fMidiOutputPackets)); + } + #endif + #endif + return noErr; } @@ -1372,8 +1465,38 @@ private: #endif #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + AUMIDIOutputCallbackStruct fMidiOutput; + MIDIPacketList fMidiOutputPackets; + #endif + // ---------------------------------------------------------------------------------------------------------------- + #if DISTRHO_PLUGIN_HAS_UI + void importRingBufferNotes() + { + 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 + void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) { for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) @@ -1398,8 +1521,20 @@ private: // DPF callbacks #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - bool writeMidi(const MidiEvent&) + bool writeMidi(const MidiEvent& midiEvent) { + DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fMidiOutput.midiOutputCallback != nullptr, false); + + if (midiEvent.size > sizeof(MIDIPacket::data)) + return true; + if (fMidiOutputPackets.numPackets == kMaxMidiEvents) + return false; + + const uint8_t* const midiData = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data; + MIDIPacket& packet(fMidiOutputPackets.packets[fMidiOutputPackets.numPackets++]); + packet.timeStamp = midiEvent.frame; + packet.length = midiEvent.size; + std::memcpy(packet.data, midiData, midiEvent.size); return true; } @@ -1410,8 +1545,18 @@ private: #endif #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST - bool requestParameterValueChange(uint32_t, float) + bool requestParameterValueChange(const uint32_t index, const float value) { + AudioUnitEvent event; + std::memset(&event, 0, sizeof(event)); + event.mEventType = kAudioUnitEvent_ParameterValueChange; + event.mArgument.mParameter.mAudioUnit = fComponent; + event.mArgument.mParameter.mParameterID = index; + event.mArgument.mParameter.mScope = kAudioUnitScope_Global; + + fLastParameterValues[index] = value; + AUEventListenerNotify(NULL, NULL, &event); + notifyListeners('DPFP', kAudioUnitScope_Global, index); return true; } diff --git a/distrho/src/DistrhoPluginChecks.h b/distrho/src/DistrhoPluginChecks.h index a7dc4eef..69c88ecd 100644 --- a/distrho/src/DistrhoPluginChecks.h +++ b/distrho/src/DistrhoPluginChecks.h @@ -120,7 +120,7 @@ # define DISTRHO_PLUGIN_AU_TYPE aumf /* kAudioUnitType_MusicEffect */ # elif (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) && DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 # define DISTRHO_PLUGIN_AU_TYPE aumu /* kAudioUnitType_MusicDevice */ -# elif (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) && DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 +# elif DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT # define DISTRHO_PLUGIN_AU_TYPE aumi /* kAudioUnitType_MIDIProcessor */ # elif DISTRHO_PLUGIN_NUM_INPUTS == 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 # define DISTRHO_PLUGIN_AU_TYPE augn /* kAudioUnitType_Generator */ diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp index 33ad7093..66689bed 100644 --- a/distrho/src/DistrhoPluginVST2.cpp +++ b/distrho/src/DistrhoPluginVST2.cpp @@ -1084,7 +1084,7 @@ public: if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading()) { uint8_t midiData[3]; - const 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/DistrhoUIAU.mm b/distrho/src/DistrhoUIAU.mm index fe7c9cc5..49d7490b 100644 --- a/distrho/src/DistrhoUIAU.mm +++ b/distrho/src/DistrhoUIAU.mm @@ -67,10 +67,11 @@ public: setStateCallback, sendNoteCallback, setSizeCallback, - nullptr, // TODO file request + nullptr, d_nextBundlePath, instancePointer) { + d_stdout("UI created"); constexpr const CFTimeInterval interval = 60 * 0.0001; CFRunLoopTimerContext context = {}; @@ -86,6 +87,7 @@ public: ~DPF_UI_AU() { + d_stdout("UI destroyed"); AudioUnitRemovePropertyListenerWithUserData(fComponent, 'DPFP', auPropertyChangedCallback, this); if (fTimerRef != nullptr) diff --git a/examples/MidiThrough/DistrhoPluginInfo.h b/examples/MidiThrough/DistrhoPluginInfo.h index 67c433c2..4ed766bd 100644 --- a/examples/MidiThrough/DistrhoPluginInfo.h +++ b/examples/MidiThrough/DistrhoPluginInfo.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * 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/MidiThrough" #define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.examples.midi-through" +#define DISTRHO_PLUGIN_AU_SUBTYPE midt +#define DISTRHO_PLUGIN_AU_MANUFACTURER Dstr + #define DISTRHO_PLUGIN_HAS_UI 0 #define DISTRHO_PLUGIN_IS_RT_SAFE 1 #define DISTRHO_PLUGIN_NUM_INPUTS 0 diff --git a/examples/MidiThrough/Makefile b/examples/MidiThrough/Makefile index 3ba2e3ec..31525acd 100644 --- a/examples/MidiThrough/Makefile +++ b/examples/MidiThrough/Makefile @@ -28,6 +28,7 @@ TARGETS += lv2_dsp TARGETS += vst2 TARGETS += vst3 TARGETS += clap +TARGETS += au all: $(TARGETS)