Search code examples
objective-cswift3core-audioavaudiosessionaudiounit

Objective-C/Swift usage in AudioUnit Render callback


Apple advices against use of Objective-C and Swift in AudioUnit input or render callbacks. So the callbacks are written mostly in C where we can quickly copy data and pass that data to secondary thread for processing. To share variables between C and Objective C, the advice is to use inRefCon as a pointer to Controller object and then access it's variable. In below code, I access two member variables of MyController object - muteAudio and callback queue. This is not the full code but just a sample to highlight the issue.

 static OSStatus renderCallback       (void *                            inRefCon,
                                     AudioUnitRenderActionFlags *      ioActionFlags,
                                     const AudioTimeStamp *            inTimeStamp,
                                     UInt32                            inBusNumber,
                                     UInt32                            inNumberFrames,
                                     AudioBufferList *                 ioData)

 {
     MyController* THIS = (MyController *)inRefCon;

    if (!THIS->muteAudio) {
         for (UInt32 i=0; i<ioData->mNumberBuffers; ++i)
         memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize);
      return noErr;
    }

    OSStatus status = AudioUnitRender(THIS->mAudioUnit,
                         ioActionFlags,
                         inTimeStamp,
                         kInputBus,
                         inNumberFrames,
                         ioData);

  if (status != noErr) {
    // printf("Render error %d", status);
  }

 dispatch_async(THIS->mCallbackQueue,^{ 
   //Execute some code
     if (THIS->mActive) {
      ....
     }
 });

 return noErr;
}

My questions:

a. Is the call such as THIS->silentAudio to access member variable of MyController instance any different from Objective C messaging to access properties? My understanding is a call like THIS->silentAudio is effectively direct access to memory at pointer location as opposed to Objective C messaging, but just to confirm if this is correct.

b. How do I translate the above (THIS->silentAudio) exactly to Swift? Will the Swift runtime behave exactly the same like Objective C to access private variables of MyController using pointers than message passing?

c. In case of Swift, can implicit/explicit unwrapping make a difference in performance?

Please excuse my knowledge of Swift as I am new to it.

EDIT: Based on answer by hotpaw2, I translated the code as follows:

  func renderCallback(inRefCon:UnsafeMutableRawPointer,                        ioActionFlags:UnsafeMutablePointer<AudioUnitRenderActionFlags>,
                inTimeStamp:UnsafePointer<AudioTimeStamp>,
                inBusNumber:UInt32,
                inNumberFrames:UInt32,
                ioData:UnsafeMutablePointer<AudioBufferList>?) -> OSStatus
 {
    let controller = unsafeBitCast(inRefCon, to: MyController) 

    if !controller.muteAudio {
       ....
    }

    return noErr;

}

My doubt now is if controller.muteAudio is indeed translated by Swift compiler as access to memory baseAddress + offset or it can be Swift message passing.


Solution

  • C code doesn't call Objective C messages via the Objective C runtime (unless done explicitly using passed pointers to the message functions). But the Xcode C compiler does know the memory layout of Objective C objects; so (THIS->silentAudio) compiles to base pointer + offset memory access code in C.

    You can get the equivalent of base pointer + offset access in Swift by using UnsafeRawPointers, UnsafeMutableRawPointers and unsafeBitCasts on C struct pointers. Wrapped has no meaning when dealing with unsafe pointers, you need to check those for validity yourself.

    You may need to use C structs or Objective C objects to share data structures with Swift code, as the non-Swift language compilers may not know or be able to use the layout of Swift objects. Whereas the Swift compiler can figure out memory layouts the other way by using a bridging header, which can contain both C struct and Obj C class and object definitions.