您的位置:首页 > 移动开发 > Android开发

android 蓝牙sco开发

2017-10-13 22:57 267 查看
近段时间在做bluetooth双向通信,坑的不轻,各种问题不断,感觉这坑都填不完的一样。把这段时间的东西写下来,给需要的小伙伴参考下,能少坑是一点

public class Main2Activity extends AppCompatActivity {

AudioManager mAm;
InavrSR inavrSR;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
mAm = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mAm.setMode(AudioManager.MODE_IN_COMMUNICATION);
initSCO();
initBlueToothHeadset();

}

private BluetoothHeadset bluetoothHeadset;
private BluetoothDevice bluetoothDevice;
BluetoothProfile.ServiceListener blueHeadsetListener = new BluetoothProfile.ServiceListener() {

@Override
public void onServiceDisconnected(int profile) {
Log.i("blueHeadsetListener>>", "onServiceDisconnected:" + profile);
if (profile == BluetoothProfile.HEADSET) {
if (bluetoothHeadset != null)
bluetoothHeadset.stopVoiceRecognition(bluetoothDevice);
mAm.stopBluetoothSco();
mAm.setBluetoothScoOn(false);
bluetoothHeadset = null;
}
//            initBlueToothHeadset();
}

@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
Log.i("blueHeadsetListener", "onServiceConnected:" + profile);
if (profile == BluetoothProfile.HEADSET) {
bluetoothHeadset = (BluetoothHeadset) proxy;
scoEnable();
Log.i("sco", mAm.isBluetoothA2dpOn() + "," + mAm.isBluetoothScoOn());
if (bluetoothHeadset.getConnectedDevices().size() > 0) {
bluetoothDevice = bluetoothHeadset.getConnectedDevices().get(0);
Log.i("Main2Activity:", bluetoothHeadset.startVoiceRecognition(bluetoothDevice) + "");

}
}
}
};

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(this, blueHeadsetListener, BluetoothProfile.HEADSET);
}

public void record() {
final Player player = new Player();
Record re = new Record(new IReward() {
@Override
public void onReward(byte[] bytes, int i, int i1) {
player.play(bytes);
}

@Override
public void onError(RewardException e) {
inavrSR.getWebsocketManager().newSocketConnection();
}
});

re.startRecord();
}

/* Broadcast receiver for the SCO State broadcast intent.*/
private final BroadcastReceiver mSCOHeadsetAudioState = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.bluetooth.device.action.ACL_CONNECTED")) {
initBlueToothHeadset();
} else {
//if(DEBUG)
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
Log.e("SCO", " mSCOHeadsetAudioState--->onReceive:" + state);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
Log.i("SCO", "SCO_AUDIO_STATE_CONNECTED");
record();
unregisterReceiver(mSCOHeadsetAudioState);
} else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
Log.i("SCO  ", "SCO_AUDIO_STATE_DISCONNECTED");
}
}

}
};

private void initSCO() {
IntentFilter newintent = new IntentFilter();
newintent.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
newintent.addAction("android.bluetooth.device.action.ACL_CONNECTED");
registerReceiver(mSCOHeadsetAudioState, newintent);
}

private void scoEnable() {
if (mAm.isBluetoothScoOn()) {
mAm.stopBluetoothSco();
mAm.setBluetoothScoOn(false);
}

mAm.setBluetoothScoOn(true);
mAm.startBluetoothSco();
}
}


/**
* Created by kakahsh on 2017/2/24.
*/

public class Record extends ACEMCanceler implements IRecordAction {
private static final int SAMPLE_RATE_IN_HZ = 16000;
private static final int CHANNEL = AudioFormat.CHANNEL_IN_MONO;
//// 2017/8/30 关于640*2的解释,理论上只要加640就可以了,但会提示RecordThread: buffer overflow
private static final int BUFFER_SIZE = (AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ,
CHANNEL, AudioFormat.ENCODING_PCM_16BIT) / 640) * 640 + 640 * 2;
private AudioRecord mAudioRecord;
private boolean isVoiceRunning;
private Thread recordThread;

private IReward reward;

public Record(IReward iReward) {
this.reward = iReward;
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION,
SAMPLE_RATE_IN_HZ, CHANNEL,
AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE);
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
if (reward != null)
reward.onError(new RewardException("AudioRecord初始化失败,请确认已经取得录音权限"));
}
int audioSessionId = mAudioRecord.getAudioSessionId();
//        initAEC(audioSessionId);
}

private byte[] buffer = new byte[BUFFER_SIZE];

private void getNoiseLevel() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
while (isVoiceRunning) {
//r是实际读取的数据长度,一般而言r会小于buffersize
int r = mAudioRecord.read(buffer, 0, BUFFER_SIZE);
//            int calculateVolume = calculateVolume(buffer, 16);
reward.onReward(buffer, 0, r);
}
}

