AudioKit 4.9.3
iOS 11+
I am working on a project where the user is recording on the device using the microphone and it continues to record, even if the app is in the background. This works fine but when receiving a phone call I get an AudioKit error. I assume it has something to do with the phone taking over the mic or something. here is the error:
[avae] AVAEInternal.h:109
[AVAudioEngineGraph.mm:1544:Start: (err = PerformCommand(*ioNode,
kAUStartIO, NULL, 0)): error 561017449
AudioKit+StartStop.swift:restartEngineAfterRouteChange(_:):198:error
restarting engine after route change
basically everything that i have recording up until that point is lost.
here is my set up AudioKit code:
func configureAudioKit() {
AKSettings.audioInputEnabled = true
AKSettings.defaultToSpeaker = true
do {
try try audioSession.setCategory((AVAudioSession.Category.playAndRecord), options: AVAudioSession.CategoryOptions.mixWithOthers)
try audioSession.setActive(true)
audioSession.requestRecordPermission({ allowed in
DispatchQueue.main.async {
if allowed {
print("Audio recording session allowed")
self.configureAudioKitSession()
} else {
print("Audio recoding session not allowed")
}
}
})
} catch let error{
print("Audio recoding session not allowed: \(error.localizedDescription)")
}
}
func configureAudioKitSession() {
isMicPresent = AVAudioSession.sharedInstance().isInputAvailable
if !isMicPresent {
return
}
print("mic present and configuring audio session")
mic = AKMicrophone()
do{
let _ = try AKNodeRecorder(node: mic)
let recorderGain = AKBooster(mic, gain: 0)
AudioKit.output = recorderGain
//try AudioKit.start()
}
catch let error{
print("configure audioKit error: ", error)
}
}
and when tapping on the record button code:
do {
audioRecorder = try AVAudioRecorder(url: actualRecordingPath, settings: audioSettings)
audioRecorder?.record()
//print("Recording: \(isRecording)")
do{
try AudioKit.start()
}
catch let error{
print("Cannot start AudioKit", error.localizedDescription)
}
}
Current audio Settings:
private let audioSettings = [
AVFormatIDKey : Int(kAudioFormatMPEG4AAC),
AVSampleRateKey : 44100,
AVNumberOfChannelsKey : 2,
AVEncoderAudioQualityKey : AVAudioQuality.medium.rawValue
]
What can I do to ensure that I can get a proper recording, even when receiving a phone call? The error happens as soon as you receive the call - whether you choose to answer it or decline.
Any thoughts?
I've done work in this area, I'm afraid you cannot access the microphone(s) while a call or a VOIP call is in progress.
This is a basic privacy measure that is enforced by iOS for self-evident reasons.
AudioKit handles only the basic route change handling for an audio playback app. We've found that when an app becomes sufficiently complex, the framework can't effectively predestine the appropriate course of action when interruptions occur. So, I would suggest turning off AudioKit's route change handling and respond to the notifications yourself.
Also, I would putting AudioKit activation code in a button.
Related
I am experiencing no haptic output while testing the code below on my physical device (iPhone XR). I followed Apple Developer's "Playing a Single-tap Haptic Pattern" article, as well as double-checked with various other articles on the internet, and I am confident I have implemented it correctly. Moreover, there are no errors that are caught when running. What could be the reason why my device is not outputting the haptic pattern?
Side Notes:
I can confirm that my phone does produce haptics for other apps, so it does not appear to be an issue with my physical device.
I also implement an AVAudioPlayer separately; I doubt that would interfere, but I thought I'd mention it just in case.
Any help would be much appreciated--thanks!
var hapticEngine: CHHapticEngine!
var hapticPlayer: CHHapticPatternPlayer!
override func viewDidLoad() {
super.viewDidLoad()
// Create and configure a haptic engine.
do {
hapticEngine = try CHHapticEngine()
} catch let error {
fatalError("Engine Creation Error: \(error)")
}
// The reset handler provides an opportunity to restart the engine.
hapticEngine.resetHandler = {
print("Reset Handler: Restarting the engine.")
do {
// Try restarting the engine.
try self.hapticEngine.start()
// Register any custom resources you had registered, using registerAudioResource.
// Recreate all haptic pattern players you had created, using createPlayer.
} catch let error {
fatalError("Failed to restart the engine: \(error.localizedDescription)")
}
}
// The stopped handler alerts engine stoppage.
hapticEngine.stoppedHandler = { reason in
print("Stop Handler: The engine stopped for reason: \(reason.rawValue)")
switch reason {
case .audioSessionInterrupt: print("Audio session interrupt")
case .applicationSuspended: print("Application suspended")
case .idleTimeout: print("Idle timeout")
case .systemError: print("System error")
#unknown default:
print("Unknown error")
}
}
// Create haptic dictionary
let hapticDict = [
CHHapticPattern.Key.pattern: [
[
CHHapticPattern.Key.event: [
CHHapticPattern.Key.eventType: CHHapticEvent.EventType.hapticTransient,
CHHapticPattern.Key.time: 0.001,
CHHapticPattern.Key.eventDuration: 1.0
]
]
]
]
// Create haptic pattern from haptic dictionary, then add it to the haptic player
do {
let pattern = try CHHapticPattern(dictionary: hapticDict)
hapticPlayer = try hapticEngine.makePlayer(with: pattern)
} catch let error {
print("Failed to create hapticPlayer: \(error.localizedDescription)")
}
// ...
}
// ...
func playHaptic() {
//audioPlayer?.play()
// Start Haptic Engine
do {
try hapticEngine.start()
} catch let error {
print("Haptic Engine could not start: \(error.localizedDescription)")
}
// Start Haptic Player
do {
try hapticPlayer.start(atTime: 0.0)
print("Why")
} catch let error {
print("Haptic Player could not start: \(error.localizedDescription)")
}
// Stop Haptic Engine
hapticEngine.stop(completionHandler: nil)
}
The problem is this line:
hapticEngine.stop(completionHandler: nil)
Delete it and all will be well. You're stopping your "sound" the very instant it is trying to get started. That doesn't make much sense.
(Of course I am also assuming that somewhere there is code that actually calls your playHaptic method. You didn't show that code, but I'm just guessing that you probably remembered to include some. Having made sure that happens, I ran your code with the stop line commented out, and I definitely felt and heard the "pop" of the taptic tap.)
I have a VOIP app implemented using the Sinch SDK and CallKit. Everything works fine, apart from when the device has headphones plugged in. In the latter case, when the call starts, audio is still routed through the main speaker of the device. If I unplug and plug the headphones back in - during the call -, audio is then correctly routed to the headphones.
All I am doing is
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
guard let c = self.currentCall else {
action.fail()
return
}
c.answer()
self.communicationClient.audioController().configureAudioSessionForCallKitCall()
action.fulfill()
}
Shouldn't this be taken care automatically by the OS?
It seems like the Sinch SDK overrides the output audio port. Try to run this code just after the audio session has been configured:
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
} catch {
print("OverrideOutputAudioPort failed: \(error)")
}
If it doesn't work, try to configure the audio session by yourself, instead of relying on Sinch SDK if you can. Replace the configureAudioSessionForCallKitCall call with something like this:
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(
.playAndRecord,
mode: .voiceChat,
options: [.allowBluetooth, .allowBluetoothA2DP])
try session.setActive(true)
} catch {
print("Unable to activate audio session: \(error)")
}
Is there an audioManager class in iOS as in Android so that I can set my phone call in speaker mode automatically?
audioManager = AudioManager()
audioManager.getSystemService(Context.AUDIO_SERVICE)
audioManager.setMode(AudioManager.MODE_IN_CALL)
audioManager.setSpeakerphoneOn(true)
You can change the output route of the current audio temporarily using the following method
do {
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
//Set the category as per your requirement
try audioSession.overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
// or AVAudioSessionPortOverride.none to disable the speaker
}
catch let error{
print(error)
}
I have an app that records audio and video using AVFoundation. I want any audio playback from other apps (or the system) to continue while recording audio in my app, but when adding the input to the session an already playing audio from another app gets magically muted.
The corresponding code looks like this:
// set up audio device
if(m_audioenabled) {
print("Audio enabled")
if self.m_audioDevice != nil {
do {
let audioinput = try AVCaptureDeviceInput(device: self.m_audioDevice!)
if self.m_session.canAddInput(audioinput) {
self.m_session.addInput(audioinput)
}
} catch _ {
print("failed adding audio capture device as input to capture session")
}
}
m_audioDataOutput = AVCaptureAudioDataOutput()
m_audioDataOutput?.setSampleBufferDelegate(self, queue: self.m_captureSessionQueueAudio)
if self.m_session.canAddOutput(m_audioDataOutput!){
self.m_session.addOutput(m_audioDataOutput!)
}
}
If I comment out the call to canAddInput(...) the audio keeps playing, when I call it, audio playback gets muted.
How can I disable that behavior ?
Please note the migrating to another Audio-API is not an option.
Sounds like your AVAudioSession is non-mixable (the default, I think). Try activating a mixable audio session before you set up the AVCaptureSession:
let session = AVAudioSession.sharedInstance()
try! session.setCategory(AVAudioSessionCategoryPlayAndRecord, with: [.mixWithOthers])
try! session.setActive(true)
I am using an AVAudioEngine object, that has many AVAudioPlayerNodes attached to it. The audio all works fine, except it stops any audio that the iPhone is playing in the background (i. e. from iTunes or another music app).
When my app is opened, it stops any other background audio. Is there a way to allow background audio to continue to play? Even when my app is using AVAudioPlayerNodes to play audio itself?
Music App has it's own audioSession, that makes audio engine stops, i had that problem too, please restart after music app.
func stepB_startEngine(){
if engine.running == false {
do { try engine.start() }
catch let error {
print(error)
}
}
}
setup audioSettion also:
func setUpAudioSession(){
do {
try AVAudioSession.sharedInstance().setCategory(.ambient, options: .mixWithOthers)
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
} catch let error {
print(error)
}
}