How do I output CAF file size in log? - ios

I know the url of blank.caf audio file what I cam creating(recording) in my iPhone app. I am concerned about its size and would like to output its size to the log. I could not find a method to do so. I am also interesting in finding out the audio file's duration.

import UIKit
import AVFoundation
extension NSURL {
var movieDuration: Double {
if checkResourceIsReachableAndReturnError(nil) {
return Double(CMTimeGetSeconds(AVURLAsset(URL: self, options: nil).duration) )
}
return 0
}
}
extension String {
var fileAttributes:NSDictionary {
if NSFileManager.defaultManager().fileExistsAtPath(self){
return NSFileManager.defaultManager().attributesOfItemAtPath(self, error: nil)! as NSDictionary
}
return[:]
}
var fileSize:Int {
if NSFileManager.defaultManager().fileExistsAtPath(self){
return Int( fileAttributes.fileSize() )
}
return 0
}
var fileSizeKB:String {
let styler = NSByteCountFormatter()
styler.allowedUnits = NSByteCountFormatterUnits.UseKB
styler.countStyle = NSByteCountFormatterCountStyle.File
return styler.stringFromByteCount(Int64(fileSize))
}
}
Testing
if let audioURL = NSURL(string:"http://freetone.org/ring/stan/iPhone_5-Alarm.mp3") {
let documentsDirectoryURL = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as! NSURL
let destinationURL = documentsDirectoryURL.URLByAppendingPathComponent(audioURL.lastPathComponent!)
if let audioData = NSData(contentsOfURL: audioURL) {
audioData.writeToURL(destinationURL, atomically: true)
destinationURL.path!.fileSizeKB // 182KB
destinationURL.movieDuration // 22.704s
}
}

In bytes:
let attributes = NSFileManager.defaultManager()
.attributesOfItemAtPath(filePath, error: nil) as NSDictionary?
let fileSize : UInt64 = attributes!.fileSize()

Related

swift ios get file path for imageView

I have already copy the file absolute path and paste in simulator browser, the image can be opened. But the fileExists is fail, i dont know why..... Can anyone help
let defaultImage = "302C3FA1-E4E1-4CD8-B6DF-2FF4E4E24C11.jpeg"
loadImage(at: defaultImage)
func fileExists(at path: String) -> Bool {
return FileManager.default.fileExists(atPath: path)
}
func loadImage(at path: String) -> UIImage? {
let tempPath = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let imagePath = "\(tempPath)\(path.trimmingCharacters(in: .whitespacesAndNewlines))"
guard fileExists(at: imagePath) else { return nil }
guard let image = UIImage(contentsOfFile: imagePath) else { return nil }
return image
}
You need split filename and extension filename.
If you use main bundle. you can follow this code
let stringPath = Bundle.main.path(forResource: "your_filename", ofType: "txt")
let urlPath = Bundle.main.url(forResource: "your_filename", withExtension: "txt")
or you can use my code.
func readConfigFromBundle(fileExtension: String) -> TCBConfigure? {
let bundle = Bundle.main
if let resPath = bundle.resourcePath {
do {
let dirContents = try FileManager.default.contentsOfDirectory(atPath: resPath)
let filteredFiles = dirContents.filter { $0.contains(fileExtension) }
for fileName in filteredFiles {
let sourceURL = bundle.bundleURL.appendingPathComponent(fileName)
let data: NSData? = NSData.init(contentsOf: sourceURL)
if let fileData = data {
// implement your logic
}
}
} catch {
// implement when error
}
}
return nil
}

How to get text-to-speech output written to a file in Swift UI?

