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

Android录音、WAV、AMR

2016-06-04 19:14 615 查看

简单介绍



Android提供了两个API用于实现录音功能:android.media.AudioRecord、android.media.MediaRecorder。

AudioRecord录音

主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的实时处理(如会说话的汤姆猫、语音)。

所以就用这个录WAV文件。这样录的文件是未经过压缩的所以,文件必要大,但是听起来也比较清晰,也支持很多其他的跨平台设备

MediaRecorder录音

已经集成了录音、编码、压缩等,支持少量的录音音频格式,大概有.aac(API = 16) .amr .3gp。

使用非常简单,MediaRecorder录音,文件小,音频是经过压缩的,所以听起来,可能效果不太好

代码实现

使用录音的代码

public class MainActivity extends AppCompatActivity {

private RippleDiffuse rippleDiffuse;
private RecordHintDialog recordHintDialog;

// 获取类的实例
ExtAudioRecorder recorder;
//录音地址
String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "media.wav";
//dialog显示的图片
private Drawable[] micImages;

private int touchSlop;

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

touchSlop = ViewConfiguration.get(MainActivity.this).getScaledTouchSlop();
micImages = getRecordAnimPic(getResources());
recordHintDialog = new RecordHintDialog(this, R.style.DialogStyle);

AuditRecorderConfiguration configuration = new AuditRecorderConfiguration.Builder()
.recorderListener(listener)
.handler(handler)
.uncompressed(true)
.builder();

recorder = new ExtAudioRecorder(configuration);

initViews();
}
/** 设置Dialog的图片 */
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
recordHintDialog.setImage(micImages[msg.what]);
}
};
/** 录音失败的提示 */
ExtAudioRecorder.RecorderListener listener = new ExtAudioRecorder.RecorderListener() {
@Override
public void recordFailed(FailRecorder failRecorder) {
if (failRecorder.getType() == FailRecorder.FailType.NO_PERMISSION) {
Toast.makeText(MainActivity.this, "录音失败,可能是没有给权限", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "发生了未知错误", Toast.LENGTH_SHORT).show();
}
}
};

private void initViews() {
rippleDiffuse = (RippleDiffuse) findViewById(R.id.rd);

rippleDiffuse.setBtnOnTouchListener(new View.OnTouchListener() {
float downY;

@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = event.getY();
//检查sdcard
if (!isExitsSdcard()) {
String needSd = getResources().getString(R.string.Send_voice_need_sdcard_support);
Toast.makeText(MainActivity.this, needSd, Toast.LENGTH_SHORT).show();
return false;
}
// 设置输出文件
recorder.setOutputFile(filePath);
recorder.prepare();
recorder.start();
//弹出dialog
if (recorder.getState() != ExtAudioRecorder.State.ERROR) {
recordHintDialog.show();
recordHintDialog.moveUpToCancel();
return true;
}
return false;

case MotionEvent.ACTION_MOVE: {
if (recorder.getState() != ExtAudioRecorder.State.RECORDING) {
return false;
}
float offsetY = downY - event.getY();
if (offsetY > touchSlop) {
recordHintDialog.releaseToCancel();
} else {
recordHintDialog.moveUpToCancel();
}
return true;
}

case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
recordHintDialog.dismiss();
if (recorder.getState() != ExtAudioRecorder.State.RECORDING) {
return false;
}
float offsetY = downY - event.getY();
if (offsetY > touchSlop) {
//删除录音
recorder.discardRecording();
} else {
//录音成功
int time = recorder.stop();
if (time > 0) {
//成功的处理
} else {
String st2 = getResources().getString(R.string.The_recording_time_is_too_short);
Toast.makeText(MainActivity.this, st2, Toast.LENGTH_SHORT).show();
}
}
recorder.reset();
return true;
}
return false;
}
});
}

/**
* Sdcard是否存在
*/
public static boolean isExitsSdcard() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}

