iOS Swift malloc error

686 views Asked by At

I want to record audio on iOS in real time, analyze the raw audio data and save parts of the recorded data. I'm recording the data with this code: https://gist.github.com/hotpaw2/ba815fc23b5d642705f2b1dedfaf0107

Now, my data is saved in a Float array and I want to save it to an audio file. I tried doing it with this code:

let fileMgr = FileManager.default
    let dirPaths = fileMgr.urls(for: .documentDirectory, in: .userDomainMask)
    let recordSettings = [AVEncoderAudioQualityKey: AVAudioQuality.min.rawValue,
                      AVEncoderBitRateKey: 16,
                      AVNumberOfChannelsKey: 2,
                      AVSampleRateKey: 44100] as [String: Any]
    let soundFileUrl = dirPaths[0].appendingPathComponent("recording-" + getDate() + ".pcm")


    do {
        let audioFile = try AVAudioFile(forWriting: soundFileUrl, settings: recordSettings)
        let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 44100, channels: 2, interleaved: true)

        let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: 3000)

        for i in 0..<circBuffer.count {
            audioFileBuffer.int16ChannelData?.pointee[i] = Int16(circBuffer[i])
        }

        try audioFile.write(from: audioFileBuffer)

    }

On the last line, I get an error which says:

ERROR: >avae> AVAudioFile.mm:306: -[AVAudioFile writeFromBuffer:error:]: error -50 amplitudeDemo(79264,0x70000f7cb000) malloc: * error for object 0x7fc5b9057e00: incorrect checksum for freed object - object was probably modified after being freed. * set a breakpoint in malloc_error_break to debug

I already searched in a lot of other questions, but I couldn't find anything that helped me.

2

There are 2 answers

2
OOPer On BEST ANSWER

In your code at this line:

let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: 3000)

You declare an AVAudioPCMBuffer with capacity 3000 frames * 2 channels, which means the allocated buffer for your audioFileBuffer can contain 6000 samples. If the index for the channel data exceeds this limit, your code destroys the nearby regions in the heap, which causes object was probably modified error.

So, your circBuffer.count may probably be exceeding this limit. You need to allocate enough buffer size, for the AVAudioPCMBuffer.

    do {
        //### You need to specify common format
        let audioFile = try AVAudioFile(forWriting: soundFileUrl, settings: recordSettings, commonFormat: .pcmFormatInt16, interleaved: true)

        let channels = 2
        let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 44100, channels: AVAudioChannelCount(channels), interleaved: true)
        let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(circBuffer.count / channels)) //<-allocate enough frames
        //### `stride` removed as it seems useless...
        let int16ChannelData = audioFileBuffer.int16ChannelData! //<-cannot be nil for the `format` above

        //When interleaved, channel data of AVAudioPCMBuffer is not as described in its doc:
        //  https://developer.apple.com/reference/avfoundation/avaudiopcmbuffer/1386212-floatchanneldata .
        //  The following code is modified to work with actual AVAudioPCMBuffer.
        //Assuming `circBuffer` as
        //  Interleaved, 2 channel, Float32
        //  Each sample is normalized to [-1.0, 1.0] (as usual floating point audio format)
        for i in 0..<circBuffer.count {
            int16ChannelData[0][i] = Int16(circBuffer[i] * Float(Int16.max))
        }
        //You need to update `frameLength` of the `AVAudioPCMBuffer`.
        audioFileBuffer.frameLength = AVAudioFrameCount(circBuffer.count / channels)

        try audioFile.write(from: audioFileBuffer)

    } catch {
        print("Error", error)
    }

Some notes added as comments, please check them before trying this code.


UPDATE Sorry, for showing untested code, two things fixed:

  • You need to specify commonFormat: when instantiating AVAudioFile.
  • int16ChannelData (and other channel data) does not return expected pointers as described in its documentation when interleaved, data filling loop modified to fit for the actual behaviour.

Please try.

0
shallowThought On

You seem to have chosen the frameCapacity arbitrarily to 3000.

Set it to the actual sample count:

let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(circBuffer.count))