Browse Source

Deal with more AU audio rendering details

Signed-off-by: falkTX <falktx@falktx.com>
pull/452/head
falkTX 1 year ago
parent
commit
19cb24186b
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
1 changed files with 248 additions and 68 deletions
  1. +248
    -68
      distrho/src/DistrhoPluginAU.cpp

+ 248
- 68
distrho/src/DistrhoPluginAU.cpp View File

@@ -200,14 +200,33 @@ struct PropertyListener {
void* userData;
};

struct RenderListener {
AURenderCallback proc;
void* userData;
};

typedef std::vector<PropertyListener> PropertyListeners;
typedef std::vector<RenderListener> RenderListeners;

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

#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
# define DPF_AU_NUM_BUFFERS 1
#elif DISTRHO_PLUGIN_NUM_INPUTS > DISTRHO_PLUGIN_NUM_OUTPUTS
# define DPF_AU_NUM_BUFFERS DISTRHO_PLUGIN_NUM_INPUTS
#else
# define DPF_AU_NUM_BUFFERS DISTRHO_PLUGIN_NUM_OUTPUTS
#endif

typedef struct {
UInt32 mNumberBuffers;
AudioBuffer mBuffers[DPF_AU_NUM_BUFFERS];
} d_AudioBufferList;

typedef struct {
UInt32 numPackets;
MIDIPacket packets[kMaxMidiEvents];
} MIDIPacketList;
} d_MIDIPacketList;

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

@@ -233,6 +252,7 @@ public:
fComponent(component),
fLastRenderError(noErr),
fPropertyListeners(),
fRenderListeners(),
fSampleRateForInput(d_nextSampleRate),
#if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
fSampleRateForOutput(d_nextSampleRate),
@@ -240,6 +260,7 @@ public:
#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
fHasWorkingAudio(false),
#endif
fUsingRenderListeners(false),
fParameterCount(fPlugin.getParameterCount()),
fLastParameterValues(nullptr),
fBypassParameterIndex(UINT32_MAX)
@@ -256,6 +277,8 @@ public:
, fStateCount(fPlugin.getStateCount())
#endif
{
const uint32_t bufferSize = fPlugin.getBufferSize();

if (fParameterCount != 0)
{
fLastParameterValues = new float[fParameterCount];
@@ -274,6 +297,15 @@ public:
fInputRenderCallback.inputProc = nullptr;
fInputRenderCallback.inputProcRefCon = nullptr;

fAudioBufferList.mNumberBuffers = DPF_AU_NUM_BUFFERS;

for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i)
{
fAudioBufferList.mBuffers[i].mNumberChannels = 1;
fAudioBufferList.mBuffers[i].mData = new float[bufferSize];
fAudioBufferList.mBuffers[i].mDataByteSize = sizeof(float) * bufferSize;
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
std::memset(&fMidiEvents, 0, sizeof(fMidiEvents));
#endif
@@ -331,6 +363,9 @@ public:
delete[] fLastParameterValues;
CFRelease(fUserPresetData.presetName);

for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i)
delete[] static_cast<float*>(fAudioBufferList.mBuffers[i].mData);

#if DISTRHO_PLUGIN_WANT_PROGRAMS
for (uint32_t i=0; i<fProgramCount; ++i)
CFRelease(fFactoryPresetsData[i].presetName);
@@ -1037,7 +1072,7 @@ public:
fPlugin.setSampleRate(sampleRate, true);
}

notifyListeners(inProp, inScope, inElement);
notifyPropertyListeners(inProp, inScope, inElement);
}
return noErr;
}
@@ -1055,7 +1090,7 @@ public:
fPlugin.setSampleRate(sampleRate, true);
}

notifyListeners(inProp, inScope, inElement);
notifyPropertyListeners(inProp, inScope, inElement);
}
return noErr;
}
@@ -1111,8 +1146,8 @@ public:
fPlugin.setSampleRate(desc->mSampleRate, true);
}

notifyListeners(inProp, inScope, inElement);
notifyListeners(kAudioUnitProperty_SampleRate, inScope, inElement);
notifyPropertyListeners(inProp, inScope, inElement);
notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement);
}
#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
fHasWorkingAudio = true;
@@ -1132,8 +1167,8 @@ public:
fPlugin.setSampleRate(desc->mSampleRate, true);
}

