Audio Device latencies & safety offsets on Mac

Hi good people at ROLI,

juce_mac_CoreAudio.cpp doesn't include the safety offsets in the input or output latency values. Can we have these included please?

The safety offset can be queried with kAudioDevicePropertySafetyOffset. This can just be added to the latency returned when querying  kAudioDeviceLatency.

​If I do this, then the sum of the reported latencies are very close to what I measure for the round trip.

-------------

While I'm at it, I've noticed that the latency values include the current block size for the default audio device on the iMac, but if I switch to a Steinberg UR22 then the current block size is excluded (which is wrong).

Does anyone else see this happening with other audio interfaces?

Thanks, we didn't know such a thing existed, but will take a look!

Either did I before today!

Saw it referenced here: http://lists.apple.com/archives/coreaudio-api/2010/Jan/msg00046.html

And then found it in CoreAudio/AudioHardwareBase.h


A polite bump, just in case this was forgotten :)

Plus some code to show how easy it is!
 

​//
// juce_mac_CoreAudio.cpp
//

int getLatencyFromDevice (AudioObjectPropertyScope scope) const
{
     UInt32 lat = 0;
     UInt32 size = sizeof (lat);
     AudioObjectPropertyAddress pa;
     pa.mElement = kAudioObjectPropertyElementMaster;
     pa.mSelector = kAudioDevicePropertyLatency;
     pa.mScope = scope;
     AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &lat);

     // =========================================================================
     // New bit to include the safety offset (assumes lat and off are same size)
     // =========================================================================
     UInt32 off = 0;
     pa.mSelector = kAudioDevicePropertySafetyOffset;
     AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &off);
     return (int) (lat + off);
}

Thanks Andrew, not forgotten! Will do it imminently!

FYI, done now, much appreciated!

Cool.  Not seeing it in the commits yet - is it just my imagination, or are you batching these up lately?

oh yes, I forgot to push it to github - should be there now.

Thanks!

Looking at the Apple documentation and also on the archived coreaudio-api email posted above, it seems like the latency calculation should also include the stream latency from the audio device’s currently active stream.

So shouldn’t the JUCE getLatencyFromDevice() implementation also include the kAudioStreamPropertyLatency value (apart from the kAudioDevicePropertyLatency and the kAudioDevicePropertySafetyOffset)?

Good thought myhrman, but after a couple of hours of mucking around I’ve yet to see a non-zero stream latency.

The following code grafted into juce_mac_CoreAudio.cpp only checks the first stream, but I’ve tried looping through them all and checking that the AudioObjectGetPropertyData is successful. However I still only get zero unfortunately.

int getLatencyFromDevice (AudioObjectPropertyScope scope) const
{
    UInt32 deviceLatency = 0;
    UInt32 size = sizeof (deviceLatency);
    AudioObjectPropertyAddress pa;
    pa.mElement = kAudioObjectPropertyElementMaster;
    pa.mSelector = kAudioDevicePropertyLatency;
    pa.mScope = scope;
    AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &deviceLatency);

    UInt32 safetyOffset = 0;
    size = sizeof (safetyOffset);
    pa.mSelector = kAudioDevicePropertySafetyOffset;
    AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &safetyOffset);

    // Query stream latency
    UInt32 streamLatency = 0;
    UInt32 numStreams;
    pa.mSelector = kAudioDevicePropertyStreams;
    if (OK(AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &numStreams)))
    {
        HeapBlock<AudioStreamID> streams (numStreams);
        size = sizeof (AudioStreamID*);
        if (OK(AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, streams)))
        {
            pa.mSelector = kAudioStreamPropertyLatency;
            size = sizeof (streamLatency);
            // We could check all streams for the device, but it only ever seems to return the stream latency on the first stream
            AudioObjectGetPropertyData (streams[0], &pa, 0, nullptr, &size, &streamLatency);
        }
    }
    
    std::cout << "deviceLatency: " << String(deviceLatency).toRawUTF8() << ", ";
    std::cout << "safetyOffset: " << String(safetyOffset).toRawUTF8() << ", ";
    std::cout << "streamLatency: " << String(streamLatency).toRawUTF8() << std::endl;

    return (int) (deviceLatency + safetyOffset + streamLatency);
}