/**
* 获取录音时动画效果的图片
*/
public static Drawable[] getRecordAnimPic(Resources res) {
return new Drawable[]{res.getDrawable(R.mipmap.record_animate_01),
res.getDrawable(R.mipmap.record_animate_02), res.getDrawable(R.mipmap.record_animate_03),
res.getDrawable(R.mipmap.record_animate_04), res.getDrawable(R.mipmap.record_animate_05),
res.getDrawable(R.mipmap.record_animate_06), res.getDrawable(R.mipmap.record_animate_07),
res.getDrawable(R.mipmap.record_animate_08), res.getDrawable(R.mipmap.record_animate_09),
res.getDrawable(R.mipmap.record_animate_10), res.getDrawable(R.mipmap.record_animate_11),
res.getDrawable(R.mipmap.record_animate_12), res.getDrawable(R.mipmap.record_animate_13),
res.getDrawable(R.mipmap.record_animate_14)};
}

}


不多不少,主要的核心代码都在onTouch里面。录音的实现使用的网上的ExtAudioRecorder.java,本来想转出是谁写,可网络上的太多了,最后也不知道是谁写的。我根据这个类,小做修改了一下,我在这个类的基础上加了一个,失败的监听,当然如果读者发现了其他的失败原因可以在FailType里面在加一些枚举类型。除了这个还加了一个任务,这个任务就是,每100毫秒获取一下Amplitude(振幅),如果设置了handler,则给handler一个消息。

在刚才的代码中看到AuditRecorderConfiguration这个类,这个类就像他的名字一样录音的些配置。

public class AuditRecorderConfiguration {

public static final int[] SAMPLE_RATES = {44100, 22050, 11025, 8000};
public static final boolean RECORDING_UNCOMPRESSED = true;
public static final boolean RECORDING_COMPRESSED = false;

private ExtAudioRecorder.RecorderListener listener;
private boolean uncompressed;
private  int timerInterval;
private int rate;
private int source;
private int channelConfig;
private int format;
private Handler handler;

/**
* 创建一个默认的配置 <br />
* <ul>
*     <li>uncompressed = false</li>
*     <li>timerInterval = 120</li>
*     <li>rate = 8000</li>
*     <li>source = {@link MediaRecorder.AudioSource#MIC}</li>
*     <li>channelConfig = {@link AudioFormat#CHANNEL_CONFIGURATION_MONO}</li>
*     <li>format = {@link AudioFormat#ENCODING_PCM_16BIT}</li>
* </ul>
*
*/
public static AuditRecorderConfiguration createDefaule(){
return new Builder().builder();
}

public ExtAudioRecorder.RecorderListener getRecorderListener(){
return listener;
}

public boolean isUncompressed(){
return uncompressed;
}

public int getTimerInterval(){
return timerInterval;
}

public int getRate(){
return rate;
}

public int getSource(){
return source;
}

public int getFormat(){
return format;
}

public Handler getHandler(){
return handler;
}

public int getChannelConfig(){
return channelConfig;
}

private AuditRecorderConfiguration(Builder builder){
this.listener = builder.listener;
this.uncompressed = builder.uncompressed;
this.timerInterval = builder.timerInterval;
this.rate = builder.rate;
this.source = builder.source;
this.format = builder.format;
this.handler = builder.handler;
this.channelConfig = builder.channelConfig;
}

public static class Builder{
private ExtAudioRecorder.RecorderListener listener;
private boolean uncompressed;
private int timerInterval = 120;
private int rate = SAMPLE_RATES[3];
private int source = MediaRecorder.AudioSource.MIC;
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
private int format = AudioFormat.ENCODING_PCM_16BIT;
private Handler handler;

/** 声道设置 */
public Builder getChannelConfig(int channelConfig){
this.channelConfig = channelConfig;
return this;
}
/** 录音失败的监听 */
public Builder recorderListener(ExtAudioRecorder.RecorderListener listener){
this.listener = listener;
return this;
}
/** 是否压缩录音 */
public Builder uncompressed(boolean uncompressed){
this.uncompressed = uncompressed;
return this;
}
/** 周期的时间间隔 */
public Builder timerInterval(int timeInterval){
timerInterval = timeInterval;
return this;
}
/** 采样率 */
public Builder rate(int rate){
this.rate = rate;
return this;
}
/** 音频源 */
public Builder source(int source){
this.source = source;
return this;
}
/** 编码制式和采样大小 */
public Builder format(int format){
this.format = format;
return this;
}
/** 返回what是振幅值 1-13  */
public Builder handler(Handler handler){
this.handler = handler;
return this;
}

public AuditRecorderConfiguration builder(){
return new AuditRecorderConfiguration(this);
}
}

}


