We're working on an application which records and persists microphone input. The use of AVAudioRecorder was not an option, because real-time audio processing is needed.
AVAudioEngine is used because it provides low-level access to the input audio.
let audioEngine = AVAudioEngine()
let inputNode = audioEngine.inputNode
let inputFormat = inputNode.inputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(inputFormat.sampleRate * sampleInterval), format: inputFormat) { (buffer: AVAudioPCMBuffer, time: AVAudioTime) -> Void in
// sound preprocessing
// writing to audio file
audioFile.write(buffer.floatChannelData![0])
})
Our issue is that the recording is quite large. For a 5 hour recording, the output audio file is 1.2GB with .caf format.
let audioFile = AVAudioFile(forWriting: recordingPath, settings: [:], commonFormat: .pcmFormatFloat32, interleaved: isInterleaved)
Is there a nice way to compress the audio file writing to it?
The default sampling frequency is 44100Hz. We will use AVAudioMixerNode to downsample the input to 20Khz (lower quality is acceptable in our case) but the size of the output won't be acceptable in size.
The recording contains large segments of background noise.
Any suggestions?
1 Answer 1
The .caf container format supports AAC compression. Enable it by setting the AVAudioFile settings dictionary to [AVFormatIDKey: kAudioFormatMPEG4AAC]:
let audioFile = try! AVAudioFile(forWriting: recordingPath, settings: [AVFormatIDKey: kAudioFormatMPEG4AAC], commonFormat: .pcmFormatFloat32, interleaved: isInterleaved)
There are other settings keys that influence the file size and quality: AVSampleRateKey, AVEncoderBitRateKey and AVEncoderAudioQualityKey.
p.s. you need to close your .caf file when you've finished with it. AVAudioFile doesn't have an explicit close() method, so you close it implicitly by nilling any references to it. Uncompressed .caf files seem to be playable without this, but AAC files are not.
p.p.s as of 2024 AVAudioFile has an explicit close() method (iOS 18+ and macOS 15+). Exciting times.
p.p.p.s 2025 update - Xcode 16 header comments indicate that close is available in all iOS versions, although I'm not sure if that's actually true:
/** @method close
@abstract Close the audio file.
@discussion
The underlying file will be closed if open.
- It is normally unnecessary to close a file opened for reading (it will be automatically closed
when the object is released)
- It is only necessary to close a file opened for writing in order to achieve specific control over
when the file's header is updated.
Note: Once closed, further file read or write operations will fail with kAudio_FileNotFoundError.
*/
@available(macOS 15.0, *)
open func close()
I love the passive aggressive, written-under-duress tone of this comment and the implication that wanting your audio file to be in a well defined, usable state at a well defined point of time and not at the mercy of ARC is somehow a bizarre, fringe use-case and should you foolishly try to write after close, well, you've been warned and be it on your own foolish head, oh ye who fail to appreciate sublime API design and instead file feature requests for mediocre and redundant methods that waste the time of artisanal, master API craftsmen, just be thankful we don't throw an uncatchable exception like we were so fond of doing back when we designed AVAudioEngine.