您的位置:首页 > 移动开发 > Android开发

Android语音录入(MP3格式)-小白无头脑实现

2017-03-24 11:44 309 查看
最近项目开发中要做一个语音提问的功能,在客户端录制MP3格式文件并且上传到服务器端.小白不才,如果下述内容中有任何问题欢迎各位大佬指出,谢谢.

Android中实现录音有三种方式 :

通过意图捕获音频。

MediaRecorder类实现录音.

AudioRecord录制原始音频.

三种方式的概念,怎么实现网上大佬写了很多我就不班门弄斧了,我的实现方式是通过AudioRecord实现的,先生成RAW格式然后通过工具((com.pocketdigi.utils))转换成MP3格式.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -华丽丽的分割线- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

实现效果 : 录制音频,一般是60秒,录制时间到达50秒时候进入最后10秒倒计时,到达60秒后自动结束录音



点击录音开始按钮启动录音功能,同时检测录音的分贝,通过图片展示出来…模仿的就是微信语音那个动画

点击录音暂停按钮结束录音,启动音频文件格式转换

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/activity_audio_record"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/audio_start"
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="@string/recording_start"/>
<Button
android:id="@+id/audio_stop"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_below="@id/audio_start"
android:text="@string/recording_stop"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/audio_stop"
android:layout_centerInParent="true"
android:layout_marginTop="10dp"
android:background="@color/cyan5"
android:padding="10dp">

<ImageView
android:id="@+id/aaa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/a9g"/>
<ImageView
android:id="@+id/mic_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/aaa"
android:src="@mipmap/tp"/>
</RelativeLayout>
</RelativeLayout>


图片资源什么的你可以去某些语音APP里面借用一下

录音代码

代码里面都有注释,个人觉得这个注释小白看得懂…所以就不一一解释了

public class AudioRecord extends Activity implements View.OnClickListener, AudioRecorder2Mp3Util.DecibelObserver {

AudioRecorder2Mp3Util util = null;
private boolean canClean = false;//判断是否要清理本地的音频文件
private ImageView mDBImg;//显示语音分贝的View
private Drawable[] micImages; //存储很多张话筒图片的数组
private int maxTime = 60;//语音录入的最大时长
private AudioRecord that;

private final Handler mHandler = new Handler() {
public void handleMess
4000
age(android.os.Message msg) {
//当录音进入最后10秒中的时候展现的不再是分贝而是倒计时
//因为分贝很难突破100你用力对着麦克吼一嗓子也就80多一点所以这里处理方式虽然不是很完美,但是可以解决问题
int mTime = msg.arg1;
if (mTime == 900) {
mDBImg.setImageDrawable(micImages[20]);
return;
} else if (mTime == 800) {
mDBImg.setImageDrawable(micImages[19]);
return;
} else if (mTime == 700) {
mDBImg.setImageDrawable(micImages[18]);
return;
} else if (mTime == 600) {
mDBImg.setImageDrawable(micImages[17]);
return;
} else if (mTime == 500) {
mDBImg.setImageDrawable(micImages[16]);
return;
} else if (mTime == 400) {
mDBImg.setImageDrawable(micImages[15]);
return;
} else if (mTime == 300) {
mDBImg.setImageDrawable(micImages[14]);
return;
} else if (mTime == 200) {
mDBImg.setImageDrawable(micImages[13]);
return;
} else if (mTime == 100) {
mDBImg.setImageDrawable(micImages[12]);
return;
} else if (mTime == 1000) {
mDBImg.setImageDrawable(micImages[11]);
//因为录音最大时长是60秒,所以在到达最后一秒的时候就自动结束录音
if (util != null) {
util.unregisterObserver(that);//注销监听分贝变化
mHandler.sendEmptyMessage(9);//将声音还原
util.stopRecordingAndConvertFile();//停止录音并且开始转换
util.cleanFile(AudioRecorder2Mp3Util.RAW);//清理文件
// 关闭
util.close();
util = null;
}
return;
}

int what = msg.what;
//根据mHandler发送what的大小决定话筒的图片是哪一张
//说话声音越大,发送过来what值越大
if (what > 0) {
if (what >= 0 && what < 10) {
mDBImg.setImageDrawable(micImages[0]);
} else if (what >= 10 && what < 20) {
mDBImg.setImageDrawable(micImages[1]);
} else if (what >= 20 && what < 30) {
mDBImg.setImageDrawable(micImages[2]);
} else if (what >= 30 && what < 40) {
mDBImg.setImageDrawable(micImages[3]);
} else if (what >= 40 && what < 50) {
mDBImg.setImageDrawable(micImages[4]);
} else if (what >= 50 && what < 60) {
mDBImg.setImageDrawable(micImages[5]);
} else if (what >= 60 && what < 70) {
mDBImg.setImageDrawable(micImages[6]);
} else if (what >= 70 && what < 80) {
mDBImg.setImageDrawable(micImages[7]);
} else if (what >= 80 && what < 90) {
mDBImg.setImageDrawable(micImages[8]);
} else if (what >= 90 && what < 100) {
mDBImg.setImageDrawable(micImages[9]);
} else if (what >= 100) {
mDBImg.setImageDrawable(micImages[10]);
}
}

}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_audio_record);

findViewById(R.id.audio_start).setOnClickListener(this);

findViewById(R.id.audio_stop).setOnClickListener(this);

mDBImg = (ImageView) findViewById(R.id.mic_image);

// 动画资源文件,用于录制语音时
micImages = new Drawable[]{
//分贝动画图片
getResources().getDrawable(R.mipmap.tp),
getResources().getDrawable(R.mipmap.ts),
getResources().getDrawable(R.mipmap.tt),
getResources().getDrawable(R.mipmap.tu),
getResources().getDrawable(R.mipmap.tv),
getResources().getDrawable(R.mipmap.tw),
getResources().getDrawable(R.mipmap.tx),
getResources().getDrawable(R.mipmap.ty),
getResources().getDrawable(R.mipmap.tz),
getResources().getDrawable(R.mipmap.tq),
getResources().getDrawable(R.mipmap.tr),
//倒计时动画图片
getResources().getDrawable(R.mipmap.akn),
getResources().getDrawable(R.mipmap.ako),
getResources().getDrawable(R.mipmap.akp),
getResources().getDrawable(R.mipmap.akq),
getResources().getDrawable(R.mipmap.akr),
getResources().getDrawable(R.mipmap.aks),
getResources().getDrawable(R.mipmap.akt),
getResources().getDrawable(R.mipmap.aku),
getResources().getDrawable(R.mipmap.akv),
getResources().getDrawable(R.mipmap.akw)
};
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.audio_start://开始录音

if (util == null) {
util = new AudioRecorder2Mp3Util(null, SDCardUtils.listAvaliableStorage(this).get(0) + "audiorecorder.raw", SDCardUtils.listAvaliableStorage(this).get(0) + "audiorecorder.mp3");
}
if (canClean) {
util.cleanFile(AudioRecorder2Mp3Util.MP3 | AudioRecorder2Mp3Util.RAW);
}
Toast.makeText(this, "请说话", Toast.LENGTH_SHORT).show();
maxTime = 60;
util.registerObserver(this);//注册监分贝变化
that = this;
util.startRecording();
startTime();//开始60秒计时
canClean = true;
break;
case R.id.audio_stop://结束录音

if (util != null) {
util.unregisterObserver(this);//注销监听分贝变化
mHandler.sendEmptyMessage(9);//将声音还原
Toast.makeText(this, "正在转换", Toast.LENGTH_SHORT).show();
util.stopRecordingAndConvertFile();
util.cleanFile(AudioRecorder2Mp3Util.RAW);
// 如果要关闭可以
util.close();
util = null;
}
break;
}
}
/**
* 分贝放生变化后收到的通知
*/
@Override
public void decibelSizeChanged(final int dbSize) {
mHandler.sendEmptyMessage(dbSize);//向handler发送消息做动画操作
}

