AudioUnit Render 回调中的 Objective-C/Swift 用法
Posted
技术标签:
【中文标题】AudioUnit Render 回调中的 Objective-C/Swift 用法【英文标题】:Objective-C/Swift usage in AudioUnit Render callback 【发布时间】:2017-04-28 13:00:30 【问题描述】:Apple 建议不要在 AudioUnit 输入或渲染回调中使用 Objective-C 和 Swift。所以回调主要是用 C 语言编写的,我们可以快速复制数据并将该数据传递给辅助线程进行处理。为了在 C 和 Objective C 之间共享变量,建议使用 inRefCon 作为指向 Controller 对象的指针,然后访问它的变量。在下面的代码中,我访问了 MyController 对象的两个成员变量 - muteAudio 和回调队列。这不是完整的代码,只是一个突出问题的示例。
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;
我的问题:
一个。诸如 THIS->silentAudio 之类的访问 MyController 实例成员变量的调用与访问属性的 Objective C 消息传递有何不同?我的理解是像 THIS->silentAudio 这样的调用实际上是在指针位置直接访问内存,而不是 Objective C 消息传递,但只是为了确认这是否正确。
b.如何将上述 (THIS->silentAudio) 准确地翻译成 Swift? Swift 运行时是否会像 Objective C 一样使用指针而不是消息传递来访问 MyController 的私有变量?
c。在 Swift 的情况下,隐式/显式展开能否对性能产生影响?
请原谅我对 Swift 的了解,因为我是新手。
编辑:根据hotpaw2的回答,我将代码翻译如下:
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;
我现在的疑问是 controller.muteAudio 是否确实被 Swift 编译器翻译为访问内存 baseAddress + offset 或者它可以是 Swift 消息传递。
【问题讨论】:
控制器是 C 结构吗?然后确定。如果没有,Swift 编译器不会对访问做出这样的保证。 不,它不是 C 结构,我把它做成了一个继承自 NSObject 的类。所以我的结论是最好按照你的建议把它变成一个 C 结构。 实际上,简单的 NSObjects 通常被实现为 C 结构体。但是,如果您想确保 Swift 代码不会尝试通过 getter/setter 访问它们,您可能想要声明简单的实例变量而不是使用属性(或从 Swift 中隐藏属性和方法)。 它们是简单的变量,但是没有 getter 或 setter 的公共变量。一些变量被声明为私有(设置)公共变量......不确定这是否会造成麻烦,但为了安全起见,我可以将所有变量声明为公共变量。在 Objective-C 中,成员变量都是 var,所以 THIS->memberVar 就像 Struct 一样。 Swift 没有成员变量这样的东西,所以不确定。 【参考方案1】:C 代码不会通过 Objective C 运行时调用 Objective C 消息(除非显式使用传递的指向消息函数的指针)。但是 Xcode C 编译器确实知道 Objective C 对象的内存布局;所以 (THIS->silentAudio) 在 C 中编译为基指针 + 偏移内存访问代码。
您可以通过在 C 结构指针上使用 UnsafeRawPointers、UnsafeMutableRawPointers 和 unsafeBitCasts 在 Swift 中获得等效的基指针 + 偏移访问。 Wrapped 在处理不安全指针时没有任何意义,您需要自己检查它们的有效性。
您可能需要使用 C 结构或 Objective C 对象来与 Swift 代码共享数据结构,因为非 Swift 语言编译器可能不知道或无法使用 Swift 对象的布局。而 Swift 编译器可以通过使用桥接头以另一种方式计算内存布局,它可以包含 C 结构和 Obj C 类和对象定义。
【讨论】:
为了访问object的成员变量,使用unsafeBitCasts和assumeMemoryBound:有什么区别吗? 我已经用编辑更新了这个问题,请清除使用 unsafeBitCasts 的疑问。以上是关于AudioUnit Render 回调中的 Objective-C/Swift 用法的主要内容,如果未能解决你的问题,请参考以下文章
将 AudioUnit 回调与 NSOutputStream 同步
启动 AVAssetReader 停止对远程 I/O AudioUnit 的回调