Xcode 8.0 beta3 AVAudioRecorder(url,setting) error catch - "Error Domain=NSOSStatusErrorDomain Code=-50 "(null)"" - avaudiorecorder

I'm trying to record the voice of the user and change it to a text data. I used AVAudioRecorder to record the sound and SpeechKit to change it to text which is included in iOS 10. When user touches down the button record starts and stops when button is touched up. But when I initialize the AVAudioRecorder using do catch syntax, error occurs and fails.
I added the appropriate frameworks(Speech, AVFoundation).
import UIKit
import Speech
import AVFoundation
class SearchViewController: UIViewController, CLLocationManagerDelegate, AVAudioRecorderDelegate {
var audioRecorder = AVAudioRecorder()
let recordSettings = [AVSampleRateKey : String(NSNumber(value: Float(44100.0))),
AVFormatIDKey : String(kAudioFileCAFType),
AVNumberOfChannelsKey : String(NSNumber(value: 2))]
#IBAction func recordButtonDown(_ sender: AnyObject) {
print("recordButtonDown")
self.audioPlayer.play()
sleep(1)
let fileManager = FileManager.default
let paths = fileManager.urlsForDirectory(.documentDirectory, inDomains: .userDomainMask)
var audioURL = paths[0] as NSURL
audioURL = audioURL.appendingPathComponent("soundForMapSearch.caf", isDirectory: false)!
do {
self.audioRecorder = try AVAudioRecorder(url: soundFileURL as URL, settings: self.recordSettings)
self.audioRecorder.delegate = self
self.audioRecorder.prepareToRecord()
self.audioRecorder.record()
} catch (let error) {
print("Error: \(error)")
}
}
#IBAction func recordButtonUp(_ sender: AnyObject) {
self.audioRecorder.stop()
}
override func viewDidLoad() {
super.viewDidLoad()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
try audioSession.setActive(true)
audioSession.requestRecordPermission({ (recordPermission) in
})
} catch {
print("record initiallizing failed")
}
And on the line
self.audioRecorder = try AVAudioRecorder(url: soundFileURL as URL, settings: self.recordSettings)
error occurs at catch
Error: Error Domain=NSOSStatusErrorDomain Code=-50 "(null)"
is printed. I searched for this error and code=-50 means the NSURL object is invalid. How can I solve this error?

