AudioUnit host getLatencySamples() reports incorrect value


#1

The AudioUnit format is calling updateLatency() before the plugins prepareToPlay() is being called. if a plugin is changing it’s latency following a change of sample rate and block size then calling getLatencySamples() will report an incorrect value that is not in sync with the plugin.

For example I created a plugin using the projucer and then added this to prepareToPlay()…

void LatencyTestPluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    DBG ("plugin: setting latency to " << (int) sampleRate);
    setLatencySamples ((int) sampleRate);
}

I then created a simple command line host like so…

#include "../JuceLibraryCode/JuceHeader.h"

String getFileForFormat (const String& formatName)
{
    if (formatName == "AudioUnit")
        return String("/Users/an/Library/Audio/Plug-Ins/Components/LatencyTestPlugin.component");
    
    if (formatName == "VST")
        return String("/Users/an/Library/Audio/Plug-Ins/VST/LatencyTestPlugin.vst");
    
    if (formatName == "VST3")
        return String("/Users/an/Library/Audio/Plug-Ins/VST3/LatencyTestPlugin.vst3");
    
    return String();
}

class Application : public JUCEApplicationBase
{
public:
    const String getApplicationName() override { return ProjectInfo::projectName; }
    
    const String getApplicationVersion() override { return ProjectInfo::versionString; }
    
    bool moreThanOneInstanceAllowed() override { return true; }
    
    void initialise (const String& commandLineParameters) override
    {
        AudioPluginFormatManager formatManager;
        formatManager.addDefaultFormats();
        
        for (int formatIndex = 0; formatIndex < formatManager.getNumFormats(); ++formatIndex)
        {
            if (AudioPluginFormat* format = formatManager.getFormat (formatIndex))
            {
                DBG ("Testing " << format->getName());
                
                PluginDescription description;
                description.fileOrIdentifier = getFileForFormat (format->getName());
                
                if (ScopedPointer<AudioPluginInstance> plugin = format->createInstanceFromDescription (description, 44100.0, 1024))
                {
                    for (auto sampleRate : { 22050.0, 24000.0, 44100.0, 48000.0, 88200.0, 96000.0 })
                    {
                        DBG ("Sample Rate: " << sampleRate);

                        plugin->prepareToPlay (sampleRate, 1024);
                        DBG ("Host: reported latency is " << plugin->getLatencySamples());
                        plugin->releaseResources();
                    }
                }
                
                DBG ("");
            }
        }
        
        quit();
    }
    
    void shutdown() override {}

    void anotherInstanceStarted (const String& commandLine) override {}
    
    void systemRequestedQuit() override {}
    
    void suspended() override {}
    
    void resumed() override {}
    
    void unhandledException (const std::exception*, const String& sourceFilename, int lineNumber) override {}
};

START_JUCE_APPLICATION (Application)

…the output of which looks like this…

JUCE v5.2.1
Testing AudioUnit
JUCE v5.2.1
Sample Rate: 22050
void juce::AudioUnitPluginInstance::updateLatency()
plugin: setting latency to 22050
plugin: setting latency to 22050
Host: reported latency is 0
plugin: setting latency to 22050
Sample Rate: 24000
void juce::AudioUnitPluginInstance::updateLatency()
plugin: setting latency to 24000
Host: reported latency is 22050
plugin: setting latency to 24000
Sample Rate: 44100
void juce::AudioUnitPluginInstance::updateLatency()
plugin: setting latency to 44100
Host: reported latency is 24000
plugin: setting latency to 44100
Sample Rate: 48000
void juce::AudioUnitPluginInstance::updateLatency()
plugin: setting latency to 48000
Host: reported latency is 44100
plugin: setting latency to 48000
Sample Rate: 88200
void juce::AudioUnitPluginInstance::updateLatency()
plugin: setting latency to 88200
Host: reported latency is 48000
plugin: setting latency to 88200
Sample Rate: 96000
void juce::AudioUnitPluginInstance::updateLatency()
plugin: setting latency to 96000
Host: reported latency is 88200
plugin: setting latency to 96000

Testing VST
Attempting to load VST: /Users/an/Library/Audio/Plug-Ins/VST/LatencyTestPlugin.vst
JUCE v5.2.1
Creating VST instance: LatencyTestPlugin
Initialising VST: LatencyTestPlugin (1.0.0.0)
Sample Rate: 22050
plugin: setting latency to 22050
Host: reported latency is 22050
Sample Rate: 24000
plugin: setting latency to 24000
Host: reported latency is 24000
Sample Rate: 44100
plugin: setting latency to 44100
Host: reported latency is 44100
Sample Rate: 48000
plugin: setting latency to 48000
Host: reported latency is 48000
Sample Rate: 88200
plugin: setting latency to 88200
Host: reported latency is 88200
Sample Rate: 96000
plugin: setting latency to 96000
Host: reported latency is 96000

Note that the AU plugin is always behind on the changes to the latency but the VST isn’t.

I’ve created a PR that fixes this issue here https://github.com/WeAreROLI/JUCE/pull/363