【android】音视频开发二:Android平台PCM 数据的采集

前言
Android SDK 提供了两套音频采集的API,分别是:MediaRecorder 和 AudioRecord,前者是一个更加上层一点的API,它可以直接把手机麦克风录入的音频数据进行编码压缩(如AMR、MP3等)并存成文件,而后者则更接近底层,能够更加自由灵活地控制,可以得到原始的一帧帧PCM音频数据。本文近记录AudioRecord的定义和使用。

AudioRecord基础概念
AudioRecord官方概念定义
AudioRecord类的主要功能是让各种java应用能够管理音频资源,以便它们通过此类能够录制声音相关的硬件所收集的声音。此功能的实现就是通过「pulling」(读取)AudioRecord对象的录音数据。AudioRecord类提供的三个获取声音数据的方法分别是 read(byte[], int, int),、read(short[], int, int) 和 read(ByteBuffer, int)。无论选择使用哪一个方法都必须事先设定方便用户的声音数据存储格式。

开始录音的时候,AudioRecord需要初始化一个相关联的声音buffer,这个buffer主要是用来保存新的声音数据。这个buffer的大小,我们可以在对象构造期间去指定。它表明一个AudioRecord对象还没有被读取(同步)声音数据前能够录多长的音(即一次可以录制的声音容量)。声音数据从音频硬件中被读出,数据大小不超过整个录音数据的大小(可以分多次读出),即每次读取初始化buffer容量的数据。

AudioRecord使用流程
1. 构造一个AudioRecord对象,其中最小录音缓存buffer大小可以通过getMinBufferSize方法获取。如果buffer容量过小,将导致对象构造的失败。
2 . 初始化一个buffer,该buffer大小等于AudioRecord对象用于写声音数据的buffer大小。
3. 开始录音
4. 创建一个数据流,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中的数据导入数据流
5. 关闭数据流
6. 停止录音
AudioRecord构造方法说明

 /**
     * 
     * @param audioSource   音频来源
     * @param sampleRateInHz   采样率
     * @param channelConfig  android支持双声道立体声和单声道。MONO单声道,STEREO立体声
     * @param audioFormat  采集来的数据当然使用PCM编码(脉冲代码调制编码,即PCM编码,PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。)
     *         //android支持的采样大小16bit 或者8bit。当然采样大小越大,那么信息量越多,音质也越高,
     *         //现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit足够了。
     * @param bufferSizeInBytes  采集数据需要的缓冲区的大小
     * @throws IllegalArgumentException
     */
    public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
                       int bufferSizeInBytes)
            throws IllegalArgumentException {
    }

参数详解:
参数sampleRateInHz 采样率(赫兹),方法注释里有说明
只能在4000到192000的范围内取值

在AudioFormat类里
public static final int SAMPLE_RATE_HZ_MIN = 4000; 最小4000
public static final int SAMPLE_RATE_HZ_MAX = 192000; 最大192000

参数channelConfig 声道配置 描述音频声道的配置,例如左声道/右声道/前声道/后声道。
在AudioFormat类录
public static final int CHANNEL_IN_LEFT = 0x4;//左声道
public static final int CHANNEL_IN_RIGHT = 0x8;//右声道
public static final int CHANNEL_IN_FRONT = 0x10;//前声道
public static final int CHANNEL_IN_BACK = 0x20;//后声道
public static final int CHANNEL_IN_LEFT_PROCESSED = 0x40;
public static final int CHANNEL_IN_RIGHT_PROCESSED = 0x80;
public static final int CHANNEL_IN_FRONT_PROCESSED = 0x100;
public static final int CHANNEL_IN_BACK_PROCESSED = 0x200;
public static final int CHANNEL_IN_PRESSURE = 0x400;
public static final int CHANNEL_IN_X_AXIS = 0x800;
public static final int CHANNEL_IN_Y_AXIS = 0x1000;
public static final int CHANNEL_IN_Z_AXIS = 0x2000;
public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000;
public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000;
public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;//单声道
public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);//立体声道(左右声道)

参数audioFormat 音频格式 表示音频数据的格式。
注意!一般的手机设备可能只支持 16位PCM编码,如果其他的都会报错为坏值.

public static final int ENCODING_PCM_16BIT = 2; //16位PCM编码
public static final int ENCODING_PCM_8BIT = 3; //8位PCM编码
public static final int ENCODING_PCM_FLOAT = 4; //4位PCM编码
public static final int ENCODING_AC3 = 5;
public static final int ENCODING_E_AC3 = 6;
public static final int ENCODING_DTS = 7;
public static final int ENCODING_DTS_HD = 8;
public static final int ENCODING_MP3 = 9; //MP3编码 此格式可能会因为不设备不支持报错
public static final int ENCODING_AAC_LC = 10;
public static final int ENCODING_AAC_HE_V1 = 11;
public static final int ENCODING_AAC_HE_V2 = 12;
注意,最后生成的音频文件是 PCM 格式的,也就是最原始的音频数据,它没有头信息,不能直接播放,必须转换成可识别的格式才行。这里我们把它转成 WAV 格式,在文件的数据开头加入 WAVE HEAD 即可。

实现录音功能代码
声明变量

private static AudioRecordManager audioRecordManager = null;
    //声明AudioRecord对象
    private AudioRecord mAudioRecord;
    //声明record数据大小
    private int recordBufferSize = 0;
    private boolean isRecording;
    private FileOutputStream outputStream = null;
    private RecordRunnable recordRunnable;
    private File mRecordVoiceFile;

进行初始化

recordBufferSize = AudioRecord.getMinBufferSize(16000 //采样率,每秒16000个采样点
                , AudioFormat.CHANNEL_IN_MONO,//单声道
                AudioFormat.ENCODING_PCM_16BIT//采样精度,一个采样点16比特,相当于2个字节。
        );
        mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,//音频来源
                16000,//采样率
                AudioFormat.CHANNEL_IN_MONO,//android支持双声道立体声和单声道。MONO单声道,STEREO立体声
                AudioFormat.ENCODING_PCM_16BIT,//采集来的数据当然使用PCM编码(脉冲代码调制编码,即PCM编码。
                //PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。)
        //android支持的采样大小16bit 或者8bit。当然采样大小越大,那么信息量越多,音质也越高,
        //现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit足够了。
                recordBufferSize//采集数据需要的缓冲区的大小
        );

获取录音保存到本地文件当中

class RecordRunnable extends Thread{

        @Override
        public void run() {
            super.run();
            Log.d(TAG, "RecordRunnable : begin");
            try {
                if (mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED){
                    byte[] buffer = new byte[recordBufferSize];
                    mAudioRecord.startRecording();
                    while (!currentThread().isInterrupted() && isRecording){
                        int read = mAudioRecord.read(buffer, 0, recordBufferSize);
                        if (AudioRecord.ERROR_INVALID_OPERATION != read){
                            outputStream.write(buffer);
                            outputStream.flush();
                        }
                    }
                    outputStream.close();
                }
            }catch (Exception e){
                Log.e(TAG, "run: --"+e.getMessage() );
            }

        }
    }

以上即能获取到录音设备的原始音频。下一节则开始记录pcm的播放----音视频开发(三):使用 AudioTrack 播放PCM音频

 



 

posted @ 2023-02-16 16:16  opensmarty  阅读(309)  评论(0编辑  收藏  举报