How do I stop AudioKit Inputs and Outputs efficiently and definitively? - ios

I've been using Audiokit.stop() to reasonable effect, and some minor issues that I'd like to resolve now. With the recent update to Audiokit 4.2, I'm now getting a (very useful) error message from the following code:
do {
try AudioKit.stop()
} catch {
AKLog("AudioKit did not stop!")
}
My error code is this:
2018-04-24 16:14:48.099606+0100 MyProject[517:62626] [avas] AVAudioSession.mm:1142:
-[AVAudioSession setActive:withOptions:error:]: Deactivating an audio session that has
running I/O. All I/O should be stopped or paused prior to deactivating the audio session.
AudioKit.swift:stop():299:couldn't stop session
Error Domain=NSOSStatusErrorDomain Code=560030580 "(null)"
GameScene.swift:update:755:AudioKit did not stop!
So my question is - how do I properly stop or pause all inputs and outputs (I/O) as suggested by the error message?

Related

CHHapticEngine not running

After a day of playing with the CHHapticEngine and custom haptic patterns, using AHAP files in particular (including adding sound samples), I have lost the sound and haptics in the deivce / iPhone. After a factory reset of the iPhone, there are still no haptics / sound in the device (no ringer, no vibrations etc.). When trying to start the haptics engine and play any pattern I can see the following output in the Xcode debugger:
2020-04-29 19:44:31.034051+0200 ProjectName[18431:5043001] [hapi] CHHapticEngine.mm:2354:-[CHHapticEngine doStartWithCompletionHandler:]_block_invoke: ERROR: Player start failed: The operation couldn’t be completed. (com.apple.CoreHaptics error 1852797029.)
2020-04-29 19:44:31.034398+0200 ProjectName[18431:5042880] [Feedback] failed to start core haptics engine for <_UIFeedbackCoreHapticsEngine: 0x28268f170>: Error Domain=com.apple.CoreHaptics Code=1852797029 "(null)"
2020-04-29 19:44:34.076491+0200 ProjectName[18431:5043124] [hapi] CHHapticEngine.mm:2354:-[CHHapticEngine doStartWithCompletionHandler:]_block_invoke: ERROR: Player start failed: The operation couldn’t be completed. (com.apple.CoreHaptics error 1852797029.)
2020-04-29 19:44:34.076703+0200 ProjectName[18431:5043124] [hapi] HapticUtils.h:56:_Haptic_Check: -[CHHapticEngine checkEngineState:]: self.running error -4805
2020-04-29 19:44:34.077641+0200 ProjectName[18431:5043124] error = Error Domain=com.apple.CoreHaptics Code=-4805 "(null)" UserInfo={Error =self.running}
The question is, is it probable to damage the hardware from code?

Connecting bluetooth headphones while app is recording in the background causes the recording to stop

