Main Content

Measure Audio Latency

This example shows how to measure the latency of an audio device. The example uses audioLatencyMeasurementExampleApp which in turn uses audioPlayerRecorder along with a test signal and cross correlation to determine latency. To avoid disk access interference, the test signal is loaded into a dsp.AsyncBuffer object first, and frames are streamed from that object through the audio device.

Introduction

In general terms, latency is defined as the time from when the audio signal enters a system until it exits. In a digital audio processing chain, there are multiple parameters that cause latency:

  1. Hardware (including A/D and D/A conversion)

  2. Audio drivers that communicate with the system's sound card

  3. Sampling rate

  4. Samples per frame (buffer size)

  5. Algorithmic latency (e.g. delay introduced by a filter or audio effect)

This example shows how to measure round trip latency. That is, the latency incurred when playing audio through a device, looping back the audio with a physical loopback cable, and recording the loopback audio with the same audio device. In order to compute latency for your own audio device, you need to connect the audio out and audio in ports using a loopback cable.

Roundtrip latency does not break down the measurement between output latency and input latency. It measures only the combined effect of the two. Also, most practical applications will not use a loopback setup. Typically the processing chain consists of recording audio, processing it, and playing the processed audio. However, the latency involved should be the same either way provided the other factors (frame size, sampling rate, algorithm latency) don't change.

Hardware Latency

Smaller frame sizes and higher sampling rates reduce the roundtrip latency. However, the tradeoff is a higher chance of dropouts occurring (overruns/underruns).

In addition to potentially increasing latency, the amount of processing involved in the audio algorithm can also cause dropouts.

Measuring Latency with audioLatencyMeasurementExampleApp.m

The function audioLatencyMeasurementExampleApp computes roundtrip latency in milliseconds for a given setup. Overruns and underruns are also presented. If the overruns/underruns are not zero, the results are likely invalid. For example:

audioLatencyMeasurementExampleApp('SamplesPerFrame',64,'SampleRate',48e3)

% The measurements in this example were done on macOS. For most
% measurements, a Steinberg UR22 external USB device was used. For the
% measurements with custom I/O channels, an RME Fireface UFX+ device was
% used. This RME device has lower latency than the Steinberg device for a
% given sample rate/frame size combination. Measurements on Windows using
% ASIO drivers should result in similar values.
Trial(s) done for frameSize 64. 
ans =
  1×5 table
    SamplesPerFrame    SampleRate_kHz    Latency_ms    Overruns    Underruns
    _______________    ______________    __________    ________    _________
      64                 48                8.3125        0           0        

Some Tips When Measuring Latency

Real-time processing on a general purpose operating system is only possible if you minimize other tasks being performed by the computer. It is recommended to:

  1. Close all other programs

  2. Ensure no underruns/overruns occur

  3. Use a large enough buffer size (SamplesPerFrame) to ensure consistent dropout-free behavior

  4. Ensure your hardware settings (buffer size, sampling rate) match the inputs to measureLatency

On Windows, you can use the asiosettings function to launch the dialog to control the hardware settings. On macOS, you should launch the Audio MIDI Setup.

When using ASIO (or CoreAudio with Mac OS), the latency measurements are consistent as long as no dropouts occur. For small buffer sizes, it is possible to get a clean measurement in one instance and dropouts the next. The Ntrials option can be used to ensure consistent dropout behavior when measuring latency. For example, to perform 3 measurements, use:

audioLatencyMeasurementExampleApp('SamplesPerFrame',96,...
    'SampleRate',48e3,'Ntrials',3)
Trial(s) done for frameSize 96. 
ans =
  3×5 table
    SamplesPerFrame    SampleRate_kHz    Latency_ms    Overruns    Underruns
    _______________    ______________    __________    ________    _________
      96                 48                10.312        0           0        
      96                 48                10.312        0           0        
      96                 48                10.312        0           0        

Measurements For Different Buffer Sizes

On macOS, it is also possible to try different frame sizes without changing the hardware settings. To make this convenient, you can specify a vector of SamplesPerFrame:

