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.
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.