Search code examples
objective-ciosipadlatencyaudiotoolbox

iPad drumcomputer touch->audio latency


We've built a drumcomputer app for iPad to play along with live music (on stage or in your bedroom). The setup is pretty basic: you press one of the buttons and the corresponding sample will play. Everything works pretty smoothly, but unfortunately there appears to be a small (though annoying) latency between the moment you put your finger down and the playback of the sound. I've tried to measure the amount of latency (by ear), it seems to be something like 0.05 - 0.1 seconds.

The audio playback has been implemented using the AudioToolbox framework (Extended Audio File Services, Audio Units). The sounds are streamed from file, which are stored on the device, to prevent loading times between different soundbanks. Samples are in raw wav format (Linear PCM, 16 bit little-endian signed integer, 2 channels, 44100 Hz), which should be the fastest to process as far as I understand (opposed to something compressed like mp3).

I've measured the time between the button press (UIButton touch event) and the delivery of the first frames of the sample to the mixer (through the playback callback). It is quite stable and between 0.02 to 0.03 seconds. To me this seems pretty fast, but it might not be fast enough.

Could that be the problem or might it be something else like a delay in the delivery of touch events?

UPDATE:

As suggested by Till, I've rewritten the loading of the samples. They are now all preloaded into memory, so disk IO is no longer an issue. On top of that I was doing quite a bit of memcpy for an echo effect, I've disabled it and will fix that later with a linked list kind of solution.

Though this reduces the latency, button press->playback still measures between 0.005 to 0.02 seconds (but more often like 0.02). This is still noticeable. I'm thinking this might be due to the buffer size of the playback callback, which is currently 1024 bytes.

Any ideas on how to do that? Setting kAudioUnitProperty_MaximumFramesPerSlice doesn't seem to work.


Solution

  • I solved the latency problem by specifying the preferred hardware I/O buffer duration, as stated in the Audio Session Cookbook

    NSTimeInterval preferredBufferDuration = 0.005;
    [audioSession setPreferredIOBufferDuration:preferredBufferDuration error:&audioSessionError];
    
    if (audioSessionError != nil) 
    {
        NSLog (@"Error setting preferred buffer duration.");
        return;
    }
    

    By setting the buffer duration to 0.005, your application will only have to supply 256 bytes (at 44Khz) on each cycle. This (obviously) reduces the latency from 0.02 to 0.005, but audio will have to be produced at a faster rate.

    The iPad 2 is perfectly able to cope with this speed, even when the IO is read from disk. The first generation iPad is not fast enough unfortunately (with and without disk IO) so I hope I can find some way to distinguish the two and give the iPad 1 a bit more latency for better performance.