BufferSizes = [64;96;128];
t = audioLatencyMeasurementExampleApp('SamplesPerFrame',BufferSizes)

% Notice that for every sample increment in the buffer size, the additional
% latency is 3*SamplesPerFrameIncrement/SampleRate (macOS only).
Trial(s) done for frameSize 64. 
Trial(s) done for frameSize 96. 
Trial(s) done for frameSize 128. 
t =
  3×5 table
    SamplesPerFrame    SampleRate_kHz    Latency_ms    Overruns    Underruns
    _______________    ______________    __________    ________    _________
       64                48                8.3125        0           0        
       96                48                10.312        0           0        
      128                48                12.312        0           0        

Specifically, in the previous example, the increment is

3*[128-96, 96-64]/48e3

% In addition, notice that the actual buffering latency is also determined
% by 3*SamplesPerFrame/SampleRate. Subtracting this value from the measured
% latency gives a measure of the latency introduced by the device (combined
% effect of A/D conversion, D/A conversion, and drivers). The numbers above
% indicate about 4.3125 ms latency due to device-specific factors.

t.Latency_ms - 3*BufferSizes/48
ans =
    0.0020    0.0020
ans =
    4.3125
    4.3125
    4.3125

Specifying Custom Input/Output Channels

The measurements performed so far assume that channel #1 is used for both input and output. If your device has a loopback cable connected to other channels, you can specify them using the IOChannels option to measureLatency. This is specified as a 2-element vector, corresponding to the input and output channels to be used (the measurement is always on a mono signal). For example for an RME Fireface UFX+:

audioLatencyMeasurementExampleApp('SamplesPerFrame',[32 64 96],...
    'SampleRate',96e3,'Device','Fireface UFX+ (23767940)',...
    'IOChannels',[1 3])
Trial(s) done for frameSize 32. 
Trial(s) done for frameSize 64. 
Trial(s) done for frameSize 96. 
ans =
  3×5 table
    SamplesPerFrame    SampleRate_kHz    Latency_ms    Overruns     Underruns 
    _______________    ______________    __________    _________    __________
      32                 96                2.6458        0           32       
      64                 96                3.6458        0            0       
      96                 96                4.6458        0            0       

Algorithmic Latency

The measurements so far have not included algorithm latency. Therefore, they represent the minimal roundtrip latency that can be achieved for a given device, buffer size, and sampling rate. You can add a linear phase FIR filter the processing chain to verify that the latency measurements are as expected. Moreover, it provides a way of verifying robustness of the real-time audio processing under a given workload. For example,

L  = 961;
Fs = 48e3;
audioLatencyMeasurementExampleApp('SamplesPerFrame',128,...
    'SampleRate',Fs,'FilterLength',L,'Ntrials',3)

% The latency introduced by the filter is given by the filter's
% group-delay.

GroupDelay = (L-1)/2/Fs

% The group delay accounts for the 10 ms of additional latency when using a
% 961-tap linear-phase FIR filter vs. the minimal achievable latency.
Trial(s) done for frameSize 128. 
ans =
  3×6 table
    SamplesPerFrame    SampleRate_kHz    FilterLength    Latency_ms    Overruns    Underruns
    _______________    ______________    ____________    __________    ________    _________
      128                48                961             22.312        0           0        
      128                48                961             22.312        0           0        
      128                48                961             22.312        0           0        
GroupDelay =
    0.0100

Plotting the Original and Recorded Signal

%The latency measurements are determined by cross-correlating a source
%audio signal with a delayed version of the signal that results after
%loopback through the audio device. You can use the Plot option in
%measureLatency to plot the original and delayed signal along with the
%cross correlation:
audioLatencyMeasurementExampleApp('SamplesPerFrame',128,'Plot',true)

% If the optional FIR filtering is used, the waveforms are not affected
% because the filter used has a broader bandwidth than the test audio
% signal.
Trial(s) done for frameSize 128. 
Plotting... 
ans =
  1×5 table
    SamplesPerFrame    SampleRate_kHz    Latency_ms    Overruns    Underruns
    _______________    ______________    __________    ________    _________
      128                48                12.312        0           0