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)
}
}
}
Related
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()
}
}
I have the following code that allows me to download a PDF file from a URL, it works correctly:
class ViewController: UIViewController {
#IBOutlet weak var progressView: UIProgressView!
override func viewDidLoad() {
let _ = DownloadManager.shared.activate()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DownloadManager.shared.onProgress = { (progress) in
OperationQueue.main.addOperation {
self.progressView.progress = progress
}
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
DownloadManager.shared.onProgress = nil
}
#IBAction func startDownload(_ sender: Any) {
let url = URL(string: "https://d0.awsstatic.com/whitepapers/KMS-Cryptographic-Details.pdf")!
let task = DownloadManager.shared.activate().downloadTask(with: url)
task.resume()
}
}
The file is currently going to: file:///Users/cybermac/Library/Developer/CoreSimulator/Devices/CAEC75D0-423A-4FB2-B0D6-9E7CADB190A1/data/Containers/Data/Application/8B5CBFC8-7058-48DB-A1C4-872302A80610/Library/Caches/com.apple.nsurlsessiond/Downloads/com.example.DownloadTaskExample/CFNetworkDownload_Q7OVlf.tmp
How do I save it in /Documents/?
Something like this: file:///Users/cybermac/Library/Developer/CoreSimulator/Devices/CAEC75D0-423A-4FB2-B0D6-9E7CADB190A1/data/Containers/Data/Application/64370B29-2C01-470F-AE76-17EF1A7BC918/Documents/
The idea is that the file saved in that directory can be used to read it offline (with PDFKit or webKit). It will only be deleted if the application is deleted.
You need to move the file to your custom location after the download. Implement URLSessionDownloadDelegate and you will receive the location of your downloaded file.
Delegate method:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
Code to move the file:
do {
let documentsURL = try
FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
let savedURL = documentsURL.appendingPathComponent("yourCustomName.pdf")
try FileManager.default.moveItem(at: location, to: savedURL)
} catch {
print ("file error: \(error)")
}
To learn more refer to this repo.
This snippet was downloaded your Remote PDF from Server and "Save" into your Photo Library
func drawPDFfromURL(url: URL) -> UIImage? {
guard let document = CGPDFDocument(url as CFURL) else { return nil }
guard let page = document.page(at: 1) else { return nil }
let pageRect = page.getBoxRect(.mediaBox)
let renderer = UIGraphicsImageRenderer(size: pageRect.size)
let img = renderer.image { ctx in
UIColor.white.set()
ctx.fill(pageRect)
ctx.cgContext.translateBy(x: 0.0, y: pageRect.size.height)
ctx.cgContext.scaleBy(x: 1.0, y: -1.0)
ctx.cgContext.drawPDFPage(page)
}
return img
}
step 2
UIImageWriteToSavedPhotosAlbum(drawPDFfromURL(url: url!) ?? UIImage(), nil, nil, nil)
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.
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?
I am new in swift also stake overflow. Advanced thank's for attention.
Basically am trying to build a custom camera that will record video with Audio. it means video will play with sound when i play this video. las few days i was try to build this custom camera. i already followed my tutorial but Still missing something from my camera. i was try as per my custom camera is only recording video. maybe it not recording audio. i don't understand. i was searching for this answer, not find appropriate answer for this.
here is What i did
import UIKit
import AVFoundation
import SVProgressHUD
import MediaPlayer
import MobileCoreServices
import AVKit
var videoUrl = [AnyObject]()
class TestViewController: UIViewController {
#IBOutlet var viewVidioPlayer: UIView!
#IBOutlet weak var myView: UIView!
var session: AVCaptureSession?
var userreponsevideoData = NSData()
var userreponsethumbimageData = NSData()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
// here i create session
func createSession() {
var input: AVCaptureDeviceInput?
let movieFileOutput = AVCaptureMovieFileOutput()
var prevLayer: AVCaptureVideoPreviewLayer?
prevLayer?.frame.size = myView.frame.size
session = AVCaptureSession()
let error: NSError? = nil
do {
input = try AVCaptureDeviceInput(device: self.cameraWithPosition(position: .front)!) } catch {return}
if error == nil {
session?.addInput(input)
} else {
print("camera input error: \(String(describing: error))")
}
prevLayer = AVCaptureVideoPreviewLayer(session: session)
prevLayer?.frame.size = myView.frame.size
prevLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
prevLayer?.connection.videoOrientation = .portrait
myView.layer.addSublayer(prevLayer!)
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let filemainurl = NSURL(string: ("\(documentsURL.appendingPathComponent("temp"))" + ".mp4"))
let maxDuration: CMTime = CMTimeMake(600, 10)
movieFileOutput.maxRecordedDuration = maxDuration
movieFileOutput.minFreeDiskSpaceLimit = 1024 * 1024
if self.session!.canAddOutput(movieFileOutput) {
self.session!.addOutput(movieFileOutput)
}
session?.startRunning()
movieFileOutput.startRecording(toOutputFileURL: filemainurl! as URL, recordingDelegate: self)
}
func cameraWithPosition(position: AVCaptureDevicePosition) -> AVCaptureDevice? {
let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo)
for device in devices! {
if (device as AnyObject).position == position {
return device as? AVCaptureDevice
}
}
return nil
}
#IBAction func pressbackbutton(sender: AnyObject) {
session?.stopRunning()
}
#IBAction func Record(_ sender: Any) {
createSession()
}
#IBAction func play(_ sender: Any) {
self.videoPlay()
}
func videoPlay()
{
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
do {
// Get the directory contents urls (including subfolders urls)
let directoryContents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil, options: [])
print(directoryContents)
// if you want to filter the directory contents you can do like this:
videoUrl = directoryContents.filter{ $0.pathExtension == "mp4" } as [AnyObject]
print("mp3 urls:",videoUrl)
let playerController = AVPlayerViewController()
playerController.delegate = self as? AVPlayerViewControllerDelegate
let movieURL = videoUrl[0]
print(movieURL)
let player = AVPlayer(url: movieURL as! URL)
playerController.player = player
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
player.play()
player.volume = 1.0
player.rate = 1.0
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
extension TestViewController: AVCaptureFileOutputRecordingDelegate
{
#available(iOS 4.0, *)
private func captureOutput(captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAtURL fileURL: URL!, fromConnections connections: [AnyObject]!) {
print(fileURL)
}
func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
let filemainurl = outputFileURL
do
{
let asset = AVURLAsset(url: filemainurl! as URL, options: nil)
//AVURLAsset(URL: filemainurl as! URL, options: nil)
print(asset)
let imgGenerator = AVAssetImageGenerator(asset: asset)
imgGenerator.appliesPreferredTrackTransform = true
let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(0, 1), actualTime: nil)
let uiImage = UIImage(cgImage: cgImage)
userreponsethumbimageData = try NSData(contentsOf: filemainurl! as URL)
print(userreponsethumbimageData.length)
print(uiImage)
// imageData = UIImageJPEGRepresentation(uiImage, 0.1)
}
catch let error as NSError
{
print(error)
return
}
SVProgressHUD.show(with: SVProgressHUDMaskType.clear)
let VideoFilePath = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("mergeVideo\(arc4random()%1000)d")!.appendingPathExtension("mp4").absoluteString
if FileManager.default.fileExists(atPath: VideoFilePath)
{
do
{
try FileManager.default.removeItem(atPath: VideoFilePath)
}
catch { }
}
let tempfilemainurl = NSURL(string: VideoFilePath)!
let sourceAsset = AVURLAsset(url: filemainurl! as URL, options: nil)
let assetExport: AVAssetExportSession = AVAssetExportSession(asset: sourceAsset, presetName: AVAssetExportPresetMediumQuality)!
assetExport.outputFileType = AVFileTypeQuickTimeMovie
assetExport.outputURL = tempfilemainurl as URL
assetExport.exportAsynchronously { () -> Void in
switch assetExport.status
{
case AVAssetExportSessionStatus.completed:
DispatchQueue.main.async(execute: {
do
{
SVProgressHUD .dismiss()
self.userreponsevideoData = try NSData(contentsOf: tempfilemainurl as URL, options: NSData.ReadingOptions())
print("MB - \(self.userreponsevideoData.length) byte")
}
catch
{
SVProgressHUD .dismiss()
print(error)
}
})
case AVAssetExportSessionStatus.failed:
print("failed \(assetExport.error)")
case AVAssetExportSessionStatus.cancelled:
print("cancelled \(assetExport.error)")
default:
print("complete")
SVProgressHUD .dismiss()
}
}
}
}
There all i have done. so I don't understand what is missing from this code. Why audio is not playing with video or why not recoding audio with video.
Use this cocopods for your project. It makes your job quiet easy.
It has all instructions on what to do and also contains a demo project to test it works as you intended it to.
SwiftyCam