diff --git a/AndroidMP3RecorderLibrary/src/com/czt/mp3recorder/DataEncodeThread.java b/AndroidMP3RecorderLibrary/src/com/czt/mp3recorder/DataEncodeThread.java
index cc8fe7c..cdd0472 100644
--- a/AndroidMP3RecorderLibrary/src/com/czt/mp3recorder/DataEncodeThread.java
+++ b/AndroidMP3RecorderLibrary/src/com/czt/mp3recorder/DataEncodeThread.java
@@ -103,8 +103,14 @@ private int processData() {
if (mTasks.size() > 0) {
Task task = mTasks.remove(0);
short[] buffer = task.getData();
+ short[] rightData = task.getRightData();
int readSize = task.getReadSize();
- int encodedSize = LameUtil.encode(buffer, buffer, readSize, mMp3Buffer);
+ if(task.getRightData() != null && task.getRightData().length > 0){
+ rightData = task.getRightData();
+ }else{
+ rightData = task.getData();
+ }
+ int encodedSize = LameUtil.encode(buffer, rightData, readSize, mMp3Buffer);
if (encodedSize > 0){
try {
mFileOutputStream.write(mMp3Buffer, 0, encodedSize);
@@ -116,6 +122,7 @@ private int processData() {
return 0;
}
+
/**
* Flush all data left in lame buffer to file
*/
@@ -143,16 +150,30 @@ private void flushAndRelease() {
public void addTask(short[] rawData, int readSize){
mTasks.add(new Task(rawData, readSize));
}
+ public void addTask(short[] rawData,short[] rightData, int readSize){
+ mTasks.add(new Task(rawData, rightData,readSize));
+ }
private class Task{
private short[] rawData;
private int readSize;
+ private short[] rightData;
public Task(short[] rawData, int readSize){
this.rawData = rawData.clone();
this.readSize = readSize;
}
+ public Task(short[] leftData, short[] rightData,int readSize){
+ this.rawData = leftData.clone();
+ this.rightData = rightData.clone();
+ this.readSize = readSize;
+ }
public short[] getData(){
return rawData;
}
+
+ public short[] getRightData(){
+ return rightData;
+ }
+
public int getReadSize(){
return readSize;
}
diff --git a/RecordTest/AndroidManifest.xml b/RecordTest/AndroidManifest.xml
new file mode 100644
index 0000000..b845282
--- /dev/null
+++ b/RecordTest/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecordTest/proguard-project.txt b/RecordTest/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/RecordTest/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/RecordTest/project.properties b/RecordTest/project.properties
new file mode 100644
index 0000000..888961c
--- /dev/null
+++ b/RecordTest/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-21
+android.library.reference.1=../AndroidMP3RecorderLibrary
diff --git a/RecordTest/res/drawable-hdpi/ic_launcher.png b/RecordTest/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..288b665
Binary files /dev/null and b/RecordTest/res/drawable-hdpi/ic_launcher.png differ
diff --git a/RecordTest/res/drawable-mdpi/ic_launcher.png b/RecordTest/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..6ae570b
Binary files /dev/null and b/RecordTest/res/drawable-mdpi/ic_launcher.png differ
diff --git a/RecordTest/res/drawable-xhdpi/ic_launcher.png b/RecordTest/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..d4fb7cd
Binary files /dev/null and b/RecordTest/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/RecordTest/res/drawable-xxhdpi/ic_launcher.png b/RecordTest/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..85a6081
Binary files /dev/null and b/RecordTest/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/RecordTest/res/layout/main.xml b/RecordTest/res/layout/main.xml
new file mode 100644
index 0000000..80d2831
--- /dev/null
+++ b/RecordTest/res/layout/main.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecordTest/res/values/strings.xml b/RecordTest/res/values/strings.xml
new file mode 100644
index 0000000..4a34a49
--- /dev/null
+++ b/RecordTest/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ RecordTest
+
+
diff --git a/RecordTest/src/com/laba/recordtest/AudioRecorder.java b/RecordTest/src/com/laba/recordtest/AudioRecorder.java
new file mode 100644
index 0000000..6fbd1c2
--- /dev/null
+++ b/RecordTest/src/com/laba/recordtest/AudioRecorder.java
@@ -0,0 +1,233 @@
+package com.laba.recordtest;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import com.czt.mp3recorder.DataEncodeThread;
+import com.czt.mp3recorder.util.LameUtil;
+
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.os.Message;
+import android.util.Log;
+
+public class AudioRecorder extends Thread {
+
+ private static final String TAG = "AudioRecorder";
+
+ private final int sampleRates[] = {44100,22050, 11025, 8000};
+ private final int configs[] = { AudioFormat.CHANNEL_IN_MONO,AudioFormat.CHANNEL_IN_STEREO};
+ private final int formats[] = { AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_PCM_8BIT };
+
+ // ======================Lame Default Settings=====================
+ private static final int DEFAULT_LAME_MP3_QUALITY = 7;
+ /**
+ * Encoded bit rate. MP3 file will be encoded with bit rate 128kbps
+ */
+ private static final int DEFAULT_LAME_MP3_BIT_RATE = 128;
+
+ private AudioRecord audioRecord = null;
+ int bufsize = AudioRecord.ERROR_BAD_VALUE;
+ private boolean mShouldRun = false;
+ private boolean mShouldRecord = false;
+
+ /**
+ * 自定义 每220帧作为一个周期,通知一下需要进行编码
+ */
+ private static final int FRAME_COUNT = 220;
+ private short[] mPCMBuffer;
+ private DataEncodeThread mEncodeThread;
+
+ private File outputFile;
+ private double mDuration;//录音时间
+
+ public AudioRecorder(File file) {
+ outputFile = file;
+ }
+
+
+ public void startRecording(){
+ mShouldRecord = true;
+ }
+
+ public void resumeRecord(){
+ mShouldRecord = true;
+ }
+
+ public void pauseRecord(){
+ mShouldRecord = false;
+ }
+
+ public void stopRecord(){
+ mShouldRecord = false;
+ mShouldRun = false;
+ if(audioRecord != null){
+ audioRecord.stop();
+ audioRecord.release();
+ audioRecord = null;
+ }
+ // stop the encoding thread and try to wait until the thread finishes its job
+ Message msg = Message.obtain(mEncodeThread.getHandler(),
+ DataEncodeThread.PROCESS_STOP);
+ msg.sendToTarget();
+ }
+
+ private int mapFormat(int format){
+ switch (format) {
+ case AudioFormat.ENCODING_PCM_8BIT:
+ return 8;
+ case AudioFormat.ENCODING_PCM_16BIT:
+ return 16;
+ default:
+ return 0;
+ }
+ }
+
+ public int getDuration(){
+ return (int)mDuration;
+ }
+
+ @Override
+ public void run() {
+ super.run();
+ if(!isFound()){
+ Log.e(TAG, "Sample rate, channel config or format not supported!");
+ return;
+ }
+ init();
+ mShouldRun = true;
+ boolean oldShouldRecord = false;
+
+ int bytesPerSecond = audioRecord.getSampleRate() * mapFormat(audioRecord.getAudioFormat()) / 8 * audioRecord.getChannelCount();
+ mDuration = 0.0;
+ while (mShouldRun) {
+ if(mShouldRecord != oldShouldRecord){
+ if(mShouldRecord){
+ audioRecord.startRecording();
+ }else{
+ audioRecord.stop();
+ }
+ oldShouldRecord = mShouldRecord;
+ }
+
+ if(mShouldRecord){
+ int readSize = audioRecord.read(mPCMBuffer, 0, bufsize);
+ if (readSize > 0) {
+ double read_ms = (1000.0 * readSize * 2) / bytesPerSecond;
+ mDuration += read_ms;
+
+ if(audioRecord.getChannelCount()==1){
+ mEncodeThread.addTask(mPCMBuffer, readSize);
+ }else if(audioRecord.getChannelCount() == 2){
+ short[] leftData = new short[readSize / 2];
+ short[] rightData = new short[readSize / 2];
+ for(int i = 0;i< readSize /2; i = i + 2){
+ leftData[i] = mPCMBuffer[2 * i];
+ if( 2 * i + 1 < readSize){
+ leftData[i+1] = mPCMBuffer[2 * i + 1];
+ }
+ if(2 * i + 2 < readSize){
+ rightData[i] = mPCMBuffer[2 * i + 2];
+ }
+ if(2 * i + 3 < readSize){
+ rightData[i + 1] = mPCMBuffer[2 * i + 3];
+ }
+ }
+ mEncodeThread.addTask(leftData,rightData, readSize / 2);
+ }
+ }
+ }
+ }
+ }
+
+
+ public boolean isRecording(){
+ return mShouldRecord;
+ }
+
+ private void init() {
+ int bytesPerFrame = audioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT ? 2
+ : 1;
+ int frameSize = bufsize / bytesPerFrame;
+ if (frameSize % FRAME_COUNT != 0) {
+ frameSize += (FRAME_COUNT - frameSize % FRAME_COUNT);
+ bufsize = frameSize * bytesPerFrame;
+ }
+ mPCMBuffer = new short[bufsize];
+ /*
+ * Initialize lame buffer
+ * mp3 sampling rate is the same as the recorded pcm sampling rate
+ * The bit rate is 128kbps
+ */
+ LameUtil.init(audioRecord.getSampleRate(), audioRecord.getChannelCount(), audioRecord.getSampleRate(), DEFAULT_LAME_MP3_BIT_RATE, DEFAULT_LAME_MP3_QUALITY);
+ // Create and run thread used to encode data
+ // The thread will
+ try {
+ if(!outputFile.exists()){
+ outputFile.createNewFile();
+ }
+ mEncodeThread = new DataEncodeThread(outputFile, bufsize);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }catch(IOException e){
+ e.printStackTrace();
+ }
+ mEncodeThread.start();
+ audioRecord.setRecordPositionUpdateListener(mEncodeThread, mEncodeThread.getHandler());
+ audioRecord.setPositionNotificationPeriod(FRAME_COUNT);
+ }
+
+ /**
+ * get the available AudioRecord
+ * @return
+ */
+ private boolean isFound(){
+ boolean isFound = false;
+
+ int sample_rate = -1;
+ int channel_config = -1;
+ int format = -1;
+
+ for(int x=0;!isFound &&x T $(int id){
+ return (T)findViewById(id);
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.btn_play:
+ File file = new File(fileName);
+ if(file.exists()){
+ playRecord();
+ }else{
+ Toast.makeText(this, "请先录音", Toast.LENGTH_SHORT).show();
+ }
+ break;
+ case R.id.btn_record_start:
+ mp3Recorder.prepare();
+ mp3Recorder.startRecording();
+
+ Log.e(TAG, "record started");
+ Toast.makeText(this, "录音开始", Toast.LENGTH_SHORT).show();
+ btnRecordStart.setEnabled(false);
+ break;
+ case R.id.btn_record_pause:
+ if(mp3Recorder.getRecorderState() == State.PAUSED){
+ mp3Recorder.resume();
+ btnRecordPause.setText("暂停录音");
+ Toast.makeText(this, "已恢复", Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "record started");
+ }else if(mp3Recorder.getRecorderState() == State.RECORDING){
+ mp3Recorder.pause();
+ Toast.makeText(this, "已暂停", Toast.LENGTH_SHORT).show();
+ btnRecordPause.setText("恢复录音");
+ Log.e(TAG, "record paused");
+ }
+
+ break;
+ case R.id.btn_record_stop:
+ mp3Recorder.stop();
+ Log.e(TAG, "record stoped");
+ Toast.makeText(this, "已停止录音", Toast.LENGTH_SHORT).show();
+ btnRecordStart.setEnabled(true);
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ private void playRecord() {
+ File file = new File(fileName);
+ if (file.exists()) {
+ MediaPlayer mp=new MediaPlayer();
+ try {
+ mp.setDataSource(fileName); // 设置数据源
+ mp.prepare();
+ } catch (IllegalStateException | IOException e) {
+ e.printStackTrace();
+ }
+ mp.start();
+ }
+ }
+}
diff --git a/RecordTest/src/com/laba/recordtest/Mp3Recorder.java b/RecordTest/src/com/laba/recordtest/Mp3Recorder.java
new file mode 100644
index 0000000..4012f01
--- /dev/null
+++ b/RecordTest/src/com/laba/recordtest/Mp3Recorder.java
@@ -0,0 +1,118 @@
+package com.laba.recordtest;
+
+import java.io.File;
+
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.Message;
+import android.util.Log;
+
+
+public class Mp3Recorder implements Callback{
+ private int mMaxDuration;// 最长录音时间,单位:秒(s)
+ private String outputFilePath;
+ private AudioRecorder audioRecorder = null;
+ private int state = State.UNINITIALIZED;
+ private Handler mHandler;
+ private OnMaxDurationReached onMaxDurationReachedListener;
+
+ public class State {
+ public static final int UNINITIALIZED = -1;
+ public static final int INITIALIZED = 0;
+ public static final int PREPARED = 1;
+ public static final int RECORDING = 2;
+ public static final int PAUSED = 3;
+ public static final int STOPPED = 4;
+ }
+
+ private Runnable r = new Runnable() {
+
+ @Override
+ public void run() {
+ if(state != State.STOPPED){
+ onMaxDurationReachedListener.onMaxDurationReached();
+ }
+ }
+ };
+
+ /**
+ * TODO 要考虑使用单例模式
+ */
+ public Mp3Recorder(){
+ mHandler = new Handler(this);
+ }
+
+ public int getmMaxDuration() {
+ return mMaxDuration;
+ }
+
+ public void setmMaxDuration(int mMaxDuration) {
+ this.mMaxDuration = mMaxDuration;
+ }
+
+ public void setOutputFile(String path){
+ this.outputFilePath = path;
+ }
+
+ public void setOnMaxDurationReachedListener(
+ OnMaxDurationReached onMaxDurationReachedListener) {
+ this.onMaxDurationReachedListener = onMaxDurationReachedListener;
+ }
+
+ public void prepare(){
+ audioRecorder = new AudioRecorder(new File(outputFilePath));
+ audioRecorder.start();
+ state = State.PREPARED;
+ }
+
+ public void startRecording(){
+ if(state == State.PREPARED){
+ audioRecorder.startRecording();
+ state = State.RECORDING;
+ mHandler.removeCallbacks(r);
+ mHandler.postDelayed(r, mMaxDuration * 1000 - audioRecorder.getDuration());
+ }
+ }
+
+ public void pause(){
+ audioRecorder.pauseRecord();
+ state = State.PAUSED;
+ }
+
+ public void resume(){
+ audioRecorder.resumeRecord();
+ state = State.RECORDING;
+ }
+
+ public void stop(){
+ audioRecorder.stopRecord();
+ state = State.STOPPED;
+ mHandler.removeCallbacks(r);
+ }
+
+ public int getRecorderState(){
+ return state;
+ }
+
+ public void reset(){
+ if(null == audioRecorder){
+ prepare();
+ return;
+ }
+ if(null != audioRecorder && state != State.STOPPED){
+ stop();
+ }
+ audioRecorder = null;
+ prepare();
+ }
+
+ private void setLocation(float latitude, float longitude){
+
+ }
+
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ return false;
+ }
+}
diff --git a/RecordTest/src/com/laba/recordtest/OnMaxDurationReached.java b/RecordTest/src/com/laba/recordtest/OnMaxDurationReached.java
new file mode 100644
index 0000000..7bde0d0
--- /dev/null
+++ b/RecordTest/src/com/laba/recordtest/OnMaxDurationReached.java
@@ -0,0 +1,5 @@
+package com.laba.recordtest;
+
+public interface OnMaxDurationReached {
+ public void onMaxDurationReached();
+}