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)
Related
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.
I am creating an music player iOS app and getting data from firebase. I can able to control play, pause, next and previous in simulator or iPhone. While headset is connect to device play, next and previous functionalities are not working properly.
Here is the code which i've used;
func setupRemoteCommandCenter() {
// Get the shared MPRemoteCommandCenter
let commandCenter = MPRemoteCommandCenter.shared()
// Add handler for Play Command
commandCenter.playCommand.addTarget { event in
player?.play()
print("headset play")
return .success
}
// Add handler for Pause Command
commandCenter.pauseCommand.addTarget { event in
player?.pause()
print("headset pause")
return .success
}
// Add handler for Next Command
commandCenter.nextTrackCommand.addTarget { event in
return .success
}
// Add handler for Previous Command
commandCenter.previousTrackCommand.addTarget { event in
return .success
}
}
And calling setupRemoteCommandCenter function in viewdidload
This document says that to receive player event notifications you need to
begin playing audio
be the "Now playing app"
The definition of "Now playing app" is hard to pin down, but it seems to be any app that has an active, non-mixable audio session and is playing audio (or has very recently played audio, there seems to be a brief grace period here) . One possible non-mixable audio session is:
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayback)
try session.setActive(true)
} catch let err as NSError {
print("Error setting up non mixable audio session \(err)")
}
p.s. if you want the headset controls to work while the screen is locked (or if you want the lockscreen controls to work for that matter), you will need to add audio to Background Modes, because technically, if the screen is locked then your app has been backgrounded:
I have an app that needs to have:
Background music playing while using the app (eg. spotify)
Background music playing while watching movie from AVPlayer
Stop the music when recording a video
Like Snapchat, the camera-viewcontroller is part of a "swipeview" and therefore always on.
However, when opening and closing the app, the music makes a short "crack" noise/sound that ruins the music.
I recorded it here:
https://soundcloud.com/morten-stulen/hacky-sound-ios
(3 occurrences)
I use these settings for changing the AVAudiosession in the appdelegate didFinishLaunchingWithOptions:
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord,withOptions:
[AVAudioSessionCategoryOptions.MixWithOthers,
AVAudioSessionCategoryOptions.DefaultToSpeaker])
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("error")
}
I use the LLSimpleCamera control for video recording and I've set the session there to:
_session.automaticallyConfiguresApplicationAudioSession = NO;
It seems others have the same problem with other camera libraries as well:
https://github.com/rFlex/SCRecorder/issues/127
https://github.com/rFlex/SCRecorder/issues/224
This guy removed the audioDeviceInput, but I kinda need that for recording video.
https://github.com/omergul123/LLSimpleCamera/issues/48
I also tried with Apple's code "AvCam", and I still have the same issue. How does Snapchat do this?!
Any help would be greatly appreciated, and I'll gladly provide more info or code!
I do something similar to what you're wanting, but without the camera aspect, but I think this will do what you want. My app allows background audio that will mix with non-fullscreen video/audio. When the user plays an audio file or a full screen video file, I stop the background audio completely.
The reason I do SoloAmbient then Playback is because I allow my audio to be played in the background when the device is locked. Going SoloAmbient will stop all background music playing and then switching to Playback lets my audio play in the app as well as in the background.
This is why you see a call to a method that sets the lock screen information in the Unload method. In this case, it is nulling it out so that there is no lock screen info.
In AppDelegate.swift
//MARK: Audio Session Mixing
func allowBackgroundAudio()
{
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions: .MixWithOthers)
} catch {
NSLog("AVAudioSession SetCategory - Playback:MixWithOthers failed")
}
}
func preventBackgroundAudio()
{
do {
//Ask for Solo Ambient to prevent any background audio playing, then change to normal Playback so we can play while locked
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch {
NSLog("AVAudioSession SetCategory - SoloAmbient failed")
}
}
When I want to stop background audio, for example when playing an audio track that should be alone, I do the following:
In MyAudioPlayer.swift
func playUrl(url: NSURL?, backgroundImageUrl: NSURL?, title: String, subtitle: String)
{
ForgeHelper.appDelegate().preventBackgroundAudio()
if _mediaPlayer == nil {
self._mediaPlayer = MediaPlayer()
_mediaPlayer!.delegate = self
}
//... Code removed for brevity
}
And when I'm done with my media playing, I do this:
private func unloadMediaPlayer()
{
if _mediaPlayer != nil {
_mediaPlayer!.unload()
self._mediaPlayer = nil
}
_controlView.updateForProgress(0, duration: 0, animate: false)
ForgeHelper.appDelegate().allowBackgroundAudio()
setLockScreenInfo()
}
Hope this helps you out!
I am using wkWebView to display web content and videos
When the user switch the device ringer off i want the webView sound to switch off to
I tried this:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategorySoloAmbient)
try audioSession.setActive(true)
} catch _ {
print("error setting catagory!")
}
But it dont work - the ringer is off but music is still on
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)
}
}