avaudioengineavaudiofile

My writing the installTap buffer to an AVAudioFile seems to fail data-wise


I am trying to save the buffer from my installTap to a file. I do it in chunks of 10 so I'll get a bigger file. When I try to play the written file (from the simulator's directory) QuickTime says that it's not compatible.

I have examined the bad m4a file and a working one. There are a lot of zero's in the bad file at the beginning followed by a lot of data. However both files appears to have the same header.

A lot of people mention that I have to nil the AudioFile, but:

audioFile = nil

is not a valid syntax, nor can I file a close method in AudioFile.

Here's the complete code, edited into one working file:

import UIKit
import AVFoundation

class ViewController: UIViewController {
    let audioEngine = AVAudioEngine()
    var audioFile = AVAudioFile()
    var x = 0


    override func viewDidLoad() {
        super.viewDidLoad()
        record()
        // Do any additional setup after loading the view.
    }

    func makeFile(format: AVAudioFormat) {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
        do {
            _ = try FileManager.default.contentsOfDirectory(at: paths!, includingPropertiesForKeys: nil)
        } catch { print ("error")}
        let destinationPath = paths!.appendingPathComponent("audioT.m4a")
        print ("\(destinationPath)")
        do {
            audioFile = try AVAudioFile(forWriting: destinationPath,
                                            settings: format.settings)

            print ("file created")
        } catch { print ("error creating file")}
    }
    
    func record(){
        let node = audioEngine.inputNode
        let recordingFormat = node.inputFormat(forBus: 0)
        makeFile(format: recordingFormat)
        
        
        node.installTap(onBus: 0, bufferSize: 8192, format: recordingFormat, block: { [self]
            
            (buffer, _) in
            do {
                try audioFile.write(from: buffer);
            print ("buffer filled");
                x += 1;
                print("wrote \(x)")
                if x > 9 {
                    endThis()
                }
            } catch {return};})
        
        audioEngine.prepare()
        do {
            try audioEngine.start()
        } catch let error {
            print ("oh catch")
        }
    }
    
    func endThis(){
        audioEngine.stop()
        audioEngine.inputNode.removeTap(onBus: 0)
    }
}

Solution

  • You need to let your AVAudioFile go out of scope (nil it at some point), that's how you call AVAudioFile's close() method, which presumably finishes writing out header information.