首页 > 解决方案 > iOS Swift:切换到耳机或从耳机切换时录音机中断

问题描述

我正在使用以下代码在 iOS Swift 应用程序中录制 5 秒的声音样本。该代码可以正常录制,并且如果插入耳机,也可以正确识别耳机。但是,如果在录制过程中添加或移除耳机,则录制会中断。我想知道为什么会这样,否则它运行顺利?

import AVFoundation

class RecorderViewController: UIViewController {

var recorder:          AVAudioRecorder!
var uploadObjectSuccess = false

@IBOutlet weak var recordButton:       UIButton!
@IBOutlet weak var statusLabel:        UILabel!
@IBOutlet weak var progressView:       UIProgressView!

var meterTimer:      Timer!
var soundFileURL:    URL!

override func viewDidLoad() {
    super.viewDidLoad()

    setSessionPlayback()
    askForNotifications()
    checkHeadphones()
}

func updateAudioMeter(_ timer:Timer) {
  self.progressView.setProgress(0, animated: false)
  if recorder != nil && recorder.isRecording {
        let sec = Int(recorder.currentTime.truncatingRemainder(dividingBy: 60))
        let s = String(format: "%02d", sec)
        statusLabel.text = s
        recorder.updateMeters()
        self.progressView.setProgress(Float(sec*4), animated: true)
    if (sec==6) && recorder != nil {
        func aux()->Bool {
            recorder.stop()
            return true
        }
        if aux()==true {
            if self.soundFileURL != nil {
            self.uploadObjectSuccess          = false
            let path                 = Auth.auth().currentUser?.uid
            let FIRStoragePath       = Storage.storage().reference().child(path!).child(FileName!)
                let uploadObject = FIRStoragePath.putFile(from: self.soundFileURL!, metadata: nil)
                uploadObject.observe(.success) { snapshot in
                    // Upload completed successfully
                    self.uploadObjectSuccess = true
                }
                uploadObject.observe(.failure) { snapshot in
                    // Upload failed
                    self.uploadObjectSuccess = true
                }
            }
        }
    }
  } else if (uploadObjectSuccess==true) {
        self.statusLabel.text             = "00"
        self.recordButton.isEnabled       = true
        isPlaying = false
        self.uploadObjectSuccess          = false
  } else {
        self.statusLabel.text             = "00"
  }
}

@IBAction func record(_ sender: Any) {

    if recorder != nil {
        recorder.stop()
    }
    recordButton.isEnabled = false

    recordWithPermission(true)
}

func setupRecorder() {
    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    self.soundFileURL = documentsDirectory.appendingPathComponent(self.currentFileName) //appendingPathComponent(currentFileName)
    if FileManager.default.fileExists(atPath: soundFileURL.absoluteString) {
        do {
            try FileManager.default.removeItem(atPath: self.soundFileURL.absoluteString)
        } catch let error as NSError {
            print("Ooops! Something went wrong: \(error)")
        }
    }

    let recordSettings:[String : AnyObject] = [
        AVFormatIDKey:             NSNumber(value:(kAudioFormatMPEG4AAC)),
        AVEncoderAudioQualityKey : NSNumber(value:AVAudioQuality.min.rawValue),
        //AVEncoderBitRateKey :      NSNumber(value:16),
        AVNumberOfChannelsKey:     NSNumber(value:1),
        AVSampleRateKey :          NSNumber(value:16000.0)
    ]

    do {
        recorder = try AVAudioRecorder(url: soundFileURL, settings: recordSettings)
        recorder.delegate = self
        recorder.isMeteringEnabled = true
        recorder.prepareToRecord() // creates/overwrites the file at soundFileURL
    } catch let error as NSError {
        recorder = nil
        print(error.localizedDescription)
    }
}

func recordWithPermission(_ setup:Bool) {

    checkHeadphones()
    setSessionPlayback()
    askForNotifications()

    let session:AVAudioSession = AVAudioSession.sharedInstance()

    // ios 8 and later
    if (session.responds(to: #selector(AVAudioSession.requestRecordPermission(_:)))) {
        AVAudioSession.sharedInstance().requestRecordPermission({(granted: Bool)-> Void in
            if granted {
                print("Permission to record granted")
                self.setSessionPlayAndRecord()
                if setup {
                    self.setupRecorder()
                }
                self.recorder.record()
                self.meterTimer = Timer.scheduledTimer(timeInterval: 0.1,
                    target:self,
                    selector:#selector(RecorderViewController.updateAudioMeter(_:)),
                    userInfo:nil,
                    repeats:true)
            } else {
                print("Permission to record not granted")

                self.statusLabel.text             = "00"
                self.recordButton.isEnabled       = true
                self.uploadObjectSuccess          = false
                self.recorder.stop()
            }
        })
    } else {
                print("requestRecordPermission unrecognized")

                self.statusLabel.text             = "00"
                self.recordButton.isEnabled       = true
                self.uploadObjectSuccess          = false
                self.recorder.stop()
    }
}

func setSessionPlayback() {
    let session:AVAudioSession = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(AVAudioSessionCategoryPlayback)
    } catch let error as NSError {
        print("could not set session category")
        print(error.localizedDescription)
    }
    do {
        try session.setActive(true)
    } catch let error as NSError {
        print("could not make session active")
        print(error.localizedDescription)
    }
}

func setSessionPlayAndRecord() {
    let session = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
    } catch let error as NSError {
        print("could not set session category")
        print(error.localizedDescription)
    }
    do {
        try session.setActive(true)
    } catch let error as NSError {
        print("could not make session active")
        print(error.localizedDescription)
    }
}

