Reputation: 55
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
Reputation: 1379
You should start Bluetooth SCO first and then play.that will wake up the Speakers in time.
Upvotes: 1