Solving CPU Frequency Scaling Issues in Android for Dropout-free Audio
The recent update of the Superpowered Audio SDK (version 1.0.0) contains the following significant improvements:
CPUs on mobile devices constantly change their frequencies to improve battery life. This feature is directly proportional to the CPU load. If computing demand is high, the CPU runs at maximum frequency. If it’s low, the CPU runs at lower frequencies to save power.
This is a great way to save power on mobile devices as the video below describes, but is also a major problem for Android apps with professional audio and low latency requirements.
Live audio is processed and produced in fixed sized chunks (audio buffers). The audio system requires a steady flow of such buffers to provide dropout-free, continuous audio experience.
Each buffer represents a small amount of time. Computing the next chunk of audio must finish within this time. For example, if the mobile device’s audio stack uses a buffer size of 192 samples at 48000 Hz sample rate, the user application must compute a new buffer within 4 milliseconds (ie 192 / 48).
If the CPU runs at a low frequency, the user application may not be able to produce the new buffer within time and then users experience “audio dropouts”.
This is a excruciating problem for developers of Android apps with pro audio requirements, where CPU scaling is quite aggressive. It’s highly audible when the audio application goes into the background and Android throttles the CPU back even more, thinking that the application doesn’t need the same amount of CPU for audio, when it was in the foreground. The result?
An audio stream with many audio dropouts (and irritated developers and users).
In this case, Google officially recommends "using fake touch events to avoid CPU scaling", however, that only works on Nexus devices.
@szantog fake touches are only intended for Nexus devices, no guarantees for OEM devices which have different CPU freq scaling algorithms
— Don Turner (@donturner) July 13, 2016
This is confirmed in the a forum on Android audio here.
In case you were wondering, iOS also faces this problem too, but thanks to it’s better scheduling than Android, audio dropouts are only audible when the audio app computes the next audio chunk in more than 50% of the time represented by the buffer.
One would think that the sustained performance mode in Android 7.0 solves this problem, however Android automatically disables this mode when the application’s window is no longer in focus.
The SuperpoweredCPU Class
This class implements a "true" sustained performance mode, where the CPU runs near the maximum frequency on all CPU cores. It works in both foreground and background states, independently of the window focus. It supports:
- All versions of iOS and Android.
- ARM and X86 CPUs.
- Any number of CPU cores.
- CPUs with multiple different cores (popular design in recent mobile devices).
NOTE: CPU monitors will report higher CPU usage if this mode is enabled. But the increased CPU usage is "fake", because this feature is achieved with the NOP (No Operations) instruction. Meaning the CPU doesn't actually do "real processing" but instead executes stepping instructions.
One may think that keeping the CPU frequency high has very bad impact on battery life. This is only true if the CPU frequency is high due to actual processing of "real work". In other words, the CPU eats a lot of power and moves a lot of electrons if it needs to move or compute a lot of data. But it doesn’t happen with NOPs.
We have been using versions of the SuperpoweredCPU class internally on iOS with great result from 2010 to present. Our own apps have significantly better battery life than competitor apps.
We’ve also measured the battery life impact of our novel Superpowered sustained performance mode on a Samsung Galaxy Note 5, and the battery life difference over a 5 hours long period was less than 5%.
SuperpoweredCPU is useful on iOS as well, if you need to double the computing power of the CPU for audio processing.
CMake & Updated and Modernized Android Example Projects
As of today, every Superpowered Android example project supports the latest official development environment, Android Studio 2.2.2.
We’ve removed Google’s recommended "fake touch" trick to keep the CPU running at a higher frequency. This "fake touch" trick only worked on Nexus devices, and most recently on Android 7.0, it worked only if the app was in focus (foreground) -- so it’s not widely useful.
Every example project has been updated to use SuperpoweredCPU’s sustained performance mode, which provides a for CPU frequency scalcing for every Android device on every Android version (since 4.4.1).
The build environment is also upgraded to Gradle+CMake, which is now the new official build environment, recommended by Google. In the past 3 years we have seen 3 different environments and we hope Google finally settles with the current solution, as Android’s build system should be finally "ready".
CMake builds faster than ndkbuild and the experimental gradle plugin and CMake is somewhat less complex. Don’t expect Xcode-like speeds though.
Updated and Modernized iOS example projects
Updated for the recent version of Xcode. The latest versions of iOS require some recording and music library permissions/messages, so we have implemented them as well.
Updated iOS Latency Test App
Fixed a few things there too.
We’ve got more stuff coming later this month. In the meantime, go get the latest and greatest in Superpowered.
- Android
- CPU
- CPU Frequency Scaling
- CPU Scaling