经过上次的AAudio踩坑后,一段时间信心指数跌到了谷底。调整情绪后,再次把目光指向了AudioRecord。
构造函数的MediaRecorder.AudioSource
创建AudioRecord时,可以指定音频源
AudioRecord audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC,
sampleRate,
inChannelConfig,
audioFormat,
bufferSize);
这里的音频源无法对应到具体的音频设备,尝试MediaRecorder.AudioSource.VOICE_xxx等,不是没什么效果,就是创建失败。
成员函数setPreferredDevice指定设备
实践发现,创建完AudioRecord后,可以使用setPreferredDevice()函数实现音频源设备的指定。其参数是AudioDeviceInfo,音频设备AudioDeviceInfo,可以通过之前提到过的AudioManager进行枚举。
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
AudioDeviceInfo[] inputDevices = new AudioDeviceInfo[]{};
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
inputDevices = audioManager.getDevices(DeviceFlag);
for (AudioDeviceInfo device : inputDevices) {
if (deviceID == device.getId()) {
return device;
}
}
}
实际应用中,setPreferredDevice()可以指定录音设备。当硬件有多个麦克风时,可以设置多声道,来实现立体声录音。比如设置双声道,底部和顶部麦克风分别对应左声道和右声道。
另外,setPreferredDevice()也可以指定输出音频设备,这样的话,可以实现任意音频设备的输入、任意音频设备的输出。
应用举例
手机麦克风>耳机——对着手机说话,可以从耳机中听到声音;如果耳机线连接的是音响,手机这时可以作为有线扩音器使用。
手机麦克风>听筒/手机外放——由于现款手机顶部/底部都有麦克风,基本上这种模式都会出现刺耳的声音回授(audio feedback)。
耳机麦克风>听筒/手机外放——手机声音较少,达不到扩音的目的。
耳机麦克风>耳机——用来测试耳机麦克风。
蓝牙麦克风>耳机/听筒/手机外放——耳机连接音响时,蓝牙麦克风可作为无线扩音器使用;可进行近距离远程监听,把蓝牙耳机需要监听的位置,躲在一旁用手机监听。
手机麦克风>蓝牙听筒——近距离远程监听,把手机需要监听的位置,躲在一旁用蓝牙耳机监听。
蓝牙耳机
以上应用中,除了蓝牙麦克风以外,目前为止其它的都可以实现了。
在上述代码实现中,设置从蓝牙麦克风中录音时,一直是静音状态。多次修改采样率、声道数都无法正常收音,差点放弃。后来想到腾讯会议时,是可以指定从蓝牙耳机收音的,因此一定有办法。
经过了解,蓝牙设备有三种类型:
- A2DP (Advanced Audio Distribution Profile)
- SCO (Synchronous Connection Oriented Link)
- BLE(Bluetooth Low Energy)
我手里的蓝牙话筒JBL KMC300连接后,设备只有A2DP类型的一种播放设备,它的麦克风实际只是在话筒扬声器里回放,并不能通过麦克风录音;
另外两款蓝牙耳机有SCO录音设备、SCO播放设备、A2DP播放设备三种。其中A2DP播放设备与其它播放设备使用方法类似,SCO两个设备无论是录音还是放音,都是静音状态;
BLE类型设备我手上没有;
经过多次尝试,要想从SCO设备录音,需要开启Sco模式。老版本的办法:
AudioManager.startBluetoothSco();
新版本(34以后)的办法:
AudioManager.setCommunicationDevice(device);
比较奇怪的是setCommunicationDevice只能设置输出音频。当仅仅是将setCommunicationDevice设置为输出音频时,存在蓝牙麦克风>手机内置扬声器不收音的情况。
解决办法是如果输入设备是SCO时,找到对应的SCO输出设备,用setCommunicationDevice设置,即使这个输出设备并不是我们指定的输出设备。
实现代码如下:
if (InputDeviceInfo != null && InputDeviceInfo.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
for (AudioDeviceInfo btDevice : audioManager.getAvailableCommunicationDevices()) {
if (btDevice.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
audioManager.setCommunicationDevice(btDevice);
}
}
}
参考
https://developer.android.com/reference/android/media/AudioManager#setCommunicationDevice(android.media.AudioDeviceInfo)
https://github.com/google/oboe/wiki/TechNote_BluetoothAudio