@Override
public void startRecord() {
mAudioRecord.startRecording();
recordThread = new Thread(new Runnable() {
@Override
public void run() {
getNoiseLevel();
}
});
recordThread.start();
isVoiceRunning = true;
}

@Override
public void stopRecord() {
isVoiceRunning = false;
if (recordThread != null) {
recordThread.interrupt();
}

if (mAudioRecord != null) {
mAudioRecord.stop();

}
}

@Override
public void realese() {

if (mAudioRecord != null) {
stopRecord();
mAudioRecord.release();
}
reward = null;
}

@Override
public boolean isInRecord() {
return isVoiceRunning;
}
}


/**
* Created by kakahsh on 2017/2/28.
*/

public interface IReward {
void onReward(byte[] bytes, int start, int lenght);

void onError(RewardException e);
}


/**
* Created by kqw on 2016/8/26.
* 播放音乐的线程
*/
public class Player extends Thread implements IAudioPlayer {

// 采样率
private final int mSampleRateInHz = 16000;
// 单声道
private final int mChannelConfig = AudioFormat.CHANNEL_OUT_MONO;
// 双声道(立体声)
// private int mChannelConfig = AudioFormat.CHANNEL_OUT_STEREO;

private static final String TAG = "PlayThread";
private AudioTrack mAudioTrack;

private byte[] data = new byte[640];

private int bufferSize;

private LinkedBlockingDeque<Object> dataQueue = new LinkedBlockingDeque<>();

private AudioDataInOut audioDataInOut;

private MessageWrapper messageWrapper;

// 互斥信号量
private Semaphore semaphore = new Semaphore(1);

public Player() {
bufferSize = AudioTrack.getMinBufferSize(mSampleRateInHz, mChannelConfig, AudioFormat.ENCODING_PCM_16BIT) / 640 * 640 + 640;
initTrack();
try {
// 默认需要抢占一个信号量。防止播放进程执行
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
start();
}

public Player(AudioDataInOut audioDataInOut) {
this();
this.audioDataInOut = audioDataInOut;

}

public Player(AudioDataInOut audioDataInOut, MessageWrapper messageWrapper) {
this();
this.audioDataInOut = audioDataInOut;
this.messageWrapper = messageWrapper;

}

private void initTrack() {
this.mAudioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
mSampleRateInHz,
mChannelConfig,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM);//, ACEMCanceler.getInstance().getAudioSessionId()
}

@Override
public void run() {
super.run();
while (true) {
if (dataQueue.size() > 0) {
Object o = dataQueue.pollFirst();
if (o instanceof MessageEntry) {
if (messageWrapper != null) {
messageWrapper.execute((MessageEntry) o);
}

} else {
data = (byte[]) o;
if (audioDataInOut != null)
audioDataInOut.onInput(data, data.length);
if (this.mAudioTrack != null)
this.mAudioTrack.write(data, 0, data.length);
}
} else {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}

@Override
public void play() {
if (mAudioTrack == null)
initTrack();
if (mAudioTrack != null && mAudioTrack.getState() != AudioTrack.PLAYSTATE_PLAYING)
mAudioTrack.play();

}

@Override
public void play(byte[] bytes, int length) {
if (!isPlaying())
play();

pushToQueue(bytes);

}

public void play(Object o) {
if (!isPlaying())
play();
pushToQueue(o);
}

private synchronized void pushToQueue(Object o) {
try {
dataQueue.putLast(o);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

@Override
public void releasePlayer() {
if (null != mAudioTrack) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
dataQueue.clear();
semaphore.release();

}

@Override
public void stopPlayer() {
if (mAudioTrack != null)
mAudioTrack.stop();
}

@Override
public boolean isPlaying() {
return mAudioTrack != null && mAudioTrack.getState() == AudioTrack.PLAYSTATE_PLAYING;
}

public void clearQueue() {
stopPlayer();
dataQueue.clear();
}

public interface AudioDataInOut {
void onInput(byte[] input, int length);
}

}


有些要注意的地方

mAm.setMode(AudioManager.MODE_IN_COMMUNICATION);
,我在参考教程的时候,设置都是
MODE_IN_CALL
,但我在用的时候是不行的,不知道是不我的使用的问题。

流程是要先获取bluetoothHeadset的对象,再通过bluetoothHeadset来发送连接成功的广播,接到广播打开sco,接到sco状态变更后再开始录音。

一定要记得在接到sco连接成功后调用
unregisterReceiver(mSCOHeadsetAudioState);
关闭广播接收器,因为可能会有多条广播过来,可能出现在异常。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: