1

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.

Deepak Sharma
  • 5,577
  • 7
  • 55
  • 131
  • Is controller a C struct? Then OK. If not, the Swift compiler makes no such guarantees regarding access. – hotpaw2 Apr 29 '17 at 17:34
  • No it's not a C struct, I made it a class which inherits from NSObject. So I conclude it is better to make it a C struct as per your suggestion. – Deepak Sharma Apr 29 '17 at 18:42
  • Actually, simple NSObjects are usually implemented as C structs. But you may want to declare simple instance variables instead of using properties (or hide the properties and methods from Swift), if you want to make sure Swift code doesn't try to access them via getters/setters. – hotpaw2 Apr 29 '17 at 18:47
  • They are simple vars, but public with no getters or setters. Some of the vars are declared are private(set) public var...Not sure if that would create a trouble, but to be safe I can declare all vars as public. In Objective-C, member variables are all vars so THIS->memberVar is simply like Struct. Swift there is no such thing as member vars, so not sure. – Deepak Sharma Apr 29 '17 at 19:06

1 Answers1

2

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.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153