蓝牙延迟和 Android 音频提示

Posted

技术标签:

【中文标题】蓝牙延迟和 Android 音频提示【英文标题】:Tips for Bluetooth lag and Android audio 【发布时间】:2018-03-09 00:37:34 【问题描述】:

我的 android 应用使用 SoundPool 播放简短的 wav 语音片段。对于连接的某些蓝牙扬声器,初始播放可能会被截断,其余部分可以听到。通过本机电话扬声器输出一切正常。

Soundpool 具有低延迟。某些蓝牙扬声器可能会因电源管理引起的睡眠而滞后。我主要考虑蓝牙 4.x,目前使用的是 Android 7.0,尽管用户将使用其他版本。

是否有弥补这种情况的最佳做法?我是否应该在语音片段中添加一个无噪音的领导者(例如“X”毫秒)——让蓝牙有时间唤醒,以便之后可以正确听到重要部分?是否首选某些声音文件规格?游戏程序员有什么见解吗?

谢谢!

我的初始化 soundPool(使用声音 id 的哈希图)和播放声音以及释放和停止 soundPool 的代码如下。在我的主程序中调用 playSound 的示例是 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());
        
    

我添加了一个 SCO 广播接收器:

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");
    

之后我的应用程序运行的日志输出是:

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 被服务器拒绝;帧数 70229 03-09 13:09:09.278 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST 被服务器拒绝;帧数 78728 03-09 13:09:21.312 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST 被服务器拒绝;帧数 79623 03-09 13:09:33.345 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST 被服务器拒绝;帧数 73808 03-09 13:09:45.377 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST 被服务器拒绝;帧数 43391 03-09 13:09:57.400 2615-2615/com.foobar.foo W/AudioTrack:AUDIO_OUTPUT_FLAG_FAST 被服务器拒绝;帧数 46074 03-09 13:10:09.425 2615-2615/com.foobar.foo W/AudioTrack: AUDIO_OUTPUT_FLAG_FAST 被服务器拒绝;帧数 46075

【问题讨论】:

有人能帮忙吗?问题未解决。 你明白了吗? 【参考方案1】:

你应该先启动蓝牙SCO,然后播放。这将及时唤醒扬声器。

【讨论】:

嗨 Neo,蓝牙 SCO 看起来很有帮助,我在原始帖子中提供了代码,以便您可以显示插入位置。我已经在清单中添加了 MODIFY_AUDIO_SETTINGS 权限。 我不在PC附近。我认为这是一个异步调用。所以可以先调用它然后获取回调结果。之后开始播放 正在努力工作,非常感谢您的帮助...我添加了一个 SCOBroadcastReceiver 并在播放声音之前在我的日志中看到 SCO_AUDIO_STATE_CONNECTED 存在持续截断问题。 SCO_AUDIO_STATE_DISCONNECTED 没有出现。还有其他想法吗?服务器拒绝 AUDIO_OUTPUT_FLAG_FAST 是否需要修复?

以上是关于蓝牙延迟和 Android 音频提示的主要内容,如果未能解决你的问题,请参考以下文章

在外部蓝牙设备和 Android 手机之间传输音频

Android:如何通过蓝牙连接传输视频和音频?

Android 音频开始缓慢通过蓝牙 sco 开始播放

Android 蓝牙开发 --手机与蓝牙音箱配对,并播放音频

android WebView 通过听筒或蓝牙播放音频

在 Android 中通过蓝牙接收音频