【问题标题】:AURenderCallback in SwiftSwift 中的 AURenderCallback
【发布时间】:2016-02-16 09:15:11
【问题描述】:

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

我已经能够设置我的 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 之外什么都不做,因为我只是想让它工作。但是,编译器返回以下错误:

(UnsafeMutablePointer, 不安全可变指针, 不安全指针,UInt32,UInt32, UnsafeMutablePointer) -> OSStatus' 不可转换 到'AURenderCallback

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

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

这似乎与我输入的内容相同,但可能是我不明白文档的要求。

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

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

    return noErr
}

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

找不到类型“AURenderCallbackStruct”的初始化程序 接受类型为 '(inputProc: (不安全可变指针, 不安全可变指针, 不安全指针,UInt32,UInt32, UnsafeMutablePointer) -> OSStatus, inputProcRefCon: 无)

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

【问题讨论】:

    标签: ios xcode swift audiounit


    【解决方案1】:

    我刚刚在试图弄清楚相同的情况下找到了您的帖子(找到结合 CoreAudio/Audio Unit 和 Swift 的示例代码和示例并不容易)。

    通过查看this repository 并阅读(多次:-))Apples 关于Using Swift with Cocoa and Objective-C 的文档,我设法拼凑了一些东西。正如它在关于Function Pointers的部分所说的那样

    调用带有函数指针参数的函数时,您可以传递顶级 Swift 函数、闭包字面量或 nil。

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

    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
    }
    

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

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

    这样做使我能够通过遵守AURenderCallbackDelegate 来“回到我的班级”,如下所示:

    class AudioUnitGraphManager: NSObject, AURenderCallbackDelegate

    然后在我的AudioUnitGraphManager 类中实现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&lt;AudioUnitRenderActionFlags&gt;, UnsafePointer&lt;AudioTimeStamp&gt;, UInt32, UInt32, UnsafeMutablePointer&lt;AudioBufferList&gt;?) -&gt; OSStatus

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

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

    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())
    

    【讨论】:

    • 使用委托是一种有趣的方法。但是,当我尝试启用回调时收到以下错误:“找不到接受类型为“(UnsafeMutablePointer)”的参数列表的“UnsafeMutablePointer”类型的初始化程序。我已经四处寻找如何解决这个问题,但我很困惑。这些指针类型至少可以说是令人沮丧的。
    • 出于调试的目的,我尝试创建一个 AURenderCallbackStruct 对象,以便可以使用点语法单独定义参数。这工作正常:“renderCallbackStruct.inputProcRefCon = UnsafeMutablePointer(unsafeAddressOf(self))”但“renderCallbackStruct.inputProc = renderCallback”抛出现在熟悉的编译错误“无法分配类型'(UnsafeMutablePointer, UnsafeMutablePointer, UnsafePointer, UInt32, UInt32, UnsafeMutablePointer) -> OSStatus' 到 'AURenderCallback' 类型的值。”
    • 这很尴尬。我尝试编译您在上面链接的项目,在收到大量错误后,我注意到我正在运行 XCode 6.4。我升级到版本 7,它就像一个魅力。谢谢!
    • 嗨@PetrusTheron,哦哇,那是很久以前的事了 :) 我为我当时工作的公司制作了这个,代码不公开。但是,我提出了这个要点:gist.github.com/pbodsk/40bee78940385847b7e9780a70e2a937,其中应该包含……嗯……关于如何设置和使用它的要点(对不起!)。我从那里删除了一些东西,但我希望它对你有一点帮助。对不起,这是我能做的最好的了。
    • 还有@PetrusTheron,请注意 AUGraph 很快就会被弃用,例如:stackoverflow.com/questions/44952582/… 只是说如果可能的话,也许你应该考虑查看永远不会的AVAudioEngine跨度>
    【解决方案2】:

    我参加聚会有点晚了,但我找到了一种可能比上述答案更直接的方法。我们可以使用您可以在回调结构中设置的inRefCon 指针。 inRefCon 被传递给你的回调函数,所以如果你将inRefCon 设置为你的类引用,你可以直接“找到回到课堂的路”,而无需使用委托进行循环循环。我是这样做的:

    /* 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)))
    
    

    【讨论】:

      猜你喜欢
      • 2023-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-24
      • 2014-08-17
      • 2015-01-25
      • 2014-08-16
      相关资源
      最近更新 更多