I'm trying to get short audio samples into my iOS app (in Kotlin Multiplatform). Here's my relatively simple setup (sampleRateInHz is defined elsewhere):
val bufferSize: AVAudioFrameCount = 512u
val session = AVAudioSession.sharedInstance()
session.setCategory(AVAudioSessionCategoryPlayAndRecord, null)
session.setMode(AVAudioSessionModeMeasurement, null)
session.setPreferredSampleRate(sampleRateInHz.toDouble(), null)
session.setPreferredIOBufferDuration(bufferSize.toDouble() / session.sampleRate, null)
session.setActive(true, null)
val engine = AVAudioEngine()
val input = engine.inputNode
val bus: AVAudioNodeBus = 0u
val format = input.inputFormatForBus(bus)
input.installTapOnBus(bus, bufferSize, format) { buffer, time ->
// ...use the data
}
val success = engine.startAndReturnError(null)
I'm using a sample rate of 48 kHz (which seems to be the default anyways). I'm targeting a buffer size of 512 frames which comes out to be a duration about 0.0107 s. No matter what I do it seems to not let me go below 4800 frames (= 0.1s). According to Apple's docs, it seems that the buffer duration should be able to go well below 0.1 s but that's not what I'm seeing here.
Logging the sample rate and IO buffer duration shows that the values are being correctly set (48 kHz and ~0.0107 s) but my AVAudioEngine insists on giving me a minimum of 4800 frames. Increasing the desired buffer size above 4800 frames works great but I still can't go lower. I've tried other devices as well as the iOS simulator but I've been seeing the same results everywhere.
Without saying why you want a smaller buffer size I could simply recommend you slice the larger buffers into smaller ones, so I'll assume you want a smaller buffer size to lower capture latency.
Your problem is that AVAudioNode.installTap()
is documented to not do what you want:
Supported range is [100, 400] ms.
AVAudioEngine
taps are designed for low/medium frequency updates. If you want higher frequency updates you can try inserting your own subclass of AVAudioSourceNode
into the audio AVAudioEngine
graph or replacing AVAudioEngine
with a remote-IO audio unit.
With either of these solutions you will likely receive ~10ms buffers without even modifying the AVAudioSession
preferredIOBufferDuration
, except during screen lock.
In any case iOS and AVAudioSession
are free to ignore your buffer duration chnage request, so don't depend on it working.