Swift 3 AVAudioEngine set microphone input format
Asked Answered
L

1

2

I want to process the bytes read from the microphone using Swift 3 on my iOS. I currently use AVAudioEngine.

print(inputNode.inputFormat(forBus: bus).settings)
print(inputNode.inputFormat(forBus: bus).formatDescription)

This gives me the following output:

["AVNumberOfChannelsKey": 1, "AVLinearPCMBitDepthKey": 32, "AVSampleRateKey": 16000, "AVLinearPCMIsNonInterleaved": 1, "AVLinearPCMIsBigEndianKey": 0, "AVFormatIDKey": 1819304813, "AVLinearPCMIsFloatKey": 1]
<CMAudioFormatDescription 0x14d5bbb0 [0x3a5fb7d8]> {
    mediaType:'soun' 
    mediaSubType:'lpcm' 
    mediaSpecific: {
        ASBD: {
            mSampleRate: 16000.000000 
            mFormatID: 'lpcm' 
            mFormatFlags: 0x29 
            mBytesPerPacket: 4 
            mFramesPerPacket: 1 
            mBytesPerFrame: 4 
            mChannelsPerFrame: 1 
            mBitsPerChannel: 32     } 
        cookie: {(null)} 
        ACL: {(null)}
        FormatList Array: {(null)} 
    } 
    extensions: {(null)}
}

The problem is that the server I want to send the data to does not expect 32 bit floats but 16 bit unsigned ints. I think I have to change the mFormatFlags. Does anybody know how I can do this and what value would be the right one?

The resulting byte stream should be equivalent to the one I get on android using

AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLES_PER_SECOND,
            AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
            recordSegmentSizeBytes);

I tried this:

let cfmt = AVAudioCommonFormat.pcmFormatInt16
        inputNode.inputFormat(forBus: bus) = AVAudioFormat(commonFormat: cfmt, sampleRate: 16000.0, channels: 1, interleaved: false)

but got this error

Cannot assign to value: function call returns immutable value

Any ideas?

Longish answered 25/5, 2017 at 15:24 Comment(0)
L
6

Oh my god, I think I got it. I was too blind to see that you can specify the format of the installTap callback. This seems to work

let audioEngine  = AVAudioEngine()

func startRecording() {
    let inputNode = audioEngine.inputNode!
    let bus = 0

    let format = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: 16000.0, channels: 1, interleaved: false)

    inputNode.installTap(onBus: bus, bufferSize: 2048, format: format) { // inputNode.inputFormat(forBus: bus)
        (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in

        let values = UnsafeBufferPointer(start: buffer.int16ChannelData![0], count: Int(buffer.frameLength))
        let arr = Array(values)
        print(arr)
    }


    audioEngine.prepare()
    do {
        try audioEngine.start()
    } catch {
        print("Error info: \(error)")
    }
}
Longish answered 25/5, 2017 at 16:2 Comment(5)
this makes crash in code: ERROR: [0x3b49de40] >avae> AVAudioIONodeImpl.mm:884: SetOutputFormat: required condition is false: format.sampleRate == hwFormat.sampleRate if you hav any soln plz sendMosque
For anyone else experiencing the issue described by @HardikDarji here, try changing the format variable to this: let format = inputNode.inputFormat(forBus: 0)Etching
@MarkLyons if you set format to inputNode.inputFormat then you won't get the 16000 sample rate the OP asked for, you'll get the microphone's sample rate. The solution to Hardik Darji's problem is to add a mixer node and connect it to the main mixer node with the format.Nicety
@Nicety any code sample of how to do that? I tried many ways but could not make it work (some times crash and some times recorded only silence). I tried with & without audio-session, different format setting... etc. I probably messed up somewhere because sometimes I was able to get data (buffer) but only silence. Should I explicitly connect like : inputNode -> fooMixuteNode -> mainMixureNode and then tap on mainMixureNode? would you please give some preciece hints.Tamah
@HassanTareq I no longer have the code, sorry. I think I ended doing AVAudioSession.sharedInstance().setPreferredSampleRate(16000.0) instead. That worked until iOS 13, where it seems to be ignored, and I just gave up.Nicety

© 2022 - 2024 — McMap. All rights reserved.