i have an app which should record audio when a button i pressed.
In my ViewDidLoad i preapare the recorder, the problem is that streaming audio interrupts when the line 'self.audioRecorder.prepareToRecord()' is called.
My setup :
do {
recordingSession = AVAudioSession.sharedInstance()
try recordingSession.setCategory(AVAudioSessionCategoryRecord, withOptions: [.DuckOthers, .AllowBluetooth, .MixWithOthers])
recordingSession.requestRecordPermission() { [unowned self] (allowed: Bool) -> Void in
dispatch_async(dispatch_get_main_queue()) {
do {
if allowed {
self.audioRecorder = try AVAudioRecorder(URL: self.tempAudioPath, settings: self.settings)
self.audioRecorder.delegate = self
self.audioRecorder.prepareToRecord()
//self.audioRecorder.record()
} else {
// failed to record!
print("No Access to Micro")
}
}catch{}
}
}
} catch {
print (error)
}
is there a way to preapare the audio recorder for record, and continue to play audio in background ? (duck it when recording the audio)
Per Apple's documentation for AVAudioSessionCategoryRecord, "this category silences playback audio". Have you tried setting the category to AVAudioSessionCategoryPlayAndRecord?
Related
The main issue here is, when the microphone input is added, the device loses any haptic system sounds.
I setup the audio session here:
let audioSession = AVAudioSession.sharedInstance()
do {
try self.audioSession.setCategory(.playAndRecord, options: .mixWithOthers)
try self.audioSession.setAllowHapticsAndSystemSoundsDuringRecording(true)
try self.audioSession.setActive(true)
} catch { }
I make sure I am using setAllowHapticsAndSystemSoundsDuringRecording.
Though out the app, I am adding the microphone and removing it on-demand:
do {
let microphonePermission = AVCaptureDevice.authorizationStatus(for: AVMediaType.audio)
if microphonePermission != .denied && microphonePermission != .restricted {
let audioDevice = AVCaptureDevice.default(for: AVMediaType.audio)
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice!)
if self.session.canAddInput(audioDeviceInput) {
self.session.addInput(audioDeviceInput)
}
else { print("Could not add audio device input to the session.") }
} else {
}
}
catch { print("Could not create audio device input: \(error).") }
As soon as the microphone is added, it loses haptic feedback and system sounds.
I am trying to build an audio app for apple watch. But the problem is whenever I keep my hands down , audio will stop playing.
I have turned background mode on as well.
Can anyone please help me with this? I am stuck at this part.
Here is the Code I have used for playing audio.
func play(url : URL) {
do {
if #available(watchOSApplicationExtension 4.0, *) {
WKExtension.shared().isFrontmostTimeoutExtended = true
} else {
// Fallback on earlier versions
}
self.player = try AVAudioPlayer(contentsOf: url)
player!.prepareToPlay()
player?.delegate = self
player?.play()
print("-----------------")
print("Playing Audio")
print("*****************\nCurrent Time \(String(describing: self.player?.currentTime))")
} catch let error as NSError {
self.player = nil
print(error.localizedDescription)
} catch {
print("*************************")
print("AVAudioPlayer init failed")
}
}
Make sure you are trying to play with Audio Data, not Audio URL and have added policy: .longFormAudio in your category setup. As per Apple documentation, these two settings have to be set for audio to play in background mode.
// Set up the session.
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(
.playback,
mode: .default,
policy: .longFormAudio
)
} catch let error {
fatalError("*** Unable to set up the audio session: \(error.localizedDescription) ***")
}
// Set up the player.
let player: AVAudioPlayer
do {
player = try AVAudioPlayer(data: audioData)
} catch let error {
print("*** Unable to set up the audio player: \(error.localizedDescription) ***")
// Handle the error here.
return
}
// Activate and request the route.
session.activate(options: []) { (success, error) in
guard error == nil else {
print("*** An error occurred: \(error!.localizedDescription) ***")
// Handle the error here.
return
}
// Play the audio file.
player.play()
}
I have tested this code and its working with only Bluetooth connectivity in Watch application not in watch speaker.
Simply turning on background mode is not enough. You also need to activate the AVAudioSession.
It's all well documented by Apple here: Playing Background Audio.
Configure and Activate the Audio Session
Before you can play audio, you need to set up and activate the audio session.
session.setCategory(AVAudioSession.Category.playback,
mode: .default,
policy: .longForm,
options: [])
Next, activate the session, by calling the activate(options:completionHandler:) method.
session.activate(options: []) { (success, error) in
// Check for an error and play audio.
}
Ref: https://developer.apple.com/documentation/watchkit/playing_background_audio
Example:
var player: AVAudioPlayer?
let session: AVAudioSession = .sharedInstance()
func prepareSession() {
do {
try session.setCategory(AVAudioSession.Category.playback,
mode: .default,
policy: .longForm,
options: [])
}
catch {
print(error)
}
}
func play(url: URL) {
do {
player = try AVAudioPlayer(contentsOf: url)
}
catch {
print(error)
return
}
session.activate(options: []) { (success, error) in
guard error == nil else {
print(error!)
return
}
// Play the audio file
self.player?.play()
}
}
Simple Test:
prepareSession()
if let url = Bundle.main.url(forResource: "test", withExtension: "mp3") {
play(url: url)
}
else {
print("test.mp3 not found in project: put any mp3 file in and name it so")
}
I'm implementing video chat using webrtc. In that I want use the main speaker when the other participant joins the session. For that, I wrote this code, but I'm getting a low voice volume (means voice coming from ear speaker)
func audioSetting() {
RTCAudioSession.sharedInstance().isAudioEnabled = true
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSession.Category.playAndRecord, mode: .videoChat, options: [])
if self.speakerOn {
try session.overrideOutputAudioPort(.none)
}
else {
try session.overrideOutputAudioPort(.speaker)
}
try session.setActive(true)
self.speakerOn = !self.speakerOn
}
catch let error {
print("Couldn't set audio to speaker: \(error)")
}
}
I am working on webRTC with socket.IO,
func setSpeakerStates(enabled: Bool)
{
let session = AVAudioSession.sharedInstance()
var _: Error?
try? session.setCategory(AVAudioSession.Category.playAndRecord)
try? session.setMode(AVAudioSession.Mode.voiceChat)
if enabled {
try? session.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
} else {
try? session.overrideOutputAudioPort(AVAudioSession.PortOverride.none)
}
try? session.setActive(true)
}
Please try this method at the end of the viewdidload after adding audio streaming and video streaming.
I am creating an application using xcode 7.1, swift. I want to play an audio. Everything is fine. Now my problem I want to hear sound when the device in silent mode or muted. How can I do it?
I am using the following code to play audio
currentAudio!.stop()
currentAudio = try? AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("sample_audio", ofType: "mp3")!));
currentAudio!.currentTime = 0
currentAudio!.play();
Put this line before calling play() method of AVPlayer.
In Objective C
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error: nil];
In Swift
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch {
// report for an error
}
Swift 5
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
} catch(let error) {
print(error.localizedDescription)
}
You can use AppDelegate class.
For enable sound (for audio or video) when device is in silent mode use AVAudioSessionCategoryPlayback:
func applicationDidBecomeActive(_ application: UIApplication) {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch {
print("AVAudioSessionCategoryPlayback not work")
}
}
For disable sound when device is in silent mode (for example when we answer the phone call) use AVAudioSessionCategorySoloAmbient:
func applicationWillResignActive(_ application: UIApplication) {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
} catch {
print("AVAudioSessionCategorySoloAmbient not work")
}
}
Swift 4
use this line before play video
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
Swift 3.0 and Xcode > 8
Play sound in Video when device is on RINGER mode and SLIENT mode
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
//print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
//print("AVAudioSession is Active")
} catch _ as NSError {
//print(error.localizedDescription)
}
} catch _ as NSError {
//print(error.localizedDescription)
}
Swift 4.2
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
} catch let error {
print("Error in AVAudio Session\(error.localizedDescription)")
}
Swift 5.0.1
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
You can go through this, it will help you out
When you use following audio session categories, sounds will not be muted on iOS: AVAudioSessionCategoryPlayback,AVAudioSessionCategoryRecord,AVAudioSessionCategoryPlayAndRecord
Example
func playSound (Sound: String, Type: String) {
//Prepare the sound file name & extension
var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(Sound, ofType: Type)!)
//Preparation to play
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
//Play audio
var error: NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
import AVKit
And add this to your AppDelegate's applicationDidBecomeActive section
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
} catch {
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
}
I need to start playing sound when user closes app. I use applicationDidEnterBackground method. Here it is:
func applicationDidEnterBackground(application: UIApplication) {
let dispatchQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(dispatchQueue, {[weak self] in
var audioSessionError: NSError?
let audioSession = AVAudioSession.sharedInstance()
NSNotificationCenter.defaultCenter().addObserver(self!,
selector: "handleInterruption:",
name: AVAudioSessionInterruptionNotification,
object: nil)
audioSession.setActive(true, error: nil)
if audioSession.setCategory(AVAudioSessionCategoryPlayback,
error: &audioSessionError){
println("Successfully set the audio session")
} else {
println("Could not set the audio session")
}
let filePath = NSBundle.mainBundle().pathForResource("MySong",
ofType:"mp3")
let fileData = NSData(contentsOfFile: filePath!,
options: .DataReadingMappedIfSafe,
error: nil)
var error:NSError?
/* Start the audio player */
self!.audioPlayer = AVAudioPlayer(data: fileData, error: &error)
/* Did we get an instance of AVAudioPlayer? */
if let theAudioPlayer = self!.audioPlayer{
theAudioPlayer.delegate = self;
if theAudioPlayer.prepareToPlay() &&
theAudioPlayer.play(){
println("Successfully started playing")
} else {
println("Failed to play")
}
} else {
/* Handle the failure of instantiating the audio player */
}
})
}
func handleInterruption(notification: NSNotification){
/* Audio Session is interrupted. The player will be paused here */
let interruptionTypeAsObject =
notification.userInfo![AVAudioSessionInterruptionTypeKey] as! NSNumber
let interruptionType = AVAudioSessionInterruptionType(rawValue:
interruptionTypeAsObject.unsignedLongValue)
if let type = interruptionType{
if type == .Ended{
/* resume the audio if needed */
}
}
}
func audioPlayerDidFinishPlaying(player: AVAudioPlayer!,
successfully flag: Bool){
println("Finished playing the song")
/* The flag parameter tells us if the playback was successfully
finished or not */
if player == audioPlayer{
audioPlayer = nil
}
}
It does not work. After debugging I see that theAudioPlayer.play() returns false. If I run this code for example in applicationDidFinishLaunching it plays sound. I added background mode App plays audio or streams audio/video using AirPlay to my Info.plist What's wrong here?
At a very basic level there are three prerequisites your app should satisfy to play audio in the background:
Music must be playing
Setup ‘Background Modes’ for Audio in your Target Capabilities.
For basic playback, initialise your audio session, and set your audio session category to AVAudioSessionCategoryPlayback. If you don’t, you’ll get the default behaviour.
You should also configure your app to respond to
changes in audio output
audio session interruptions (e.g. phone call)
remote control events (via control centre, or the lock screen)
Check out this Swift example.