FMOD Engine User Manual 2.03

4. Platform Details | Android

Android Specific Starter Guide

SDK Version

FMOD is compiled using the following tools.

Compatibility

FMOD supports devices of the below ABIs back to API level 21 (Android 5.0, Lollipop).

Libraries

Substitute $ABI with your desired ABI from the 'Compatibility' list above.

Core API library

Studio API library (used in conjunction with the core API library)

Java

FMOD is primarily a native C/C++ library implementation but does have a Java component that is invoked from native code. To ensure the Java component is properly operating please make sure you reference the fmod.jar in your project. This means telling the IDE or build system where to find the fmod.jar file so it's included in the application.

It is also highly recommended that you initialize the FMOD Java component, this will allow loading assets from the APK and automatic configuration for lowest latency. This should be done before System_Create or Studio::System::create, and should be closed after System::release or Studio::System::release.

A basic example is listed below, for more details please check the provided examples.

public class MainActivity extends Activity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        org.fmod.FMOD.init(this);
    }

    @Override
    protected void onDestroy()
    {
        org.fmod.FMOD.close();
    }
}

static
{
    System.loadLibrary("fmod");
}

If you do not have a Java activity class, you can use FMOD_Android_JNI_Init to perform the org.fmod.FMOD.init and System.loadLibrary initialization actions natively. You can likewise use FMOD_Android_JNI_Close to perform the org.fmod.FMOD.close cleanup. For example:

void android_main(struct android_app* app)
{
    jniEnv = NULL;
    app->activity->vm->AttachCurrentThread(&jniEnv, NULL);
    FMOD_Android_JNI_Init(app->activity->vm, app->activity->clazz);

    // ... game loop

    FMOD_Android_JNI_Close();
    app->activity->vm->DetachCurrentThread();
}

Examples

FMOD examples are shipped as Android Studio projects using the Gradle build system with the Android Gradle plug-in. Examples are provided for using both CMake and ndk-build to perform the external native build:

Linking FMOD to your code

To link FMOD into your native code refer to the Android documentation relevant to the build tools you are using. You may also find it helpful to refer to the Android.mk files or the CMakeLists.txt files from the FMOD examples.

Audio Latency

Reducing the amount of audio latency between calling an API function and hearing its effect is generally controlled via System::setDSPBufferSize. However it should be noted that on this platform there is significantly more OS latency (which is out of the control of developers). It is currently not mandatory for device manufactures to adhere to audio latency guidelines (section 5.3 Audio Latency of the Android CDD). Devices which report FEATURE_AUDIO_LOW_LATENCY will be able to achieve lower latency playback. This is handled internally by FMOD and requires no additional configuration. Latency test results for specific devices can be found on the Superpowered Latency Table.

Pairing with a BlueTooth speaker or headset will incur significant extra latency, 120ms in some tests. This is currently unavoidable due to the OS taking extra buffering beyond developer control.

Asset Manager

To load files from the APK using the Asset Manager (for files stored in the asset directory at build time) you need to use a special syntax. FMOD will recognize any path that is prefixed with file:///android_asset/ as an asset, so passing a path of file:///android_asset/drumloop.wav will load the file drumloop.wav which was stored in the asset directory at build time. For this functionality to work your device must be running Gingerbread or newer and have called org.fmod.FMOD.init from Java.

Native Threads

If you call FMOD from a native thread (not Java) you will need to ensure the thread is attached to the Java runtime environment JavaVM::AttachCurrentThread. It's recommended you remain attached for the life of the thread but you may call JavaVM::DetachCurrentThread after the invocation of FMOD if you prefer.

FMOD often makes calls to Java code contained within fmod.jar and therefore requires the thread to be attached. All internal FMOD threads are attached when they are created so this only concerns user threads.

Suspend in Background

FMOD native threads will continue running when your application transitions to the background, this will continue to use resources. To completely stop FMOD without losing your current setup you can call System::mixerSuspend as part of your backgrounding process. When you return to the foreground, use System::mixerResume to reactivate FMOD. It is extremely important to ensure no FMOD APIs are called in-between suspend and resume as they run the risk of causing a deadlock. You must also call suspend and resume pairs on the same thread.

