Swift 中的 AURenderCallback

AURenderCallback in Swift

我正在创建一个使用音频单元的应用程序,虽然 Objective-C 中有很多代码示例(包括 Apple 自己的 aurioTouch 和其他),但我正试图在 [=41= 中编写整个代码].

我已经能够通过它设置我的 AUGraph 和 运行 一些音频,但我似乎无法弄清楚渲染回调的语法。我尝试了几种方法:

方法一:直接创建AURenderCallback

let render : AURenderCallback = { (
    inRefCon: UnsafeMutablePointer<Void>,
    ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBufNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus in

    return noErr
}

我在这个回调中除了返回 noErr 之外什么都不做,因为我只是想让它工作。但是,编译returns出现如下错误:

(UnsafeMutablePointer, UnsafeMutablePointer, UnsafePointer, UInt32, UInt32, UnsafeMutablePointer) -> OSStatus' is not convertible to 'AURenderCallback

文档中AURenderCallback的定义是这样的:

typealias AURenderCallback = (UnsafeMutablePointer<Void>,UnsafeMutablePointer<AudioUnitRenderActionFlags>,UnsafePointer<AudioTimeStamp>, UInt32, UInt32,UnsafeMutablePointer<AudioBufferList>) -> OSStatus

好像和我输入的一样,虽然可能是我不明白文档要求什么。

方法二:创建一个代表AURenderCallback的函数

func render(
    inRefCon: UnsafeMutablePointer<Void>,
    ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBufNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {

    return noErr
}

这不会作为函数给出任何错误,但是当我将它放入 inputProc 参数中的 AURenderCallbackStruct 时,我收到一个错误:

Cannot find an initializer for type 'AURenderCallbackStruct' that accepts an argument list of type '(inputProc: (UnsafeMutablePointer, UnsafeMutablePointer, UnsafePointer, UInt32, UInt32, UnsafeMutablePointer) -> OSStatus, inputProcRefCon: nil)

我在Swift中没有找到很多创建AURenderCallbacks的例子,而且和Objective-C相比语法上似乎有很大差异。任何帮助将不胜感激。

我刚刚找到你的 post,同时试图找出相同的(找到示例代码和结合 CoreAudio/Audio 单元和 Swift 的示例并不容易)。

通过查看 this repository and reading (several times :-)) Apples documentation about Using Swift with Cocoa and Objective-C,我设法拼凑了一些东西。正如Function Pointers

部分所说

When calling a function that takes a function pointer argument, you can pass a top-level Swift function, a closure literal, or nil.

所以。在我的 class 之外,我有一个看起来像这样的方法:

func renderCallback(inRefCon:UnsafeMutablePointer<Void>,
ioActionFlags:UnsafeMutablePointer<AudioUnitRenderActionFlags>,
inTimeStamp:UnsafePointer<AudioTimeStamp>,
inBusNumber:UInt32,
inNumberFrames:UInt32,
ioData:UnsafeMutablePointer<AudioBufferList>) -> OSStatus {
    let delegate = unsafeBitCast(inRefCon, AURenderCallbackDelegate.self)
    let result = delegate.performRender(ioActionFlags,
        inTimeStamp: inTimeStamp,
        inBusNumber: inBusNumber,
        inNumberFrames: inNumberFrames,
        ioData: ioData)
    return result
}

如您所见,我只是在这里调用了一个代理。那个委托是这样声明的(也在 class 之外,但你已经知道了:-))

@objc protocol AURenderCallbackDelegate {
func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBusNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus
}

这样做使我能够 "get back inside my class" 通过符合 AURenderCallbackDelegate 像这样:

class AudioUnitGraphManager: NSObject, AURenderCallbackDelegate

然后在我的 AudioUnitGraphManager class

中实现 renderCallback 方法
func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>, inTimeStamp: UnsafePointer<AudioTimeStamp>, inBusNumber: UInt32, inNumberFrames: UInt32, ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {
    print("Hello there!")
    return noErr
}

