AVaudioplayer not loading an existing and playable file swift - ios

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?

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()
}
}

Retrieving the audio file from the document directory

is it possible to retrieve the recorded audio file from the document directory? I have looked through the whole internet, but using AVAsset/WKAudioFileAsset does not seem to be the right path.
Is there just the way to convert the recorded audio file to NSData (binary format), send it to the server and then the server will convert the binary data back into the audio file?
I have found this here: https://iosdevcenters.blogspot.com/2016/04/save-and-get-image-from-document.html
(This example is about retrieving images and they have used UIImage)
So, I am curious whether there is an equivalent way for retrieving an audio file? Is there a class for audio to save the file into that class just as UIImage for images?
I hope someone can help me out. Thank you very much.
My code looks as follows:
import WatchKit
import Foundation
import AVFoundation
class InterfaceController: WKInterfaceController, AVAudioRecorderDelegate{
#IBOutlet weak var btn: WKInterfaceButton!
var recordingSession : AVAudioSession!
var audioRecorder : AVAudioRecorder!
var settings = [String : Any]()
override func awake(withContext context: Any?) {
super.awake(withContext: context)
recordingSession = AVAudioSession.sharedInstance()
do{
try recordingSession.setCategory(AVAudioSession.Category.playAndRecord)
try recordingSession.setActive(true)
recordingSession.requestRecordPermission(){[unowned self] allowed in
DispatchQueue.main.async {
if allowed{
print("Allow")
} else{
print("Don't Allow")
}
}
}
}
catch{
print("failed to record!")
}
// Configure interface objects here.
// Audio Settings
settings = [
AVFormatIDKey:Int(kAudioFormatLinearPCM),
AVSampleRateKey:44100.0,
AVNumberOfChannelsKey:1,
AVLinearPCMBitDepthKey:8,
AVLinearPCMIsFloatKey:false,
AVLinearPCMIsBigEndianKey:false,
AVEncoderAudioQualityKey:AVAudioQuality.max.rawValue
]
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
print("Test")
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
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("sound.wav")
// var soundUrlStr = soundUrl?.path
//print(fileManager.fileExists(atPath: soundUrlStr))
let filePath = (soundUrl).path
print(filePath)
print("URL")
print(soundUrl)
return soundUrl as URL?
}
func startRecording(){
let audioSession = AVAudioSession.sharedInstance()
do{
audioRecorder = try AVAudioRecorder(url: self.directoryURL()! as URL,
settings: settings)
audioRecorder.delegate = self
audioRecorder.prepareToRecord()
audioRecorder.record(forDuration: 5.0)
}
catch {
finishRecording(success: false)
}
do {
try audioSession.setActive(true)
audioRecorder.record()
} catch {
}
}
func finishRecording(success: Bool) {
audioRecorder.stop()
if success {
print(success)
} else {
audioRecorder = nil
print("Somthing Wrong.")
}
}
#IBAction func recordAudio() {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: path)
let pathPart = url.appendingPathComponent("sound.wav")
let filePath = pathPart.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath){
print("File exists!")
let audioAsset = WKAudioFileAsset(url:pathPart)
// let playerItem = WKAudioFilePlayerItem(asset:audioAsset)
print("Audio File")
print(audioAsset)
print("WAV file")
print(NSData(contentsOfFile: filePath) as Any)
}else{
print("File does not exist")
}
if audioRecorder == nil {
print("Pressed")
self.btn.setTitle("Stop")
self.btn.setBackgroundColor(UIColor(red: 119.0/255.0, green: 119.0/255.0, blue: 119.0/255.0, alpha: 1.0))
self.startRecording()
} else {
self.btn.setTitle("Record")
print("Pressed2")
self.btn.setBackgroundColor(UIColor(red: 221.0/255.0, green: 27.0/255.0, blue: 50.0/255.0, alpha: 1.0))
self.finishRecording(success: true)
}
}
func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
if !flag {
finishRecording(success: false)
}
}
}

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.

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

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()

AVAudioPlayer not playing audio in Swift