没有什么,因为录音的核心代码都在ExtAudioRecorder.java里面。这个类可以录WAV和AMR两种格式的文件,所以,基本每个方法都会通过uncompressed字段来判断,录哪种文件。

AMR

amr的录音比较简单MediaRecorder都做了相关处理,所以设置完一些配置之后,只需要调用录音方法就可以了。如何使用MediaRecorder,它的注释写的很全如:



WAV

这个相对比较复杂,需要写WAV的头文件,设置监听,然后在往文件里面写数据。

/**@param uncompressed  是否压缩录音 true不压缩,false压缩
* @param audioSource   音频源:指的是从哪里采集音频。通过 {@link AudioRecord} 的一些常量去设置
* @param sampleRate    采样率:音频的采样频率,每秒钟能够采样的次数,采样率越高,音质越高。给出的实例是44100、22050、11025但不限于这几个参数。
*                      例如要采集低质量的音频就可以使用4000、8000等低采样率。
* @param channelConfig 声道设置:Android支持双声道立体声和单声道。MONO单声道,STEREO立体声
* @param audioFormat   编码制式和采样大小:采集来的数据当然使用PCM编码(脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。)
*                      android支持的采样大小16bit 或者8bit。当然采样大小越大,那么信息量越多,音质也越高,现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit足够了。
* @param bufferSize:录制缓冲大小:可以通过getMinBufferSize来获取
*/
audioRecorder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSize);
audioRecorder.setRecordPositionUpdateListener(updateListener);
audioRecorder.setPositionNotificationPeriod(framePeriod);


看光初始化就这一大坨代码。

重点是后面的2行代码,设置了一个监听,和一个通知周期。这个通知周期就是,每次通过audioRecorder.read()方法读取framePeriod这么多的数据了就会回调updateListener的onPeriodicNotification方法,然后我们在这个方法里面,一边read,一遍往sd卡写数据。

prepare录音时的准备

// 写文件头
randomAccessWriter = new RandomAccessFile(filePath, "rw");
//设置文件长度为0,为了防止这个file以存在
randomAccessWriter.setLength(0);
randomAccessWriter.writeBytes("RIFF");
//不知道文件最后的大小,所以设置0
randomAccessWriter.writeInt(0);
randomAccessWriter.writeBytes("WAVE");
randomAccessWriter.writeBytes("fmt ");
// Sub-chunk
// size,
// 16
// for
// PCM
randomAccessWriter.writeInt(Integer.reverseBytes(16));
// AudioFormat, 1 为 PCM
randomAccessWriter.writeShort(Short.reverseBytes((short) 1));
// 数字为声道, 1 为 mono, 2 为 stereo
randomAccessWriter.writeShort(Short.reverseBytes(channels));
// 采样率
randomAccessWriter.writeInt(Integer.reverseBytes(configuration.getRate()));
// 采样率, SampleRate*NumberOfChannels*BitsPerSample/8
randomAccessWriter.writeInt(Integer.reverseBytes(configuration.getRate() * samples * channels / 8));
randomAccessWriter.writeShort(Short.reverseBytes((short) (channels * samples / 8)));
// Block
// align,
// NumberOfChannels*BitsPerSample/8
randomAccessWriter.writeShort(Short.reverseBytes(samples)); // Bits per sample
randomAccessWriter.writeBytes("data");
randomAccessWriter.writeInt(0); // Data chunk size not