I'm trying to get Text-to-Speech output written to a sound file. I basically copied the code from this stackoverflow answer and I found that it worked in a storyboard application but no longer worked when I migrated to SwiftUI.
The following is a complete application, just paste it into ContentView in a new SwiftUI app and hit run.
import SwiftUI
import AVFoundation
struct ContentView: View {
var body: some View {
Button(action: runSpeech) {
Text("Button")
}
}
func runSpeech() {
let fileURL = generateSpeech("hello world", 1.0)
if FileManager.default.fileExists(atPath: fileURL!.path) {
print("🔴 Speech file exists at \(fileURL!)")
} else {
print("🔴 no file exists at \(fileURL!)")
}
return
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
func generateSpeech(_ speech: String, _ speechRate: Float) -> URL? {
let synth = AVSpeechSynthesizer()
let myUtterance = AVSpeechUtterance(string: speech)
myUtterance.rate = speechRate
myUtterance.voice = AVSpeechSynthesisVoice(language: "en")!
myUtterance.postUtteranceDelay = 2
let dataPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
do {
try FileManager.default.createDirectory(atPath: dataPath?.path ?? "", withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
print("🔵Error creating directory: \(error.localizedDescription)")
}
let fileName = "myspeech.caf"
let fileURL = dataPath?.appendingPathComponent(fileName)
if FileManager.default.fileExists(atPath: fileURL!.path) {
print("🔵Speech file exists at \(fileURL!)")
return fileURL
}
print("🔵Speech file does not exist at \(fileURL!)")
var output: AVAudioFile?
synth.write(myUtterance) { (buffer: AVAudioBuffer) in
print("🔵callback")
guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
fatalError("unknown buffer type: \(buffer)")
}
if pcmBuffer.frameLength == 0 {
// done
} else {
// append buffer to file
do {
if output == nil {
try output = AVAudioFile(
forWriting: fileURL!,
settings: pcmBuffer.format.settings,
commonFormat: .pcmFormatInt16,
interleaved: false)
}
try output?.write(from: pcmBuffer)
} catch {
print("🔵error")
}
}
}
print("🔵return")
return fileURL!
}
Expected output:
🔵Speech file does not exist at file:///var/mobile/Containers/Data/Application/A5E69CE0-FB83-481C-89B2-26C802446A03/Documents/myspeech.caf
🔵callback
🔵return
🔴Speech file exists at file:///var/mobile/Containers/Data/Application/A5E69CE0-FB83-481C-89B2-26C802446A03/Documents/myspeech.caf
Actual Output:
🔵Speech file does not exist at file:///var/mobile/Containers/Data/Application/A5E69CE0-FB83-481C-89B2-26C802446A03/Documents/myspeech.caf
🔵return
🔴no file exists at file:///var/mobile/Containers/Data/Application/A5E69CE0-FB83-481C-89B2-26C802446A03/Documents/myspeech.caf
Note particularly that 🔵callback never gets run, something is going wrong there.
let fileName = "Content.txt";
let fileManger = FileManager.default;
let doumentDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString;
let filePath = doumentDirectoryPath.appendingPathComponent(fileName);
if fileManger.fileExists(atPath: filePath) {
let url = URL(fileURLWithPath: filePath);
//your file is in url
}else{
// file not exist
}

GPUImage3 Unable to export video to Document Directory

I am using following source code to export filtered video to document directory but the exported file is corrupted/wrong.
Would you please go through following source and let me know where I am making mistake?
class ViewController: UIViewController {
#IBOutlet weak var renderView: RenderView!
var movie:MovieInput!
var writer:MovieOutput!
var filter:LookupFilter!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let bundleURL = Bundle.main.resourceURL!
let movieURL = URL(string:"sample_iPod.m4v", relativeTo:bundleURL)!
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:true)
let fileURL = documentDirectory.appendingPathComponent("TestVideo.mov")
movie = try MovieInput(url:movieURL, playAtActualSpeed:true)
writer = try MovieOutput(URL: fileURL, size: Size(width: 100.0, height: 100.0))
filter = LookupFilter()
filter.lookupImage = PictureInput(image: UIImage(named: "Image")!)
movie --> filter --> renderView
movie.runBenchmark = true
movie.addTarget(writer)
movie.start()
writer.startRecording()
self.writer.finishRecording {
print("Written")
}
} catch {
print("Couldn't process movie with error: \(error)")
}
}
}
Simple answer: now you have 5 seconds .
self.movie.addTarget(writer)
self.movie.start()
self.filter --> self.writer
self.writer.startRecording()
let interval = 5 // now you have 5 seconds .
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + interval) {
self.writer.finishRecording {
print("Written")
}
}
To solve your problem forward,
You should extract out the record logic. put that in a filter button action.
like the following demo code .
#IBAction func capture(_ sender: AnyObject) {
if (!isRecording) {
do {
self.isRecording = true
let documentsDir = try FileManager.default.url(for:.documentDirectory, in:.userDomainMask, appropriateFor:nil, create:true)
let fileURL = URL(string:"test.mp4", relativeTo:documentsDir)!
do {
try FileManager.default.removeItem(at:fileURL)
} catch {
}
movieOutput = try MovieOutput(URL:fileURL, size:Size(width:480, height:640), liveVideo:true)
filter --> movieOutput!
movieOutput!.startRecording()
DispatchQueue.main.async {
// Label not updating on the main thread, for some reason, so dispatching slightly after this
(sender as! UIButton).titleLabel!.text = "Stop"
}
} catch {
fatalError("Couldn't initialize movie, error: \(error)")
}
} else {
movieOutput?.finishRecording{
self.isRecording = false
DispatchQueue.main.async {
(sender as! UIButton).titleLabel!.text = "Record"
}
self.movieOutput = nil
}
}
}
You miss one line code filter --> writer
movie.addTarget(writer)
movie.start()
filter --> writer
writer.startRecording()
self.writer.finishRecording {
print("Written")
}