Suspending Recording

Input streams are not guaranteed to be released when closing the application. If you are using audio input features such as System::recordStart and System::recordStop, calling System::mixerSuspend during backrounding is recommended to avoid leaking stream resources.

AAudio Device Selection

If you are targeting API 23 and above, you will be able to access all valid input and output devices when using FMOD_OUTPUTTYPE_AAUDIO. Some of these devices may require special permissions, feature sets, or API handling that are not available on all devices, and it is your responsibility to meet these prerequisites if you want to target that device type.
The device id can be retrieved from the Data1 value stored in the FMOD_GUID returned from System::getDriverInfo or System::getRecordDriverInfo, and the device type will be stored in the Data3 value. The device type can be compared to the corresponding Android AudioDeviceInfo type constants, and any special handling for that device type will need to be implemented before calling System::setDriver or System::recordStart with that device.

Input Devices

If you are targeting API 28 and above, each AAudio input device will have a corresponding device containing a "(Voice)" suffix in its name, and a Data2 value of true stored in the FMOD_GUID returned from System::getRecordDriverInfo. These input devices use the same physical device as their non "(Voice)" counterpart, but will have hardware echo cancellation enabled as well.

Permissions

Some functionality inside of FMOD will require you set relevant permissions in your AndroidManifest.xml file.

Thread Affinity

All threads will default to FMOD_THREAD_AFFINITY_CORE_ALL, this is recommended due to the wide variety of devices available but can be customized with Thread_SetAttributes.

Thread Priority

The relationship between FMOD platform agnostic thread priority and the platform specific values is as follows:

Known Issues

Application Lifecycle Management

FMOD will happily continue to operate when your device is in the background, for media playback applications this may be desirable. For the vast majority of use cases though, you want FMOD to be quiet and use no CPU. You can achieve this goal by using System::mixerSuspend and System::mixerResume, often it is convenient to implement these in the activity onStart and onStop overrides. To avoid issues when shutting down ensure you resume the mixer before releasing, it is recommended you perform this in the onDestroy override.

Example Java code

@Override
protected void onStart()
{
    super.onStart();
    setStateStart();
}

@Override
protected void onStop()
{
    setStateStop();
    super.onStop();
}

@Override
protected void onDestroy()
{
    setStateDestroy();
    super.onDestroy();
}

private native void setStateStart();
private native void setStateStop();
private native void setStateDestroy();

Example C++ code

void Java_org_fmod_example_MainActivity_setStateStart(JNIEnv *env, jobject thiz)
{
    gSystem->mixerResume();
}

void Java_org_fmod_example_MainActivity_setStateStop(JNIEnv *env, jobject thiz)
{
    gSystem->mixerSuspend();
}

void Java_org_fmod_example_MainActivity_setStateDestroy(JNIEnv *env, jobject thiz)
{
    gSystem->mixerResume();
}

The result of using this API will be the halt of the audio hardware and a complete lock of all FMOD threads. It is important that you do not call any FMOD API functions after System::mixerSuspend other than System::mixerResume, even if you intend to shutdown FMOD as you may cause a deadlock.

Performance Reference

This section is a companion for the CPU Performance white paper and serves as a quick reference of facts targeting this platform.

Format Choice

Each compression format provided in FMOD has a reason for being included, the below list will detail our recommendations for this platform. Formats listed as primary are considering the best choice, secondary formats should only be considered if the primary doesn't satisfy your requirements.

Channel Count

To give developers an idea about the costs of a particular format we provide synthetic benchmark results. These results are based on simple usage of the Studio API using recommended configuration settings.

Due to the CPU governor that controls the power saving features of the device, getting accurate CPU numbers requires rooting the device and setting the CPU frequency to maximum.

Settings

Test Device: A

Results: A

Test Device: B

Results: B