Our iOS apps had horrible audio performance on iOS 18. We also noticed that we only saw one available buffer size (96) instead of the usual list from 128 to 4096 (on my iPad Mini, for example).
The cause of this is a bug in tryBufferSize(). We use JUCE 6 and 7, but I just looked at the JUCE 8 source, and it is still there as well.
setPreferredIOBufferDuration must be called with a buffer duration in seconds. The code was calculating the duration as:
((newBufferSize + 1) / currentSampleRate)
But it should be this:
(newBufferSize / currentSampleRate)
Apparently on iOS 18 this makes a difference. All calls to tryBufferSize() from updateAvailableBufferSizes() were only ever getting an end result of 96 samples. No error was reported by setPreferredIOBufferDuration either.
Thanks for reporting, I can repro the behaviour you’re seeing where only a single buffer size is reported as available. This seems specific to hardware devices running iOS 18. The iOS 18 simulator isn’t affected, and an older iPhone running iOS 16.4.1 also isn’t affected.
The proposed change looks reasonable. However, seeing as the + 1 was initially added back in 2016, I’m worried that removing it may break compatibility with some older devices running iOS 12+. Unfortunately I don’t have any such devices to hand right now - have you been able to test the change on any older hardware devices?
The change works on devices going back to iOS 15 for sure, but we just tracked down an old iPad running iOS 12, and the new code does not work, but the original code does. So this is a change that is iOS version specific. Considering that the old code works on iOS 17 and older, this could be a change for just iOS 18+ to be safe.
Guys this is a massive bug, when will be released an update with this fix? Is there something we can do besides changing manually the JUCE source code (which is something I really would like to avoid in a production scenario!) ?
For the JUCE team: this bug is critical, we all have apps on the App Store that are unusable on iOS 18 because of this bug, I think this should be addressed with maximum priority
Unfortunately it does not work for us, there are also problems when switching sample rate.
So, before adding the above mod the buffer size drop down menu in our app was simply empty (usually shows only power of 2 buffers from 32 to 1024, to avoid unnecessary long menus when the soundcard supports lots of buffer sizes).
After adding that mod some buffer sizes appears in the combobox, but the behavior is totally unpredictable and often selecting a value from the menu does not change anything or makes the sound corrupted. Changing sample rate also affects the issue, making it even more difficult to understand and fix. That’s not all, sometimes when you change buffer size it changes the sample rate!
Of course the exact same app on iOS 17 works flawlessly.
This looks like the code snippet I’ve uploaded before, and unfortunately I’ve tried it and it is not enough to fix the problem. As described before, this issue is affecting also the sample rate selection and seems to be unpredictable. I’ve tested it with a couple of very famous sound cards with the same results. The problem does not arise in the iOS simulator, only on real devices.
In “updateBufferSizeComboBox” inside my custom AudioDeviceSelectorComponent I’ve got this code, which is pretty the same as the original JUCE class
I discovered changes in the behaviour of the AVAudioSession setPreferredIOBufferDuration:error: method. Previously calling this method would synchronously result in an observable change of the AVAudioSession.IOBufferDuration property, and this is what we used to enumerate the available buffer sizes, for which Apple is not providing a direct API.
On iOS 18 however the setPreferredIOBufferDuration:error: appears to behave in an asynchronous way, and its effect is not immediately visible on the IOBufferDuration property. This has very difficult to mitigate consequences on our buffer size enumeration code, because there is no callback on the iOS API which would tell us when the requested buffer change has taken effect. At least I can’t find one.
As a workaround, it is possible to sleep “enough” so that the changes take effect. I’m sharing a patch doing just that. It would be very helpful if you could take a look @aleFX to see if this behaves acceptably with the external devices in your use.
We’ll file a bug report with Apple in the hope that they can point us to a workaround or maybe address this change, but there’s no telling how that will unfold.
As far as I can tell this isn’t causing any problems with the audio processing itself. When the audio callback function is called the actual buffer size is reported and used.
So in order to notice this you have to spend enough time on the buffer size enumerating / selecting functions. Even I missed the issue on my first testing, but if you just keep enumerating and selecting different buffer sizes you’ll eventually see strange results - all the while audio processing is still carrying on correctly.
Just another data point, we are seeing the same buffer issue which results in choppy audio in our iOS app. I have tried the latest patch and it does not solve the issue. Xcode 16 and iOS 18.0.
Can you share more about how that’s happening? Is the selected buffer size ending up as 64 despite wanting a larger buffer? Is that causing the choppy audio?
I’ve tested the patch, unfortunately the issue is still there. Sometimes the sample rate also is not set correctly and you can clearly hear pitched up/down sound, I’ve tried with 200 ms instead of 100 but nothing changes.