my working code
let recordingName = "recording1" + ".m4a"
let pathArray = [dirPath, recordingName]
print(pathArray)
let filePath = NSURL.fileURL(withPathComponents: pathArray)
print(filePath)
do{
let session = AVAudioSession.sharedInstance()
try! session.setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch {
assertionFailure("AVAudioSession setup error: \(error)")
}
let recordSettings: [String: AnyObject] = [
AVFormatIDKey: NSNumber(value: kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100.0,
AVNumberOfChannelsKey: 1,
]
try! audioRecorder = AVAudioRecorder(url: filePath!, settings: recordSettings)
audioRecorder.delegate = self
audioRecorder.isMeteringEnabled = true
audioRecorder.prepareToRecord()
audioRecorder.record()

Related

Best Implementation of .wav Audio Recording in Swift

Thanks in advance for your help,
I have been able to record in .m4a format for a while. Unfortunately, this project I'm working on needs to be recorded in .wav. I have been searching for a way of recording in .wav but I've only been able to find resources on converting to .wav after recording in .m4a. Is there a way of recording specifically in .wav?
The code I've pasted is my recording service. In settings specifically, I pass an audio format type. I haven't been able to have this type be of '.wav'.
import Foundation
import Combine
import AVFoundation
class AudioRecorder: NSObject, ObservableObject {
override init() {
super.init()
fetchRecordings()
}
let objectWillChange = PassthroughSubject<AudioRecorder, Never>()
var audioRecorder: AVAudioRecorder!
var recordings = [Recording]()
var recording = false {
didSet {
objectWillChange.send(self)
}
}
func startRecording(taskNum: Int) {
let recordingSession = AVAudioSession.sharedInstance()
do {
try recordingSession.setCategory(.playAndRecord, mode: .default)
try recordingSession.setActive(true)
} catch {
print("Failed to set up recording session")
}
let documentPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let audioFilename = documentPath.appendingPathComponent("\(Date().toString(dateFormat: "dd-MM-YY_'at'_HH:mm:ss"))-task_\(taskNum).m4a")
let settings = [
// Change to kAudioFileWAVEType from kAudioFormatMPEG4AAC for .wav files?
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.record()
recording = true
} catch {
print("Could not start recording")
}
}
func stopRecording() {
audioRecorder.stop()
recording = false
fetchRecordings()
}
func fetchRecordings() {
recordings.removeAll()
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryContents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil)
for audio in directoryContents {
let recording = Recording(fileURL: audio, createdAt: getCreationDate(for: audio))
recordings.append(recording)
}
recordings.sort(by: { $0.createdAt.compare($1.createdAt) == .orderedAscending})
objectWillChange.send(self)
}
func getCreationDate(for file: URL) -> Date {
if let attributes = try? FileManager.default.attributesOfItem(atPath: file.path) as [FileAttributeKey: Any],
let creationDate = attributes[FileAttributeKey.creationDate] as? Date {
return creationDate
} else {
return Date()
}
}
func deleteRecording(urlsToDelete: [URL]) {
for url in urlsToDelete {
print(url)
do {
try FileManager.default.removeItem(at: url)
} catch {
print("File could not be deleted!")
}
}
fetchRecordings()
}
}

FileManager cannot find audio file

Helloo! My ultimate goal is to create a UITableViewController with recordings made in-app, using FileManager to traverse the /Documents/ directory and list all the recordings found there.
The recording and playback are functioning just fine with one recording, with the following setup:
// In VC#1
func setupRecorder(){
let audioSession:AVAudioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch {
print("Audio Setup Error: \(error)")
}
do {
try audioSession.setActive(true)
} catch {
print("Audio Setup Error: \(error)")
}
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
audioFileName = "test.caf"
audioFileUrl = documentsDirectory.appendingPathComponent(audioFileName)
print("\n\nrecording url :\(audioFileUrl.absoluteString)\n\n")
let recordSettings = [
AVFormatIDKey: Int(kAudioFormatAppleLossless),
AVEncoderAudioQualityKey : AVAudioQuality.max.rawValue,
AVEncoderBitRateKey : 320000,
AVNumberOfChannelsKey: 2,
AVSampleRateKey : 44100.0
] as [String : Any]
do {
audioRecorder = try AVAudioRecorder(url: audioFileUrl, settings: recordSettings)
} catch let error as NSError{
print("Audio Setup Error: \(error)")
audioRecorder = nil
}
audioRecorder.delegate = self
audioRecorder.isMeteringEnabled = true
audioRecorder.prepareToRecord()
}
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == GraphViewController.getEntrySegue() {
let navController = segue.destination as! UINavigationController
let destination = navController.topViewController as! GraphViewController
destination.audioFileName = audioFileName
destination.audioFileUrl = audioFileUrl
}
// In VC#2
func setupAudioPlayer() {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch {
print(error)
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print(error)
}
do {
audioPlayer = try AVAudioPlayer(contentsOf: audioFileUrl)
} catch {
print("Audio Player Setup Error: \(error)")
}
audioPlayer.prepareToPlay()
}
This is working all fine and dandy; it plays the recorded audio file with no problems whatsoever. However, when I try to find the file via FileManager, FileManager cannot find it. Listed below are my attempts to find this file.
print("\(FileManager.default.fileExists(atPath: audioFileUrl.absoluteString))") // -> False
print("\(FileManager.default.contents(atPath: audioFileUrl.absoluteString))") // -> nil
Clearly I must be missing something... especially given AVAudioPlayer is successfully reading the audio file with the exact same url. The path prints as:
file:///var/mobile/Containers/Data/Application/1DA6BD0F-5A23-4228-92A9-A083E55ACE21/Documents/test.caf
Any ideas? 🧐🤔
Many thanks!
EDIT: Before storing the url as a property, I was doing the following, recalculating the documentsDirectory in VC#2 however was also not succesfull:
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let results = try? FileManager.default.contentsOfDirectory(atPath: documentsDirectory.absoluteString)
You cannot save a sandboxed file URL or path string for later use, because the sandbox can move. You need to do what you were doing before, successfully: calculate the documents directory URL and then examine it. You should work entirely with URLs and never call absoluteString at all; it is the wrong call entirely and is letting you down here.
In this example, I check the documents directory for any files whatever:
let documentsDirectory =
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let results =
try? FileManager.default.contentsOfDirectory(at: documentsDirectory,
includingPropertiesForKeys: nil)

record user voice and clear text file every 2 minutes

I am new to swift and I'm working in project that record user sound ,and convert sound file into text file every two minutes. I use timer to repeat the step every 2 minutes.
the problem is that the recorder is disable for the second call. Also, the text file does not clear the content to be prepared to the next call.
here is the full code.
import UIKit
import Speech
import AVFoundation
class ViewController: UIViewController {
var audioRecorder:AVAudioRecorder!
var inString = ""
let fileName = "Test"
var str=""
appropriateFor: nil, create: true)
let recordSettings = [AVSampleRateKey : NSNumber(value: Float(44100.0)),
AVFormatIDKey : NSNumber(value: Int32(kAudioFormatMPEG4AAC)),
AVNumberOfChannelsKey : NSNumber(value: Int32(1)),
AVEncoderAudioQualityKey : NSNumber(value: Int32(AVAudioQuality.high.rawValue))]
var timer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
try audioRecorder = AVAudioRecorder(url: directoryURL()!, settings: recordSettings)
audioRecorder.prepareToRecord()
} catch {
print("error")
}
audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
requestSpeechAuth()
} catch {}
timer = Timer.scheduledTimer(timeInterval: 120, target: self, selector: #selector (ViewController.stopAudio), userInfo: nil, repeats: true)
}
#objc func stopAudio() {
audioRecorder.stop()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(false)
let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "ar_SA"))
let request = SFSpeechURLRecognitionRequest(url: audioRecorder.url)
recognizer?.recognitionTask(with: request) { (result, error) in
if let error = error {
print("There was an error: \(error)")
} else {
let dir = try? FileManager.default.url(for: .documentDirectory,
in: .userDomainMask, appropriateFor: nil, create: true)
if let fileURL = dir?.appendingPathComponent(self.fileName).appendingPathExtension("txt") {
do {
self.str=""
self.str = (result?.bestTranscription.formattedString)!
try self.str.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
do {
self.inString = try String(contentsOf: fileURL)
} catch {
print("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
}
self.getIqama(fileN: self.inString,status: self.str)
}
}//end elsd
} //end result
} catch {} //end do for false
// requestSpeechAuth()
}
func directoryURL() -> URL? {
let fileManager = FileManager.default
let urls = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
let documentDirectory = urls[0] as URL
let soundURL = documentDirectory.appendingPathComponent("AqimAlsalat.m4a")
return soundURL
}
func getIqama(fileN : String, status:String)
{
var st: String!
st = "السلام عليكم ورحمة الله السلام عليكم ورحمة الله"
let st1 : String!
st1 = String (fileN)
print(st1)
if st1 == st {
// audioEngine.stop()
//speechRecognitionRequest?.endAudio()
print(st1)
print("JJalal")
}
else {
print("Dalal")
print(fileN)
}
}
func requestSpeechAuth(){
SFSpeechRecognizer.requestAuthorization { authStatus in
if authStatus == SFSpeechRecognizerAuthorizationStatus.authorized {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setActive(true)
self.audioRecorder.record()
} catch {}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
any suggestion or idea?
Thanks
It seems like you need to call self.audioRecorder.record() again after you stop the recording to convert the soundfile into text. The Apple docs say that calling record() will create or erase an audio file, so that should solve your problem.
However, you may encounter another problem where you miss a period of recording while you are transcribing the text. You could consider fixing that problem by switching back and forth between two recorders, or you could try to change the audio recorder's file location (or change the location of the previous file) before starting to record again.

AVaudioplayer not loading an existing and playable file swift

I run into a problem creating a module that records voice and plays the result. The problem is that if a record and play it works but if a record and later I get the file created before and try to play I get the: Error Domain=NSOSStatusErrorDomain Code=1685348671 "(null)"
103: OpenFromDataSource failed
78: Open failed
here's a simplified version of the code I'm using:
var soundRecorder : AVAudioRecorder!
var SoundPlayer : AVAudioPlayer!
var recordingSession: AVAudioSession!
func getFileURL() -> URL {
let record = getDocumentsDirectory().appendingPathComponent(fileName)
return record
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
override func viewDidLoad() {
super.viewDidLoad()
setupRecorder()
displaylinkRecording()
}
func setupRecorder(){
let recordSettings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue
]
do {
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
soundRecorder = try AVAudioRecorder(url: getFileURL(), settings: recordSettings)
soundRecorder.delegate = self
soundRecorder.isMeteringEnabled = true
soundRecorder.prepareToRecord()
} catch {
print("error recording")
}
}
I'm updating the file name from a different ViewController:
func UpdateNameFile() {
fileName = reg.idEvent + ".m4a"
setupRecorder()
}
And the corresponding record and play functions:
#IBAction func Recording(_ sender: UIBarButtonItem) {
soundRecorder.record()
displaylinkRecording()
}
#IBAction func PlayButton(_ sender: UIBarButtonItem) {
preparePlayer()
SoundPlayer.play()
displaylinkPlaying()
}
func preparePlayer(){
do {
try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
SoundPlayer = try AudioPlayer(contentsOf: getFileURL() as URL)
SoundPlayer.delegate = self
SoundPlayer.isMeteringEnabled = true
SoundPlayer.prepareToPlay()
SoundPlayer.volume = 1.0
} catch {
print(error)
}
}
so in other words, if I try to record and play, works, but if I try to play getting a file that I know is in the directory and was created before, doesn't work. And I cannot see why...
Any advice?