notifyListeners(inProp, inScope, inElement);
notifyListeners(kAudioUnitProperty_SampleRate, inScope, inElement);
notifyPropertyListeners(inProp, inScope, inElement);
notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement);
}
return noErr;
}
@@ -1145,10 +1180,23 @@ public:
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(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue);
{
const UInt32 bufferSize = *static_cast<const UInt32*>(inData);

if (fPlugin.setBufferSize(*static_cast<const UInt32*>(inData), true))
notifyListeners(inProp, inScope, inElement);
if (fPlugin.setBufferSize(bufferSize, true))
{
notifyPropertyListeners(inProp, inScope, inElement);

#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0
for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i)
{
delete[] static_cast<float*>(fAudioBufferList.mBuffers[i].mData);
fAudioBufferList.mBuffers[i].mData = new float[bufferSize];
fAudioBufferList.mBuffers[i].mDataByteSize = sizeof(float) * bufferSize;
}
#endif
}
}
return noErr;

case kAudioUnitProperty_BypassEffect:
@@ -1164,7 +1212,7 @@ public:
const float value = bypass ? 1.f : 0.f;
fLastParameterValues[fBypassParameterIndex] = value;
fPlugin.setParameterValue(fBypassParameterIndex, value);
notifyListeners(inProp, inScope, inElement);
notifyPropertyListeners(inProp, inScope, inElement);
}
}
return noErr;
@@ -1190,7 +1238,7 @@ public:
std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize);

if (changed)
notifyListeners(inProp, inScope, inElement);
notifyPropertyListeners(inProp, inScope, inElement);
}
return noErr;
#else
@@ -1212,7 +1260,7 @@ public:
fCurrentProgram = presetNumber;
fLastFactoryProgram = presetNumber;
fPlugin.loadProgram(fLastFactoryProgram);
notifyListeners('DPFo', kAudioUnitScope_Global, 0);
notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0);
}
}
else
@@ -1251,16 +1299,16 @@ public:
return noErr;

#if DISTRHO_PLUGIN_WANT_PROGRAMS
notifyListeners('DPFo', kAudioUnitScope_Global, 0);
notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0);
#endif

#if DISTRHO_PLUGIN_WANT_STATE
for (uint32_t i=0; i<fStateCount; ++i)
notifyListeners('DPFs', kAudioUnitScope_Global, i);
notifyPropertyListeners('DPFs', kAudioUnitScope_Global, i);
#endif

for (uint32_t i=0; i<fParameterCount; ++i)
notifyListeners('DPFp', kAudioUnitScope_Global, i);
notifyPropertyListeners('DPFp', kAudioUnitScope_Global, i);
}
return noErr;

@@ -1310,7 +1358,7 @@ public:
AUEventListenerNotify(NULL, NULL, &event);

if (fBypassParameterIndex == inElement)
notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
}
return noErr;

@@ -1382,9 +1430,6 @@ public:
prop, proc, userData
};

if (fPropertyListeners.empty())
fPropertyListeners.reserve(32);

fPropertyListeners.push_back(pl);
return noErr;
}
@@ -1425,15 +1470,29 @@ public:

OSStatus auAddRenderNotify(const AURenderCallback proc, void* const userData)
{
d_stdout("WIP AddRenderNotify(%p, %p)", proc, userData);
// TODO
fUsingRenderListeners = true;

const RenderListener rl = {
proc, userData
};

fRenderListeners.push_back(rl);
return noErr;
}

OSStatus auRemoveRenderNotify(const AURenderCallback proc, void* const userData)
{
d_stdout("WIP RemoveRenderNotify(%p, %p)", proc, userData);
// TODO
for (RenderListeners::iterator it = fRenderListeners.begin(); it != fRenderListeners.end(); ++it)
{
const RenderListener& rl(*it);

if (rl.proc == proc && rl.userData == userData)
{
fRenderListeners.erase(it);
return auRemoveRenderNotify(proc, userData);
}
}

return noErr;
}

@@ -1468,10 +1527,10 @@ public:
fPlugin.setParameterValue(param, value);

// TODO flag param only, notify listeners later on bg thread (sem_post etc)
notifyListeners('DPFp', kAudioUnitScope_Global, param);
notifyPropertyListeners('DPFp', kAudioUnitScope_Global, param);

if (fBypassParameterIndex == elem)
notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
}

return noErr;
@@ -1523,61 +1582,158 @@ public:
return noErr;
}

