Audio ducking stops when AVAssetWriter.startWriting is called - ios

We have an app where we play some audio while ducking any sounds from other apps that might be playing (music, spotify, etc).
This works most of the time and external audio is ducked properly. However whenever we call on an AVAssetWriter instance to startWriting, the ducking stops entirely and never resumes. The only way to resume it seems to be stopping the session and restarting it, setting the category again doesn't do anything. Has anyone run into similar problems with ducking and have any solutions? Thanks!
...
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .spokenAudio, options: [.duckOthers, .interruptSpokenAudioAndMixWithOthers])
try AVAudioSession.sharedInstance().setActive(true)
...
self.assetWriter.startWriting()
...

Related

Swift Stop System Audio

I'm developing a Sleep Timer App. As soon as the timer is done, I want to stop the audio on the device. Is there any way to stop the system Sound or do I have to play a silent audio file?
I already tried this Code:
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
try AVAudioSession.sharedInstance().setActive(true)
} catch { print(error.localizedDescription) }
.playback will only pause non-mixable sessions. If the other app has a mixable session, then it will continue. The only mode that will pause mixable sessions is .record (which will require that you request recording permission, even though you're not going to use it).

How to create an iOS alarm clock that runs in the background properly

I would like to insert an alarm clock function in an iOS app I am developing, and as a reference, I installed a popular App called "Alarmy."
I managed to keep my app running in the background, just using AVAudioSession properties; however, I noticed that the app consumes a lot of battery during the phone sleep.
After some testing, I think this is due to the app activating the speakers (and keeping them ON) immediately after the AVAudioSession activation.
Even if there is no actual sound playing until the audioPlayer.play(atTime: audioPlayer.deviceCurrentTime + Double(seconds)) is triggered, if I get very very close to my iPhone 7 speakers, I can hear the little buzzing sound that indicates that the speakers are ON. This implicates that the speakers are playing an "empty sound" de facto.
This buzzing sound does not exist when I set the alarm with Alarmy; it just starts playing when it is supposed to.
I did not find any other way to maintain my app in the background and play an alarm sound at a specified time. There are Local Notifications, of course, but they do not allow to play a sound when the phone is silenced.
Going back to "Alarmy," I've seen that they are not only able to play a background alarm without any need to activate the speakers first, but they are also able to put the volume at the max level in the background. Are they maybe triggering some other iOS background mode to achieve those, perhaps using Background Fetch or Processing in some clever way? Is there any known way to replicate those behaviors?
Thanks in advance!
Here is the current code I use to set the alarm:
private func setNewAlarm(audioPlayer: AVAudioPlayer, seconds: Int, ringtone: String) {
do {
self.setNotificationAlarm(audioPlayer: audioPlayer, seconds: seconds, ringtone: ringtone, result: result)
//This calls the method I use to set a secondary alarm using local notifications, just in case the user closes the app
try AVAudioSession.sharedInstance().setActive(false)
try AVAudioSession.sharedInstance().setCategory(.playback, options: [ .mixWithOthers])
try AVAudioSession.sharedInstance().setActive(true)
} catch let error as NSError {
print("AVAudioSession error: \(error.localizedDescription)")
}
audioPlayer.prepareToPlay()
audioPlayer.play(atTime: audioPlayer.deviceCurrentTime + Double(seconds))
result(true)
}

AVAudioSession deactivate while another session is running

I have one audio session running in playAndRecord category and mixWithOthers option for a audio recorder. Let's call it recordAudioSession.
let recordAudioSession = AudioSession()
recordAudioSession.setCategory(.playAndRecord, options: [.mixWithOthers])
Also I have another audio session running in playback category and mixWithOthers option for playing audio stream. Let's call it playAudioSession.
let playAudioSession= AudioSession()
playAudioSession.setCategory(.playback, options: [.mixWithOthers])
I have one audio is playing through my playAudioSession using an AVAudioPlayer, at the same time there is a recorder is running through my recordAudioSession trying to record the audio.
Now, once audio finishes playing, I try to deactivate playAudioSession, but leave recordAudioSession alive:
audioPlayer.stop()
playAudioSession.setActive(false, options: .notifyOthersOnDeactivation)
So basically, One session finishes its task and I want to deactivate, the other one is still in use, so I want to keep it working.
But I'm getting an error log, and my recorder getting closed by system:
AVAudioSession.mm:692: -[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.
Is this error means any audio session cannot be running I/O while deactivating an audio session, or I'm doing it in an incorrect way?
Is there any way that I can keep that recorder reading and deactivate the playAudioSession?

Can I use ReplayKit to record both microphone and system audio?

There is not much documentation online about this because it's an odd task. I am trying to record my screen, the internal microphone, and the system audio at the same time using ReplayKit.
Here is how I am recording my screen right now:
if([self.screenRecorder isAvailable]){
[self.screenRecorder setMicrophoneEnabled:YES];
[self.screenRecorder startRecordingWithHandler:nil];
}
When this runs, the user is prompted to record with the microphone, or without the microphone. Could I possibly do both? Is there a workaround? If I choose microphone, when my app plays sound, the microphone gets disabled.
If anyone could propose a possible solution that does not involve replaykit, that would be greatly appreciated too!
Thanks
yes, it's possible, you can using AVAudioEngine which provide manual rendering mode, two playerNode (audio app, audio mic) into mixerNode and render.
So after looking into this you can also just do this very simply using the AVAudioSession API:
let audioSession = AVAudioSession.sharedInstance()
try! audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, with: AVAudioSessionCategoryOptions.mixWithOthers)

App get suspended from background state even if app playing a music

I have a music player application. Where user can listen music even the app is not in foreground as desire. Everything's work fine.
Now the problem is when user left the device idle music playing smoothly. But after two or three songs it's stop working. As far I noticed when a song ended it's trying to change the track. For that app needs to execute some business logic but it is not happening as system suspended the app. I tried the following.
Capabilities:
Audio session:
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("Failed to register")
}
Still it's not working. Am I missing something?
The problem is that you need to execute your business logic and start playing next track as quick as possible, once you finish a track in the background.
The iOS suspend your app if it's not playing audio in the background for about 3~4 seconds. If your business logic exceed this time limit, try UIApplication.shared.beginBackgroundTask before execute your logic.

Resources