github源码:https://github.com/heinsven/Test5.git
AudioRecord 是Android提供的用于录音的工具类。
AudioRecord管理应用的各类音频资源,记录平台的各类输入设备的音频输入。
AudioRecord实现Android录音的流程:
- 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败
- 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小
- 调用startRecording函数,开始录音
- 创建一个数据流,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中数据导入数据流,生成PCM格式文件
- 录音结束时,关闭数据流, 停止录音
AudioRecord由于输出的数据是原始数据PCM,MediaPlayer播放器是不能识别播放的,需要通过AudioTrack处理播放,或者把PCM转化为可以播放的格式类似wav,MP3等。
public class Test5 extends AppCompatActivity {
private ProgressBar mProgressBar;
private Button mBtn1;
private Button mBtn2;
private Button mBtn3;
private AudioRecord mAudioRecord;
private boolean isRecording = false;
private byte[] audioData;
int laiyuan = MediaRecorder.AudioSource.MIC;//来源
int samHz = 16000;//采样频率
int shengdao = AudioFormat.CHANNEL_IN_MONO;//声道
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;//格式
int bufferSize = 2 * AudioRecord.getMinBufferSize(samHz, shengdao, audioFormat);//缓冲区大小
private String voicePath = Environment.getExternalStorageDirectory().getPath() + "/audio";
private String voiceName = "/test.pcm";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test5);
init();
}
private void init() {
verifyStoragePermissions(this);
mProgressBar = findViewById(R.id.progressbar);
mProgressBar.setVisibility(View.GONE);
mBtn1 = findViewById(R.id.btn1);
mBtn2 = findViewById(R.id.btn2);
mBtn3 = findViewById(R.id.btn3);
mBtn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAudioRecord == null) {
startRecord();
}
}
});
mBtn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAudioRecord != null) {
stopRecord();
}
}
});
mBtn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
InputStream in = new FileInputStream(voicePath + voiceName);
try {
ByteArrayOutputStream out = new ByteArrayOutputStream(264848);
for (int b; (b = in.read()) != -1; ) {
out.write(b);
}
audioData = out.toByteArray();
} finally {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, samHz,
AudioFormat.CHANNEL_OUT_MONO, audioFormat,
audioData.length, AudioTrack.MODE_STATIC);
audioTrack.write(audioData, 0, audioData.length);
audioTrack.play();
}
});
}
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.RECORD_AUDIO"};
public static void verifyStoragePermissions(Activity activity) {
try {
//检测是否有写的权限
int permission1 = ActivityCompat.checkSelfPermission(activity,
"android.permission.WRITE_EXTERNAL_STORAGE");
int permission2 = ActivityCompat.checkSelfPermission(activity,
"android.permission.RECORD_AUDIO");
if (permission1 != PackageManager.PERMISSION_GRANTED || permission2 != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void startRecord() {
mAudioRecord = new AudioRecord(laiyuan, samHz, shengdao, audioFormat, bufferSize);
final byte data[] = new byte[bufferSize];
final File file = new File(voicePath);
final File fileaudio = new File(voicePath + voiceName);
if (fileaudio.exists()) {
fileaudio.delete();
}
if (!file.exists()) {
file.mkdirs();
}
mAudioRecord.startRecording();
isRecording = true;
mProgressBar.setVisibility(View.VISIBLE);
new Thread(new Runnable() {
@Override
public void run() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(fileaudio);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (null != fos) {
while (isRecording) {
int read = mAudioRecord.read(data, 0, bufferSize);
//返回正确时才读取数据
if (AudioRecord.ERROR_INVALID_OPERATION != read) {
try {
fos.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void stopRecord() {
isRecording = false;
if (mAudioRecord != null) {
mProgressBar.setVisibility(View.GONE);
mAudioRecord.stop();
mAudioRecord.release();
//调用release之后必须置为null
mAudioRecord = null;
}
}
}
AudioTrack只能播放已经解码的PCM流,对于文件,也支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件。
AudioTrack可以在两种模式下工作,一种是静态(static),一种是流(streaming)
MODE_STREAM:在这种模式下,通过write一次次把音频数据写到AudioTrack中。但这种工作方式每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引入延时。
MODE_STATIC:在play之前,只需要把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用量较小,延时要求较高的文件。