robg
robg

Reputation: 55

Tips for Bluetooth lag and Android audio

My Android app uses SoundPool to play brief wav voice clips. For some Bluetooth speakers connected, initial playback may be truncated and the remainder is audible. Output is all good via the native phone speaker.

Soundpool is low-latency. Some Bluetooth speakers may lag from sleep induced in power management. I'm mostly considering Bluetooth 4.x and currently use Android 7.0 though users will be in other versions.

Are there best practices to compensate for this situation? Should I add a noiseless leader (e.g. 'X' milliseconds) to the voice clips -- giving Bluetooth time to wake up so the important part will properly be heard afterwards? Are certain sound file specs preferred? Any insights from game programmers?

Thanks!

My code to initialize soundPool (with a hashmap of sound ids) and play sounds as well as to release and stop soundPool follows. A sample call to playSound in my main program is SoundPoolManager.getInstance().playSound(R.raw._whistle);

public void InitializeSoundPool(Activity activity, final ISoundPoolLoaded callback) throws Exception {
    if (sounds == null || sounds.size() == 0) {
        throw new Exception("Sounds not set");
    }

    int maxStreams = 1;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        soundPool = new SoundPool.Builder()
                .setMaxStreams(maxStreams)
                .build();
    } else {
        soundPool = new SoundPool(maxStreams, AudioManager.STREAM_MUSIC, 0);
    }

    soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
        @Override
        public void onLoadComplete(SoundPool soundPool, int sampleId,
                                   int status) {
            SoundSampleEntity entity = getEntity(sampleId);
            if (entity != null) {
                entity.setLoaded(status == 0);
            }

            if (sampleId == maxSampleId()) {
                callback.onSuccess();
            }
        }
    });
    int length = sounds.size();
    hashMap = new HashMap<Integer, SoundSampleEntity>();
    int index;
    for (index = 0; index < length; index++) {
        hashMap.put(sounds.get(index), new SoundSampleEntity(0, false));
    }
    index = 0;
    for (Map.Entry<Integer, SoundSampleEntity> entry : hashMap.entrySet()) {
        index++;
        entry.getValue().setSampleId(soundPool.load(activity, entry.getKey(), index));
    }
}

public void playSound(int resourceId) {
    if (isPlaySound()) {
        SoundSampleEntity entity = hashMap.get(resourceId);
        if (entity.getSampleId() > 0 && entity.isLoaded()) {
            soundPool.play(entity.getSampleId(), .99f, .99f, 1, 0, 1f);
        }
    }
}

public void release() {
    if (soundPool != null) {
        soundPool.release();
    }
}

public void stop() {
    if (soundPool != null) {
        for (Map.Entry<Integer, SoundSampleEntity> entry : hashMap.entrySet()) {
            SoundSampleEntity entity = entry.getValue();
            soundPool.stop(entity.getSampleId());
        }
    }
}

I've added a SCO Broadcast Receiver:

public class SCOBroadcastReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

    if (intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1) ==
            AudioManager.SCO_AUDIO_STATE_CONNECTED) {
        // SCO now connected
        SoundPoolManager.isPlaySound = true;
        Log.e("SCOBroadcastReceiver", "SCO_AUDIO_STATE_CONNECTED");
    } else if (intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1) ==
            AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
        // SCO now disconnected
        SoundPoolManager.isPlaySound = false;
        Log.e("SCOBroadcastReceiver", "SCO_AUDIO_STATE_DISCONNECTED");
    }
}

}

After which the log output of my app running is:

03-09 13:08:34.160 2615-2615/com.foobar.foo E/SCOBroadcastReceiver: SCO_AUDIO_STATE_CONNECTED 03-09 13:08:39.404 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount 70229 03-09 13:09:09.278 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount 78728 03-09 13:09:21.312 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount 79623 03-09 13:09:33.345 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount 73808 03-09 13:09:45.377 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount 43391 03-09 13:09:57.400 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount 46074 03-09 13:10:09.425 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount 46075

Upvotes: 2

Views: 3321

Answers (1)

Neo
Neo

Reputation: 1379

You should start Bluetooth SCO first and then play.that will wake up the Speakers in time.

Upvotes: 1

Related Questions