Android项目中媒体是很多人头疼的一个问题,不仅仅因为处理起来很麻烦,而且不同的手机差别很大(和硬件,系统都有关系),今天就总结一下Android中的录音和播放,可保存成mp3或amr格式。
小米1s,三星,酷派8721移动版 测试通过
1、首先说一下amr格式和mp3格式的区别
----------------------------------------------------------------------------------------------
amr由欧洲通信标准化委员会提出,是在移动通信系统中使用最广泛的语音标准。它是被各大手机厂商认可的一种保存手机录音的格式。由于amr文件容量很小,因此即便是长达一分钟的音频文件,也能符合中国移动现行的彩信不超过50KB容量的技术规范,所以amr也是实现在彩信中加载人声的唯一格式。但是受体积所限,amr在音质方面不太乐观。
mp3是一个实用的有损音频压缩编码,以此获得较高的压缩和较小的体积。
所以录音优先选择amr格式,微信生成的语音文件就是amr格式的。
2、录音播放的步骤
---------------------------------------------------------------------------------------
Android api中可以录音并播放的类主要有2个,一个是AudioRecord,另一个是MediaRecorder,这篇文章我们用AudioRecord,至于他们的区别,在什么情况下用那个,大家可以自行百度。
1)实例化一个AudioRecord
2)设置录音频率,录制通道,编码格式等参数
3)设置录制缓存区大小
4)创建一个文件,用于保存录制的内容
5)打开一个流,指向创建的文件
6)开始录制
先定义一些音频录制的参数
private boolean isRecording = true, isPlaying = false; // 标记
private int frequence = 16000;// 8000;
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
在录制之前,先要判断一个sd卡是否存在:
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))// 手机有SD卡的情况
{
// 在这里我们创建一个文件,用于保存录制内容
fpath = new File(Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/test_record/");
fpath.mkdirs();// 创建文件夹
} else// 手机无SD卡的情况
{
//返回在文件系统上应用程序特定的缓存目录的绝对路径
fpath = this.getCacheDir();
}
try {
// 创建临时文件,注意这里的格式为.pcm .amr .mp3
audioFile = File.createTempFile("recording", ".amr", fpath);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
class RecordTask extends AsyncTask {
@Override
protected Void doInBackground(Void... arg0) {
isRecording = true;
try {
// 开通输出流到指定的文件
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(audioFile)));
// 根据定义好的几个配置,来获取合适的缓冲大小
int bufferSize = AudioRecord.getMinBufferSize(frequence,
channelConfig, audioEncoding);
// 实例化AudioRecord
AudioRecord record = new AudioRecord(
MediaRecorder.AudioSource.MIC, frequence,
channelConfig, audioEncoding, bufferSize);
// 定义缓冲
short[] buffer = new short[bufferSize];
// 开始录制
record.startRecording();
int r = 0; // 存储录制进度
// 定义循环,根据isRecording的值来判断是否继续录制
while (isRecording) {
// 从bufferSize中读取字节,返回读取的short个数
// 这里老是出现buffer overflow,不知道是什么原因,试了好几个值,都没用,TODO:待解决
int bufferReadResult = record
.read(buffer, 0, buffer.length);
// 循环将buffer中的音频数据写入到OutputStream中
for (int i = 0; i < bufferReadResult; i++) {
dos.writeShort(buffer[i]);
}
publishProgress(new Integer(r)); // 向UI线程报告当前进度
r++; // 自增进度值
}
// 录制结束
record.stop();
Log.v("The DOS available:", "::" + audioFile.length());
dos.close();
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
// 当在上面方法中调用publishProgress时,该方法触发,该方法在I线程中被执行
protected void onProgressUpdate(Integer... progress) {
stateView.setText(progress[0].toString());
}
protected void onPostExecute(Void result) {
btnStop.setEnabled(false);
btnStart.setEnabled(true);
btnPlay.setEnabled(true);
btnFinish.setEnabled(false);
}
protected void onPreExecute() {
// stateView.setText("正在录制");
btnStart.setEnabled(false);
btnPlay.setEnabled(false);
btnFinish.setEnabled(false);
btnStop.setEnabled(true);
}
}
将音频录制的过程放到线程中取执行,然后更新ui给用户。接下来就是播放了,仍然在线程中播放
class PlayTask extends AsyncTask {
@Override
protected Void doInBackground(Void... arg0) {
isPlaying = true;
int bufferSize = AudioTrack.getMinBufferSize(frequence,
channelConfig, audioEncoding);
short[] buffer = new short[bufferSize / 4];
try {
// 定义输入流,将音频写入到AudioTrack类中,实现播放
DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream(audioFile)));
// 实例AudioTrack
AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC,
frequence, channelConfig, audioEncoding, bufferSize,
AudioTrack.MODE_STREAM);
// 开始播放
track.play();
// 由于AudioTrack播放的是流,所以,我们需要一边播放一边读取
while (isPlaying && dis.available() > 0) {
int i = 0;
while (dis.available() > 0 && i < buffer.length) {
buffer[i] = dis.readShort();
i++;
}
// 然后将数据写入到AudioTrack中
track.write(buffer, 0, buffer.length);
}
// 播放结束
track.stop();
dis.close();
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
protected void onPostExecute(Void result) {
btnPlay.setEnabled(true);
btnFinish.setEnabled(false);
btnStart.setEnabled(true);
btnStop.setEnabled(false);
}
protected void onPreExecute() {
btnStart.setEnabled(false);
btnStop.setEnabled(false);
btnPlay.setEnabled(false);
btnFinish.setEnabled(true);
}
}