本代码是俺借鉴的,并加入了详细的注解,主要用到AudioRecord,和AudioTrack两个类
简单说一下两个类的构造函数和基本方法:
AudioRecord
audioSource |
音频源:指的是从哪里采集音频。这里我们当然是从麦克风采集音频,所以此参数的值为MIC |
---|
sampleRateInHz |
采样率:音频的采样频率,每秒钟能够采样的次数,采样率越高,音质越高。给出的实例是44100、22050、11025但不限于这几个参数。例如要采集低质量的音频就可以使用4000、8000等低采样率。 |
---|---|
channelConfig |
声道设置:android支持双声道立体声和单声道。MONO单声道,STEREO立体声 |
audioFormat |
编码制式和采样大小:采集来的数据当然使用PCM编码(脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。) android支持的采样大小16bit或者8bit。当然采样大小越大,那么信息量越多,音质也越高,现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit 足够了。 |
bufferSizeInBytes |
采集数据需要的缓冲区的大小,如果不知道最小需要的大小可以在getMinBufferSize()查看。 |
getMinBufferSize(frequency, channelConfiguration, audioEncoding);
AudioTrack类
AudioTrack比AudioRecord多出来一个参数
public AudioTrack (int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes,int mode)
其中streamType
l STREAM_ALARM:警告声
l STREAM_MUSCI:音乐声,例如music等
l STREAM_RING:铃声
l STREAM_SYSTEM:系统声音
l STREAM_VOCIE_CALL:电话声音
其中mode
AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
代码部分:
1.布局文件
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
android:layout_height="wrap_content"
android:text="@string/hello"
/>
android:versionCode="1"
android:versionName="1.0">
3.主要实现代码:
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.Toast;
public class textRecord extends Activity {
Button recordButton,stopButton,exitButton;
SeekBar skbVolume;
boolean isRecording = false;
static final int frequency = 44100;
static final int channelConfiguration=AudioFormat.CHANNEL_CONFIGURATION_MONO;
static final int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
int recBufSize,playBufSize;
AudioRecord audioRecord;
AudioTrack audioTrack;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setTitle("助听器");
//用getMinBufferSize()方法得到采集数据所需要的最小缓冲区的大小
recBufSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
playBufSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
//实例化AudioRecord(声音来源,采样率,声道设置,采样声音编码,缓存大小)
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,frequency,channelConfiguration,audioEncoding,recBufSize);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,frequency,channelConfiguration,audioEncoding,playBufSize,AudioTrack.MODE_STREAM);
recordButton =(Button)this.findViewById(R.id.recordButton);
stopButton =(Button) this.findViewById(R.id.stopButton);
exitButton =(Button)this.findViewById(R.id.exitButton);
recordButton.setOnClickListener(new ClickEvent());
stopButton.setOnClickListener(new ClickEvent());
exitButton.setOnClickListener(new ClickEvent());
skbVolume = (SeekBar)this.findViewById(R.id.seekBar1);
skbVolume.setMax(100);
skbVolume.setProgress(70);
// 设置声音大小
audioTrack.setStereoVolume(0.7f, 0.7f);
skbVolume.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onStopTrackingTouch(SeekBar seekBar) {
float vol = (float)(seekBar.getProgress())/(float)(seekBar.getMax());
audioTrack.setStereoVolume(vol, vol);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
}
});
}
protected void onDestroy(){
super.onDestroy();
// 杀死当前进程
android.os.Process.killProcess(android.os.Process.myPid());
}
class ClickEvent implements View.OnClickListener{
public void onClick(View v){
if(v == recordButton){
isRecording = true;
new RecordPlayThread().start();
}else if(v == stopButton){
isRecording = false;
}else if(v== exitButton){
isRecording = false;
textRecord.this.finish();
}
}
}
class RecordPlayThread extends Thread{
public void run(){
try{
//byte 文件来存储声音
byte[] buffer = new byte[recBufSize];
//开始采集声音
audioRecord.startRecording();
//播放声音
audioTrack.play();
while(isRecording){
//从MIC存储到缓存区
int bufferReadResult = audioRecord.read(buffer,0, recBufSize);
byte[] tmpBuf = new byte[bufferReadResult];
System.arraycopy(buffer, 0, tmpBuf, 0, bufferReadResult);
//播放缓存区的数据
audioTrack.write(tmpBuf, 0, tmpBuf.length);
}
audioTrack.stop();
audioRecord.stop();
}catch(Throwable t){
Toast.makeText(textRecord.this, t.getMessage(), 1000);
}
}
};
}
AudioRecord负责录音
AudioTrack负责播放
当然也可以用audioRecord.read(buffer,0, recBufSize);
读取到的buffer通过socket网络传输出去实现广播。