Problems resuming recording in the background using AVAudioRecorder in Swift

Viewed 139

To put it in context, I'm developing an app that is recording a lot of time while in background or in the app. I'm facing two problems using AVAudioRecorder to record in my app:

  • My recording resumes fine when, for example, I play an audio in WhatsApp or a video in Instagram. But when I play a song in Apple Music or a play a voice note, it doesn't resume the recording again.

  • If I'm in a Phone Call and I start the recording, my app crashes and outputs the following:

    (Error de OSStatus 561017449.)

  • I think also need to handle when the input/output device changes so my app doesn't crash.

The code that I implemented for the class where I'm recording (and also playing the sound I use recorder so I can try it and see if it works) is this:

class RecordViewController: UIViewController, AVAudioPlayerDelegate , AVAudioRecorderDelegate {


@IBOutlet weak var principalLabel: UILabel!
@IBOutlet weak var loginLabel: UILabel!
@IBOutlet weak var recordBTN: UIButton!
@IBOutlet weak var playBTN: UIButton!

var audioRecorder : AVAudioRecorder!
var audioPlayer : AVAudioPlayer!

var isAudioRecordingGranted: Bool!
var isRecording = false
var isPlaying = false

override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .white
    check_record_permission()
    checkActivateMicrophone()
    
    NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption(notification:)), name: AVAudioSession.interruptionNotification, object: audioRecorder)
    
    let tapRegister: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.goToLogin))
    loginLabel.addGestureRecognizer(tapRegister)
    loginLabel.isUserInteractionEnabled = true
}

@objc func goToLogin() {
    self.performSegue(withIdentifier: "goToLogin", sender: self)
}

@objc func handleInterruption(notification: Notification) {
    guard let userInfo = notification.userInfo,
        let typeInt = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
        let type = AVAudioSession.InterruptionType(rawValue: typeInt) else {
            return
    }

    switch type {
    
    case .began:
        
        if isRecording {
            print("OOOOOOO")
            audioRecorder.pause()
        }
        
        break
    case .ended:
        guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else { return }
        let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
        if options.contains(.shouldResume) {
            audioRecorder.record()
            print("AAAAAAA")
        } else {
            print("III")
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                self.audioRecorder.record()
            }
        }

    default: ()
    }
}

func check_record_permission()
{
    switch AVAudioSession.sharedInstance().recordPermission {
    case AVAudioSessionRecordPermission.granted:
        isAudioRecordingGranted = true
        break
    case AVAudioSessionRecordPermission.denied:
        isAudioRecordingGranted = false
        break
    case AVAudioSessionRecordPermission.undetermined:
        AVAudioSession.sharedInstance().requestRecordPermission({ (allowed) in
                if allowed {
                    self.isAudioRecordingGranted = true
                } else {
                    self.isAudioRecordingGranted = false
                }
        })
        break
    default:
        break
    }
}

func checkActivateMicrophone() {
    if !isAudioRecordingGranted {
        playBTN.isEnabled = false
        playBTN.alpha = 0.5
        recordBTN.isEnabled = false
        recordBTN.alpha = 0.5
        principalLabel.text = "Activa el micrófono desde ajustes"
    } else {
        playBTN.isEnabled = true
        playBTN.alpha = 1
        recordBTN.isEnabled = true
        recordBTN.alpha = 1
        principalLabel.text = "¡Prueba las grabaciones!"
    }
}

func getDocumentsDirectory() -> URL
{
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let documentsDirectory = paths[0]
    return documentsDirectory
}

func getFileUrl() -> URL
{
    let filename = "myRecording.m4a"
    let filePath = getDocumentsDirectory().appendingPathComponent(filename)
    return filePath
}

func setup_recorder()
{
    if isAudioRecordingGranted
    {
        let session = AVAudioSession.sharedInstance()
        do
        {
            try session.setCategory(AVAudioSession.Category.playAndRecord, options: .defaultToSpeaker)
            try session.setActive(true)
            let settings = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                AVSampleRateKey: 44100,
                AVNumberOfChannelsKey: 2,
                AVEncoderAudioQualityKey:AVAudioQuality.high.rawValue
            ]
            audioRecorder = try AVAudioRecorder(url: getFileUrl(), settings: settings)
            audioRecorder.delegate = self
            audioRecorder.isMeteringEnabled = true
            audioRecorder.prepareToRecord()
        }
        catch let error {
            print(error.localizedDescription)
        }
    }
    else
    {
        print("AAAAA")
    }
}


@IBAction func recordAct(_ sender: Any) {
            
    if(isRecording) {
        finishAudioRecording(success: true)
        recordBTN.setTitle("Record", for: .normal)
        playBTN.isEnabled = true
        isRecording = false
    }
    else
    {
        setup_recorder()
        audioRecorder.record()//aaaaaa
        recordBTN.setTitle("Stop", for: .normal)
        playBTN.isEnabled = false
        isRecording = true
    }
}

func finishAudioRecording(success: Bool)
{
    if success
    {
        audioRecorder.stop()
        audioRecorder = nil
        print("recorded successfully.")
    }
    else
    {
        print("Recording failed.")
    }
}


func prepare_play()
{
    do
    {
        audioPlayer = try AVAudioPlayer(contentsOf: getFileUrl())
        audioPlayer.delegate = self
        audioPlayer.prepareToPlay()
    }
    catch{
        print("Error")
    }
}

@IBAction func playAct(_ sender: Any) {
    
    if(isPlaying)
        {
            audioPlayer.stop()
            recordBTN.isEnabled = true
            playBTN.setTitle("Play", for: .normal)
            isPlaying = false
        }
        else
        {
            if FileManager.default.fileExists(atPath: getFileUrl().path)
            {
                recordBTN.isEnabled = false
                playBTN.setTitle("pause", for: .normal)
                prepare_play()
                audioPlayer.play()
                isPlaying = true
            }
            else
            {
                print("Audio file is missing.")
            }
        }
}

func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool)
{
    if !flag
    {
        print("UUU")
        finishAudioRecording(success: false)
    }
    playBTN.isEnabled = true
}

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool)
{
    recordBTN.isEnabled = true
}
}

I implemented the "Background Mode" of Audio, AirPlay and Picture in Picture

0 Answers
Related