func askForNotifications() {
    NotificationCenter.default.addObserver(self,
        selector:#selector(RecorderViewController.background(_:)),
        name:NSNotification.Name.UIApplicationWillResignActive,
        object:nil)

    NotificationCenter.default.addObserver(self,
        selector:#selector(RecorderViewController.foreground(_:)),
        name:NSNotification.Name.UIApplicationWillEnterForeground,
        object:nil)

    NotificationCenter.default.addObserver(self,
        selector:#selector(RecorderViewController.routeChange(_:)),
        name:NSNotification.Name.AVAudioSessionRouteChange,
        object:nil)
}

@objc func background(_ notification:Notification) {
    print("background")

    if recorder != nil {
        recorder.stop()
    }
}

@objc func foreground(_ notification:Notification) {
    print("foreground")
}

@objc func routeChange(_ notification:Notification) {
    print("routeChange \(String(describing: (notification as NSNotification).userInfo))")

    if let userInfo = (notification as NSNotification).userInfo {
        //print("userInfo \(userInfo)")
        if let reason = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt {
            //print("reason \(reason)")
            switch AVAudioSessionRouteChangeReason(rawValue: reason)! {
            case AVAudioSessionRouteChangeReason.newDeviceAvailable:
                print("NewDeviceAvailable")
                print("did you plug in headphones?")
                checkHeadphones()
            case AVAudioSessionRouteChangeReason.oldDeviceUnavailable:
                print("OldDeviceUnavailable")
                print("did you unplug headphones?")
                checkHeadphones()
            case AVAudioSessionRouteChangeReason.categoryChange:
                print("CategoryChange")
            case AVAudioSessionRouteChangeReason.override:
                print("Override")
            case AVAudioSessionRouteChangeReason.wakeFromSleep:
                print("WakeFromSleep")
            case AVAudioSessionRouteChangeReason.unknown:
                print("Unknown")
            case AVAudioSessionRouteChangeReason.noSuitableRouteForCategory:
                print("NoSuitableRouteForCategory")
            case AVAudioSessionRouteChangeReason.routeConfigurationChange:
                print("RouteConfigurationChange")

            }
        }
    }
}

func checkHeadphones() {
    // check NewDeviceAvailable and OldDeviceUnavailable for them being plugged in/unplugged
    let currentRoute = AVAudioSession.sharedInstance().currentRoute
    if currentRoute.outputs.count > 0 {
        for description in currentRoute.outputs {
            if description.portType == AVAudioSessionPortHeadphones {
                print("headphones are plugged in")
                break
            } else {
                print("headphones are unplugged")
            }
        }
    } else {
        print("checking headphones requires a connection to a device")
    }
}
}

// MARK: AVAudioRecorderDelegate
extension RecorderViewController : AVAudioRecorderDelegate {

func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder,
    successfully flag: Bool) {
        print("finished recording \(flag)")
}

func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder,
    error: Error?) {
        if let e = error {
        print("\(e.localizedDescription)")
        }
}
}

标签: iosswiftaudioavfoundation

解决方案


推荐阅读