Reputation: 9426
I'm attempting to connect an AVAudioUnitEffect
to an instance of AVAudioEngine
like so:
required init(inputFormat: AVAudioFormat, outputFormat: AVAudioFormat, andAVAudioEngine avAudioEngine:AVAudioEngine) {
self.inputFormat = inputFormat
self.outputFormat = outputFormat
self.avAudioEngine = avAudioEngine
self.myAudioUnit = MyAVAudioUnit()
super.init()
avAudioEngine.attach(myAudioUnit)
avAudioEngine.connect(myAudioUnit, to: avAudioEngine.outputNode, format: self.inputFormat)
}
The overarching class is simply a subclass of NSObject
and MyAudioUnit
is a subclass of AVAudioUnitEffect
.
At seemingly random times, the last line of this initializer (the call to connect
) will throw a SIGABRT with the following error: com.apple.coreaudio.avfaudio: error -10875
Which amounts to kAudioUnitErr_FailedInitialization
.
Can anyone shed some light on this error and what might be going on here? I thought that maybe the initializer for MyAVAudioUnit
was failing, but its internal initializer (init(audioComponentDescription: AudioComponentDescription)
) does not throw any errors and has a non-optional return type. Has anyone else had any experience with this particular error?
UPDATE
Here is the initialization of inputFormat
:
guard let stereoFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32,
sampleRate: 44100,
channels: 2,
interleaved: false) else {
return
}
let numChannels = UInt32(10)
guard let multiChannelLayout = AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_Unknown | numChannels) else {
return
}
inputFormat = AVAudioFormat(commonFormat: stereoFormat.commonFormat,
sampleRate: stereoFormat.sampleRate,
interleaved: stereoFormat.isInterleaved,
channelLayout: multiChannelLayout)
MyAVAudioUnit
contains one additional custom parameter (volumeParameter
) and is initialized as such:
required override init() {
var componentDescription = AudioComponentDescription()
componentDescription.componentType = kAudioUnitType_Effect
componentDescription.componentSubType = xxxxx
componentDescription.componentManufacturer = xxxxx
componentDescription.componentFlags = 0
componentDescription.componentFlagsMask = 0
AUAudioUnit.registerSubclass(MyAVAudioUnit.self,
as: componentDescription,
name:"MyAVAudioUnit",
version: UInt32.max)
super.init(audioComponentDescription: componentDescription)
guard let paramTree = self.auAudioUnit.parameterTree else { return }
volumeParameter = paramTree.value(forKey: "volumeParameter") as? AUParameter
}
Upvotes: 5
Views: 777
Reputation: 9426
Here's what ended up solving this issue. I noticed from crash logs that a print-out of the audio graph just before attempting to connect the audio unit showed this:
“ ________ GraphDescription ________ AVAudioEngineGraph 0x101538ba0: initialized = 1, running = 0, number of nodes = 23 ”
The interesting part is that, while running
is false, initialized
is true. There isn't much documentation on what this initialized
piece of information means, but I figured out that it shows true when the AVAudioEngine has been started before, but paused. Now, I wasn't explicitly calling avAudioEngine.pause()
from anywhere, but I supposed the system might be initiating the pause as part of AVAudioSessionRouteChangeNotification
(which is what I'm responding to in this whole series of events).
A lot of that is speculation, but it's clear that calling avAudioEngine.connect()
while a print-out of the engine shows initialized = 1
(or by inference, the engine is paused) will cause this crash.
The audio engine must be FULLY stopped before attempting to connect audio units. My problem was that my attempt to call avAudioEngine.stop()
was wrapped in an if
statement like so:
if avAudioEngine.isRunning {
avAudioEngine.stop()
}
Of course, this if
statement was skipped over because the audio engine was, in fact, not running. So stop()
was never called. Removing the if
statement and calling stop()
unquestionably sets initialized = 0
on the engine and allows connection without this crash.
Hope this helps someone else. Took me ages to unravel.
Upvotes: 5
Reputation: 70673
You say this happens at “random times” plural. That might mean you are doing this often, and possibly soon after releasing or de-initing the unit. Note that the audio unit subsystem runs in different completely asynchronous threads, and so the unit may still be in use by the OS (for a short period of time) after your app stops and releases the unit. If you put a long enough delay between any stop/release calls and any (re)inits, say perhaps one or two seconds, it may prevent this issue.
Upvotes: 1