AURenderCallback в Swift
Я создаю приложение, которое использует аудиоустройства, и хотя в Objective-C есть много примеров кода (в том числе собственный aurioTouch от Apple и другие), я пытаюсь все это кодировать в Swift.
Мне удалось настроить мой AUGraph и запустить через него звук, но я не могу понять синтаксис обратных вызовов рендеринга. Я попробовал несколько методов:
Способ 1. Создайте 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, UnsafeMutablePointer, UnsafePointer, 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
}
Это не дает никаких ошибок как функция, но когда я помещаю это в AURenderCallbackStruct в параметре inputProc, я получаю ошибку:
Не удается найти инициализатор для типа 'AURenderCallbackStruct', который принимает список аргументов типа '(inputProc: (UnsafeMutablePointer, UnsafeMutablePointer, UnsafePointer, UInt32, UInt32, UnsafeMutablePointer) -> OSStatus, inputProcRefCon: n>
Я не нашел много примеров создания AURenderCallbacks в Swift, и, кажется, существует большая разница в синтаксисе по сравнению с Objective-C. Любая помощь будет оценена.
2 ответа
Я только что нашел ваш пост, пытаясь выяснить то же самое (нелегко найти примеры кода и примеры, объединяющие CoreAudio/Audio Unit и Swift).
Посмотрев на этот репозиторий и прочитав (несколько раз:-)) документацию Apple об использовании Swift с Какао и 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
И затем реализация renderCallback
метод в моем AudioUnitGraphManager
учебный класс
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())
Я немного опоздал на вечеринку, но я нашел способ, который может быть более простым, чем приведенный выше ответ. Мы можем использовать указатель, который вы можете установить в callbackStruct. передается вашей функции обратного вызова, поэтому, если вы установите
/* 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)))