Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to minimise audio output latency?



  • I'm porting an emulator to Qt. So I have programmatically-generated audio, and I want only a limited amount of forward buffering.

    As a first attempt I tried push audio, grabbing a QIODevice and writing to it as and when data is available. I am testing under macOS and have called setBufferSize with a small number before calling start (though I'm suspicious that it doesn't work; see below), but latency is still noticeably dreadful. I had the idea that maybe I'm allowing too much audio to become buffered during initial startup, but there's no obvious way to determine how much data is currently buffered so there's no way to compensate for that at runtime.

    So I switched to implementing my own QIODevice and using the QAudioOutput pull interface. I figured that gives me sufficient control to avoid over-buffering. But then the interface appears to act illogically. In my specific case, I call setBufferSize with a value of 1024. I then start the output. I query bufferSize() and get a value of 2048. Fine, not great, but fine. Except that the buffer size appears to have no bearing on the QIODevice calls — the QAudioOutput always requests buffers in 16384-byte chunks despite the buffer size being 2048.

    I guess this cuts to the documentation offering no definition for what the buffer size actually means, but I definitely can't come up with any meaningful interpretation in which a buffer which advertises itself as being 2048 bytes would then attempt to read data in 16384-byte chunks.

    As an aside, my machine's output rate is 48,000 Hz and as a result that's the output rate I'm requesting (via QAudioDeviceInfo::defaultOutputDevice().preferredFormat(), but manually verified as being 48,000 on my particular machine). Given that I'm requesting 16-bit audio, 1/6th of a second's latency could well be what I'm hearing, though it feels a lot longer.

    So:

    • what does the buffer size actually mean, given that a value of 2048 is reported by an audio output that pulls data in 16384-byte chunks?
    • is there something I'm supposed to do other than call setBufferSize to request the lowest latency that Qt can provide?

    As an aside, the native macOS version of this app works without disturbance with buffers as small as 512 bytes. So I don't think that either my machine or my OS is causing a problem.

    Also, I'm aware that using some other library for the relatively specialised purpose of low-latency audio wouldn't necessarily be a bad idea, but I also want a working pure-Qt build, that does the best that Qt can do.



  • Hello, I've just looked into that problem not long time ago.
    The key point is, when opening the io device, remember to add QIODevice::Unbuffered to OpenMode, or it will always ask at least 16384 bytes, which is hardcoded in qiodevice_p.h:

    #ifndef QIODEVICE_BUFFERSIZE
    #define QIODEVICE_BUFFERSIZE 16384
    #endif
    


  • Fantastic, thanks!

    That seems to make the QAudioOutput act much closer to what I'd expect given its bufferSize(); it still announces a size of 2048 but now seems primarily to read in 1024-byte increments.

    It's a shame that there isn't an exact meaning of buffer size — empirically on macOS I guess it's a ring buffer that is filled half at a time — but I guess if I take it to be 'the maximum amount that may be read at a time' then I have more than enough information to obtain that minimum available latency, and provide the absolute best plain-Qt experience that I can.

    Thanks again!



  • @ThomH could your issue be called as solved? Thanks.


Log in to reply