因为上一篇博客代码有点太多,就单开了,这两天主要在看android语音录制和压缩转码相关知识,前端时间看见腾讯官方微博宣布,已经开放出即使聊天软件正在等待审批,但这个直接影响电话运营商,能通过的可能性应该不大,但我对这方面的技术很有兴趣,所以就试试看。其中涉及到很多声音方面的处理问题,在android中有两个类可以录制语音:AudioRecord和MediaRecorder,MediaRecorder主要是录制音频并写入文件,而AudioRecord主要是录制音频流,录制的音频流为pcm格式,关于pcm格式可以自行搜索一下,在传输过程中可以转换为amr格式,但没有相关可以类库有点麻烦,另外iphone不支持播放amr格式音频,如果需要跨两个平台可以使用AAC,压缩比也不错,音质也很好,我还没有测试过,网上评议而已。编码方面大家都推荐speex,我看了一下,需要是用System.loadLibrary加载进speex提供的类库。下面记录一下写的一个边录制边播放的一段代码吧
package voice.hcent.com;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
public class VoiceHcentActivity extends Activity {
static {
System.loadLibrary("media_jni");
}
public int frequency = 8000;
private int rBufferSize, pBufferSize;
private Button startSpeech;
private AudioRecord recorder;
private VoiceSpeech vspeech;
private AudioTrack player;
private boolean stopSpeech = false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
test();
}
public void init(){
try{
startSpeech = (Button)findViewById(R.id.StartSpeech);
//设置播放器缓冲区大小
pBufferSize = AudioTrack.getMinBufferSize(frequency, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
//获取播放器对象
player = new AudioTrack(AudioManager.STREAM_MUSIC, frequency,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
pBufferSize, AudioTrack.MODE_STREAM);
//设置录音缓冲区大小
rBufferSize = AudioRecord.getMinBufferSize(frequency,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
//获取录音机对象
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
frequency, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, rBufferSize);
}catch (Exception e) {
String msg = "ERROR init: "+e.getStackTrace();
VoiceHcentActivity.this.toastMsg(msg);
}
}
/**
* 开始录音
*/
public void startRecord(){
stopSpeech = false;
vspeech = new VoiceSpeech();
vspeech.start();
}
/**
* 结束录音
*/
public void stopRecord() {
stopSpeech = true;
}
/**
* 开始播放录音
*/
public void startPlay(){
//设置播放器音量
player.setStereoVolume(0.7f, 0.7f);
player.play();
}
/**
* 结束播放录音
*/
public void stopPlay(){
player.stop();
}
public void test(){
startSpeech.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View arg0, MotionEvent arg1) {
switch (arg1.getAction()) {
case MotionEvent.ACTION_DOWN: //开始说话
startPlay();
startRecord();
toastMsg("starting record!");
break;
case MotionEvent.ACTION_UP: //停止说话
Log.i("hcent", "111");
stopPlay();
Log.i("hcent", "222");
stopRecord();
toastMsg("stoped record!");
break;
default:
break;
}
return false;
}
});
}
public class VoiceSpeech extends Thread{
@Override
public void run() {
super.run();
try {
byte[] tempBuffer, readBuffer = new byte[rBufferSize];
int bufResult = 0;
recorder.startRecording();
while(!stopSpeech){
bufResult = recorder.read(readBuffer, 0, rBufferSize);
if(bufResult>0 && bufResult%2==0){
tempBuffer = new byte[bufResult];
System.arraycopy(readBuffer, 0, tempBuffer, 0, rBufferSize);
player.write(tempBuffer, 0, tempBuffer.length);
}
Log.d("hcent", "get read:"+bufResult+"___"+readBuffer.length);
}
recorder.stop();
Looper.prepare();
VoiceHcentActivity.this.toastMsg("AudioSpeech have ended!");
Looper.loop();
} catch (Exception e) {
String msg = "ERROR AudioRecord: "+e.getStackTrace();
Looper.prepare();
VoiceHcentActivity.this.toastMsg(msg);
Looper.loop();
}
}
}
@Override
protected void onDestroy(){
player.release();
recorder.release();
super.onDestroy();
Process.killProcess(Process.myPid());
}
public void toastMsg(String msg){
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
Log.e("hcent", msg);
}
}