这跟MediaRecorder直接不能比呀,MediaRecorder一个prepare方法就足够了。

关于AWV的头文件可以查看这篇文章

http://blog.csdn.net/bluesoal/article/details/932395

开始录音

audioRecorder.startRecording();
audioRecorder.read(buffer, 0, buffer.length);


启动录音后,要先read一次,才会通知激活 listener,然后再listener里面在一边read,一边写。

停止录音

audioRecorder.stop();
//头文件
randomAccessWriter.seek(4); // Write size to RIFF header
randomAccessWriter.writeInt(Integer.reverseBytes(36 + payloadSize));

randomAccessWriter.seek(40); // Write size to Subchunk2Size
// field
randomAccessWriter.writeInt(Integer.reverseBytes(payloadSize));

randomAccessWriter.close();


ExtAudioRecorder.java全部代码

public class ExtAudioRecorder {

private AuditRecorderConfiguration configuration;

public interface RecorderListener {
void recordFailed(FailRecorder failRecorder);
}

public ExtAudioRecorder(AuditRecorderConfiguration configuration) {
this.configuration = configuration;

if (configuration.isUncompressed()) {
init(configuration.isUncompressed(),
configuration.getSource(),
configuration.getRate(),
configuration.getChannelConfig(),
configuration.getFormat());
} else {
int i = 0;
do {
init(configuration.isUncompressed(),
configuration.getSource(),
AuditRecorderConfiguration.SAMPLE_RATES[i],
configuration.getChannelConfig(),
configuration.getFormat());

}
while ((++i < AuditRecorderConfiguration.SAMPLE_RATES.length) & !(getState() == ExtAudioRecorder.State.INITIALIZING));
}
}

/**
* 录音的状态
*/
public enum State {
/**
* 录音初始化
*/
INITIALIZING,
/**
* 已准备好录音
*/
READY,
/**
* 录音中
*/
RECORDING,
/**
* 录音生了错误
*/
ERROR,
/**
* 停止录音
*/
STOPPED
}

// 不压缩将使用这个进行录音
private AudioRecord audioRecorder = null;

// 压缩将使用这进行录音
private MediaRecorder mediaRecorder = null;

// 当前的振幅 (只有在未压缩的模式下)
private int cAmplitude = 0;

// 录音状态
private State state;

// 文件 (只有在未压缩的模式下)
private RandomAccessFile randomAccessWriter;

private int bufferSize;

// 录音 通知周期(只有在未压缩的模式下)
private int framePeriod;
// 输出的字节(只有在未压缩的模式下)
private byte[] buffer;

private short samples;
private short channels;

// 写入头文件的字节数(只有在未压缩的模式下)
// after stop() is called, this size is written to the header/data chunk in
// the wave file
private int payloadSize;
//录音的开始时间
private long startTime;

private String filePath;

/**
* 返回录音的状态
*
* @return 录音的状态
*/
public State getState() {
return state;
}

/*
*
* Method used for recording.
*/
private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener() {
public void onPeriodicNotification(AudioRecord recorder) {
audioRecorder.read(buffer, 0, buffer.length); // Fill buffer
try {
randomAccessWriter.write(buffer); // Write buffer to file
payloadSize += buffer.length;
if (samples == 16) {
for (int i = 0; i < buffer.length / 2; i++) { // 16bit sample size
short curSample = getShort(buffer[i * 2], buffer[i * 2 + 1]);
if (curSample > cAmplitude) { // Check amplitude
cAmplitude = curSample;
}
}
} else { // 8bit sample size
for (int i = 0; i < buffer.length; i++) {
if (buffer[i] > cAmplitude) { // Check amplitude
cAmplitude = buffer[i];
}
}
}
} catch (IOException e) {
e.printStackTrace();
Log.e(ExtAudioRecorder.class.getName(), "Error occured in updateListener, recording is aborted");
//stop();
}
}

public void onMarkerReached(AudioRecord recorder) {
// NOT USED
}
};

/**
* 默认的构造方法,如果压缩录音,剩下的参数可以为0.这个方法不会抛出异常,但是会设置状态为 {@link State#ERROR}
*
* @param uncompressed 是否压缩录音 true不压缩,false压缩
* @param audioSource 音频源:指的是从哪里采集音频。通过 {@link AudioRecord} 的一些常量去设置
* @param sampleRate 采样率:音频的采样频率,每秒钟能够采样的次数,采样率越高,音质越高。给出的实例是44100、22050、11025但不限于这几个参数。
* 例如要采集低质量的音频就可以使用4000、8000等低采样率。
* @param channelConfig 声道设置:Android支持双声道立体声和单声道。MONO单声道,STEREO立体声
* @param audioFormat 编码制式和采样大小:采集来的数据当然使用PCM编码(脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。)
* android支持的采样大小16bit 或者8bit。当然采样大小越大,那么信息量越多,音质也越高,现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit足够了。
*/
public void init(boolean uncompressed, int audioSource, int sampleRate, int channelConfig, int audioFormat) {
try {
if (uncompressed) { // RECORDING_UNCOMPRESSED
if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
samples = 16;
} else {
samples = 8;
}

if (channelConfig == AudioFormat.CHANNEL_CONFIGURATION_MONO) {
channels = 1;
} else {
channels = 2;
}

framePeriod = sampleRate * configuration.getTimerInterval() / 1000;
bufferSize = framePeriod * 2 * samples * channels / 8;
if (bufferSize < AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)) {
// Check to make sure
// buffer size is not
// smaller than the
// smallest allowed one
bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
// Set frame period and timer interval accordingly
framePeriod = bufferSize / (2 * samples * channels / 8);
Log.w(ExtAudioRecorder.class.getName(), "Increasing buffer size to " + Integer.toString(bufferSize));
}

audioRecorder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSize);

if (audioRecorder.getState() != AudioRecord.STATE_INITIALIZED)
throw new Exception("AudioRecord initialization failed");
audioRecorder.setRecordPositionUpdateListener(updateListener);
audioRecorder.setPositionNotificationPeriod(framePeriod);
} else { // RECORDING_COMPRESSED
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
}
cAmplitude = 0;
filePath = null;
state = State.INITIALIZING;
} catch (Exception e) {
fireFailEvent(FailRecorder.FailType.NO_PERMISSION, e);
if (e.getMessage() != null) {
Log.e(ExtAudioRecorder.class.getName(), e.getMessage());
} else {
Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured while initializing recording");
}
state = State.ERROR;
}
}

