音视频

AudioRecord录制PCM与AudioTrack播放PCM

2018-05-15  本文已影响25人  钉某人

音频采集AudioRecord工作流程(PCM)

1.配置AudioRecord参数、初始化内部的音频缓冲区
AudioRecord的构造函数如下:

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

参数说明:

public int getMinBufferSize(int sampleRateInHz,int channelConfig,int audioFormat);

配置好后,检查一下AudioRecord的当前状态。

2.开始采集

mAudioRecord.startRecording();

3.提取数据
此时需要一个线程,从AudioRecord的缓冲区中将音频数据不断的读取出来。这里一定要及时,否则会出现“overrun”的错误,意味着应用层没有及时地取走音频数据,导致内部的音频缓冲区溢出。读取数据的函数如下:

//byte类型数据
public int read(byte[] audioData,int offsetInBytes,int sizeInBytes);

//short类型数据
public int read(short[] audioData,int offsetInShorts,int sizeInShorts);

拿到数据后通过Java层将数据直接写入文件

4.停止采集,释放资源
先读取数据再结束线程,再通过stop停止录音,通过release释放录音器,然后关闭io流。

音频采集AudioTrack工作流程(PCM)

1.配置AudioTrack参数、初始化内部的音频缓冲区
AudioTrack构造函数如下:

public AudioTrack(int streamType,int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes,int mode) 

绝大部分参数与AudioRecord相同,不同的是streamType mode,下面讲一下:

STREAM_VOCIE_CALL:电话声音
STREAM_SYSTEM:系统声音
STREAM_RING:铃声
STREAM_MUSIC:音乐声
STREAM_ALARM:警告声
STREAM_NOTIFICATION:通知声

2.将AudioTrack切换到播放状态,并开启播放线程写入pcm数据,关键函数如下:

//开始播放
mAudioTrack.play();

//写入数据,还有其他的函数
mAudioTrack.write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes)

3.先停止播放,再关闭播放线程并销毁资源

AudioRecord录制PCM音频参考代码:
/**
 * Created by 钉某人
 * github: https://github.com/DingMouRen
 * email: naildingmouren@gmail.com
 */

public class AudioRecordManager {
    private static final String TAG = "AudioRecordManager";
    /*录音管理类实例*/
    public static AudioRecordManager sInstance;
    /*录音实例*/
    protected AudioRecord mAudioRecord;
    /*录音线程*/
    private Thread mRecordThread;
    /*音频采集的输入源,麦克风*/
    private static int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
    /*音频采集的采样频率*/
    public static int SAMPLE_RATE_IN_HZ = 44100;
    /*音频采集的声道数,此处为单声道,后期处理可以转换成双声道的立体声,如果这里是MONO声道的话,会有变声的情况*/
    private static int CHANNEL_CONFIGURATION = AudioFormat.CHANNEL_IN_STEREO;
    /*音频采集的格式,数据位宽16位*/
    private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    /*音频缓冲区大小*/
    private int mBufferSizeInBytes = 0;
    /*是否正在录音*/
    private boolean mIsRecording = false;
    /*文件输出流*/
    private FileOutputStream mFileOutputStream;
    /*文件输出路径*/
    private String mOutputFilePath;

    /*单例模式*/
    private AudioRecordManager(){}
    public static AudioRecordManager getInstance(){
        if (null == sInstance){
            synchronized (AudioRecordManager.class){
                if (null == sInstance){
                    sInstance = new AudioRecordManager();
                    return sInstance;
                }
            }
        }
        return sInstance;
    }

    /**
     * 初始化配置
     */
    public void initConfig() throws AudioConfigurationException {

        if (null != mAudioRecord) mAudioRecord.release();

        //使用44.1kHz的采样率初始化录音器
        try {
            mBufferSizeInBytes = AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ,CHANNEL_CONFIGURATION,AUDIO_FORMAT);
            mAudioRecord = new AudioRecord(AUDIO_SOURCE,SAMPLE_RATE_IN_HZ,CHANNEL_CONFIGURATION,AUDIO_FORMAT,mBufferSizeInBytes);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            Log.e(TAG,"采样率44.1kHZ的AudioRecord初始化失败");
        }

/*        //44.1kHz采样率没有成功的话,再使用16kHz的采样率初始化录音器
        if (mAudioRecord == null || mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED){
            try {
                SAMPLE_RATE_IN_HZ = 16000;
                mBufferSizeInBytes = AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ,CHANNEL_CONFIGURATION,AUDIO_FORMAT);
                mAudioRecord = new AudioRecord(AUDIO_SOURCE,SAMPLE_RATE_IN_HZ,CHANNEL_CONFIGURATION,AUDIO_FORMAT,mBufferSizeInBytes);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
                Log.e(TAG,"采样率16kHZ的AudioRecord初始化失败");
            }
        }*/

