user2875404
user2875404

Reputation: 3218

iOS objective C low-latency audio playback like SoundPool

Hey on my Android apps I can preload my sounds in a SoundPool and then play them with almost no latency at all. Now I am looking for the same thing on iOS/obj-c, but I just can't find anything similar.

I followed a couple of tutorials but eventually there was a bigger lag than I expected and most of the tutorials are advising you to convert your audio to an uncompressed format like wav or caf but my MP3's are already at 14 mb and converting them to lossless audio leads to 81 mb of data which is way too much for me.

The most promising thing I tried was preloading the file (just like I did in Android's SoundPool) like shown in this OAL example:

- (bool) preloadUrl:(NSURL*) url seekTime:(NSTimeInterval)seekTime
{
    if(nil == url)
    {
        OAL_LOG_ERROR(@"%@: Cannot open NULL file / url", self);
        return NO;
    }

    OPTIONALLY_SYNCHRONIZED(self)
    {
        // Bug: No longer re-using AVAudioPlayer because of bugs when using multiple players.
        // Playing two tracks, then stopping one and starting it again will cause prepareToPlay to fail.

        bool wasPlaying = playing;

        [self stopActions];

        if(playing || paused)
        {
            [player stop];
        }

        as_release(player);

        if(wasPlaying)
        {
            [[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:[NSNotification notificationWithName:OALAudioTrackStoppedPlayingNotification object:self] waitUntilDone:NO];
        }

        NSError* error;
        player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
        if(nil == player)
        {
            OAL_LOG_ERROR(@"%@: Could not load URL %@: %@", self, url, [error localizedDescription]);
            return NO;
        }

        player.volume = muted ? 0 : gain;
        player.numberOfLoops = numberOfLoops;
        player.meteringEnabled = meteringEnabled;
        player.delegate = self;
        player.pan = pan;

        as_release(currentlyLoadedUrl);
        currentlyLoadedUrl = as_retain(url);

        self.currentTime = seekTime;
        playing = NO;
        paused = NO;

        BOOL allOK = [player prepareToPlay];
        if(!allOK)
        {
            OAL_LOG_ERROR(@"%@: Failed to prepareToPlay: %@", self, url);
        }
        else
        {
            [[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:[NSNotification notificationWithName:OALAudioTrackSourceChangedNotification object:self] waitUntilDone:NO];
        }
        preloaded = allOK;
        return allOK;
    }
}

But this still makes a quite considerable delay of about ~60ms which is way too much for an audio app like mine. My audio files don't have any delay in the beginning so it must have to do something with the code.

I tried all that stuff on an iPhone 5c.

Upvotes: 1

Views: 795

Answers (1)

Gordon Childs
Gordon Childs

Reputation: 36074

You should be able to create several AVAudioPlayers and call prepareToPlay on them, but personally I like to use AVAssetReader to keep a buffer of LPCM audio ready to play at a moment's notice.

Upvotes: 1

Related Questions