最后一块拼图是实际启用我喜欢的渲染通知回调:

AudioUnitAddRenderNotify(mixerUnit, renderCallback, UnsafeMutablePointer(unsafeAddressOf(self)))

希望这能给你继续奋斗的动力。

变化 Swift 3

在 Swift 3 中 AURenderCallback 的声明已更改为:

typealias AURenderCallback = (UnsafeMutableRawPointer, UnsafeMutablePointer<AudioUnitRenderActionFlags>, UnsafePointer<AudioTimeStamp>, UInt32, UInt32, UnsafeMutablePointer<AudioBufferList>?) -> OSStatus

请注意,与之前的 UnsafeMutablePointer<AudioBufferList> 相比,最后一个参数现在是 UnsafeMutablePointer<AudioBufferList>?(现在是可选参数)。

这意味着代码现在看起来像这样。

renderCallback函数

func renderCallback(inRefCon:UnsafeMutablePointer<Void>,
ioActionFlags:UnsafeMutablePointer<AudioUnitRenderActionFlags>,
inTimeStamp:UnsafePointer<AudioTimeStamp>,
inBusNumber:UInt32,
inNumberFrames:UInt32,
ioData:UnsafeMutablePointer<AudioBufferList>?) -> OSStatus {
    let delegate = unsafeBitCast(inRefCon, AURenderCallbackDelegate.self)
    let result = delegate.performRender(ioActionFlags,
        inTimeStamp: inTimeStamp,
        inBusNumber: inBusNumber,
        inNumberFrames: inNumberFrames,
        ioData: ioData)
    return result
}

AURenderCallbackDelegate 协议

@objc protocol AURenderCallbackDelegate {
func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
    inTimeStamp: UnsafePointer<AudioTimeStamp>,
    inBusNumber: UInt32,
    inNumberFrames: UInt32,
    ioData: UnsafeMutablePointer<AudioBufferList>?) -> OSStatus
}

实际执行performRender

    func performRender(ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>, inTimeStamp: UnsafePointer<AudioTimeStamp>, inBusNumber: UInt32, inNumberFrames: UInt32, ioData: UnsafeMutablePointer<AudioBufferList>?) -> OSStatus {
    print("Hello there!")
    return noErr
}

启用渲染通知回调

AudioUnitAddRenderNotify(mixerUnit!, renderCallback, Unmanaged.passUnretained(self).toOpaque())

我来晚了一点,但我找到了一种可能比上述答案更直接的方法。我们可以使用您可以在回调结构中设置的 inRefCon 指针。 inRefCon 被传递给您的回调函数,因此如果您将 inRefCon 设置为您的 class 引用,您可以直接“找到回到 class 的方式”而无需去loopdyloop 与代表。这是我的做法:

/* below code is inside some function in MyClass */ 

var callbackStruct = AURenderCallbackStruct()

// set inRefCon to reference to self by casting to pointer
callbackStruct.inputProcRefCon = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())

// create our C closure
callbackStruct.inputProc = {
             (inRefCon : UnsafeMutableRawPointer,
              ioActionFlags : UnsafeMutablePointer<AudioUnitRenderActionFlags>,
              inTimeStamp : UnsafePointer<AudioTimeStamp>,
              inBusNumber : UInt32,
              inNumberFrames : UInt32,
              ioData : UnsafeMutablePointer<AudioBufferList>?) -> OSStatus in
            
            // get reference to my class by de-referencing inRefCon
            let _self = Unmanaged<MyClass>.fromOpaque(inRefCon).takeUnretainedValue()
            // time to profit with reference to our class _self
            // ...
            return 0
}

// set callback
AudioUnitSetProperty(audioUnit!,
                     kAudioUnitProperty_SetRenderCallback,
                     kAudioUnitScope_Global,
                     0,
                     &callbackStruct,
                     UInt32(MemoryLayout.size(ofValue: callbackStruct)))