        //都失败的话,抛出异常
        if (mAudioRecord == null || mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) throw new AudioConfigurationException();

    }

    /**
     * 开始录音
     */
    public void startRecord(String filePath) throws AudioStartRecordingException {

        if (mAudioRecord != null && mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED){
            try {
                mAudioRecord.startRecording();
            } catch (Exception e) {
                throw new AudioStartRecordingException();
            }
        }else {
            throw new AudioStartRecordingException();
        }

        mIsRecording = true;
        mRecordThread = new Thread(new RecordRunnable(),"RecordThread");

        try {
            this.mOutputFilePath = filePath;
            mRecordThread.start();
        } catch (Exception e) {
            e.printStackTrace();
            throw new AudioStartRecordingException();
        }
    }

    /**
     * 结束录音
     */
    public void stopRecord(){
        try {
            if (mAudioRecord != null){

                mIsRecording = false;

                //关闭线程
                try {
                    if (mRecordThread != null){
                        mRecordThread.join();
                        mRecordThread = null;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //关闭录音,释放资源
                releaseAudioRecord();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭录音,释放资源
     */
    private void releaseAudioRecord(){
        if (mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
            mAudioRecord.stop();
            mAudioRecord.release();
            mAudioRecord = null;
        }
    }


    class RecordRunnable implements Runnable{
        @Override
        public void run() {
            try {
                mFileOutputStream = new FileOutputStream(mOutputFilePath);
                byte[] audioDataArray = new byte[mBufferSizeInBytes];
                while (mIsRecording){
                    int audioDataSize = getAudioRecordBufferSize(mBufferSizeInBytes,audioDataArray);
                    if (audioDataSize > 0) {
                        mFileOutputStream.write(audioDataArray);
                    }else {
                        mIsRecording = false;
                    }
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    if (null != mFileOutputStream){
                        mFileOutputStream.close();
                        mFileOutputStream = null;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 读取缓存区的大小
     * @param bufferSizeInBytes
     * @param audioDataArray
     * @return
     */
    private int getAudioRecordBufferSize(int bufferSizeInBytes, byte[] audioDataArray) {
        if (mAudioRecord != null){
            int size = mAudioRecord.read(audioDataArray,0,bufferSizeInBytes);
            return size;
        }else {
            return 0;
        }
    }

    /**
     * 是否正在录音
     */
    public boolean isRecording(){
        return mIsRecording;
    }
}

AudioTrack播放PCM音频参考代码:
/**
 * Created by 钉某人
 * github: https://github.com/DingMouRen
 * email: naildingmouren@gmail.com
 */

public class AudioTrackManager {
    private static final String TAG = "AudioTrackManager";
    public static AudioTrackManager sInstance;
    private AudioTrack mAudioTrack;
    /*音频管理策略*/
    private static int STREAM_TYPE = AudioManager.STREAM_MUSIC;
    /*音频的采样率,44.1kHz可以所有手机*/
    private static int SAMPLE_RATE_IN_HZ = 44100;
    /*音频的声道数,此处为单声道*/
    private static int CHANNEL_CONFIGURATION = AudioFormat.CHANNEL_IN_STEREO;
    /*采样格式,数据位宽是16位*/
    private static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    /*音频缓存区大小*/
    private int mBufferSizeInBytes = 0;
    /*是否正在播放*/
    private boolean mIsPlaying = false;
    private Thread mPlayingThread;
    /*播放文件的路径*/
    private String mFilePath;
    /*读取文件IO流*/
   private DataInputStream mDataInputStream;

    /*单例模式*/
    private AudioTrackManager(){}
    public static AudioTrackManager getInstance(){
        if (null == sInstance){
            synchronized (AudioTrackManager.class){
                if (null == sInstance){
                    sInstance = new AudioTrackManager();
                    return sInstance;
                }
            }
        }
        return sInstance;
    }

    /**
     * 初始化配置
     */
    public void initConfig() throws AudioConfigurationException {

        if (null != mAudioTrack) mAudioTrack.release();

        mBufferSizeInBytes = AudioTrack.getMinBufferSize(SAMPLE_RATE_IN_HZ,CHANNEL_CONFIGURATION,AUDIO_FORMAT);
        mAudioTrack = new AudioTrack(STREAM_TYPE,SAMPLE_RATE_IN_HZ,CHANNEL_CONFIGURATION,AUDIO_FORMAT,mBufferSizeInBytes,AudioTrack.MODE_STREAM);

        if (mAudioTrack == null || mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) throw new AudioConfigurationException();

    }

    /**
     * 开始播放录音
     */
    public synchronized void play(String filePath){
        Log.e(TAG,"播放状态:"+mAudioTrack.getState());
        if (mIsPlaying) return;
        if (null != mAudioTrack && mAudioTrack.getState() == AudioTrack.STATE_INITIALIZED){
            mAudioTrack.play();
        }
        this.mFilePath = filePath;
        mIsPlaying = true;
        mPlayingThread = new Thread(new PlayingRunnable(),"PlayingThread");
        mPlayingThread.start();
    }

    /**
     * 停止播放
     */
    private void stop(){
        try {
            if (mAudioTrack != null){

                mIsPlaying = false;

                //首先停止播放
                mAudioTrack.stop();

                //关闭线程
                try {
                    if (mPlayingThread != null){
                        mPlayingThread.join();
                        mPlayingThread = null;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //释放资源
                releaseAudioTrack();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放资源
     */
    private void releaseAudioTrack(){
        if (mAudioTrack.getState() == AudioRecord.STATE_INITIALIZED) {
            mAudioTrack.release();
            mAudioTrack = null;
        }
    }


    class PlayingRunnable implements Runnable{
        @Override
        public void run() {
            try {
                FileInputStream fileInputStream = new FileInputStream(new File(mFilePath));
                mDataInputStream = new DataInputStream(fileInputStream);
                byte[] audioDataArray = new byte[mBufferSizeInBytes];
                int readLength = 0;
                while (mDataInputStream.available() > 0){
                    readLength = mDataInputStream.read(audioDataArray);
                    if (readLength > 0) {
                        mAudioTrack.write(audioDataArray,0,readLength);
                    }
                }
                stop();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Github源码

上一篇下一篇

猜你喜欢

热点阅读