5

I'm creating an AVAudioFile for writing sound to a sound file. If the file already exists I want to move the framePosition to the end of the file, to continue writing at the end, instead of replacing the existing file.

I did some tests, trying to read the buffer from the file into a new file, with a different URL, so that it won't overwrite the original file. I'm getting a crash when I'm trying to read the buffer into the new file:

let audioFile = try AVAudioFile(forReading: [URL to existing .caf file])
let audioFrameCount = AVAudioFrameCount(UInt32(audioFile.length))
let audioBuffer = AVAudioPCMBuffer(PCMFormat: audioFile.processingFormat, frameCapacity: audioFrameCount)

let newAudioFile = try AVAudioFile(forWriting: [another URL], settings: self.engine.mainMixerNode.outputFormatForBus(0).settings)
try newAudioFile.readIntoBuffer(audioBuffer, frameCount: audioFrameCount!) <-- CRASHES ON THIS LINE

Crash log: Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'error -50'

Man, I really hate the CoreAudio crash logs. They tell me absolutely nothing!

Isn't it possible to read data into a file that's been created for writing?

UPDATE

OK, so after some suggestions I did some changes. Basically, these are the steps I'm taking:

  1. Check if a file already exists.
  2. If it does, open it for reading and get the audio buffer.
  3. Create a new file for writing (using the same file URL)
  4. Use writeFromBuffer to write the buffer from the old file to the new file
  5. Move the framePosition of the new file to the end, so that I can continue writing/recording to it.

However, the length of the new file is 0 after I have written to it.

Here's my code:

//Check if a file already exists. If so continue to record at the end of it
var audioBuffer : AVAudioPCMBuffer!
var audioFrameCount : AVAudioFrameCount!
if (NSFileManager.defaultManager().fileExistsAtPath(self.audioRecordURL.path!)) {
    do {
        let existingAudioFile = try AVAudioFile(forReading: self.audioRecordURL)
        audioFrameCount = AVAudioFrameCount(existingAudioFile.length)
        if (audioFrameCount > 0) {
            audioBuffer = AVAudioPCMBuffer(PCMFormat: existingAudioFile.processingFormat, frameCapacity: audioFrameCount)
        }
    } catch let error as NSError {
        NSLog("Error reading buffer from file %@", error.localizedDescription)
    }
}

//Create a new file. This will replace the old file
do {
    self.audioFile = try AVAudioFile(forWriting: self.audioRecordURL, settings: self.engine.mainMixerNode.outputFormatForBus(0).settings)
} catch let error as NSError {
    NSLog("Error creating AVAudioFile %@", error.localizedDescription)
}

//Read the audio buffer from the old file into the new file
if (audioBuffer != nil) {
    do {
        try self.audioFile.writeFromBuffer(audioBuffer)
        self.audioFile.framePosition = self.audioFile.length
    } catch let error as NSError {
        NSLog("Error reading buffer into file %@", error.localizedDescription)
    }
}

By the way, the naming of the readIntoBuffer is extremely confusing to me. It sounds as if you should use that method to read a file into a buffer, but according to the documentation you should use it to read a buffer into a file? So why can't I use that method to add the buffer to my file? Why do I have to use writeFromBuffer?

UPDATE 2

So, I managed to solve it. Apparently I had to call readIntoBuffer to actually fill the buffer with data before I could use it. So I added this line

try existingAudioFile.readIntoBuffer(audioBuffer)

after

audioBuffer = AVAudioPCMBuffer(PCMFormat: existingAudioFile.processingFormat, frameCapacity: audioFrameCount)
rodskagg
  • 3,827
  • 4
  • 27
  • 46
  • `-50` is usually a parameter error. Is that `!` behind `audioFrameCount` really necessary? Does `audioFrameCount` have a valid value? – Hendrik Dec 16 '15 at 08:28
  • I removed the ! (don't know why I had that there). Yes, audioFrameCount has a valid value. – rodskagg Dec 16 '15 at 09:48
  • And you are still getting -50? Either it's really not possible to read from a file opened for writing or your settings somehow don't match the audio buffer format. Why don't you open the file for reading first, read, close the file and then open it for writing to work around this issue? – Hendrik Dec 16 '15 at 09:52
  • Hi @andlin, I have a problem that i want to discuss with you. I am saving audio file using AVAudioEngine & AVAudioFile, But the problem is that my App crash while creating new file. In my code, crash on this line "AVAudioFile(forWriting: MyUrl, settings: self.recordingEngine.mainMixerNode.outputFormatForBus(0).settings)". Can you guide me please. Thanks – Ahtazaz May 03 '19 at 12:37

1 Answers1

1

Not sure if this is just a typo in the code you've provided here, and I'm not an expert in this area, but presumably you meant to have the line that crashes be:

try audioFile.readIntoBuffer(audioBuffer, frameCount: audioFrameCount!)

because it stands to reason that you can't read from a file opened for writing (newAudioFile).

And then after filling that buffer you would want to write to the new file using writeFromBuffer https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVAudioFile_Class/#//apple_ref/occ/instm/AVAudioFile/writeFromBuffer:error:

craigts
  • 2,857
  • 1
  • 25
  • 25
  • I want to add the buffer from my old audio file to my newly created one (and then continue recording to the end of it). Should I use writeFromBuffer instead? Doing that I don't get a crash, my the length of my audio file is 0 after the write. I'll update my question with some more info – rodskagg Dec 16 '15 at 09:58
  • Thanks for helping me solve this. I didn't realize that I had to call readIntoBuffer to actually fill the buffer with data from the old file, before I could use the buffer. The documentation for readIntoBuffer states "The buffer from which to read into the file." which to me sounds like the exact opposite of what it's actually for. – rodskagg Dec 16 '15 at 10:47
  • Hi @andlin, I have a problem that i want to discuss with you. I am saving audio file using AVAudioEngine & AVAudioFile, But the problem is that my App crash while creating new file. In my code, crash on this line "AVAudioFile(forWriting: MyUrl, settings: self.recordingEngine.mainMixerNode.outputFormatForBus(0).settings)" with above same CRASH LOG "Crash log: Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'error -50'". Can you guide me, please? Thanks – Ahtazaz May 04 '19 at 05:01