Я использую приведенный ниже код для записи 5-секундного образца звука в приложении iOS Swift.Код отлично работает для записи, а также правильно распознает наушники, если они подключены. Однако, если во время записи наушники будут добавлены или удалены, запись будет прервана.Интересно, почему это так, иначе все идет гладко?
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)")
}
}
}