Cannot invoke function with an argument list of type '()'

I'm new to IOS development. The constructor of AVAudioRecorder is AVAudioRecorder(URL: filePath, settings: nil, error: nil) in the resource that I'm following but I'm coding in Swift 2 which has different constructor.
The error Cannot invoke function with an argument list of type '()' occurs on "try audioRecorder = AVAudioRecorder(URL: filePath, settings: nil)" line
This is my record audio file :
#IBAction func recordAudio(sender: UIButton) {
stopButton.hidden = false
recordButton.enabled = false
recordingInProgress.hidden = false
let dirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let currentDateTime = NSDate()
let formatter = NSDateFormatter()
formatter.dateFormat = "ddMMyyyy-HHmmss"
let recordingName = formatter.stringFromDate(currentDateTime)+".wav"
let pathArray = [dirPath, recordingName]
let filePath = NSURL.fileURLWithPathComponents(pathArray)
print(filePath)
var session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch {}
do {
try audioRecorder = AVAudioRecorder(URL: filePath, settings: nil)
} catch {}
audioRecorder.meteringEnabled = true
audioRecorder.prepareToRecord()
audioRecorder.record()
}
The way of initializing AVAudioRecorder using its initializer in Swift 2 is to leave out the parameter of type NSErrorPointer and catch it:
do
{
audioRecorder = try AVAudioRecorder(URL:filePath, settings:[:])
}
catch let error as NSError
{
printf(error.description)
}
You are missing error part of the audio recorder initializer:
audioRecorder = AVAudioRecorder(URL: filePath, settings: nil, error: nil)

Resources