AudioRecord 录制PCM

AudioRecord 是 Android 提供的用于实现录音功能,录制得到无损的PCM音频数据。

从AudioRecord构造函数就可以看出:

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig,                    int audioFormat, int bufferSizeInBytes)
  • audioSource 音频源,如麦克风MIC
  • sampleRateInHz 采样率,每秒采样次数,常用有8000、44100
  • channelConfig 声道,有单声道MONO和立体声STEREO
  • audioFormat 采样大小,8bit或16bit,采样大小越大,音质越好
  • bufferSizeInBytes 采集数据的缓冲区,可以通过getMinBufferSize()获得最小的buffer size
    (参考:https://www.jianshu.com/p/80a140cf3d99)

1.初始化

private final static int AudioSource = MediaRecorder.AudioSource.DEFAULT;private final static int AudioRate = 44100;private final static int AudioInChannel = AudioFormat.CHANNEL_IN_MONO;private final static int AudioOutChannel = AudioFormat.CHANNEL_OUT_MONO;private final static int AudioFormater = AudioFormat.ENCODING_PCM_16BIT;private AudioRecord mAudioRecord;     private int recordBufferMinSize;private AudioTrack mAudioTrack;

2.开始录制

private void startRecord() {    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) !=   PackageManager.PERMISSION_GRANTED) {       ActivityCompat.requestPermissions(this, MICROPHONE, 2);       return;    }    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {      ActivityCompat.requestPermissions(this, STORAGE, 2);      return;    }    isRecording = true;    mExecutor.execute(new RecordRunnable()); // mExecutor是一个简单的线程池}
RecordRunnable实现:
  • 调用 startRecording() 开始录制
  • 读取 AudioRecord音频数据:int readSize = mAudioRecord.read(bufferbytes, 0, recordBufferMinSize);
  • 往 FileOutputStream写数据:mOutputStream.write(bufferbytes, 0, readSize);
  • 关闭 AudioRcord
private class RecordRunnable implements Runnable {        private OutputStream mOutputStream;        private byte[] bufferbytes;        private File mFile;        public RecordRunnable() {            try {                mFile = getRecordFile(true);                bufferbytes = new byte[recordBufferMinSize];                mOutputStream = new FileOutputStream(mFile);            } catch (FileNotFoundException e) {                e.printStackTrace();            }        }        @Override        public void run() {            try {                mAudioRecord.startRecording();                while(isRecording) {                    int readSize = mAudioRecord.read(bufferbytes, 0, recordBufferMinSize);                    if (readSize > 0) {                        mOutputStream.write(bufferbytes, 0, readSize);                    }                }                mOutputStream.close();                View contentView = RecordingActivity.this.getWindow().getDecorView().findViewById(android.R.id.content);                contentView.post(new Runnable() {                    @Override                    public void run() {                        Toast.makeText(RecordingActivity.this, "录制完成: " + mFile.getAbsolutePath(),Toast.LENGTH_SHORT).show();                    }                });            } catch (IOException e) {                e.printStackTrace();            }        }    };

3.停止录制

private void stopRecord() {    if (isRecording) {        isRecording = false;        mAudioRecord.stop();    }}

AudioTrack 播放PCM

Android中可以使用AudioTrack播放PCM,播放的流程跟AudioRecord很类似:

AudioTrack 有两种数据加载模式:MODE_STREAM 和 MODE_STATIC,

MODE_STREAM 数据加载模式,将音频数据不断写入AudioTrack中,缺点是会有延迟
MODE_STATIC 音频流类型,将音频数据一次性写入AudioTrack中,不会有延迟,适合小文件,缺点是对大文件可能内存不足,需要 先write写数据,最后再调用 play()

1 初始化

playBufferMinSize = AudioTrack.getMinBufferSize(AudioRate, AudioOutChannel, AudioFormater);mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, AudioRate, AudioOutChannel, AudioFormater, playBufferMinSize, AudioTrack.MODE_STREAM);

####2 播放

 private void playPcm() {    isPlaying = true;    mExecutor.execute(new PlayerRunnable(PlayerMode.PCM)); }
PlayerRunnable 是同样三个流程:
  • 开始播放 mAudioTrack.play()
  • 输出数据
    int readSize = mInputStream.read(bufferbytes);
    mAudioTrack.write(bufferbytes, 0, readSize);
  • 关闭 mAudioTrack