/**
* 开始自动减时
*/
private void startTime() {
final Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
Log.i("检查", "倒计时" + maxTime);
maxTime--;
if (maxTime == 9) {//如果倒计时到第九秒就不要在监听分贝变化了,已经不需要在做分贝动画了
util.unregisterObserver(that);//注销监听分贝变化
}
if (maxTime <= 9) {//到达第九秒后开始发送倒计时消息
Log.i("检查", "发送消息" + maxTime);
Message message = new Message();
message.arg1 = maxTime * 100;//乘以100是让数字足够大 不至于和分贝数值发生冲突
if (maxTime == 0) {//到达0秒后不在进行计时操作
timer.cancel();
message.arg1 = 1000;
mHandler.sendMessage(message);
return;
}
mHandler.sendMessage(message);
}
}
};
//参数2:1000毫秒之后执行第一次run  参数3:之后每隔1000毫秒执行一次run
timer.schedule(task, 1000, 1000);
}
}


音频录制和格式转换

public class AudioRecorder2Mp3Util {
/**
* 构造时候需要的Activity,主要用于获取文件夹的路径
*/
private Activity activity;

/**
* 文件代号
*/
public static final int RAW = 0X00000001;
public static final int MP3 = 0X00000002;

/**
* 文件路径
*/
private String rawPath = null;
private String mp3Path = null;

/**
* 采样频率
*/
private static final int SAMPLE_RATE = 8000;

/**
* 录音需要的一些变量
*/
private short[] mBuffer;
private AudioRecord mRecorder;
private int bufferSize;

private static ArrayList<DecibelObserver> mObservers = new ArrayList<>();

public AudioRecorder2Mp3Util(Activity activity) {
this.activity = activity;
}

/**
* 录音状态
*/
private boolean isRecording = false;

/**
* 是否转换成功
*/
private boolean convertOk = false;

/**
* @param activity
* @param rawPath
* @param mp3Path
*/
public AudioRecorder2Mp3Util(Activity activity, String rawPath, String mp3Path) {
this.activity = activity;
this.rawPath = rawPath;
this.mp3Path = mp3Path;
}

/**
* 开始录音
*/
public boolean startRecording() {
// 如果正在录音,则返回
if (isRecording) {
return isRecording;
}
// 初始化
if (mRecorder == null) {
initRecorder();
}

getFilePath();
mRecorder.startRecording();

startBufferedWrite(new File(rawPath));

isRecording = true;
return isRecording;
}

/**
* 停止录音,并且转换文件,这很可能是个耗时操作,建议在后台中做
*/
public boolean stopRecordingAndConvertFile() {
if (!isRecording) {
return isRecording;
}

// 停止
mRecorder.stop();
isRecording = false;

// 开始转换
FLameUtils lameUtils = new FLameUtils(1, SAMPLE_RATE, 96);
convertOk = lameUtils.raw2mp3(rawPath, mp3Path);

return isRecording ^ convertOk;// convertOk==true,return true
}

/**
* 获取文件的路径
*
* @param fileAlias RAW or MP3
* @return
*/
public String getFilePath(int fileAlias) {
if (fileAlias == RAW) {
return rawPath;
} else if (fileAlias == MP3) {
return mp3Path;
} else return null;
}

/**
* 清理文件
*/
public void cleanFile(int cleanFlag) {
File f = null;
try {
switch (cleanFlag) {
case MP3:
f = new File(mp3Path);
if (f.exists()) f.delete();
break;
case RAW:
f = new File(rawPath);
if (f.exists()) f.delete();
break;
case RAW | MP3:
f = new File(rawPath);
if (f.exists()) f.delete();
f = new File(mp3Path);
if (f.exists()) f.delete();
break;
}
f = null;
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 关闭,可以先调用cleanFile来清理文件
*/
public void close() {
if (mRecorder != null) mRecorder.release();
activity = null;
}

// -------内部的一些工具方法-------

/**
* 初始化
*/
private void initRecorder() {
bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
mBuffer = new short[bufferSize];
mRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
}

/**
* 设置路径,第一个为raw文件,第二个为mp3文件
*
* @return
*/
private void getFilePath() {
try {
String folder = "audio_recorder_2_mp3";
String fileName = String.valueOf(System.currentTimeMillis());
if (rawPath == null) {
File raw = new File(activity.getDir(folder, activity.MODE_PRIVATE), fileName + ".raw");
raw.createNewFile();
rawPath = raw.getAbsolutePath();
raw = null;
}
if (mp3Path == null) {
File mp3 = new File(activity.getDir(folder, activity.MODE_PRIVATE), fileName + ".mp3");
mp3.createNewFile();
mp3Path = mp3.getAbsolutePath();
mp3 = null;
}

Log.d("rawPath", rawPath);
Log.d("mp3Path", mp3Path);

runCommand("chmod 777 " + rawPath);
runCommand("chmod 777 " + mp3Path);
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 执行cmd命令,并等待结果
*
* @param command 命令
* @return 是否成功执行
*/
private boolean runCommand(String command) {
boolean ret = false;
Process process = null;
try {
process = Runtime.getRuntime().exec(command);
process.waitFor();
ret = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
process.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
return ret;
}

/**
* 写入到raw文件
*
* @param file
*/
private void startBufferedWrite(final File file) {
new Thread(new Runnable() {
@Override
public void run() {
DataOutputStream output = null;
try {
output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
while (isRecording) {
int readSize = mRecorder.read(mBuffer, 0, mBuffer.length);
long v = 0;
// 将 buffer 内容取出,进行平方和运算
for (int i = 0; i < mBuffer.length; i++) {
v += mBuffer[i] * mBuffer[i];
}
// 平方和除以数据总长度,得到音量大小。
double mean = v / (double) readSize;
double volume = 10 * Math.log10(mean);
notifySizeChanged((int)volume);
for (int i = 0; i < readSize; i++) {
output.writeShort(mBuffer[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (output != null) {
try {
output.flush();
} catch (IOException e) {
e.printStackTrace();

} finally {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}).start();
}

/**
* 注册观察者
*/
public void registerObserver(DecibelObserver observer) {
if (observer != null) {//不为null并且不存在
mObservers.add(observer);
}
}
/**
* 注销观察者
*/
public void unregisterObserver(DecibelObserver observer) {
if (observer != null) {
mObservers.remove(observer);
}
}
/**
* 声音分贝发生变化后通知观察者
*/
public void notifySizeChanged(int dbSize) {
for(int i =0; i<mObservers.size(); i++){
mObservers.get(0).decibelSizeChanged(dbSize);
}
}

/**
* 定义观察者接口
*/
public interface DecibelObserver{
//录入声音分贝发生变化后通知
public void decibelSizeChanged(int dbSize);
}
}


到这里录音所有的代码就完成了…有了音频转换工具的轮子,所以实现起来很简单.如果发现什么问题可以给我留言,谢谢.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息