From 145ccf1304e6de6a53120199bbcf3af346e49c7e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 24 Feb 2024 19:49:20 +0100 Subject: [PATCH] Implement time position for AU, cleanup Signed-off-by: falkTX --- distrho/src/DistrhoPluginAU.cpp | 288 +++++++++++++++++++++--------- distrho/src/DistrhoPluginLV2.cpp | 2 +- distrho/src/DistrhoPluginVST3.cpp | 2 +- distrho/src/DistrhoUIAU.mm | 5 +- distrho/src/DistrhoUIDSSI.cpp | 12 +- 5 files changed, 216 insertions(+), 93 deletions(-) diff --git a/distrho/src/DistrhoPluginAU.cpp b/distrho/src/DistrhoPluginAU.cpp index 758e09b1..c7c55c2a 100644 --- a/distrho/src/DistrhoPluginAU.cpp +++ b/distrho/src/DistrhoPluginAU.cpp @@ -282,6 +282,13 @@ public: fStateMap[dkey] = fPlugin.getStateDefaultValue(i); } #endif + + #if DISTRHO_PLUGIN_WANT_TIMEPOS + std::memset(&fHostCallbackInfo, 0, sizeof(fHostCallbackInfo)); + + // ticksPerBeat is not possible with AU + fTimePosition.bbt.ticksPerBeat = 1920.0; + #endif } ~PluginAU() @@ -298,6 +305,9 @@ public: #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT fMidiOutputPackets.numPackets = 0; + #endif + #if DISTRHO_PLUGIN_WANT_TIMEPOS + fTimePosition.clear(); #endif return noErr; } @@ -417,7 +427,7 @@ public: if (inScope == kAudioUnitScope_Global && fBypassParameterIndex != UINT32_MAX) { outDataSize = sizeof(UInt32); - outWritable = false; + outWritable = true; return noErr; } return kAudioUnitErr_InvalidProperty; @@ -436,6 +446,17 @@ public: outWritable = true; return noErr; + case kAudioUnitProperty_HostCallbacks: + 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_TIMEPOS + outDataSize = sizeof(HostCallbackInfo); + outWritable = false; + return noErr; + #else + return kAudioUnitErr_InvalidProperty; + #endif + case kAudioUnitProperty_InPlaceProcessing: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); @@ -758,6 +779,12 @@ public: // TODO break; + #if DISTRHO_PLUGIN_WANT_TIMEPOS + case kAudioUnitProperty_HostCallbacks: + std::memcpy(outData, &fHostCallbackInfo, sizeof(HostCallbackInfo)); + return noErr; + #endif + case kAudioUnitProperty_InPlaceProcessing: *static_cast(outData) = 1; return noErr; @@ -1011,6 +1038,7 @@ public: } notifyListeners(inProp, inScope, inElement); + notifyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); } return noErr; } @@ -1031,6 +1059,7 @@ public: } notifyListeners(inProp, inScope, inElement); + notifyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); } return noErr; } @@ -1075,6 +1104,25 @@ public: // TODO return noErr; + #if DISTRHO_PLUGIN_WANT_TIMEPOS + case kAudioUnitProperty_HostCallbacks: + DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); + DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); + { + const UInt32 usableDataSize = std::min(inDataSize, static_cast(sizeof(HostCallbackInfo))); + const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0; + + std::memcpy(&fHostCallbackInfo, inData, usableDataSize); + + if (sizeof(HostCallbackInfo) > usableDataSize) + std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize); + + if (changed) + notifyListeners(inProp, inScope, inElement); + } + return noErr; + #endif + case kAudioUnitProperty_PresentPreset: DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); @@ -1162,6 +1210,9 @@ public: event.mArgument.mParameter.mParameterID = inElement; event.mArgument.mParameter.mScope = kAudioUnitScope_Global; AUEventListenerNotify(NULL, NULL, &event); + + if (fBypassParameterIndex == inElement) + notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); } return noErr; @@ -1309,10 +1360,12 @@ public: { fLastParameterValues[param] = value; fPlugin.setParameterValue(param, value); - #if DISTRHO_PLUGIN_HAS_UI + // TODO flag param only, notify listeners later on bg thread (sem_post etc) notifyListeners('DPFp', kAudioUnitScope_Global, param); - #endif + + if (fBypassParameterIndex == elem) + notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); } return noErr; @@ -1357,6 +1410,9 @@ public: #endif #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT fMidiOutputPackets.numPackets = 0; + #endif + #if DISTRHO_PLUGIN_WANT_TIMEPOS + fTimePosition.clear(); #endif return noErr; } @@ -1372,6 +1428,7 @@ public: if (inFramesToProcess > fPlugin.getBufferSize()) { +d_stdout("auRender inFramesToProcess invalid"); setLastRenderError(kAudioUnitErr_TooManyFramesToProcess); return kAudioUnitErr_TooManyFramesToProcess; } @@ -1382,7 +1439,10 @@ public: // TODO there must be something more to this... if (buffer.mData == nullptr) + { +d_stdout("auRender null buffer"); return noErr; + } DISTRHO_SAFE_ASSERT_UINT_RETURN(buffer.mDataByteSize == inFramesToProcess * sizeof(float), buffer.mDataByteSize, kAudio_ParamError); } @@ -1409,62 +1469,8 @@ 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 - importRingBufferNotes(); - #endif - - fPlugin.run(inputs, outputs, inFramesToProcess, fMidiEvents, fMidiEventCount); - fMidiEventCount = 0; - #else - 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)); - event.mEventType = kAudioUnitEvent_ParameterValueChange; - event.mArgument.mParameter.mAudioUnit = fComponent; - event.mArgument.mParameter.mScope = kAudioUnitScope_Global; - - for (uint32_t i=0; i(&fMidiOutputPackets)); - } - #endif - #endif - return noErr; } @@ -1579,11 +1568,27 @@ private: StringMap fStateMap; #endif + #if DISTRHO_PLUGIN_WANT_TIMEPOS + HostCallbackInfo fHostCallbackInfo; + TimePosition fTimePosition; + #endif + // ---------------------------------------------------------------------------------------------------------------- - #if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI - void importRingBufferNotes() + void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) + { + for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) + { + const PropertyListener& pl(*it); + + if (pl.prop == prop) + pl.proc(pl.userData, fComponent, prop, scope, elem); + } + } + + void run(const float** inputs, float** outputs, const uint32_t frames) { + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT && DISTRHO_PLUGIN_HAS_UI if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading()) { uint8_t midiData[3]; @@ -1603,17 +1608,122 @@ private: break; } } - } - #endif + #endif - void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) - { - for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + fMidiOutputPackets.numPackets = 0; + #endif + + #if DISTRHO_PLUGIN_WANT_TIMEPOS + if (fHostCallbackInfo.beatAndTempoProc != nullptr || + fHostCallbackInfo.musicalTimeLocationProc != nullptr || + fHostCallbackInfo.transportStateProc != nullptr) { - const PropertyListener& pl(*it); + // reused values so we can check null and return value together + Boolean b1 = false; + Float32 f1 = 4.f; // initial value for beats per bar + Float64 g1 = 0.0; + Float64 g2 = 0.0; + UInt32 u1 = 0.0; + + if (fHostCallbackInfo.musicalTimeLocationProc != nullptr + && fHostCallbackInfo.musicalTimeLocationProc(fHostCallbackInfo.hostUserData, + nullptr, &f1, &u1, nullptr) == noErr) + { + fTimePosition.bbt.beatsPerBar = f1; + fTimePosition.bbt.beatType = u1; + } + else + { + fTimePosition.bbt.beatsPerBar = 4.f; + fTimePosition.bbt.beatType = 4.f; + } - if (pl.prop == prop) - pl.proc(pl.userData, fComponent, prop, scope, elem); + if (fHostCallbackInfo.beatAndTempoProc != nullptr + && fHostCallbackInfo.beatAndTempoProc(fHostCallbackInfo.hostUserData, &g1, &g2) == noErr) + { + const double beat = static_cast(g1); + + fTimePosition.bbt.valid = true; + fTimePosition.bbt.bar = static_cast(beat / f1) + 1; + fTimePosition.bbt.beat = static_cast(std::fmod(beat, f1)) + 1; + fTimePosition.bbt.tick = std::fmod(g1, 1.0) * 1920.0; + fTimePosition.bbt.beatsPerMinute = g2; + } + else + { + fTimePosition.bbt.valid = false; + fTimePosition.bbt.bar = 1; + fTimePosition.bbt.beat = 1; + fTimePosition.bbt.tick = 0.0; + fTimePosition.bbt.beatsPerMinute = 120.0; + } + + if (fHostCallbackInfo.transportStateProc != nullptr + && fHostCallbackInfo.transportStateProc(fHostCallbackInfo.hostUserData, + &b1, nullptr, &g1, nullptr, nullptr, nullptr) == noErr) + { + fTimePosition.playing = b1; + fTimePosition.frame = static_cast(g1); + } + else + { + fTimePosition.playing = false; + fTimePosition.frame = 0; + } + + fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat * + fTimePosition.bbt.beatsPerBar * + (fTimePosition.bbt.bar - 1); + + fPlugin.setTimePosition(fTimePosition); + } + else + { + d_stdout("no time"); + } + #endif + + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + fPlugin.run(inputs, outputs, frames, fMidiEvents, fMidiEventCount); + fMidiEventCount = 0; + #else + fPlugin.run(inputs, outputs, frames); + #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)); + event.mEventType = kAudioUnitEvent_ParameterValueChange; + event.mArgument.mParameter.mAudioUnit = fComponent; + event.mArgument.mParameter.mScope = kAudioUnitScope_Global; + + for (uint32_t i=0; i= 0.0f) { - const double rest = std::fmod(fLastPositionData.barBeat, 1.0f); + const double rest = std::fmod(fLastPositionData.barBeat, 1.0); fTimePosition.bbt.beat = std::round(fLastPositionData.barBeat - rest + 1.0); fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; } diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 3c417c2a..fb12c186 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -1419,7 +1419,7 @@ public: fTimePosition.bbt.valid = true; fTimePosition.bbt.bar = static_cast(ppqPos) / ppqPerBar + 1; - fTimePosition.bbt.beat = d_roundToIntPositive(barBeats - rest) + 1; + fTimePosition.bbt.beat = static_cast(barBeats - rest) + 1; fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; fTimePosition.bbt.beatsPerBar = ctx->time_sig_numerator; fTimePosition.bbt.beatType = ctx->time_sig_denom; diff --git a/distrho/src/DistrhoUIAU.mm b/distrho/src/DistrhoUIAU.mm index aeb76bba..7f18b877 100644 --- a/distrho/src/DistrhoUIAU.mm +++ b/distrho/src/DistrhoUIAU.mm @@ -45,6 +45,9 @@ static constexpr const setStateFunc setStateCallback = nullptr; static constexpr const sendNoteFunc sendNoteCallback = nullptr; #endif +// unsupported in AU +static constexpr const fileRequestFunc fileRequestCallback = nullptr; + // -------------------------------------------------------------------------------------------------------------------- // Static data, see DistrhoPlugin.cpp @@ -71,7 +74,7 @@ public: setStateCallback, sendNoteCallback, setSizeCallback, - nullptr, + fileRequestCallback, d_nextBundlePath, instancePointer) { diff --git a/distrho/src/DistrhoUIDSSI.cpp b/distrho/src/DistrhoUIDSSI.cpp index 6b96a8e9..1d1fb679 100644 --- a/distrho/src/DistrhoUIDSSI.cpp +++ b/distrho/src/DistrhoUIDSSI.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 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 @@ -26,11 +26,17 @@ START_NAMESPACE_DISTRHO +// -------------------------------------------------------------------------------------------------------------------- + #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT static constexpr const sendNoteFunc sendNoteCallback = nullptr; #endif -// ----------------------------------------------------------------------- +// unsupported in DSSI +static constexpr const fileRequestFunc fileRequestCallback = nullptr; + +// -------------------------------------------------------------------------------------------------------------------- + struct OscData { lo_address addr; @@ -98,7 +104,7 @@ class UIDssi : public DGL_NAMESPACE::IdleCallback public: UIDssi(const OscData& oscData, const char* const uiTitle, const double sampleRate) : fUI(this, 0, sampleRate, nullptr, - setParameterCallback, setStateCallback, sendNoteCallback, nullptr, nullptr), + setParameterCallback, setStateCallback, sendNoteCallback, fileRequestCallback, nullptr), fHostClosed(false), fOscData(oscData) {