I have this code in a very simple, single view Swift application in my ViewController:
var audioPlayer = AVAudioPlayer()
#IBAction func playMyFile(sender: AnyObject) {
let fileString = NSBundle.mainBundle().pathForResource("audioFile", ofType: "m4a")
let url = NSURL(fileURLWithPath: fileString)
var error : NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
if (audioPlayer.isEqual(nil)) {
println("There was an error: (er)")
} else {
audioPlayer.play()
NSLog("working")
}
I have added import AVFoundation and audioPlayer is a global variable. When I execute the code, it does print "working", so it makes it through without errors but no sound is played. The device is not in silent.
There's so much wrong with your code that Socratic method breaks down; it will probably be easiest just to throw it out and show you:
var player : AVAudioPlayer! = nil // will be Optional, must supply initializer
#IBAction func playMyFile(sender: AnyObject?) {
let path = NSBundle.mainBundle().pathForResource("audioFile", ofType:"m4a")
let fileURL = NSURL(fileURLWithPath: path)
player = AVAudioPlayer(contentsOfURL: fileURL, error: nil)
player.prepareToPlay()
player.delegate = self
player.play()
}
I have not bothered to do any error checking, but the upside is you'll crash if there's a problem.
One final point, which may or may not be relevant: not every m4a file is playable. A highly compressed file, for example, can fail silently (pun intended).
Important that AvPlayer is class member and not in the given function, else it goes out of scope... :)
I had to declare a global player variable
var player: AVAudioPlayer!
and set it in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
player = AVAudioPlayer()
}
Then I could play the audio file wherever like this:
func playAudioFile(){
do {
if audioFileUrl == nil{
return
}
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
/* The following line is required for the player to work on iOS 11. Change the file type accordingly*/
player = try AVAudioPlayer(contentsOf: audioFileUrl, fileTypeHint: AVFileType.m4a.rawValue)
/* iOS 10 and earlier require the following line:
player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3) */
guard let player = player else { return }
player.play()
print("PLAYING::::: \(audioFileUrl)")
}
catch let error {
print(error.localizedDescription)
}
}
}
Here is a working snippet from my swift project. Replace "audiofile" by your file name.
var audioPlayer = AVAudioPlayer()
let audioPath = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("audiofile", ofType: "mp3"))
audioPlayer = AVAudioPlayer(contentsOfURL: audioPath, error: nil)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
audioPlayer.play()
You can download fully functional Swift Audio Player application source code from here https://github.com/bpolat/Music-Player
for some reason (probably a bug) Xcode can't play certain music files in the .m4a and the .mp3 format I would recommend changing them all to .wav files to get it to play
//top of your class
var audioPlayer = AVAudioPlayer
//where you want to play your sound
let Sound = NSURL(fileURLWithPath: Bundle.main.path(forResource: "sound", ofType: "wav")!)
do {
audioPlayer = try AVAudioPlayer(contentsOf: Sound as URL)
audioPlayer.prepareToPlay()
} catch {
print("Problem in getting File")
}
audioPlayer.play()
var audioPlayer = AVAudioPlayer()
var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("KiepRongBuon", ofType: "mp3")!)
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
I used the below code in my app and it works. Hope that is helpful.
var audioPlayer: AVAudioPlayer!
if var filePath = NSBundle.mainBundle().pathForResource("audioFile", ofType:"mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: nil)
audioPlayer.play()
}else {
println("Path for audio file not found")
}
In Swift Coding using Try catch, this issues will solve and play audio for me and my code below,
var playerVal = AVAudioPlayer()
#IBAction func btnPlayAction(sender: AnyObject) {
let fileURL: NSURL = NSURL(string: url)!
let soundData = NSData(contentsOfURL: fileURL)
do {
playerVal = try AVAudioPlayer(data: soundData!)
}
catch {
print("Something bad happened. Try catching specific errors to narrow things down",error)
}
playerVal.delegate = self
playerVal.prepareToPlay()
playerVal.play()
}
Based on #matt answer but little bit detailed 'cause original answer did not completely satisfied me.
import AVFoundation
class YourController: UIViewController {
private var player : AVAudioPlayer?
override func viewDidLoad() {
super.viewDidLoad()
prepareAudioPlayer()
}
#IBAction func playAudio() {
player?.play()
}
}
extension YourController: AVAudioPlayerDelegate {}
private extension YourController {
func prepareAudioPlayer() {
guard let path = Bundle.main.path(forResource: "you-audio", ofType:"mp3") else {
return
}
let fileURL = URL(fileURLWithPath: path)
do {
player = try AVAudioPlayer(contentsOf: fileURL)
} catch let ex {
print(ex.localizedDescription)
}
player?.prepareToPlay()
player?.delegate = self
}
}
swift 3.0:
import UIKit
import AVFoundation
class ViewController: UIViewController
{
var audioplayer = AVAudioPlayer()
#IBAction func Play(_ sender: Any)
{
audioplayer.play()
}
#IBAction func Pause(_ sender: Any)
{
if audioplayer.isPlaying
{
audioplayer.pause()
}
else
{
}
}
#IBAction func Restart(_ sender: Any)
{
if audioplayer.isPlaying
{
audioplayer.currentTime = 0
audioplayer.play()
}
else
{
audioplayer.play()
}
}
override func viewDidLoad()
{
super.viewDidLoad()
do
{
audioplayer = try AVAudioPlayer(contentsOf:URL.init(fileURLWithPath:Bundle.main.path(forResource:"bahubali", ofType: "mp3")!))
audioplayer.prepareToPlay()
var audioSession = AVAudioSession.sharedInstance()
do
{
try audioSession.setCategory(AVAudioSessionCategoryPlayback)
}
catch
{
}
}
catch
{
print (error)
}
}
}

Resources