Android audio and video development-AudioRecord realizes the recording function

AudioRecord realizes recording function

Introduction

The AudioRecord class manages the audio resources of the Java application to record audio from the audio input hardware of the platform.

AudioRecord records audio files in PCM format, which need to be played by AudioTrack ( introduced in the next issue ). AudioTrack is closer to the bottom layer and more professional than MediaRecorder.

Implementation steps and analysis

1. Add permissions
Insert picture description here

 <uses-permission android:name="android.permission.RECORD_AUDIO" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

They are recording permissions; read and write external storage permissions

2. Write a recording tool class based on AudioRecord

AudioRecordTool.java

package com.audioandvideo.two.Tool;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 基于AudioRecord的录音工具类
 */
public class AudioRecordTool{
    
    
    private AudioRecord audioRecord;
    private int recordBufSize = 0;
    private byte data[];
    private PcmToWavTool tool;
    private boolean isRecording = false;
    //录音得到的文件 的储存位置及文件名
    private final String pcmFileName = Environment.getExternalStorageDirectory() + "/Download/record.pcm";
    //转换成wav文件后新文件的存储位置及文件名
    private final String wavFileName = Environment.getExternalStorageDirectory() + "/Download/record1.wav";
    // 音频源:音频输入-麦克风
    private final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;
    // 采样率:音频的采样频率,每秒钟能够采样的次数,采样率越高,音质越高
    // 44100是目前的标准,但是某些设备仍然支持22050,16000,11025
    // 采样频率一般共分为22.05KHz、44.1KHz、48KHz三个等级
    private final static int AUDIO_SAMPLE_RATE = 44100;

    // 声道设置:android支持双声道立体声和单声道。MONO单声道,STEREO立体声
    private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO;

    // 编码制式和采样大小:采集来的数据当然使用PCM编码
    // (脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。)
    // android支持的采样大小16bit 或者8bit。当然采样大小越大,那么信息量越多,音质也越高,现在主流的采样
    // 大小都是16bit,在低质量的语音传输的时候8bit 足够了。
    private final static int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
   
