背景:本人負責公司android平台的app開發,最近要開發一個語音助手類的app,類似於靈犀語音助手、蟲洞語音助手等。其中有兩個藍牙耳機下的語音識別問題,比較折騰人,問題描述:1.藍牙耳機連接下捕獲藍牙按鍵事件,啟動語音識別;2.正常啟動識別時也必須通過藍牙耳機錄入音頻進行語音識別。這兩個問題,測試發現靈犀語音助手都解決了,所以本人負責的這個app也必須解決。網上搜了相關的資料,基本上是鳳毛麟角,因此本人在此貢獻一點小發現供大家參考,如有不對的地方歡迎指正。
針對第一個問題,藍牙耳機的按鍵監聽,牆內牆外的資料搜遍,沒有發現完美的解決方案(這里看到有人提出的解決辦法:http://blog.csdn.net/kangear/article/details/40430673,感覺有點另類,而且也不適合我的app的應用場景,所以沒嘗試),雖然接聽鍵(該鍵還有很多功能,不細說,以下都稱接聽鍵)的單按、雙按沒法監聽,但是長按卻是可以捕獲到,默認情況下,已經連接到android手機的藍牙耳機,長按接聽鍵幾秒后會系統會發出一個action=android.intent.action.VOICE_COMMAND的Intent,靈犀語音助手就是使用這個來監聽長按的,既然如此,我就仿照靈犀來做吧:
1.首先,在AndroidManifest.xml中指定的一個activity (用於捕獲藍牙耳機長按事件的activity,以下以A代替之)中添加:
<intent-filter android:priority="2147483647">
<action android:name="android.intent.action.VOICE_COMMAND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
當在連接了藍牙耳機的情況下,長按接聽鍵幾秒,出現提示音后(請戴着藍牙耳機按,要不聽不見,一不小心就成關機了),馬上松開,就會彈出一個選擇啟動某個app的對話框,凡是添加了以上intent-filter的activity的app都會出現對話框中,這需要引導用戶選擇你的app並選擇始終啟動你的app(注意A的launchMode,我這里建議設成singleTask),選中確定之后你的app就會被啟動,如果A還沒有創建,那A自然會被創建啦,如果A已經被創建了,則調用A的onNewIntent(Intent intent)方法,因此你只要在A中檢查接收到的intent的action就能監聽藍牙耳機的長按事件了。
2.關於藍牙耳機下的識別問題,本app用的語音識別sdk是訊飛的,針對這個問題訊飛有給出解決方法:
在調用語音識別引擎識別前,打開sco,關鍵代碼:
AudioManager mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
mAudioManager.setBluetoothScoOn(true);
mAudioManager.startBluetoothSco();
識別啟動並識別完成后,關閉sco:
mAudioManager.setBluetoothScoOn(false);
mAudioManager.stopBluetoothSco();
按照這個方法,便可以實現音頻錄入。
當然你會問why,這里簡單的介紹一下藍牙耳機的兩種鏈路,A2DP及SCO。android的api表明:A2DP是一種單向的高品質音頻數據傳輸鏈路,通常用於播放立體聲音樂;而SCO則是一種雙向的音頻數據的傳輸鏈路,該鏈路只支持8K及16K單聲道的音頻數據,只能用於普通語音的傳輸,若用於播放音樂那就只能呵呵了。兩者的主要區別是:A2DP只能播放,默認是打開的,而SCO既能錄音也能播放,默認是關閉的。既然要錄音肯定要打開sco啦,因此識別前調用上面的代碼就可以通過藍牙耳機錄音了,錄完記得要關閉。
雖然上面的方法能夠實現錄音,但測試中發現一個問題:startBluetoothSco()和stopBluetoothSco()時,藍牙耳機都會有一個提示音,如果識別本身就有提示音,那么加上藍牙的提示音就會讓人莫名其妙了,在體驗上很不友好。而本人在測試靈犀的藍牙功能時竟發現沒有提示音?為了完整的復制,必須把提示音去掉,然后我又上網搜了一遍,資料真的是鳳毛麟角,沒什么收獲。無奈中翻翻android關於藍牙部分的api,發現打開及關閉sco還有另外一種辦法,那就是android.bluetooth.BluetoothHeadset類的startVoiceRecognition(BluetoothDevice device)及stopVoiceRecognition(BluetoothDevice device),經過測試發現,通過這兩個方法打開sco及關閉sco藍牙耳機是不會有提示音,題外說一句:訊飛真會坑!!!下面列出關鍵代碼:
//以下代碼用於在已經連接藍牙耳機的狀態下獲取BluetoothHeadset,監聽藍牙耳機的連接只需接收action=BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED的廣播即可,此處不再贅述。
private BluetoothHeadset bluetoothHeadset;
BluetoothProfile.ServiceListener blueHeadsetListener=new BluetoothProfile.ServiceListener() {
@Override
public void onServiceDisconnected(int profile) {
Log.i("blueHeadsetListener", "onServiceDisconnected:"+profile);
if(profile==BluetoothProfile.HEADSET){
bluetoothHeadset=null;
}
}
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
Log.i("blueHeadsetListener", "onServiceConnected:"+profile);
if(profile==BluetoothProfile.HEADSET){
bluetoothHeadset=(BluetoothHeadset) proxy;
}
}
};
private void initBlueToothHeadset(){
BluetoothAdapter adapter;
if(android.os.Build.VERSION.SDK_INT<android.os.Build.VERSION_CODES.JELLY_BEAN_MR2){//android4.3之前直接用BluetoothAdapter.getDefaultAdapter()就能得到BluetoothAdapter
adapter=BluetoothAdapter.getDefaultAdapter();
}
else{
BluetoothManager bm=(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
adapter=bm.getAdapter();
}
adapter.getProfileProxy(context, blueHeadsetListener, BluetoothProfile.HEADSET);
}