Saving an Asset List (array) to specific CKRecord

I've a CKRecord type created in the CloudKit backend with some properties related to that class.
I've String properties, Bytes and I have a Asset List property, so store some images (multiple images related to a single record).
Now I'm trying so store some images and then fill the property and then trying to save it to CloudKit, but it's not working.
Code goes as it follows:
var images_array = [CKAsset]()
// append the an image to the array
images_array.append(CKAsset(fileURL: writeImage(image: selectedImage) as URL))
let record = CKRecord(recordType: recordName)
record["class_title"] = someString as CKRecordValue
record["class_body"] = someString as CKRecordValue
record["images_array"] = images_array as CKRecordValue
saveRecord(record)
func saveRecord(_ xrecord: CKRecord) {
let publicData = CKContainer.default().publicCloudDatabase
let record: [CKRecord] = [xrecord]
let saveOperation = CKModifyRecordsOperation.init(recordsToSave: record, recordIDsToDelete: nil)
saveOperation.perRecordCompletionBlock = {(record, error) -> Void in
if (error != nil) {
print("error")
}
}
publicData.add(saveOperation)
}
func writeImage(image: UIImage) -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = NSURL(fileURLWithPath: documentsURL.absoluteString).appendingPathComponent(".jpg")
if let imageData = image.lowestQualityJPEGNSData {
do {
try imageData.write(to: fileURL!)
} catch {
print("ERRO 001 = \(error.localizedDescription)")
}
}
return fileURL!
}
extension UIImage {
var uncompressedPNGData: Data? { return UIImagePNGRepresentation(self) }
var highestQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 1.0) }
var highQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.75) }
var mediumQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.5) }
var lowQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 0.25) }
var lowestQualityJPEGNSData:Data? { return UIImageJPEGRepresentation(self, 0.0) }
}
If I only save the strings, everything works perfectly but with images it doesn't save the record.
I know there might be any issue with the appending, or I have to save the array in other way, or I shouldn't save it as CKRecordValue.
Do you have any tip on how to achieve this?
Thanks
When you create your local asset file you should do so with the atomic write option. This will ensure that the file is completely written before CloudKit attempts to upload the asset.
This is the asset file creation function I use in the Seam 3 library:
fileprivate func createAsset(data: Data) -> CKAsset? {
var returnAsset: CKAsset? = nil
let tempStr = ProcessInfo.processInfo.globallyUniqueString
let filename = "\(tempStr)_file.bin"
let baseURL = URL(fileURLWithPath: NSTemporaryDirectory())
let fileURL = baseURL.appendingPathComponent(filename, isDirectory: false)
do {
try data.write(to: fileURL, options: [.atomicWrite])
returnAsset = CKAsset(fileURL: fileURL)
} catch {
print("Error creating asset: \(error)")
}
return returnAsset
}
You have to take Array of CKAsset for images.
var imageUrls = [CKAsset]()
Now get all images using for-loop. And save CKAsset of images.
for images in self.arrayImageSelected{
var myImage = UIImage()
if (images.isKindOfClass(PHAsset)){
let imageC = images as? PHAsset
myImage = self.getAssetThumbnail(imageC!)
}else if (images.isKindOfClass(UIImage)){
myImage = (images as? UIImage)!
}
let imagePath = self.storeImageAtDocumentDirectory(myImage, titleName: self.strTitle)
myPAth.append(imagePath)
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let FbPath = paths.stringByAppendingString("/Custom")
let filePathToWrite = "\(FbPath)" + imagePath
let urls = NSURL(fileURLWithPath: filePathToWrite)
let imageAsset = CKAsset(fileURL: urls)
imageUrls.append(imageAsset)
}
Set Your array.
record.setObject(imageUrls, forKey: "images_array")

