[Android] mixerResume bug

System::mixerResume() crash on Android using Native Activity.
All is fine using Java activity. The problem is only with Native Activity.

The first call to mixerResume make the last few milliseconds of sound repeat forever and the second call to mixerResume crashes the app.

The error code is always OK and the logcat shows nothing suspicious.

I call mixerSuspend on APP_CMD_STOP command and mixerResume on APP_CMD_START.

Like this:

static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
	FMOD_RESULT result;
    switch (cmd) {
        case APP_CMD_START:
        	result = mpSystem->mixerResume();        	
        	break;
        case APP_CMD_STOP:
        	result = mpSystem->mixerSuspend();        	
        	break;
    }
}

This is the full native code:

#include <jni.h>
#include <android_native_app_glue.h>
#include "fmod.hpp"

struct engine {
    struct android_app* app;
};

FMOD::System	*mpSystem = 0;
FMOD::Sound  	*sound = 0;


static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {

	// play sound
	if(sound != 0)
	{
		FMOD_RESULT       result;
		FMOD::Channel    *channel = 0;
		result = mpSystem->playSound(sound, 0, false, &channel);		
	}
    return 0;
}


static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
	FMOD_RESULT result;
    switch (cmd) {
        case APP_CMD_START:
        	result = mpSystem->mixerResume();
        	break;
        case APP_CMD_STOP:
        	result = mpSystem->mixerSuspend();
        	break;
    }
}

void android_main(struct android_app* state) {

	if(mpSystem == 0)
	{
		FMOD_RESULT       result;
		void             *extradriverdata = 0;
		result = FMOD::System_Create(&mpSystem);

		result = mpSystem->init(32, FMOD_INIT_NORMAL, extradriverdata);

		result = mpSystem->createSound("/storage/extSdCard/sdMedia/sound/concrete_break2.wav", FMOD_HARDWARE, 0, &sound);

		result = sound->setMode(FMOD_LOOP_OFF);
	}

    struct engine engine;

    app_dummy();

    memset(&engine, 0, sizeof(engine));
    state->userData = &engine;
    state->onAppCmd = engine_handle_cmd;
    state->onInputEvent = engine_handle_input;
    engine.app = state;

    while (1) {      
        int ident;
        int events;
        struct android_poll_source* source;

        while ((ident=ALooper_pollAll(0, 0, &events, (void**)&source)) >= 0)
        {

        	if (source != 0) {
                source->process(state, source);
            }

        	 // Check if we are exiting.
        	if (state->destroyRequested != 0) {
        		return;
        	}
        }
    }
}

And this is the Java code:

package com.example.native_activity;

import android.os.Bundle;

import org.fmod.FMODAudioDevice;

public class DummyActivity extends android.app.NativeActivity 
{

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

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);		
		FMODAudioDevice.init(this);		
	}
	
	static
	{
		System.loadLibrary("fmodL");		
		System.loadLibrary("native-activity");
	}
}
1 Like

Sorry to hear you are having a crash with the new mixerSuspend functionality, could you please provide a call stack of the crash from GDB?

After some investigation: the second call to mixerSuspend never return.

[list=1]
[] app start - sound works fine[/:m:25svwqe2]
[] back button pressed[/:m:25svwqe2]
[] onStop → mixerSuspend
[/
:m:25svwqe2]
[] app restart[/:m:25svwqe2]
[] onStart-> mixerResume[/:m:25svwqe2]
[] last few milliseconds of sound repeat forever[/:m:25svwqe2]
[] back button pressed[/:m:25svwqe2]
[] onStop → mixerSuspend - function call never return[/:m:25svwqe2][/list:o:25svwqe2]

The problem may have something to do with the way native activity handles lifecycle events and threads:
Every time the app is started a new rendering thread is created.
When the app is destroyed / minimized the rendering thread is killed
When the app is restarted a new rendering thread is created

FMOD function are being called from different thread every time the app is restarted.

I’ve protected the calls to FMOD with a mutex to make sure that FMOD isn’t being used from different threads at the same time, but that didn’t help.

I would expect that when the app goes into the background you would receive an onStop, not an onDestroy. From what you are saying it sounds like you receive state->destroyRequested == true, you main thread exits and the thread goes away, is this accurate?

In this case I believe you should clean up the FMOD System you create at the start of your main function because if onDestroy has occurred in Java land the FMOD Audio device has been cleaned up already.

Thanks, that solved the issue.

Hi. I’ve been looking for some way to suspend the mixer thread rather than totally killing off FMOD and re-initialising when dealing with the Android life cycle. This mixerSuspend() looks ideal but I can’t seem to find it in the latest API package? I’m assuming this hasn’t made it in to the stable release yet?

I did a little more searching (didn’t realise this topic was in the FMOD Studio forum) and it seems these methods are exposed in the low level FMOD lib provided with FMOD Studio, but they’re not in the actual FMOD Ex lib. Is there a reason these libs are not in sync?

Yes, System::mixerSuspend / System::mixerResume are now FMOD 5 functions and are not available in FMOD 4.

To get similar behavior for Android on FMOD 4 you would need to use the AudioTrack output mode, System::setOutput(FMOD_OUTPUTTYPE_AUDIOTRACK), then use the Java (FMODAudioDevice) start and stop calls.