OSStatus auRender(AudioUnitRenderActionFlags* const ioActionFlags,
OSStatus auRender(const AudioUnitRenderActionFlags actionFlags,
const AudioTimeStamp* const inTimeStamp,
const UInt32 inBusNumber,
const UInt32 inFramesToProcess,
AudioBufferList& ioData)
AudioBufferList* const ioData)
{
DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement);
DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData.mNumberBuffers == std::max<uint>(DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS), ioData.mNumberBuffers, kAudio_ParamError);

if (inFramesToProcess > fPlugin.getBufferSize())
{
setLastRenderError(kAudioUnitErr_TooManyFramesToProcess);
return kAudioUnitErr_TooManyFramesToProcess;
}

for (uint i=0; i<ioData.mNumberBuffers; ++i)
if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0)
{
AudioBuffer& buffer(ioData.mBuffers[i]);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement);
DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == DPF_AU_NUM_BUFFERS,
ioData->mNumberBuffers, kAudio_ParamError);

// TODO there must be something more to this...
if (buffer.mData == nullptr)
if (inFramesToProcess > fPlugin.getBufferSize())
{
return noErr;
setLastRenderError(kAudioUnitErr_TooManyFramesToProcess);
return kAudioUnitErr_TooManyFramesToProcess;
}

DISTRHO_SAFE_ASSERT_UINT_RETURN(buffer.mDataByteSize == inFramesToProcess * sizeof(float), buffer.mDataByteSize, kAudio_ParamError);
for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i)
{
if (ioData->mBuffers[i].mDataByteSize != sizeof(float) * inFramesToProcess)
{
setLastRenderError(kAudio_ParamError);
return kAudio_ParamError;
}
}
}

#if DISTRHO_PLUGIN_NUM_INPUTS != 0
const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS];

for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
{
inputs[i] = static_cast<const float*>(ioData.mBuffers[i].mData);
}
#else
constexpr const float** inputs = nullptr;
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS];

for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
{
outputs[i] = static_cast<float*>(ioData.mBuffers[i].mData);
}
#else
constexpr float** outputs = nullptr;
#endif

if (fInputRenderCallback.inputProc != nullptr)
{
bool adjustDataByteSize, usingHostBuffer = true;
UInt32 prevDataByteSize;

for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i)
{
if (ioData->mBuffers[i].mData == nullptr)
{
usingHostBuffer = false;
ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData;
}
}

if (! usingHostBuffer)
{
prevDataByteSize = fAudioBufferList.mBuffers[0].mDataByteSize;
adjustDataByteSize = prevDataByteSize != sizeof(float) * inFramesToProcess;

if (adjustDataByteSize)
{
for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i)
fAudioBufferList.mBuffers[i].mDataByteSize = sizeof(float) * inFramesToProcess;
}
}
else
{
adjustDataByteSize = false;
}

AudioUnitRenderActionFlags rActionFlags = 0;
AudioBufferList* const rData = usingHostBuffer ? ioData : reinterpret_cast<AudioBufferList*>(&fAudioBufferList);
const OSStatus err = fInputRenderCallback.inputProc(fInputRenderCallback.inputProcRefCon,
&rActionFlags,
inTimeStamp,
inBusNumber,
inFramesToProcess,
rData);

if (err != noErr)
{
if (adjustDataByteSize)
{
for (uint16_t i=0; i<DPF_AU_NUM_BUFFERS; ++i)
fAudioBufferList.mBuffers[i].mDataByteSize = prevDataByteSize;
}

setLastRenderError(err);
return err;
}

if (usingHostBuffer)
{
#if DISTRHO_PLUGIN_NUM_INPUTS != 0
for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
inputs[i] = static_cast<const float*>(ioData->mBuffers[i].mData);
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData);
#endif

}
else
{
#if DISTRHO_PLUGIN_NUM_INPUTS != 0
for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
inputs[i] = static_cast<const float*>(fAudioBufferList.mBuffers[i].mData);
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData);
#endif
}
}
else
{
#if DISTRHO_PLUGIN_NUM_INPUTS != 0
for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
{
if (ioData->mBuffers[i].mData == nullptr)
ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData;

inputs[i] = static_cast<const float*>(ioData->mBuffers[i].mData);
}
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
for (uint16_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
{
if (ioData->mBuffers[i].mData == nullptr)
ioData->mBuffers[i].mData = fAudioBufferList.mBuffers[i].mData;

outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData);
}
#endif
}

if (fUsingRenderListeners)
{
AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PreRender;
notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData);
}

run(inputs, outputs, inFramesToProcess, inTimeStamp);

