All the sample code I can find that uses AudioConverterRef
focuses on use cases where I have all the data up-front (such as converting a file on disk). They commonly call AudioConverterFillComplexBuffer
with the PCM to be converted as the inInputDataProcUserData
and just fill it in in the callback. (Is that really how it's supposed to be used? Why does it need a callback, then?) For my use case, I'm trying to stream aac audio from the microphone, so I have no file, and my PCM buffer is being filled in in real time.
Since I don't have all the data up-front, I've tried doing *ioNumberDataPackets = 0
in the callback once my input data is out, but that just puts the AudioConverter in a dead state where it needs to be AudioConverterReset()
ted, and I don't get any data out of it.
One approach I've seen suggested online is to return an error from the callback if the data I have stored is too small, and just try again once I have more data, but that seems like such a waste of resources that I can't bring myself to even try it.
Do I really need to do the "retry until my input buffer is big enough", or is there a better way?
For future reference, there is a way way easier option.
The CoreAudio header's state:
So, do exactly that. Instead of returning noErr with *ioNumberDataPackets = 0, return any error (just make one up, I used -1), and the already converted data will be returned, while the Audio Converter stays alive and does not need to be reset.
AudioConverterFillComplexBuffer
does not actually mean "fill the encoder with my input buffer that I have here". It means "fill this output buffer here with encoded data from the encoder". With this perspective, the callback suddenly makes sense -- it is used to fetch source data to satisfy the "fill this output buffer for me" request. Maybe this is obvious to others, but it took me a long time to understand this (and from all the AudioConverter sample code I see floating around where people send input data throughinInputDataProcUserData
, I'm guessing I'm not the only one).The
AudioConverterFillComplexBuffer
call is blocking, and is expecting you to deliver data to it synchronously from the callback. If you are encoding in real time, you will thus need to callFillComplexBuffer
on a separate thread that you set up yourself. In the callback, you can then check for available input data, and if it is not available, you need to block on a semaphore. Using an NSCondition, the encoder thread would then look something like this:And the callback could look like this: (note that I normally use this trampoline to immediately forward to a method on my instance (by forwarding my instance in
inUserData
; this step is omitted for brevity)):And for completeness, here's how you would then feed this encoder with data, and how to shut it down properly: