I am creating an AVAudioFile to record sound in an audio file. If the file already exists, I want to move 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 a file to a new file with a different URL so that it would not overwrite the original file. I get a crash when I try to read the buffer into a 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: application terminated due to an uncaught exception "com.apple.coreaudio.avfaudio", reason: "error -50"
Man, I really hate CoreAudio crash logs. They donโt tell me anything!
Is it impossible to read the data in the file that was created for writing?
UPDATE
OK, so after some suggestions I made some changes. Basically, these are the steps that I take:
- Check if the file exists.
- If so, open it for reading and get a sound buffer.
- Create a new file for writing (using the same file URL)
- Use writeFromBuffer to write buffer from old file to new file
- Move the framePosition of the new file to the end so I can continue recording / writing to it.
However, the length of the new file is 0 after I wrote it.
Here is 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 name readIntoBuffer is very confusing to me. It seems you should use this method to read the file into the buffer, but according to the documentation you should use it to read the buffer into the file? So why can't I use this method to add a buffer to my file? Why should I 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)