【问题标题】:Text-To-Speech over bluetooth通过蓝牙进行文字转语音
【发布时间】:2013-08-20 16:17:08
【问题描述】:

我正在使用 Text-To-Speech 设计一个应用程序,它可以工作。现在我想使用蓝牙耳机(不是 AD2P)来接收音频。阅读有关 startBluetoothScoOn 的 Android 文档我看到了:

请注意,电话应用程序始终优先使用 SCO 连接进行电话。如果在电话通话时调用此方法,它将被忽略。同样,如果在应用程序使用 SCO 连接时接收或发送呼叫,则应用程序的连接将丢失,并且不会在呼叫结束时自动返回。

现在我的问题是:

  1. 通话结束时是否需要监听电话状态才能重新调用 startBluetoothScoOn?如果是,如何?我没有找到任何例子;
  2. 是否需要监听耳机连接/断开连接?如果是,怎么做?

我想当通话结束或我的耳机关闭时,系统会发送一个 SCO_AUDIO_STATE_DISCONNECTED,对吧?

编辑:我发布我正在使用的代码:

public class BluetoothHeadSetManager implements
    BluetoothProfile.ServiceListener {
BluetoothManager manager;
BluetoothHeadset bluetoothHeadset;
Context context;
HeadSetBroadcastReceiver recv;

public BluetoothHeadSetManager(Context cont, BluetoothManager manager) {
    this.manager = manager;
    this.context = cont;
    recv = null;
}

@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
    // mBluetoothHeadset is just a headset profile,
    // it does not represent a headset device.
    bluetoothHeadset = (BluetoothHeadset) proxy;

    List<BluetoothDevice> devices = bluetoothHeadset.getConnectedDevices();
    if (devices.size() > 0)
        manager.onHeadSetConnected();

    // During the active life time of the app, a user may turn on and off
    // the headset.
    // So register for broadcast of connection states.
    if (recv == null) {
        recv = new HeadSetBroadcastReceiver();
        context.registerReceiver(recv, new IntentFilter(
                BluetoothDevice.ACTION_ACL_CONNECTED));
        context.registerReceiver(recv, new IntentFilter(
                BluetoothDevice.ACTION_ACL_DISCONNECTED));
    }
}

public void close() {
    if (recv != null)
        context.unregisterReceiver(recv);
    if (bluetoothHeadset != null)
        BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
                BluetoothProfile.HEADSET, bluetoothHeadset);
}

@Override
public void onServiceDisconnected(int arg0) {
    Logger.getInstance().writeLog("onServiceDisconnected called");
    if (recv != null)
        context.unregisterReceiver(recv);
    manager.onHeadSetDisconnected();
}

private class HeadSetBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        BluetoothDevice mConnectedHeadset;

        if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
            mConnectedHeadset = intent
                    .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            BluetoothClass bluetoothClass = mConnectedHeadset
                    .getBluetoothClass();
            if (bluetoothClass != null) {
                // Check if device is a headset. Besides the 2 below, are
                // there other
                // device classes also qualified as headset?
                int deviceClass = bluetoothClass.getDeviceClass();
                if (deviceClass == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE
                        || deviceClass == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) {
                    // override this if you want to do other thing when the
                    // device is connected.
                    manager.onHeadSetConnected();
                }
            }
        } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
            manager.onHeadSetDisconnected();
        }
    }
}

Sco 接收器:

public class BluetoothScoReceiver extends BroadcastReceiver {
BluetoothManager manager;

public BluetoothScoReceiver(BluetoothManager manager) {
    this.manager = manager;
}

@Override
public void onReceive(Context arg0, Intent intent) {
    Bundle extras = intent.getExtras();
    if (extras == null)
        return;
    int state = extras.getInt(AudioManager.EXTRA_SCO_AUDIO_STATE);
    if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED)
        manager.onScoConnected();
    else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED)
        manager.onScoDisconnected();
}

主要:

@Override
public void onScoConnected() {
    this.headsetReady = true;
    requestState = CONNECTED;
}

@Override
public void onScoDisconnected() {
    this.headsetReady = false;
    if (requestState == TRY_CONNECTION && headsetConnected)
        this.onHeadSetConnected(); //try again
}

