在第 57 节我们使用 MediaPlayer 实现了一个 mp3 播放器,除了播放 Android 还提供了 MediaRecorder 用于录音。Android 设备基本都会有一个麦克风,通过 MediaRecorder 可以打开麦克风进行语音采集,这一节我们就来学习如何在 Android 系统上实现一个录音功能。
1. MediaRecorder 常用接口
- setAudioSource():
- 设置录制的音频源
- setVideoSource():
- 设置录制的视频源
- setOutputFormat():
- 设置输出格式
- setAudioEncoder():
- 设置音频编码器
- setOutputFile():
- 配置录制的音频存储文件路径
- stop():
- 停止录制
- release():
- 释放 MediaRecorder 实例,回收其所占资源
2. MediaRecorder 的使用步骤
- 创建 MediaRecorder
直接通过无参构造器创建即可:
MediaRecorder myAudioRecorder = new MediaRecorder();
- 配置录音参数
创建好之后,再对 MediaRecorder 实例进行参数配置,配置项有很多,一般需要设置音频输入源、音频输出格式、音频编码方式以及输出文件目录,如下:
myAudioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); myAudioRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); myAudioRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB); myAudioRecorder.setOutputFile(outputFile);
- 开始录音
在确定了录音配置和输出目录之后,我们就可以调用一下两个方法开始录音了:
myAudioRecorder.prepare(); myAudioRecorder.start();
- 停止录音
录音结束调用stop()
停止录制,如果不再使用还要记得调用release()
释放 MediaRecorder 所占用的资源
3. 录音示例
本节使用 MediaRecorder 实现一个录音机:
3.1 MediaRecorder 控制逻辑
作为一个完整的录音功能,当然在录音结束之后也得要能回放录音。所以整个的控制要分为录音控制和播放控制,我们按照上一小节讲述的步骤用 MediaRecorder 完成录音,然后调用 MediaPlayer 来完成录音回放:
package com.emercy.myapplication; import android.app.Activity; import android.content.pm.PackageManager; import android.media.MediaPlayer; import android.media.MediaRecorder; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.Button; import android.widget.Toast; import java.io.IOException; import java.util.Random; import static android.Manifest.permission.RECORD_AUDIO; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; public class MainActivity extends Activity { Button buttonStart, buttonStop, buttonPlayLastRecordAudio, buttonStopPlayingRecording; String AudioSavePathInDevice = null; MediaRecorder mediaRecorder; Random random; String RandomAudioFileName = "emercy"; public static final int RequestPermissionCode = 1; MediaPlayer mediaPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); buttonStart = findViewById(R.id.button); buttonStop = findViewById(R.id.button2); buttonPlayLastRecordAudio = findViewById(R.id.button3); buttonStopPlayingRecording = findViewById(R.id.button4); buttonStop.setEnabled(false); buttonPlayLastRecordAudio.setEnabled(false); buttonStopPlayingRecording.setEnabled(false); random = new Random(); buttonStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (checkPermission()) { AudioSavePathInDevice = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + CreateRandomAudioFileName(5) + "AudioRecording.3gp"; MediaRecorderReady(); try { mediaRecorder.prepare(); mediaRecorder.start(); } catch (IllegalStateException | IOException e) { e.printStackTrace(); } buttonStart.setEnabled(false); buttonStop.setEnabled(true); Toast.makeText(MainActivity.this, "Recording started", Toast.LENGTH_LONG).show(); } else { requestPermission(); } } }); buttonStop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mediaRecorder.stop(); buttonStop.setEnabled(false); buttonPlayLastRecordAudio.setEnabled(true); buttonStart.setEnabled(true); buttonStopPlayingRecording.setEnabled(false); Toast.makeText(MainActivity.this, "Recording Completed", Toast.LENGTH_LONG).show(); } }); buttonPlayLastRecordAudio.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) throws IllegalArgumentException, SecurityException, IllegalStateException { buttonStop.setEnabled(false); buttonStart.setEnabled(false); buttonStopPlayingRecording.setEnabled(true); mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(AudioSavePathInDevice); mediaPlayer.prepare(); } catch (IOException e) { e.printStackTrace(); } mediaPlayer.start(); Toast.makeText(MainActivity.this, "录音完毕", Toast.LENGTH_LONG).show(); } }); buttonStopPlayingRecording.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { buttonStop.setEnabled(false); buttonStart.setEnabled(true); buttonStopPlayingRecording.setEnabled(false); buttonPlayLastRecordAudio.setEnabled(true); if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); MediaRecorderReady(); } } }); } public void MediaRecorderReady() { mediaRecorder = new MediaRecorder(); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mediaRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB); mediaRecorder.setOutputFile(AudioSavePathInDevice); } public String CreateRandomAudioFileName(int string) { StringBuilder stringBuilder = new StringBuilder(string); int i = 0; while (i < string) { stringBuilder.append(RandomAudioFileName. charAt(random.nextInt(RandomAudioFileName.length()))); i++; } return stringBuilder.toString(); } private void requestPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE, RECORD_AUDIO}, RequestPermissionCode); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == RequestPermissionCode) { if (grantResults.length > 0) { boolean StoragePermission = grantResults[0] == PackageManager.PERMISSION_GRANTED; boolean RecordPermission = grantResults[1] == PackageManager.PERMISSION_GRANTED; if (StoragePermission && RecordPermission) { Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_LONG).show(); } } } } public boolean checkPermission() { int storagePermission = 0; int recordPermission = 0; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { storagePermission = checkSelfPermission(WRITE_EXTERNAL_STORAGE); recordPermission = checkSelfPermission(RECORD_AUDIO); } return storagePermission == PackageManager.PERMISSION_GRANTED && recordPermission == PackageManager.PERMISSION_GRANTED; } }
和拍照一样,录音也需要获取权限(注意 Android 6.0 以上还需要动态获取权限),除此之外只需要按照步骤进行录音就可以实现功能了。
3.2 录音布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="30dp"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_marginTop="37dp" android:text="录音" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/button" android:layout_centerHorizontal="true" android:text="停止" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/button2" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:text="回放" /> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/button2" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:text="结束回放" /> </RelativeLayout>
对于两大功能:“录音”和“回放”,各提供两个按键控制,分别对应开始和结束。
最终编译效果:
4. 小结
MediaRecorder 很多时候都是配合 MediaPlayer 或者其他播放器一起使用,而两者之间也有很多相似之处。当然本节提到的只是最基本的录音及音频输出功能,如果需要进一步研究音视频相关的内容就得深入到底层编解码原理。不过对于大多数非专业音视频制作 App 而言已经足够。