/**
* 设置输出的文件路径
*
* @param argPath 文件路径
*/
public void setOutputFile(String argPath) {
try {
if (state == State.INITIALIZING) {
filePath = argPath;
if (!configuration.isUncompressed()) {
mediaRecorder.setOutputFile(filePath);
}
}
} catch (Exception e) {
if (e.getMessage() != null) {
Log.e(ExtAudioRecorder.class.getName(), e.getMessage());
} else {
Log.e(ExtAudioRecorder.class.getName(),
"Unknown error occured while setting output path");
}
state = State.ERROR;
fireFailEvent(FailRecorder.FailType.UNKNOWN, e);
}
}

/**
* Returns the largest amplitude sampled since the last call to this method.
*
* @return returns the largest amplitude since the last call, or 0 when not
* in recording state.
*/
public int getMaxAmplitude() {
if (state == State.RECORDING) {
if (configuration.isUncompressed()) {
int result = cAmplitude;
cAmplitude = 0;
return result;
} else {
try {
return mediaRecorder.getMaxAmplitude();
} catch (IllegalStateException e) {
return 0;
}
}
} else {
return 0;
}
}

/**
* 准备录音的录音机, 如果 state 不是 {@link State#INITIALIZING} 或文件路径为null
* 将设置 state 为 {@link State#ERROR}。如果发生异常不会抛出,而是设置 state 为
* {@link State#ERROR}
*/
public void prepare() {
try {
if (state == State.INITIALIZING) {
if (configuration.isUncompressed()) {
if ((audioRecorder.getState() == AudioRecord.STATE_INITIALIZED) & (filePath != null)) {
// 写文件头 randomAccessWriter = new RandomAccessFile(filePath, "rw"); //设置文件长度为0,为了防止这个file以存在 randomAccessWriter.setLength(0); randomAccessWriter.writeBytes("RIFF"); //不知道文件最后的大小,所以设置0 randomAccessWriter.writeInt(0); randomAccessWriter.writeBytes("WAVE"); randomAccessWriter.writeBytes("fmt "); // Sub-chunk // size, // 16 // for // PCM randomAccessWriter.writeInt(Integer.reverseBytes(16)); // AudioFormat, 1 为 PCM randomAccessWriter.writeShort(Short.reverseBytes((short) 1)); // 数字为声道, 1 为 mono, 2 为 stereo randomAccessWriter.writeShort(Short.reverseBytes(channels)); // 采样率 randomAccessWriter.writeInt(Integer.reverseBytes(configuration.getRate())); // 采样率, SampleRate*NumberOfChannels*BitsPerSample/8 randomAccessWriter.writeInt(Integer.reverseBytes(configuration.getRate() * samples * channels / 8)); randomAccessWriter.writeShort(Short.reverseBytes((short) (channels * samples / 8))); // Block // align, // NumberOfChannels*BitsPerSample/8 randomAccessWriter.writeShort(Short.reverseBytes(samples)); // Bits per sample randomAccessWriter.writeBytes("data"); randomAccessWriter.writeInt(0); // Data chunk size not
// known yet, write 0

buffer = new byte[framePeriod * samples / 8 * channels];
state = State.READY;
} else {
Log.e(ExtAudioRecorder.class.getName(),
"prepare() method called on uninitialized recorder");
state = State.ERROR;
fireFailEvent(FailRecorder.FailType.UNKNOWN, null);
}
} else {
mediaRecorder.prepare();
state = State.READY;
}
} else {
Log.e(ExtAudioRecorder.class.getName(), "prepare() method called on illegal state");
release();
state = State.ERROR;
fireFailEvent(FailRecorder.FailType.UNKNOWN, null);
}
} catch (Exception e) {
if (e.getMessage() != null) {
Log.e(ExtAudioRecorder.class.getName(), e.getMessage());
} else {
Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured in prepare()");
}
state = State.ERROR;
fireFailEvent(FailRecorder.FailType.UNKNOWN, e);
}
}