public enum PlayerMode { WAV, PCM};private class PlayerRunnable implements Runnable {    private InputStream mInputStream;    private File mFile;    private byte[] bufferbytes;    private RecordingActivity.PlayerMode playerMode;    public PlayerRunnable(RecordingActivity.PlayerMode playerMode) {        this.playerMode = playerMode;    }    @Override    public void run() {        View contentView = RecordingActivity.this.getWindow().getDecorView().findViewById(android.R.id.content);        try {            mFile = playerMode == RecordingActivity.PlayerMode.PCM ? getPlayerFile() : getWAVFile();            if (mFile == null || !mFile.exists()) {                contentView.post(new Runnable() {                    @Override                    public void run() {                        Toast.makeText(RecordingActivity.this, "音频不存在", Toast.LENGTH_SHORT).show();                    }                });                return;            }            mAudioTrack.play();            bufferbytes = new byte[playBufferMinSize];            mInputStream = new FileInputStream(mFile);            if (playerMode == WAV) {                mInputStream.skip(44); // 去除WAV头部            }            while (mInputStream.available() > 0) {                int readSize = mInputStream.read(bufferbytes);                mAudioTrack.write(bufferbytes, 0, readSize);            }            mInputStream.close();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        mAudioTrack.stop();        isPlaying = false;    }}

PCM转WAV

在PCM增加WAV的头部就可以了,因为pcm 是wav 文件中音频数据的一种编码方式,事实上 wav 还有很多其它的方式编码

public class Pcm2WavUtil {    private int mSampleRate;    private int minBufferSize;    public Pcm2WavUtil(int mSampleRate, int mChannels, int mFormater) {        this.mSampleRate = mSampleRate;        //this.mChannels = mChannels;        this.minBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannels, mFormater);    }    public void pcm2wav(File inFile, File outFile) {        FileInputStream pcmFileStream;        FileOutputStream wavFileStream;        byte[] buffer = new byte[minBufferSize];        try {            wavFileStream = new FileOutputStream(inFile);            pcmFileStream = new FileInputStream(outFile);            long pcmLen = pcmFileStream.getChannel().size();            byte[] head = wavHeader(pcmLen, 1, mSampleRate, 16);            wavFileStream.write(head);            while(pcmFileStream.available() > 0) {                int readSize = pcmFileStream.read(buffer);                wavFileStream.write(buffer);            }            pcmFileStream.close();            wavFileStream.close();        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * @param pcmLen pcm 数据长度     * @param numChannels 声道设置, mono = 1, stereo = 2     * @param sampleRate 采样频率     * @param bitPerSample 单次数据长度, 例如 8bits     * @return wav 头部信息     */    public static byte[] wavHeader(long pcmLen, int numChannels, int sampleRate, int bitPerSample) {        byte[] header = new byte[44];        // ChunkID, RIFF, 占 4bytes        header[0] = 'R';        header[1] = 'I';        header[2] = 'F';        header[3] = 'F';        // ChunkSize, pcmLen + 36, 占 4bytes        long chunkSize = pcmLen + 36;        header[4] = (byte) (chunkSize & 0xff);        header[5] = (byte) ((chunkSize >> 8) & 0xff);        header[6] = (byte) ((chunkSize >> 16) & 0xff);        header[7] = (byte) ((chunkSize >> 24) & 0xff);        // Format, WAVE, 占 4bytes        header[8] = 'W';        header[9] = 'A';        header[10] = 'V';        header[11] = 'E';        // Subchunk1ID, 'fmt', 占 4bytes        header[12] = 'f';        header[13] = 'm';        header[14] = 't';        header[15] = ' ';        // Subchunk1Size, 16, 占 4bytes        header[16] = 16;        header[17] = 0;        header[18] = 0;        header[19] = 0;        // AudioFormat, pcm = 1, 占 2bytes        header[20] = 1;        header[21] = 0;        // NumChannels, mono = 1, stereo = 2, 占 2bytes        header[22] = (byte) numChannels;        header[23] = 0;        // SampleRate, 占 4bytes        header[24] = (byte) (sampleRate & 0xff);        header[25] = (byte) ((sampleRate >> 8) & 0xff);        header[26] = (byte) ((sampleRate >> 16) & 0xff);        header[27] = (byte) ((sampleRate >> 24) & 0xff);        // ByteRate = SampleRate * NumChannels * BitsPerSample / 8, 占 4bytes        long byteRate = sampleRate * numChannels * bitPerSample / 8;        header[28] = (byte) (byteRate & 0xff);        header[29] = (byte) ((byteRate >> 8) & 0xff);        header[30] = (byte) ((byteRate >> 16) & 0xff);        header[31] = (byte) ((byteRate >> 24) & 0xff);        // BlockAlign = NumChannels * BitsPerSample / 8, 占 2bytes        header[32] = (byte) (numChannels * bitPerSample / 8);        header[33] = 0;        // BitsPerSample, 占 2bytes        header[34] = (byte) bitPerSample;        header[35] = 0;        // Subhunk2ID, data, 占 4bytes        header[36] = 'd';        header[37] = 'a';        header[38] = 't';        header[39] = 'a';        // Subchunk2Size, 占 4bytes        header[40] = (byte) (pcmLen & 0xff);        header[41] = (byte) ((pcmLen >> 8) & 0xff);        header[42] = (byte) ((pcmLen >> 16) & 0xff);        header[43] = (byte) ((pcmLen >> 24) & 0xff);        return header;    }}

AudioTrack播放PCM编码的WAV

与播放pcm的代码一致,只需要过滤掉wav的头部 mInputStream.skip(44)

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. android 打开移动数据流程
  4. 基于Android(安卓)6.0修改的音乐播放器可设置卡1卡2铃声
  5. Android:giraffeplayer2 ConnectException:Failed to connect to
  6. Android通过AudioFocus机制对音频焦点进行管理
  7. 如何在Android设备中用NDK编译SQLite并且对SQLite进行操作(增删)-H
  8. Android查询短信数据库 查询联系人数据库
  9. Android录音,和实现微信长按录音效果!

随机推荐

  1. Android如何解析Intent Filter
  2. android中shape,selector,layer-list的使用
  3. Android照相机竖屏研究引导
  4. Jquery 仿 android Toast效果
  5. Android音频系统之AudioPolicyService的
  6. Android上的bug定位(troubleshooting)
  7. OPhone/Android的学习(3)—再熟悉几个常
  8. Android消息推送完美解决方案全析
  9. Android(安卓)webview设置背景透明,去掉白
  10. android 安全机制