Generate and export an animated gif via Swift 3.0?

I've learned that the Image IO Framework has changed syntactically since iOS 9 according to the documentation, however I have done my research and the following code seems to be correct. I have to procedures listed below; one procedure takes images and writes those images to the application's document folder as a gif. I can confirm this works as I can view the actually gif file if I go to the app's documents folder using iTunes. Despite this, in the second procedure where I attempt to read from that same file, an error is throw which states the file at that path does not exist. I have posted the code below.
class GifManager {
private func getDocumentsDirectory() -> URL? {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
}
public func generateGif(photos: [UIImage], filename: String) -> Bool {
if let docsDirectory = getDocumentsDirectory() {
let url = docsDirectory.appendingPathComponent(filename)
let fileProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]
let gifProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: 0.125]]
if let destination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeGIF, photos.count, nil) {
CGImageDestinationSetProperties(destination, fileProperties as CFDictionary?)
for photo in photos {
CGImageDestinationAddImage(destination, photo.cgImage!, gifProperties as CFDictionary?)
}
return CGImageDestinationFinalize(destination)
}
}
return false
}
public func saveGifToCameraRoll(filename: String) {
if let docsDirectory = getDocumentsDirectory() {
let fileUrl: URL = docsDirectory.appendingPathComponent(filename)
do {
let data = try Data(contentsOf: fileUrl)
if let _ = UIImage(data: data) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: fileUrl)
}, completionHandler: {completed, error in
if error != nil {
print("error")
} else if completed {
print("completed")
} else {
print("not completed")
}
})
}
} catch let error {
print(error)
}
}
}
Swift 3.1, 4 and 5
For those wanting an updated version of the GIF generation function, I have included it here.
This function requires the ImageIO and MobileCoreServices import statements.
import ImageIO
import MobileCoreServices
Here is the function.
func generateGif(photos: [UIImage], filename: String) -> Bool {
let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let path = documentsDirectoryPath.appending(filename)
let fileProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]
let gifProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: 0.125]]
let cfURL = URL(fileURLWithPath: path) as CFURL
if let destination = CGImageDestinationCreateWithURL(cfURL, kUTTypeGIF, photos.count, nil) {
CGImageDestinationSetProperties(destination, fileProperties as CFDictionary?)
for photo in photos {
CGImageDestinationAddImage(destination, photo.cgImage!, gifProperties as CFDictionary?)
}
return CGImageDestinationFinalize(destination)
}
return false
}
EDIT:
It has a Bool so you know you can safely use the file it creates.
if generateGif(arrayOfImages, "/myGIFfile.gif") {
// do something with gif
} else {
// failed to create and close the gif file
}

Resources