if (ioActionFlags != nullptr)
if (fUsingRenderListeners)
{
// TODO what now?
AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PostRender;
notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData);
}

return noErr;
@@ -1667,14 +1823,17 @@ private:
// AUv2 related fields
OSStatus fLastRenderError;
PropertyListeners fPropertyListeners;
RenderListeners fRenderListeners;
AURenderCallbackStruct fInputRenderCallback;
Float64 fSampleRateForInput;
#if DISTRHO_PLUGIN_NUM_OUTPUTS != 0
Float64 fSampleRateForOutput;
#endif
d_AudioBufferList fAudioBufferList;
#if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0
bool fHasWorkingAudio;
#endif
bool fUsingRenderListeners;

// Caching
const uint32_t fParameterCount;
@@ -1689,7 +1848,7 @@ private:

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
AUMIDIOutputCallbackStruct fMidiOutput;
MIDIPacketList fMidiOutputPackets;
d_MIDIPacketList fMidiOutputPackets;
#endif

#if DISTRHO_PLUGIN_WANT_PROGRAMS
@@ -1712,7 +1871,7 @@ private:

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

void notifyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem)
void notifyPropertyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem)
{
for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it)
{
@@ -1723,6 +1882,22 @@ private:
}
}

void notifyRenderListeners(AudioUnitRenderActionFlags* const ioActionFlags,
const AudioTimeStamp* const inTimeStamp,
const UInt32 inBusNumber,
const UInt32 inNumberFrames,
AudioBufferList* const ioData)
{
for (RenderListeners::iterator it = fRenderListeners.begin(); it != fRenderListeners.end(); ++it)
{
const RenderListener& rl(*it);

rl.proc(rl.userData, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
}

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

void run(const float** inputs, float** outputs, const uint32_t frames, const AudioTimeStamp* const inTimeStamp)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
@@ -1862,7 +2037,7 @@ private:
// TODO flag param only, notify listeners later on bg thread (sem_post etc)
event.mArgument.mParameter.mParameterID = i;
AUEventListenerNotify(NULL, NULL, &event);
notifyListeners('DPFp', kAudioUnitScope_Global, i);
notifyPropertyListeners('DPFp', kAudioUnitScope_Global, i);
}
}
}
@@ -1873,7 +2048,7 @@ private:
return;

fLastRenderError = err;
notifyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0);
notifyPropertyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0);
}

// ----------------------------------------------------------------------------------------------------------------
@@ -2063,10 +2238,10 @@ private:
{
fLastFactoryProgram = program;
fPlugin.loadProgram(fLastFactoryProgram);
notifyListeners('DPFo', kAudioUnitScope_Global, 0);
notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0);
}

notifyListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
notifyPropertyListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
}
}
#endif
@@ -2124,7 +2299,7 @@ private:
if (fPlugin.getStateKey(j) == key)
{
if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0)
notifyListeners('DPFs', kAudioUnitScope_Global, j);
notifyPropertyListeners('DPFs', kAudioUnitScope_Global, j);

break;
}
@@ -2178,10 +2353,10 @@ private:

fLastParameterValues[j] = value;
fPlugin.setParameterValue(j, value);
notifyListeners('DPFp', kAudioUnitScope_Global, j);
notifyPropertyListeners('DPFp', kAudioUnitScope_Global, j);

if (fBypassParameterIndex == j)
notifyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);

break;
}
@@ -2230,7 +2405,7 @@ private:

fLastParameterValues[index] = value;
AUEventListenerNotify(NULL, NULL, &event);
notifyListeners('DPFp', kAudioUnitScope_Global, index);
notifyPropertyListeners('DPFp', kAudioUnitScope_Global, index);
return true;
}

@@ -2253,7 +2428,7 @@ private:
fStateMap[dkey] = newValue;

if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0)
notifyListeners('DPFs', kAudioUnitScope_Global, i);
notifyPropertyListeners('DPFs', kAudioUnitScope_Global, i);

return true;
}
@@ -2571,10 +2746,15 @@ struct AudioComponentPlugInInstance {
const UInt32 inNumberFrames,
AudioBufferList* const ioData)
{
DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError);
DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError);
const AudioUnitRenderActionFlags actionFlags = ioActionFlags != nullptr ? *ioActionFlags : 0;

// if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0)
{
DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError);
DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError);
}

return self->plugin->auRender(ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, *ioData);
return self->plugin->auRender(actionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT


Loading…
Cancel
Save