本文介绍的 是录音采用AudioRecorder 然后 保存PCM 格式到文件 然后 使用AudioTrack 播放PCM 同时 支持 蓝牙耳机录制 音频
下面贴下代码
1 AudioRecorder 录制和播放
package com.kwai.video.uikit.bluetoothapplication;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.text.TextUtils;
import android.util.Log;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class AudioRecordManager {
public static final String TAG = "AudioRecordManager";
private AudioRecord mRecorder;
private DataOutputStream dos;
private Thread recordThread;
private boolean isStart = false;
private static AudioRecordManager mInstance;
private int bufferSize;
public AudioRecordManager() {
//CHANNEL_IN_STEREO 双声道 输入
//ENCODING_PCM_16BIT 16 bit 字节
bufferSize = AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
8000, AudioFormat.CHANNEL_IN_STEREO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
}
/**
* 获取单例引用
*
* @return
*/
public static AudioRecordManager getInstance() {
if (mInstance == null) {
synchronized (AudioRecordManager.class) {
if (mInstance == null) {
mInstance = new AudioRecordManager();
}
}
}
return mInstance;
}
/**
* 销毁线程方法
*/
private void destroyThread() {
try {
isStart = false;
if (null != recordThread && Thread.State.RUNNABLE == recordThread.getState()) {
try {
Thread.sleep(500);
recordThread.interrupt();
} catch (Exception e) {
recordThread = null;
}
}
recordThread = null;
} catch (Exception e) {
e.printStackTrace();
} finally {
recordThread = null;
}
}
/**
* 启动录音线程
*/
private void startThread() {
destroyThread();
isStart = true;
if (recordThread == null) {
recordThread = new Thread(recordRunnable);
recordThread.start();
}
}
/**
* 录音线程
*/
Runnable recordRunnable = new Runnable() {
@Override
public void run() {
try {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
int bytesRecord;
//int bufferSize = 320;
byte[] tempBuffer = new byte[bufferSize];
if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
stopRecord();
return;
}
mRecorder.startRecording();
//writeToFileHead();
while (isStart) {
if (null != mRecorder) {
bytesRecord = mRecorder.read(tempBuffer, 0, bufferSize);
if (bytesRecord == AudioRecord.ERROR_INVALID_OPERATION || bytesRecord == AudioRecord.ERROR_BAD_VALUE) {
continue;
}
if (bytesRecord != 0 && bytesRecord != -1) {
Log.i(TAG,"bytes");
//在此可以对录制音频的数据进行二次处理 比如变声,压缩,降噪,增益等操作
//我们这里直接将pcm音频原数据写入文件 这里可以直接发送至服务器 对方采用AudioTrack进行播放原数据
dos.write(tempBuffer, 0, bytesRecord);
dos.flush();
} else {
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
/**
* 保存文件
*
* @param path
* @throws Exception
*/
private void setPath(String path) throws Exception {
File file = new File(path);
if (file.exists()) {
file.delete();
}
dos = new DataOutputStream(new FileOutputStream(file, true));
}
/**
* 启动录音
*
* @param path
*/
public void startRecord(String path) {
try {
this.path = path;
setPath(path);
startThread();
} catch (Exception e) {
e.printStackTrace();
}
}
public String path;
/**
* 播放录音
*/
public void PlayRecord() {
new Thread(playPCMRecord).start();
}
private Runnable playPCMRecord = new Runnable() {
@Override
public void run() {
int bufferSize = AudioTrack.getMinBufferSize(8000,
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
8000, AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
FileInputStream fis = null;
try {
audioTrack.play();
fis = new FileInputStream(path);
byte[] buffer = new byte[bufferSize*2];
int len = 0;
while ((len = fis.read(buffer)) != -1) {
Log.d(TAG, "playPCMRecord: len " + len);
audioTrack.write(buffer, 0, len);
}
} catch (Exception e) {
Log.e(TAG, "playPCMRecord: e : " + e);
} finally {
if (audioTrack != null) {
audioTrack.stop();
audioTrack = null;
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
/**
* 停止录音
*/
public void stopRecord() {
try {
destroyThread();
if (mRecorder != null) {
if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
mRecorder.stop();
}
if (mRecorder != null) {
mRecorder.release();
}
}
if (dos != null) {
dos.flush();
dos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2 BlueTooth 支持蓝牙耳机录制
package com.kwai.video.uikit.bluetoothapplication;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.util.Log;
import androidx.annotation.NonNull;
public class BluetoothUtil {
private String TAG = "bluetooth";
private static BluetoothUtil mBluetoothUtil;
//第一次打开sco没成功的情况,持续连接的次数
private static final int SCO_CONNECT_TIME = 5;
private int mConnectIndex = 0;
private AudioManager mAudioManager = null;
static Context mContext;
private BluetoothUtil() {
if (mAudioManager == null) {
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
}
public static BluetoothUtil getInstance(Context context) {
mContext = context;
if (mBluetoothUtil == null) {
mBluetoothUtil = new BluetoothUtil();
}
return mBluetoothUtil;
}
public void openSco(@NonNull final IBluetoothConnectListener listener) {
if (!mAudioManager.isBluetoothScoAvailableOffCall()) {
Log.e(TAG, "系统不支持蓝牙录音");
listener.onError("Your device no support bluetooth record!");
return;
}
new Thread(new Runnable() {
@Override
public void run() {
//蓝牙录音的关键,启动SCO连接,耳机话筒才起作用
mAudioManager.stopBluetoothSco();
mAudioManager.startBluetoothSco();
//蓝牙SCO连接建立需要时间,连接建立后会发出ACTION_SCO_AUDIO_STATE_CHANGED消息,通过接收该消息而进入后续逻辑。
//也有可能此时SCO已经建立,则不会收到上述消息,可以startBluetoothSco()前先
mConnectIndex = 0;
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
boolean bluetoothScoOn = mAudioManager.isBluetoothScoOn();
Log.i(TAG, "onReceive state=" + state + ",bluetoothScoOn=" + bluetoothScoOn);
if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) { // 判断值是否是:1
Log.e(TAG, "onReceive success!");
mAudioManager.setBluetoothScoOn(true); //打开SCO
listener.onSuccess();
mContext.unregisterReceiver(this); //取消广播
} else {//尝试重新启动SCO
Log.e(TAG, "onReceive failed index=" + mConnectIndex);
if (mConnectIndex < SCO_CONNECT_TIME) {
mAudioManager.startBluetoothSco();//再次尝试连接
} else {
listener.onError("open sco failed!");
mContext.unregisterReceiver(this); //取消广播
}
mConnectIndex++;
}
}
}, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED));
}
}).start();
}
public void closeSco() {
boolean bluetoothScoOn = mAudioManager.isBluetoothScoOn();
Log.i(TAG, "bluetoothScoOn=" + bluetoothScoOn);
if (bluetoothScoOn) {
mAudioManager.setBluetoothScoOn(false);
mAudioManager.stopBluetoothSco();
}
mBluetoothConnectListener = null;
}
public interface IBluetoothConnectListener {
void onError(String error);
void onSuccess();
}
IBluetoothConnectListener mBluetoothConnectListener;
}