8

I want to read the buffer from my microphone into an array, with 44.1khz its working fine but with the sample rate 8khz it comes to an error

ERROR:    >avae> AVAudioIONodeImpl.mm:884: SetOutputFormat: required condition is false: format.sampleRate == hwFormat.sampleRate
2016-11-26 19:32:40.674 Atem[5800:1168274] *** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: format.sampleRate == hwFormat.sampleRate'

with my following code :

 var engine = AVAudioEngine()
    func setup() {

    print("new")
    let input = engine.inputNode!
    let bus = 0

    let mixer = AVAudioMixerNode()
    engine.attach(mixer)

    engine.connect(input, to: mixer, format: input.outputFormat(forBus: 0))
    //pcmFormatFloat64 -- pcmFormatFloat32
    print(engine.isRunning)
    let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 12000, channels: 1, interleaved: true)

    do {
        try engine.start()


     print(engine.isRunning)

        mixer.installTap(onBus: bus, bufferSize: 1024, format: fmt) { (buffer, time) -> Void in
            // 8kHz buffers!
            print(buffer.format)
        }
    }catch {
        //print("An error occurred \(error)")
        return
    }

}

can anyone help ?

WestCoastProjects
  • 58,982
  • 91
  • 316
  • 560
Osman
  • 1,496
  • 18
  • 22

1 Answers1

14

Updated answer this answer used to try to rate convert with taps and mixers, which does not work.

You can use an AVAudioConverter to do rate conversion on the AVAudioEngine inputNode:

let engine = AVAudioEngine()

func setup() {
    let input = engine.inputNode
    let bus = 0
    let inputFormat = input.outputFormat(forBus: 0)
    let outputFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 8000, channels: 1, interleaved: true)!

    let converter = AVAudioConverter(from: inputFormat, to: outputFormat)!

    input.installTap(onBus: bus, bufferSize: 512, format: inputFormat) { (buffer, time) -> Void in
        var newBufferAvailable = true

        let inputCallback: AVAudioConverterInputBlock = { inNumPackets, outStatus in
            if newBufferAvailable {
                outStatus.pointee = .haveData
                newBufferAvailable = false
                return buffer
            } else {
                outStatus.pointee = .noDataNow
                return nil
            }
        }

        let convertedBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: AVAudioFrameCount(outputFormat.sampleRate) * buffer.frameLength / AVAudioFrameCount(buffer.format.sampleRate))!

        var error: NSError?
        let status = converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback)
        assert(status != .error)

        // 8kHz buffers!
        print(convertedBuffer.format)
    }

    try! engine.start()
}
App Dev Guy
  • 5,396
  • 4
  • 31
  • 54
Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
  • thx for the good answer but nothing changed.....i stil gets the buffer with 4100 Hz. i checked the input format from the mixer and it is still 4100 Hz. – Osman Nov 27 '16 at 01:32
  • sorry, fixed it – Rhythmic Fistman Nov 27 '16 at 11:16
  • 1
    i have now this error : kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=1024, mMaxFramesPerSlice=768 – Osman Nov 27 '16 at 18:09
  • I don't no i have just the the print(buffer.format) i think the error is coming from mixer.installTap because with 44100 and 16000 Hz is working fine – Osman Nov 27 '16 at 20:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/129171/discussion-between-steve-john-and-rhythmic-fistman). – Osman Nov 27 '16 at 22:01
  • @Rhythmic Fistman I read some comments on github that we can not set a sample rate different than then the device. I got excited when I saw your code, but couldnt make it work. Can I use it with AKPlayer? I dont use AvAudioEgine – Spring Feb 03 '19 at 21:27
  • 1
    I've never used AKPlayer, but you can definitely use `AVAudioConverter` to easily do sample rate conversion. Now that I look at it, I think that converter code should have a flag to stop `AVAudioConverterInputBlock` returning the same buffer more than once. – Rhythmic Fistman Feb 04 '19 at 10:09
  • @ Rhythmic Fistman I am trying your code now. indeed "convertedBuffer" has the new sample rate but I lose the data in "buffer"? How convertedBuffer supposed to have the data? its just newly created? Also the "inputCallback" and "converter" is never used, what s the purpose of those? THANKS – Spring Feb 04 '19 at 20:20
  • Huh this answer is missing a line. Where did that go? Let me fix it. – Rhythmic Fistman Feb 06 '19 at 17:40
  • Fixed it, I don't know how this answer was accepted. – Rhythmic Fistman Feb 06 '19 at 22:29
  • 1
    What is fmt here ? – Krishna Datt Shukla Oct 03 '19 at 05:14
  • I don’t see anything called fmt in this answer. Can you be more specific? – Rhythmic Fistman Oct 03 '19 at 05:18