0

I am generating audio with an AVAudioEngine. I am then installing a tap on the mainMixerNode output of the engine which provides a AVAudioPCMBuffer which is then written into an MPEG4ACC AVAudioFile. The issue I have is that the audio played on the resulting .m4a file is slower in speed than the one that you can hear on the device output itself (speakers, headphones). This is the code I am implementing:


// Audio Format
let audioFormat = AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 2)

// Engine
var engine = AVAudioEngine()

// Two AVAudioNodes are hooked up to the AVAudioEngine
engine.connect(myAVAudioNode0, to: engine.mainMixerNode, format: audioFormat)
engine.connect(myAVAudioNode1, to: engine.mainMixerNode, format: audioFormat)

// Function to Write Audio to a File
func writeAudioToFile() {
        // File to write
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let audioURL = documentsDirectory.appendingPathComponent("share.m4a")

        // Format parameters
        let sampleRate = Int(audioFormat!.sampleRate)
        let channels = Int(audioFormat!.channelCount)

        // Audio File settings
        let settings = [
            AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
            AVSampleRateKey: Int(audioFormat!.sampleRate),
            AVNumberOfChannelsKey: Int(audioFormat!.channelCount),
            AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue
        ]

        // Audio File
        var audioFile = AVAudioFile()
        do {
            audioFile = try AVAudioFile(forWriting: audioURL, settings: settings, commonFormat: .pcmFormatFloat32, interleaved: false)
        }
        catch {
            print ("Failed to open Audio File For Writing: \(error.localizedDescription)")
        }

        // Install Tap on mainMixer
        // Write into buffer and then write buffer into AAC file
        engine.mainMixerNode.installTap(onBus: 0, bufferSize: 8192, format: nil, block: { (pcmBuffer, when) in
            do {
                try audioFile.write(from: pcmBuffer)
            }
            catch {
                print("Failed to write Audio File: \(error.localizedDescription)")
            }
        })
}
  • You can't simply set the format of your own on a node from the main mixer. You'll have to use an `AVAudioConverter`. The audio is single channel, and if you split it in 2 nodes, probably your speed will be slowed by a factor of 2. – Vlad Rusu Mar 28 '20 at 00:08
  • Yes that's what I was going to say, I can't help noticing that you are setting the audio file settings based on an arbitrary `audioFormat` rather than using the format that the input file actually has. – matt Mar 28 '20 at 00:17
  • Thanks for your comments. I need to clarify that the input nodes to the engine are internal `AVAudioUnitSampler`nodes, not coming from a node playing an input file nor a node hooked up to an external source. If I don’t set the format of the `AVAudioEngine` and the `AVAudioPCMBuffer` to be the format of the `AVAudioFile` I get different types of errors related to incorrect settings or incorrect format. Also the audio generated isn’t slowed by a factor of 2, if I was to guess it is probably 10 to 20% slower. – caminante errante Mar 28 '20 at 07:09
  • Another thing to point out is that even when I am not storing to a file, setting the engine to the format specified above produces perfect 2 channel stereo audio at the correct speed to the speakers or headphones. I have even tried changing the format to specify 1 channel instead of 2 and it produces the expected result of mono audio and at the correct speed. – caminante errante Mar 28 '20 at 07:14

1 Answers1

0

I printed out the AVAudioPCMBuffer format and found that it was defaulting to a sample rate of 48000Hz. But this is not always the case. First, determine the audio session sample rate and use that sample rate for the audio format:

let audioSession = AVAudioSession.sharedInstance()
let sampleRate = audioSession.sampleRate
let audioFormat = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channels: 2)