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 用法的主要内容,如果未能解决你的问题,请参考以下文章

AVAudioUnit(渲染)回调

将 AudioUnit 回调与 NSOutputStream 同步

OSX:AudioUnit 回调没有被足够的调用

启动 AVAssetReader 停止对远程 I/O AudioUnit 的回调

MacOS Catalina 10.15.7 AudioUnit 麦克风通知回调未调用

在 AudioUnit 播放回调中从 AudioBuffer->mdata 播放音频