@Override
public void onHeadSetConnected() {
    headsetConnected = true;
    if (requestState == TRY_CONNECTION) {
        requestState = TRY_CONNECTION;
        audioMng.setMode(AudioManager.MODE_NORMAL);
        audioMng.stopBluetoothSco();
        audioMng.setMode(AudioManager.MODE_IN_CALL);
        audioMng.startBluetoothSco();
    } else {
        requestState = TRY_CONNECTION;
        audioMng.setMode(AudioManager.MODE_IN_CALL);
        audioMng.startBluetoothSco();
    }
}

@Override
public void onHeadSetDisconnected() {
    audioMng.setMode(AudioManager.MODE_NORMAL);
    if (requestState != NOT_CONNECTED_IDLE) {
        audioMng.stopBluetoothSco();
        audioMng.setBluetoothScoOn(false);
        requestState = NOT_CONNECTED_IDLE;
    }
    headsetConnected = false;
}

@SuppressLint("InlinedApi")
@SuppressWarnings("deprecation")
@Override
public void startBluetooth() {
    Intent currentScoState;
    if (audioMng.isBluetoothScoAvailableOffCall()) {
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        if (adapter == null)
            return;
        blueInit = adapter.getProfileProxy(this, headsetMng,
                BluetoothProfile.HEADSET);
        if (blueInit) {
            scoListner = new BluetoothScoReceiver(this);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
                currentScoState = this
                        .registerReceiver(
                                scoListner,
                                new IntentFilter(
                                        AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
            else
                currentScoState = this
                        .registerReceiver(
                                scoListner,
                                new IntentFilter(
                                        AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED));
            Bundle extras = currentScoState.getExtras();
            if (extras == null)
                return;
            if (extras
                    .getInt(AudioManager.EXTRA_SCO_AUDIO_STATE) == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
                this.headsetReady = true;
                requestState = CONNECTED;
            } else {
                this.headsetReady = false;
                requestState = NOT_CONNECTED_IDLE;
            }
        }
    }
}

@Override
public void stopBluetooth() {
    this.onHeadSetDisconnected();
    if (blueInit) {
        headsetMng.close();
        this.unregisterReceiver(scoListner);
    }
}

【问题讨论】:

    标签: android text-to-speech android-bluetooth


    【解决方案1】:
    1. 您不必监听电话状态,当通话结束时 (CALL_STATE_IDLE) 您将收到 SCO_AUDIO_STATE_DISCONNECTED。
    2. 如果您不听耳机连接,您如何知道何时连接音频?对于蓝牙耳机的实现,您可以查看Using the Android RecognizerIntent with a bluetooth headset

    【讨论】:

    • 在这个例子中 startBluetoothScoOn 根本没有被调用,所以我想我只能作为第 2 点的灵感。但是我真的不明白 onServiceConnected/onServiceDisconnected 和意图的接收者之间的区别ACTION_CONNECTION_STATE_CHANGED 是一回事。事实上,它们都调用 onHeadsetConnected()。或者 onServiceConnected/onServiceDisconnected 与耳机连接无关,而是与其他方面有关。
    • 你在说什么例子?如果您参考我提供的链接,则在 mBroadcastReceiver 中调用 startBluetoothSco() 并且代码中的任何地方都没有 onServiceConnected/onServiceDisconnected 所以我不知道您指的是什么。
    • 我使用 API >= 11,我说的是 BluetoothHeadsetUtils 类
    • 我尝试了一点,但只有在我的应用程序启动之前已经连接耳机的情况下才有效。我看到的是 ACTION_CONNECTION_STATE_CHANGED 从未被调用,所以我使用 ACTION_ACL_CONNECTED/ACTION_ACL_DISCONNECTED 来了解耳机何时连接并且它工作。但我不明白为什么 startBluetoothSco 可以工作(即使过了一段时间),但只有在设备已经连接时才有效。有什么建议吗?
    • 看看我链接里的代码,我要设置倒计时,反复调用startBluetoothSco。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    相关资源
    最近更新 更多