Android 錄音(錄音時爲pcm,然後轉爲MP3)

項目中用的評論回覆功能,錄製語言時爲pcm格式,然後轉換爲MP3格式:

package zhiji.dajing.com.util;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;

import com.jiangdg.lametomp3.LameMp3;
import com.jiangdg.lametomp3.Mp3Recorder;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class AudioRecoderUtils {

    //文件路徑
    private String filePath;
    //文件夾路徑
    private String FolderPath;

    AudioRecord audioRecord;

    private final String TAG = "AudioRecoderUtils";
    public static final int MAX_LENGTH = 1000 * 1000 * 1000 ;// 最大錄音時長

    private OnAudioStatusUpdateListener audioStatusUpdateListener;
    private  String voicePath = Environment.getExternalStorageDirectory()+"/dajing/audio/pcm/";
    private String voiceName = TimeUtils.getCurrentTime() + ".pcm";
    private boolean isRecording = false;

    int nMinBufSize =AudioRecord.getMinBufferSize(16000,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT);
    private double volume;
    private ExecutorService mThreadExecutor;
    private Handler handler;
    static AudioRecoderUtils audioRecoderUtils_p ;
    public boolean isTmpConversition;


    /**
     * 文件存儲默認sdcard/record
     */
    public AudioRecoderUtils(){
        //默認保存路徑爲/sdcard/record/下
        this(Environment.getExternalStorageDirectory()+"/dajing/audio/pcm/");
        mThreadExecutor = Executors.newScheduledThreadPool(3);
    }

    public AudioRecoderUtils(String filePath) {

        File path = new File(filePath);
        if(!path.exists())
            path.mkdirs();

        this.FolderPath = filePath;

        handler = new Handler();
    }

    public static AudioRecoderUtils getInstance(){

        if (audioRecoderUtils_p == null){
            audioRecoderUtils_p = new AudioRecoderUtils();
        }

        return audioRecoderUtils_p;
    }

    private long startTime;
    private long endTime;


    public void startRecord(){

        audioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 16000,
                AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, nMinBufSize);


        final File file = new File(voicePath);
        voiceName = TimeUtils.getCurrentTime() + ".pcm";

        final File fileaudio = new File(voicePath + voiceName);

        filePath = voicePath + voiceName;

        if (fileaudio.exists()) {
            fileaudio.delete();
        }

        if (!file.exists()) {
            file.mkdirs();
        }

        audioRecord.startRecording();
        isRecording = true;
        startTime = System.currentTimeMillis();


        mThreadExecutor.execute(new Runnable() {
            @Override
            public void run() {
                FileOutputStream fos = null;
                byte data[] = new byte[nMinBufSize];
                try {
                    fos = new FileOutputStream(fileaudio);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }

                if (null != fos) {
                    while (isRecording) {
                        int read = audioRecord.read(data, 0, nMinBufSize);
                        volume = getVolume(read, data.clone());

                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                audioStatusUpdateListener.onUpdate(volume,System.currentTimeMillis()-startTime);
                            }
                        });
                        //返回正確時纔讀取數據
                        if (AudioRecord.ERROR_INVALID_OPERATION != read) {
                            try {
                                fos.write(data);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });


    }


    private double getVolume(int r, byte[] bytes_pkg) {
        int v = 0;
        for(int i = 0; i < bytes_pkg.length; i+=2){

            int v1 = bytes_pkg[i] & 0xFF;

            int v2 = bytes_pkg[i + 1] & 0xFF;

            int temp = v1 + (v2 << 8);// 小端

            if (temp >= 0x8000) {
                temp = 0xffff - temp;
            }

            v += Math.abs(temp);
        }

        int tmp_v = v / bytes_pkg.length / 2;

        volume = Math.log10(1 + tmp_v) * 10;
        return volume;

    }

    public Long stopRecord() {
        isRecording = false;
        if (audioRecord != null) {
            audioRecord.stop();
            audioRecord.release();
            //調用release之後必須置爲null
            audioRecord = null;
        }


        endTime = System.currentTimeMillis();
        process();

        return endTime - startTime;
    }

    public static String path;
    private File mFile;
    public  String processPath = voicePath + System.currentTimeMillis() + "process.pcm";
    private Timer timer;

    private void process() {
        mThreadExecutor.execute(new Runnable() {
            @Override
            public void run() {
                mFile = new File(filePath);
                try {
                    FileInputStream ins = new FileInputStream(mFile);
                    byte[] bbytes = new byte[(int) mFile.length()];
                    int readBytes = ins.read(bbytes);
                    ins.close();


                    Log.i(TAG,"讀取pcm數據流,大小爲:"+readBytes);

                    //錄音用16K採用,單聲道,16位
                    //讀取PCM文件到bbytes[]

                    int count = bbytes.length/2;		//16位數組的長度
                    short[] data = new short[count];
                    for (int i = 0; i < count; i++) {
                        data[i] = (short) (bbytes[i * 2] & 0xff | (bbytes[2 * i + 1] & 0xff)<< 8);			//需要確認是否是小端模式
                    }

                    int num = 0;
                    float allNum = 0;
                    for (int i = 0; i < count; i++) {
                        if (data[i] > 1800) {
                            allNum = allNum + data[i];
                            num = num + 1;
                        }
                    }

                    float multiple;
                    if(allNum==0||num==0){
                        multiple=6;
                    }
                    else{
                        if (allNum / num < 2500) {
                            multiple = (float) (32000 / (allNum / num) / 2.5);
                        }else{
                            multiple = (float) (32000 / (allNum / num) / 6.5);
                        }
                        if (multiple < 1) {
                            multiple = 1;
                        }
                    }
                    for (int i = 0; i < count; i++) {
                        if (Math.abs(data[i]) < ((short)32000 / multiple)) {
                            data[i] = (short) (data[i] * multiple);
                        }
                    }

                    for (int i = 0; i < count; i++) {
                        bbytes[i * 2] = (byte) (data[i] >> 0);
                        bbytes[i * 2 + 1] = (byte) (data[i] >> 8);			//需要確認是否是小端模式
                    }




                    byte[] bbytes8000 = new byte[count];
                    int j = 0;
                    for (int i = 0; i < bbytes.length ; i++) {
                        if((i%4) == 0){
                            bbytes8000[j] = bbytes[i];
                            bbytes8000[j+1] = bbytes[i+1];
                            j+=2;
                        }
                    }


                    // 保存bbytes[]到PCM文件
                    try {

                        String finalPath = "";
                        if (isTmpConversition){
                            OutputStream out = new FileOutputStream(processPath);
                            out.write(bbytes8000);
                            out.close();
                            finalPath = AmrEncoder.pcm2Amr(processPath, Environment.getExternalStorageDirectory() +
                                    "/dajing/audio/amr/");

                        }else {
                            /** 初始化lame庫,配置相關信息
                             * @param inSampleRate pcm格式音頻採樣率
                             * @param outChannel pcm格式音頻通道數量
                             * @param outSampleRate mp3格式音頻採樣率
                             * @param outBitRate mp3格式音頻比特率
                             * @param quality mp3格式音頻質量,0~9,最慢最差~最快最好
                             */


                            LameMp3.lameInit(16000,AudioFormat.CHANNEL_IN_MONO,16000, Mp3Recorder.LAME_BITRATE_32,5);
                            byte[] mp3Bytes = new byte[bbytes.length];


                            int lameFlush = LameMp3.lameEncode(data,data,data.length,mp3Bytes);

                            Log.i(TAG,"lame編碼,大小爲:"+ lameFlush);
                            int flush = LameMp3.lameFlush(mp3Bytes);

                            Log.i(TAG,"錄製完畢,大小爲:" + flush);

                            OutputStream mp3Out = null;
                            String mp3Path = Environment.getExternalStorageDirectory() + "/dajing/audio/mp3/" + System.currentTimeMillis() + ".mp3";
                            File file = new File(Environment.getExternalStorageDirectory() + "/dajing/audio/mp3/");
                            if (!file.exists()){
                                file.mkdirs();
                            }

                            try {
                                mp3Out = new FileOutputStream(mp3Path);
                                mp3Out.write(mp3Bytes,0,lameFlush );
                                mp3Out.close();
                                finalPath = mp3Path;

                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }


                        String finalPath1 = finalPath;
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                audioStatusUpdateListener.onStop(finalPath1);
                            }
                        });

                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally {
                        LameMp3.lameClose();
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

    }

    private short[] transferByte2Short(byte[] data,int readBytes){
        // byte[] 轉 short[],數組長度縮減一半
        int shortLen = readBytes / 2;
        // 將byte[]數組裝如ByteBuffer緩衝區
        ByteBuffer byteBuffer = ByteBuffer.wrap(data, 0, readBytes);
        // 將ByteBuffer轉成小端並獲取shortBuffer
        ShortBuffer shortBuffer = byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
        short[] shortData = new short[shortLen];
        shortBuffer.get(shortData, 0, shortLen);
        return shortData;
    }

    /**
     * 取消錄音
     */
    public void cancelRecord(){
        isRecording = false;
        try {
            audioRecord.stop();
            audioRecord.release();
            audioRecord = null;

        }catch (RuntimeException e){
            audioRecord.release();
        }finally {
            audioRecord = null;

        }
        File file = new File(filePath);
        if (file.exists())
            file.delete();

        filePath = "";

    }



    public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) {
        this.audioStatusUpdateListener = audioStatusUpdateListener;
    }

    /**
     * 更新麥克狀態
     */
    private void updateMicStatus() {
        if (audioRecord != null) {
            timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        byte data[] = new byte[nMinBufSize];
                        int read = audioRecord.read(data, 0, nMinBufSize);
                        volume = getVolume(read, data.clone());
                        Log.e("reaudioTime","time 3 = " + volume);
                        double ratio =0.0;
                        if (volume < 1000 && volume > 0){
                            ratio = volume / 600;
                        }else {
                            ratio = volume / 4000;
                        }


                        double db = 0;// 分貝
                        if (ratio > 1) {
                            db = 20 * Math.log10(ratio);
                            if(null != audioStatusUpdateListener) {
                                double finalDb = db;
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {

                                    }
                                });
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            },100,100);

        }

    }

    public interface OnAudioStatusUpdateListener {
        /**
         * 錄音中...
         * @param db 當前聲音分貝
         * @param time 錄音時長
         */
        public void onUpdate(double db, long time);

        /**
         * 停止錄音
         * @param filePath 保存路徑
         */
        public void onStop(String filePath);
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章