Filestream equivalent in Swift - swift

I'm having a little bit of trouble. I'm translating my App to native Swift Language. I was using AS3. What I want to accomplish is to read a file in binary mode. A snippet of what I want accomplish is this:
ba:FileStream
ba.open(someFile, readMode)
ba.endian = "littleEndian"
ba.position = 128
ba.readMultiByte(4, someVariableHere)

NSData is the class you are looking for.
do {
let path = "apath"
let data = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
let buffer: UInt32 = 0
data.getBytes(&buffer, range: NSRange(location: 128,length: 4))
}
catch error {
print(error)
}
NSData documentation. NSData Class Reference

Related

How can I safe audiosamples in a textfile --> Swift

The question is how it is possible to save audio samples of an audio file into a text file. I have the special case that I have stored the samples in an array of UnsafeMutableRawBufferPointers and now I wonder how I can put them into a text file. In C++ I would have taken a stream operator here, however I am new to Swift and am wondering how to do this.
My code so far looks like this:
let Data = Array(UnsafeMutableRawBufferPointer(start:buffer.mData, count: Int(buffer.mDataByteSize))
.... your buffer .....
let yourBuffer = Array(UnsafeMutableRawBufferPointer(start:buffer.mData, count: Int(buffer.mDataByteSize))
let dd = Data.withUnsafeBytes(yourBuffer)
let fileName = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!.appendingPathComponent("yourFile")
let result = try? dd.write(to: fileName)
This should create the file "yourFile" in your downloadsDirectory
Please let me know.
You already have code that creates an UnsafeMutableRawBufferPointer. Use that to create Data, and write that to disk:
let buffer = UnsafeRawBufferPointer(start:buffer.mData, count: Int(buffer.mDataByteSize)
let data = Data.withUnsafeBytes(buffer)
let fileURL = //the path to which you want to save
data.write(to: fileURL, options: []
I solved the problem by refraining from the special case of using the array of pointers. If anyone else wants to write audio samples to a text file, this is how I approached the problem via AVAudioFile:
if FileManager.default.fileExists(atPath: getFileURL().path)
{
do{
//Load Audio into Buffer and then write it down to a .txt
let file = try AVAudioFile(forReading: getFileURL())
let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: file.fileFormat.channelCount, interleaved: false)
guard let buf = AVAudioPCMBuffer(pcmFormat: format!, frameCapacity: AVAudioFrameCount(file.length)) else{
throw NSError()
}
try file.read(into: buf)
guard buf.floatChannelData != nil else{print("Channel Buffer was not able to be created")
throw NSError()}
let arraySize = Int(buf.frameLength)
print(arraySize, "Samples saved")
let samples = Array(UnsafeBufferPointer(start:buf.floatChannelData![0],count:arraySize))
//Array is going to be encoded and safe
let encoded = try! JSONEncoder().encode(samples)
try encoded.write(to: outputURL())
print("Textfile created.\n")
}catch {print(error)}
}else {print("AudioFile is missing")}

How to write an array of samples into a 24 bits audio file with AVAudioBuffer?

I'm having trouble writing wav files in 24bits with AVAudioEngine in swift.
For my usage, my input is an array of Float.
I have the audio format of the input file (retrieved with AVAudioFile).
So, I need to convert my input Float array to a value that will be writable for the buffer. Also, I want to find the right channel to write my data.
My code is working with 16bit and 32 bit files, but I don't know how to handle 24 bit files...
Here it is :
//Static func to write audiofile
fileprivate func writeAudioFile(to outputURL : URL,
withFormat format : AVAudioFormat,
fromSamples music : [Float] )
{
var outputFormatSettings = format.settings
guard let bufferFormat = AVAudioFormat(settings: outputFormatSettings) else{
return
}
var audioFile : AVAudioFile?
do{
audioFile = try AVAudioFile(forWriting: outputURL,
settings: outputFormatSettings,
commonFormat: format.commonFormat,
interleaved: true)
} catch let error as NSError {
print("error:", error.localizedDescription)
}
let frameCount = music.count / Int(format.channelCount)
let outputBuffer = AVAudioPCMBuffer(pcmFormat: bufferFormat,
frameCapacity: AVAudioFrameCount(frameCount))
//We write the data in the right channel
guard let bitDepth = (outputFormatSettings["AVLinearPCMBitDepthKey"] as? Int) else {
return
}
switch bitDepth {
case 16:
for i in 0..<music.count {
var floatValue = music[i]
if(floatValue > 1){
floatValue = 1
}
if(floatValue < -1){
floatValue = -1
}
let value = floatValue * Float(Int16.max)
outputBuffer?.int16ChannelData!.pointee[i] = Int16(value)
}
case 24:
//Here I am not sure of what I do ... Could'nt find the right channel !
for i in 0..<music.count {
outputBuffer?.floatChannelData!.pointee[i] = music[i]
}
case 32:
for i in 0..<music.count {
outputBuffer?.floatChannelData!.pointee[i] = music[i]
}
default:
return
}
outputBuffer?.frameLength = AVAudioFrameCount( frameCount )
do{
try audioFile?.write(from: outputBuffer!)
} catch let error as NSError {
print("error:", error.localizedDescription)
return
}
}
Thanks by advance if someone have an idea of how to handle this !
Representing a 24 bit int in C isn't fun so in Swift I'm sure it's downright painful, and none of the API's support it anyway. Your best bet is to convert to a more convenient format for processing.
AVAudioFile has two formats and an internal converter to convert between them. Its fileFormat represents the format of the file on disk, while its processingformat represents the format of the lpcm data when it is read from, and the format of the lpcm data that it will accept when being written to.
The typical workflow is choose a standard processingFormat, do all of your processing using this format, and let AVAudioFile convert to and from the file format for reading and writing to disk. All of the Audio Unit APIs accept non-interleaved formats, so I tend to use non interleaved for all of my processing formats.
Here's an example that copies the first half of an audio file. It doesn't address your existing code, but illustrates a more common approach:
func halfCopy(src: URL, dst: URL) throws {
let srcFile = try AVAudioFile(forReading: src) //This opens the file for reading using the standard format (deinterleaved floating point).
let dstFile = try AVAudioFile(forWriting: dst,
settings: srcFile.fileFormat.settings,
commonFormat: srcFile.processingFormat.commonFormat,
interleaved: srcFile.processingFormat.isInterleaved) //AVAudioFile(forReading: src) always returns a non-interleaved processing format, this will be false
let frameCount = AVAudioFrameCount(srcFile.length) / 2 // Copying first half of file
guard let buffer = AVAudioPCMBuffer(pcmFormat: srcFile.processingFormat,
frameCapacity: frameCount) else {
fatalError("Derp")
}
try srcFile.read(into: buffer, frameCount: frameCount)
try dstFile.write(from: buffer)
}

Write to file not working (Swift 4)

I am starting learning about Swift programming. Up to now, I have already developed my first working app. Although simple, it is a very useful one. But with the introduction of Swift 4 and XCode 9, I'm facing some headaches. Right now I am trying to write a small piece of code to write a string to a file, like this:
let fileName = "myFile.txt"
let path = NSURL(fileURLWithPath:
NSTemporaryDirectory()).appendingPathComponent(fileName)
var myText = "Some text to write to file"
do {
try myText.write(to: path!, atomically: true, encoding: UTF8)
} catch {
// Handle error
}
When I write myText.write, XCode suggests the above syntax, but right after I choose it and fill the placeholders, XCode displays the error "Extra argument 'atomically' in call. Note that it is the very structure it suggested. I haven't found any workaround until now. Can anyone help me?
The error is misleading. The encoding parameter is wrong
try myText.write(to: path!, atomically: true, encoding: .utf8)
And don't use NSURL in Swift 3+, use native URL:
let url = URL(fileURLWithPath: ...
However I recommend this way
let fileName = "myFile.txt"
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
let myText = "Some text to write to file"
let data = Data(myText.utf8)
do {
try data.write(to: url, options: .atomic)
} catch {
print(error)
}

Converting Docx Files To Text In Swift

I have a .docx file in my temporary storage:
let location: NSURL = NSURL.fileURLWithPath(NSTemporaryDirectory())
let file_Name = location.URLByAppendingPathComponent("5 November 2016.docx")
What I now want to do is extract the text inside this document. But I cannot seem to find any converters or methods of doing this.
I have tried this:
let file_Content = try? NSString(contentsOfFile: String(file_Name), encoding: NSUTF8StringEncoding)
print(file_Content)
However it prints nil.
So how do I read the text in a docx file?
Swift 4, Xcode 9.1, OSX targets from 10.10 to 10.13
I have found that the following code extracts text handily from a Word .doc file, which then easily goes into a string. (The attributed string contains formatting information that might be parsed to good effect.) The main info that I wanted to convey was the bit about using .docFormat to specify the document type.
let openPanel = NSOpenPanel()
var fileString = String("")
var fileData = NSData()
let fileURL = openPanel.url
do {
fileData = try NSData(contentsOf: fileURL!)
if let tryForString = try? NSAttributedString(data: fileData as Data, options: [
.documentType: NSAttributedString.DocumentType.docFormat,
.characterEncoding: String.Encoding.utf8.rawValue
], documentAttributes: nil) {
fileString = tryForString.string
} else {
fileString = "Data conversion error."
}
fileString = fileString.trimmingCharacters(in: .whitespacesAndNewlines)
} catch {
print("Word Document File Not Found")
}
Your initial issue is with how you get the string from the URL. String(File_Name) is not the correct way to convert a file URL into a file path. The proper way is to use the path function.
let location = NSURL.fileURLWithPath(NSTemporaryDirectory())
let fileURL = location.URLByAppendingPathComponent("My File.docx")
let fileContent = try? NSString(contentsOfFile: fileURL.path, encoding: NSUTF8StringEncoding)
Note the many changes. Use proper naming conventions. Name variables more clearly.
Now here's the thing. This still won't work because a docx file is a zipped up collection of XML and other files. You can't load a docx file into an NSString. You would need to use NSData to load the zip contents. Then you would need to unzip it. Then you would need to go through all of the files and find the desired text. It's far from trivial and it is far beyond the scope of a single stack overflow post.

Simple way to read local file using Swift?

I'm trying to learn the new Swift programming language. It looks great, but I'm having a difficult time doing something as simple as reading the content of a local .txt file.
I have tried the few examples I could find through Google, but they give compile errors, like this answer here: Read and write data from text file
If I tweak the code a bit, it works, but can only read from a special location within the project.
Why isn't it just as simple to read a .txt file with Swift as it is with for instance Ruby? And how would I go about reading the content of a file located at ~/file.txt?
Thnx
If you have a tilde in your path you can try this:
let location = "~/file.txt".stringByExpandingTildeInPath
let fileContent = NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding, error: nil)
otherwise just use this:
let location = "/Users/you/Desktop/test.txt"
let fileContent = NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding, error: nil)
This gives you a string representation of the file, which I assumed is what you want.
You can use NSData(contentsOfFile: location) to get a binary representation, but you would normally do that for, say, music files and not a text file.
Update: With Xcode 7 and Swift 2 this doesn't work anymore. You can now use
let location = NSString(string:"~/file.txt").stringByExpandingTildeInPath
let fileContent = try? NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding)
let file = "/Users/user/Documents/text.txt"
let path=URL(fileURLWithPath: file)
let text=try? String(contentsOf: path)
This would work:
let path = "~/file.txt"
let expandedPath = path.stringByExpandingTildeInPath
let data: NSData? = NSData(contentsOfFile: expandedPath)
if let fileData = data {
let content = NSString(data: fileData, encoding:NSUTF8StringEncoding) as String
}
Note that data may be nil, so you should check for that.
EDIT:
Don't forget conditional unwrapping - looks much nicer ;)
Relative path tip:
Instead of doing this:
NSString("~/file.txt").stringByExpandingTildeInPath
You can do this:
"\(NSHomeDirectory())/file.txt"
You may find this tool useful to not only read from file in Swift but also parse it simultaneously: https://github.com/shoumikhin/StreamScanner
Just specify the file path and data delimiters like this (see readme for more options):
import StreamScanner
if let input = NSFileHandle(forReadingAtPath: "/file/path")
{
let scanner = StreamScanner(source: input, delimiters: NSCharacterSet(charactersInString: ":\n")) //separate data by colons and newlines
while let field: String = scanner.read()
{
//use field
}
}
Hope, this helps.
Using the answer by Atomix, this will work in Swift 4:
let location = NSString(string: "~/test.txt").expandingTildeInPath
let fileContent = try? NSString(contentsOfFile: location, encoding: String.Encoding.utf8.rawValue)
This worked for me in Swift 2.1, XCode7 to get the location and print the contents of CSV. ( you can create a simple CSV in Text Wrangler)
override func viewDidLoad() {
super.viewDidLoad()
let location = NSString(string:"/Users/*Myusername*/Documents/myCSVfile.csv")
let fileContent = try? NSString(contentsOfFile: location as String, encoding: NSUTF8StringEncoding)
print(fileContent)
}
Swift 4:
let filePath = "/Users/UserName/Desktop/FolderName/FileName.txt"
let fullPath = NSString(string: filePath).expandingTildeInPath
do
{
let fileContent = try NSString(contentsOfFile: fullPath, encoding: String.Encoding.utf8.rawValue)
print(fileContent)
}
catch
{
print(error)
}
filename doesn't need to have scheme like file://, and can be relative like ../docs/test.txt.
Remember to catch any error thrown, or rethrow.
let url = URL(fileURLWithPath: filename)
let contents = try String(contentsOf: url, encoding: .utf8)