Why call to CFRunLoopRunInMode() in Audio Queue Playback code?
Asked Answered
M

3

9

I'm following the iOS "Audio Queue Programming Guide - Playing Audio". Near the end of the guide, there are calls to CFRunLoopRunInMode() in the step Start and Run an Audio Queue:

do {                                               // 5
    CFRunLoopRunInMode (                           // 6
        kCFRunLoopDefaultMode,                     // 7
        0.25,                                      // 8
        false                                      // 9
    );
} while (aqData.mIsRunning);
//...

The documentation about line 6 says: "The CFRunLoopRunInMode function runs the run loop that contains the audio queue’s thread." But isn't that run loop executed anyways when my method returns? The code above is executed by the main thread upon pressing the play button in my app.

Now I'm having a hard time understanding what these calls to CFRunLoopRunInMode() are good for, because they have the disadvantage that my play-button does not update correctly (it looks pressed down for the whole time that the audio plays) and there is no positive effect, i.e. the audio also plays nicely if I remove the do-while-loop from my code along with the calls to CFRunLoopRunInMode() and instead directly return from this method. Well this points to the obvious solution to simply keep these calls removed as this doesn't create a problem. Can someone explain why then this code is included in the official guide by Apple on using Audio Queues in iOS for Audio Playback?

Edit:

I'm just seeing that in Mac OS X, there exists the same audio queues API as on iOS, and the guide for iOS seems to be a copy-paste duplication of the Mac OS guide. This leads me to the suspicion that those calls to the run loop are only required in Mac OS and not anymore in iOS, e.g. because otherwise the Mac OS application would exit or something like that. Can someone please verify this or rule it out?

Martial answered 8/1, 2013 at 16:14 Comment(1)
I think you are correct: to me, this just looks like sample code for a standalone (command-line?) app that does nothing else but play an audio file to completion. In a "real" app, there is no need to run the run loop "manually." FWIW, I use AudioQueues and never noticed that sample code, and never call CFRunLoopRunInMode() in a loop like that.Plash
A
2

@bunnyhero is right, CFRunLoopRunInMode() is usually for command line examples

https://github.com/abbood/Learning-Core-Audio-Book-Code-Sample/blob/master/CH05_Player/CH05_Player/main.c

As long as your AudioQueueRef is not deallocated, you dont have to use CFRunLoopRunInMode() in IOS...

What I do is create a separate class for audio queue and as long as my class pointer and AudioQueueRef is allocated I can playback, pause, resume, stop etc....

Anesthetic answered 21/5, 2015 at 20:44 Comment(0)
C
1

Related to OP's question, regarding AQ blocking UI thread, to further liberate AQ user from copying that CoreAudio AQ example cited blindly,

I shall add that the example configures the AQ to run in the current runloop in the main thread, in Listing 3-11 Creating a playback audio queue:

AudioQueueNewOutput (                                // 1
    &aqData.mDataFormat,                             // 2
    HandleOutputBuffer,                              // 3
    &aqData,                                         // 4
    CFRunLoopGetCurrent (),                          // 5
    kCFRunLoopCommonModes,                           // 6
    0,                                               // 7
    &aqData.mQueue                                   // 8
);

, see the parameter value CFRunLoopGetCurrent () above. The text explains

The current run loop, and the one on which the audio queue playback callback will be invoked.

Looking at the function prototype:

OSStatus AudioQueueNewOutput(
    const AudioStreamBasicDescription *inFormat,   // 2
    AudioQueueOutputCallback inCallbackProc,       // 3
    void *inUserData,                              // 4
    CFRunLoopRef inCallbackRunLoop,                // 5
    CFStringRef inCallbackRunLoopMode,             // 6
    UInt32 inFlags,                                // 7
    AudioQueueRef  _Nullable *outAQ                // 8
);

If you replace #5 with NULL, then AQ will run in a CoreAudio internal thread, making it more efficient for you app.

Coattail answered 9/6, 2020 at 13:20 Comment(0)
H
1

CFRunLoopRunInMode is needed to keep the audio queue alive while the execution of your code has ended, for example, when running a terminal app. iOS apps contain a lifecyle: To keep an audio queue alive you only need to declare AudioQueueRef as a member variable. Otherwise, if it is declared within a method scope it gets destroyed after execution of that method - and thus, it will stop - unless you keep it alive with CFRunLoopRunInMode.

To summarize, as long as you hold a member variable of AudioQueueRef - or the new AVAudioEngine - in an instantiated class that is not free'ed from memory, a CFRunLoopRunInMode is not needed.

Hansom answered 24/8, 2020 at 17:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.