/**
* 释放与这个类相关的资源,和移除不必要的文件,在必要的时候
*/
public void release() {
if (state == State.RECORDING) {
stop();
} else {
if ((state == State.READY) & (configuration.isUncompressed())) {
try {
randomAccessWriter.close(); // 删除准备文件
} catch (IOException e) {
Log.e(ExtAudioRecorder.class.getName(), "I/O exception occured while closing output file");
}
(new File(filePath)).delete();
}
}

if (configuration.isUncompressed()) {
if (audioRecorder != null) {
audioRecorder.release();
}
} else {
if (mediaRecorder != null) {
mediaRecorder.release();
}
}
}

public void discardRecording() {
stop();

File file = new File(filePath);
if (file.exists() && !file.isDirectory()) {
file.delete();
}
}

/**
* 重置录音,并设置 state 为 {@link State#INITIALIZING},如果当前状态为 {@link State#RECORDING},将会停止录音。
* 这个方法不会抛出异常,但是会设置状态为 {@link State#ERROR}
*/
public void reset() {
try {
if (state != State.ERROR) {
release();
filePath = null; // Reset file path
cAmplitude = 0; // Reset amplitude
if (configuration.isUncompressed()) {
audioRecorder = new AudioRecord(configuration.getSource(), configuration.getRate(),
channels + 1, configuration.getFormat(), bufferSize);
audioRecorder.setRecordPositionUpdateListener(updateListener);
audioRecorder.setPositionNotificationPeriod(framePeriod);
} else {
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder
.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder
.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
}
state = State.INITIALIZING;
}
} catch (Exception e) {
Log.e(ExtAudioRecorder.class.getName(), e.getMessage());
state = State.ERROR;
fireFailEvent(FailRecorder.FailType.UNKNOWN, e);
}
}