    //初始化
    public void createAudioRecord() {
    
    
        recordBufSize = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING);  //audioRecord能接受的最小的buffer大小
        //构造方法,传入的参数上面在有解析
        audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING, recordBufSize);
        data = new byte[recordBufSize];
        tool = new PcmToWavTool(AUDIO_SAMPLE_RATE,AUDIO_CHANNEL,AUDIO_ENCODING);
    }
    //开始录音
    public void start(){
    
    
         Log.i("AUDIO","开始录音");
         isRecording = true;
         //调用startRecording()方法开始录制
         audioRecord.startRecording();
         MyThread myThread = new MyThread();
         myThread.start();
    }
    //停止录音
    public void stop(){
    
    
        isRecording = false;
        if (audioRecord != null){
    
    
            Log.i("AUDIO","停止录音");
            //调用stop()方法停止录制
            audioRecord.stop();
            //调用release() 释放本机录音资源。
            audioRecord.release();
            audioRecord = null;
        }
        //利用自定义工具类将pcm格式的文件转换为wav格式文件才能进行播放
        tool.pcmToWav(pcmFileName,wavFileName);
    }
    private class MyThread extends Thread{
    
    
        @Override
        public void run() {
    
    
            super.run();
            FileOutputStream os = null;
            try {
    
    
                //如果文件不存在,就创建文件
                if(!new File(pcmFileName).exists()){
    
    
                    new File(pcmFileName).createNewFile();
                }
                os = new FileOutputStream(pcmFileName);
            } catch (FileNotFoundException e) {
    
    
                e.printStackTrace();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }

            if (null != os) {
    
    
                while (isRecording) {
    
    
                    //调用read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes)方法
                    // 从音频硬件读取音频数据,以便记录到字节数组中。
                    int read = audioRecord.read(data, 0, recordBufSize);

                    // 如果读取音频数据没有出现错误,就将数据写入到文件
                    if (AudioRecord.ERROR_INVALID_OPERATION != read) {
    
    
                        try {
    
    
                            os.write(data);
                        } catch (IOException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                }

                try {
    
    
                    //关闭文件
                    os.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}

3. Write tools that convert pcm format to wav format before the player can play

PcmToWavTool .java

package com.audioandvideo.two.Tool;

import android.media.AudioFormat;
import android.media.AudioRecord;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**pcm格式转化为wav格式的工具类
 * PCM编码是直接存储声波采样被量化后所产生的非压缩数据,故被视为单纯的无损耗编码格式,其优点是可获得高质量的音频信号
 * WAV是最常见的声音文件格式之一
 */
public class PcmToWavTool {
    
    
        /**
         * 缓存的音频大小
         */
        private int mBufferSize;
        /**
         * 采样率
         */
        private int mSampleRate;
        /**
         * 声道数
         */
        private int mChannel;


        /**
         * @param sampleRate sample rate、采样率
         * @param channel channel、声道
         * @param encoding Audio data format、音频格式
         */
        PcmToWavTool(int sampleRate, int channel, int encoding) {
    
    
            this.mSampleRate = sampleRate;
            this.mChannel = channel;
            this.mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannel, encoding);
        }


        /**
         * pcm文件转wav文件
         *
         * @param inFilename 源文件路径
         * @param outFilename 目标文件路径
         */
        public void pcmToWav(String inFilename, String outFilename) {
    
    
            FileInputStream in;
            FileOutputStream out;
            long totalAudioLen;
            long totalDataLen;
            long longSampleRate = mSampleRate;
            int channels = mChannel == AudioFormat.CHANNEL_IN_MONO ? 1 : 2;
            long byteRate = 16 * mSampleRate * channels / 8;
            byte[] data = new byte[mBufferSize];
            try {
    
    
                in = new FileInputStream(inFilename);
                out = new FileOutputStream(outFilename);
                totalAudioLen = in.getChannel().size();
                totalDataLen = totalAudioLen + 36;

                writeWaveFileHeader(out, totalAudioLen, totalDataLen,
                        longSampleRate, channels, byteRate);
                while (in.read(data) != -1) {
    
    
                    out.write(data);
                }
                in.close();
                out.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        /**
         * 加入wav文件头
         */
        private void writeWaveFileHeader(FileOutputStream out, long totalAudioLen,
                                         long totalDataLen, long longSampleRate, int channels, long byteRate)
                throws IOException {
    
    
            byte[] header = new byte[44];
            // RIFF/WAVE header
            header[0] = 'R';
            header[1] = 'I';
            header[2] = 'F';
            header[3] = 'F';
            header[4] = (byte) (totalDataLen & 0xff);
            header[5] = (byte) ((totalDataLen >> 8) & 0xff);
            header[6] = (byte) ((totalDataLen >> 16) & 0xff);
            header[7] = (byte) ((totalDataLen >> 24) & 0xff);
            //WAVE
            header[8] = 'W';
            header[9] = 'A';
            header[10] = 'V';
            header[11] = 'E';
            // 'fmt ' chunk
            header[12] = 'f';
            header[13] = 'm';
            header[14] = 't';
            header[15] = ' ';
            // 4 bytes: size of 'fmt ' chunk
            header[16] = 16;
            header[17] = 0;
            header[18] = 0;
            header[19] = 0;
            // format = 1
            header[20] = 1;
            header[21] = 0;
            header[22] = (byte) channels;
            header[23] = 0;
            header[24] = (byte) (longSampleRate & 0xff);
            header[25] = (byte) ((longSampleRate >> 8) & 0xff);
            header[26] = (byte) ((longSampleRate >> 16) & 0xff);
            header[27] = (byte) ((longSampleRate >> 24) & 0xff);
            header[28] = (byte) (byteRate & 0xff);
            header[29] = (byte) ((byteRate >> 8) & 0xff);
            header[30] = (byte) ((byteRate >> 16) & 0xff);
            header[31] = (byte) ((byteRate >> 24) & 0xff);
            // block align
            header[32] = (byte) (2 * 16 / 8);
            header[33] = 0;
            // bits per sample
            header[34] = 16;
            header[35] = 0;
            //data
            header[36] = 'd';
            header[37] = 'a';
            header[38] = 't';
            header[39] = 'a';
            header[40] = (byte) (totalAudioLen & 0xff);
            header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
            header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
            header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
            out.write(header, 0, 44);
        }

}

4. Write Activity

AudioRecordActivity.java

package com.audioandvideo.two.Activity;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.audioandvideo.R;
import com.audioandvideo.two.Tool.AudioRecordTool;

/**
 * 录音Activity
 */
public class AudioRecordActivity extends AppCompatActivity implements View.OnClickListener{
    
    
private Button btnStart;
private Button btnStop;
private AudioRecordTool audioRecordTool;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audio_record);
        btnStart = findViewById(R.id.btn_start);
        btnStop = findViewById(R.id.btn_stop);
        audioRecordTool = new AudioRecordTool();
        //初始化
        audioRecordTool.createAudioRecord();
        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
    
    
        switch (v.getId()){
    
    
            case R.id.btn_start:
                audioRecordTool.start();
                break;
            case R.id.btn_stop:
                audioRecordTool.stop();
                break;
        }
    }

    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        audioRecordTool.stop();
    }
}

There are only 2 buttons on the page, one to start recording and one to stop recording
Insert picture description here

test

The phone has files and can play them.
Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_44758662/article/details/114079812