Dear Juce Devs (@ed95)
Thanks to one of one Beta testers, we’ve found a pretty serious bug in your patch for this issue.
It happens when a hosted AUv3 tries to use the “AU Clock” for timing events
Example:
- Use hosted miRack AUv3 - set-up trivial patch connecting “CLOCK” to “TRIG” input on Mutable Instrument’s Macro Oscillator 2.
- Start playback from host - all is good
- Stop playback from host
- Re-start playback on host - miRack doesn’t generate notes; timings are missing somehow
NB: any other configurations / plug-ins that don’t rely on (host) AU Clock, things all play fine
Solution: patch juce_AudioUnitPluginFormat.mm as per the attached update.
Best wishes,
Pete
void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override
{
if (audioUnit != nullptr)
{
releaseResources();
// MARK: MPC this is in the RIGHT PLACE (begin)
// See https://forum.juce.com/t/bug-juce-audiounitpluginformat-mm/47638
setPluginCallbacks();
// MARK: MPC this is in the RIGHT PLACE (end)
for (int dir = 0; dir < 2; ++dir)
{
const bool isInput = (dir == 0);
const AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output;
const int n = getBusCount (isInput);
for (int i = 0; i < n; ++i)
{
Float64 sampleRate;
UInt32 sampleRateSize = sizeof (sampleRate);
const Float64 sr = newSampleRate;
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope, static_cast<UInt32> (i), &sampleRate, &sampleRateSize);
if (sampleRate != sr)
{
if (isAUv3) // setting kAudioUnitProperty_SampleRate fails on AUv3s
{
AudioStreamBasicDescription stream;
UInt32 dataSize = sizeof (stream);
auto err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope, static_cast<UInt32> (i), &stream, &dataSize);
if (err == noErr && dataSize == sizeof (stream))
{
stream.mSampleRate = sr;
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope, static_cast<UInt32> (i), &stream, sizeof (stream));
}
}
else
{
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope, static_cast<UInt32> (i), &sr, sizeof (sr));
}
}
if (isInput)
{
AURenderCallbackStruct info;
zerostruct (info); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
info.inputProcRefCon = this;
info.inputProc = renderGetInputCallback;
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
static_cast<UInt32> (i), &info, sizeof (info));
}
else
{
outputBufferList.add (new AUBuffer (static_cast<size_t> (getChannelCountOfBus (false, i))));
}
}
}
UInt32 frameSize = (UInt32) estimatedSamplesPerBlock;
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0,
&frameSize, sizeof (frameSize));
setRateAndBufferSizeDetails ((double) newSampleRate, estimatedSamplesPerBlock);
updateLatency();
zerostruct (timeStamp);
timeStamp.mSampleTime = 0;
timeStamp.mHostTime = GetCurrentHostTime (0, newSampleRate, isAUv3);
timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid;
currentBuffer = nullptr;
wasPlaying = false;
resetBuses();
bool ignore;
if (! syncBusLayouts (getBusesLayout(), false, ignore))
return;
prepared = (AudioUnitInitialize (audioUnit) == noErr);
if (prepared)
{
if (! haveParameterList)
refreshParameterList();
// MARK: MPC this is in the WRONG PLACE (begin)
// See https://forum.juce.com/t/bug-juce-audiounitpluginformat-mm/47638
//setPluginCallbacks();
// MARK: MPC this is in the WRONG PLACE (end)
if (! syncBusLayouts (getBusesLayout(), true, ignore))
{
prepared = false;
AudioUnitUninitialize (audioUnit);
}
}
}
}