Reputation: 21
I'm trying to create an app that simply records and plays audio. I have tried multiple different methods of playing audio but it does not seem to work when playing on an iPhone 6 and an iPhone 8 Plus but works on the simulator.
The error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
This happens when SoundPlayer is trying to play. The error printed in the console is:
Error Domain=NSOSStatusErrorDomain Code=2003334207 "(null)"
@IBOutlet weak var recordBtn: UIButton!
@IBOutlet weak var playBtn: UIButton!
var soundRecorder : AVAudioRecorder!
var soundPlayer : AVAudioPlayer!
var fileName = "audioFile0.aac"
var screenCounter = 0 (global variable)
override open func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override open func viewDidLoad() {
super.viewDidLoad()
if (screenCounter == 0){
// Do any additional setup after loading the view, typically from a nib.
setupRecorder()
screenCounter += 1
}
}
func setupRecorder(){
let recordSettings = [ AVFormatIDKey : kAudioFormatMPEG4AAC
, AVEncoderAudioQualityKey : AVAudioQuality.max.rawValue, AVEncoderBitRateKey : 320000, AVNumberOfChannelsKey : 2, AVSampleRateKey : 44100.0 ] as [String : Any]
let _ : NSError?
do{
soundRecorder = try AVAudioRecorder(url : getFileURL() as URL, settings : recordSettings)
}catch{
print("Something went wrong")
}
soundRecorder.delegate = self
soundRecorder.prepareToRecord()
}
func getCacheDirectory() -> String{
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
return paths[0]
}
func getFileURL() -> NSURL{
let pathURL = NSURL(fileURLWithPath : getCacheDirectory()).appendingPathComponent(fileName)
let pathString = pathURL?.path
let filePath = NSURL(fileURLWithPath : pathString!)
return filePath
}
@IBAction func record(_ sender: UIButton) {
if sender.titleLabel?.text == "Record"{
soundRecorder.record()
sender.setTitle("Stop", for : .normal)
playBtn.isEnabled = false
}else{
soundRecorder.stop()
sender.setTitle("Record", for : .normal)
playBtn.isEnabled = false
}
}
@IBAction func playSound(_ sender: UIButton) {
if sender.titleLabel?.text == "Play"{
recordBtn.isEnabled = false
sender.setTitle("Stop", for : .normal)
preparePlayer()
soundPlayer.play()
}else{
soundPlayer.stop()
sender.setTitle("Play", for : .normal)
}
}
func preparePlayer(){
let _ : NSError?
do{
soundPlayer = try AVAudioPlayer(contentsOf : getFileURL() as URL)
}catch{
print(error)
}
soundPlayer.delegate = self
soundPlayer.prepareToPlay()
soundPlayer.volume = 1.0
}
open func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
playBtn.isEnabled = true
}
open func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
recordBtn.isEnabled = true
playBtn.setTitle("Play", for : .normal)
}
Any help would be greatly appreciated.
UPDATE
Thanks for all your suggestions! I ended up finding the error on another forum here. It appears there needed to be an intermediary between recording and playing the audio called an audio session.
Upvotes: 2
Views: 385
Reputation: 5052
It seems that audio file url is nil. Would you try with this FileManager
//func to give path(URL) to store the recording
func getDirectory() -> URL {
let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectory = path[0]
return docDirectory
}
And your file URL full path should be:
let path = getDirectory().appendingPathComponent(fileName)
NOTE: As I could not run and test your code, I am not sure of the solution. Let me know the result. Otherwise, I will delete my post.
Upvotes: 1
Reputation: 66
Try initializing the soundPlayer outside of the playSoung method.
var soundPlayer = AVAudioPlayer()
func preparePlayer() {
...
}
Upvotes: 1