I am facing the following issue and hoping someone else encountered it and can offer a solution:
I am using AVAudioEngine to access the microphone. Until iOS 12.4, every time the audio route changed I was able to restart the AVAudioEngine graph to reconfigure it and ensure the input/output audio formats fit the new input/output route. Due to changes introduced in iOS 12.4 it is no longer possible to start (or restart for that matter) an AVAudioEngine graph while the app is backgrounded (unless it's after an interruption).
The error Apple now throw when I attempt this is:
2019-10-03 18:34:25.702143+0200 [1703:129720] [aurioc] 1590: AUIOClient_StartIO failed (561145187)
2019-10-03 18:34:25.702528+0200 [1703:129720] [avae] AVAEInternal.h:109 [AVAudioEngineGraph.mm:1544:Start: (err = PerformCommand(*ioNode, kAUStartIO, NULL, 0)): error 561145187
2019-10-03 18:34:25.711668+0200 [1703:129720] [Error] Unable to start audio engine The operation couldn’t be completed. (com.apple.coreaudio.avfaudio error 561145187.)
I'm guessing Apple closed a security vulnerability there. So now I removed the code to restart the graph when an audio route is changed (e.g. bluetooth headphones are connected).
It seems like when an I/O audio format changes (as happens when the user connects a bluetooth device), an .AVAudioEngingeConfigurationChange notification is fired, to allow the integrating app to react to the change in format. This is really what I should've used to handle changes in I/O formats from the beginning, instead of brute forcing restarting the graph. According to the Apple documentation - “When the audio engine’s I/O unit observes a change to the audio input or output hardware’s channel count or sample rate, the audio engine stops, uninitializes itself, and issues this notification.” (see the docs here). When this happens while the app is backgrounded, I am unable to start the audio engine with the correct audio i/o formats, because of point #1.
So bottom line, it looks like by closing a security vulnerability, Apple introduced a bug in reacting to audio I/O format changes while the app is backgrounded. Or am I missing something?
I'm attaching a code snippet to better describe the issue. For a plug-and-play AppDelegate see here - https://gist.github.com/nevosegal/5669ae8fb6f3fba44505543e43b5d54b.
class RCAudioEngine {
​
private let audioEngine = AVAudioEngine()
init() {
setup()
start()
NotificationCenter.default.addObserver(self, selector: #selector(handleConfigurationChange), name: .AVAudioEngineConfigurationChange, object: nil)
}
​
#objc func handleConfigurationChange() {
//attempt to call start()
//or to audioEngine.reset(), setup() and start()
//or any other combination that involves starting the audioEngine
//results in an error 561145187.
//Not calling start() doesn't return this error, but also doesn't restart
//the recording.
}
public func setup() {
​
//Setup nodes
let inputNode = audioEngine.inputNode
let inputFormat = inputNode.inputFormat(forBus: 0)
let mainMixerNode = audioEngine.mainMixerNode
​
//Mute output to avoid feedback
mainMixerNode.outputVolume = 0.0
​
inputNode.installTap(onBus: 0, bufferSize: 4096, format: inputFormat) { (buffer, _) -> Void in
//Do audio conversion and use buffers
}
}
​
public func start() {
RCLog.debug("Starting audio engine")
guard !audioEngine.isRunning else {
RCLog.debug("Audio Engine is already running")
return
}
​
do {
audioEngine.prepare()
try audioEngine.start()
} catch {
RCLog.error("Unable to start audio engine \(error.localizedDescription)")
}
}
}
I see only a fix that had gone into iOS 12.4. I am not sure if that causes the issue.
With the release notes https://developer.apple.com/documentation/ios_ipados_release_notes/ios_12_4_release_notes
"Resolved an issue where running an app in iOS 12.2 or later under the Leaks instrument resulted in random numbers of false-positive leaks for every leak check after the first one in a given run. You might still encounter this issue in Simulator, or in macOS apps when using Instruments from Xcode 10.2 and later. (48549361)"
You can raise issue with Apple , if you are a signed developer. They might help you if the defect is on their part.
You can also test with upcoming iOS release to check if your code works in the future release (with the apple beta program)

Starting AudioKit results in AudioHAL_Client errors 50% of the time

I've been using AudioKit for over 8 months, but recently I've run into a weird issue.
When I start AudioKit, in (roughly) 50% of the cases the audio stops playing after a few seconds and I get a stream of lower-level AudioHAL_Client errors:
2019-03-14 17:17:15.567027+0100 TestApp[68164:1626512] [AudioHAL_Client] HALC_ProxyIOContext.cpp:1399:IOWorkLoop: HALC_ProxyIOContext::IOWorkLoop: failed to send the final message to the server, Error: 0x10000003
2019-03-14 17:17:16.104180+0100 TestApp[68164:1626365] [AudioHAL_Client] HALC_ShellPlugIn.cpp:817:HAL_HardwarePlugIn_ObjectHasProperty: HAL_HardwarePlugIn_ObjectHasProperty: no object
or:
2019-03-15 08:15:33.756244+0100 macOSDevelopment[47186:2925180] [AudioHAL_Client] HALC_ProxyIOContext.cpp:1399:IOWorkLoop: HALC_ProxyIOContext::IOWorkLoop: failed to send the final message to the server, Error: 0x10000003
2019-03-15 08:15:34.290366+0100 macOSDevelopment[47186:2925038] [AudioHAL_Client] HALC_ShellPlugIn.cpp:817:HAL_HardwarePlugIn_ObjectHasProperty: HAL_HardwarePlugIn_ObjectHasProperty: no object
2019-03-15 08:15:34.290431+0100 macOSDevelopment[47186:2925038] [AudioHAL_Client] HALC_ShellPlugIn.cpp:817:HAL_HardwarePlugIn_ObjectHasProperty: HAL_HardwarePlugIn_ObjectHasProperty: no object
It is not related to my specific app, because when I build the AudioKit macOS development app, the same happens. I've also tried it with a clean macOS project.
This is enough to trigger the bug:
AudioKit.output = AKMixer()
AudioKit.start()
Same happens when I connect an AKOscillator instead of AKMixer.
I've tried to debug this, but I cannot figure out what's going wrong.

AudioSession maximumInputNumberOfChannels returning 0

I made an iOS plugin that captures audio data and forwards it along to a listener in the form of a byte stream. It was working flawlessly in an emulator and on various devices, but on an iPhone 6 running iOS 11.3 it is crashing during initialization. I've tracked the problem to this code:
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
try session.setPreferredInputNumberOfChannels(1) // This is the line that is throwing
try session.setPreferredIOBufferDuration(65)
} catch {
print(error.localizedDescription) // Prints: The operation couldn’t be completed. (OSStatus error -50.)
return -1
}
As the comment shows, the error is being caused by the call to session.setPreferredIOBufferDuration. Looking at the documentation, it says that the call will throw if the input number is greater than session.maximumInputNumberOfChannels, and judging from the error message, this seems to be the case. Checking that value on this phone, it is returning 0.
What would be causing that value to be 0? As far as I can tell, I don' think it's a permissions issue, as I request microphone permissions prior to the app reaching this point in the code. The only other thing I can think of is that the phone essentially has no microphone capabilities... but it's a phone, so the inclusion of a microphone seems fairly standard.
EDIT: I pulled out an iPad Air that's running iOS 12, and it's having the same issue.
I found the problem. I needed to add session.setActive(true) before trying to set the number of channels. I've never had to do that before, but I guess it's something you should do anyway just in case.
AVAudioSession.sharedInstance()
you can change it anyway,
search it?

Prime failed ('nope'); will stop (1/5925 frames)

When started the audioqueue work well, it can playback audio, but when I manually change the progess of player, AudioQueuePrime will fail ,output Prime failed ('nope'); will stop (1/5925 frames), the errorcode returned is 1852797029, but next AudioQueueStart will succeed, and so the player will work, then just AudioQueuePrime failed sometimes. So why?

Resources