/**
* 开始录音,并设置 state 为 {@link State#RECORDING}。在调用这个方法前必须调用 {@link ExtAudioRecorder#prepare()} 方法
*/
public void start() {
if (state == State.READY) {
if (configuration.isUncompressed()) {
payloadSize = 0;
audioRecorder.startRecording(); audioRecorder.read(buffer, 0, buffer.length);
} else {
mediaRecorder.start();
}
state = State.RECORDING;
this.startTime = (new Date()).getTime();
startGetMaxAmplitudeThread();
} else {
Log.e(ExtAudioRecorder.class.getName(), "start() called on illegal state");
state = State.ERROR;
fireFailEvent(FailRecorder.FailType.UNKNOWN, null);
}
}

/**
* 停止录音,并设置 state 为 {@link State#STOPPED}。如果要继续使用,则需要调用 {@link #reset()} 方法
*
* @return 录音的时间
*/
public int stop() {
if (state == State.RECORDING) {
if (configuration.isUncompressed()) {
audioRecorder.stop();

try {
randomAccessWriter.seek(4); // Write size to RIFF header
randomAccessWriter.writeInt(Integer.reverseBytes(36 + payloadSize));

randomAccessWriter.seek(40); // Write size to Subchunk2Size
// field
randomAccessWriter.writeInt(Integer.reverseBytes(payloadSize));

randomAccessWriter.close();
} catch (IOException e) {
Log.e(ExtAudioRecorder.class.getName(),
"I/O exception occured while closing output file");
state = State.ERROR;
}
} else {
try{
mediaRecorder.stop();
} catch (Exception e){}
}
state = State.STOPPED;

File file = new File(filePath);
if (file.exists() && file.isFile()) {
if (file.length() == 0L) {
file.delete();
return 0;
} else {
int time = (int) ((new Date()).getTime() - this.startTime) / 1000;
return time;
}
} else {
return 0;
}
} else {
Log.e(ExtAudioRecorder.class.getName(), "stop() called on illegal state");
state = State.ERROR;
fireFailEvent(FailRecorder.FailType.UNKNOWN, null);
return 0;
}
}

private void startGetMaxAmplitudeThread() {
if (configuration.getHandler() != null) {
new Thread(new Runnable() {
public void run() {
while (true) {
if (state == State.RECORDING) {
Message var1 = new Message();
var1.what = getMaxAmplitude() * 13 / 32767;
configuration.getHandler().sendMessage(var1);
SystemClock.sleep(100L);
continue;
}
return;
}
}
}).start();
}
}
/** Converts a byte[2] to a short, in LITTLE_ENDIAN format */
private short getShort(byte argB1, byte argB2) {
return (short) (argB1 | (argB2 << 8));
}

private void fireFailEvent(final FailRecorder.FailType failType, final Throwable failCause) {

if (configuration.getRecorderListener() != null) {
Runnable r = new Runnable() {
@Override
public void run() {
configuration.getRecorderListener().recordFailed(new FailRecorder(failType, failCause));
}
};
r.run();
}
}

}


下载地址

https://github.com/wu-liao-de-ren-sheng/AndroidRecord

http://download.csdn.net/detail/wu_liao_de_ren_sheng/9541715

参考资料:

http://blog.csdn.net/xu_fu/article/details/12648845

https://yq.aliyun.com/articles/8637

http://blog.csdn.net/jbgtwang/article/details/20642351
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android