音视频 — AudioRecorder 和 AudioTrack

基础知识

PCM(Pulse Code Modulation),脉冲编码调制。人耳听到的是模拟信号,PCM是把声音从模拟信号转化为数字信号的技术。简单来说就是一种无压缩编码

采样频率、量化精度(采样位数)和声道数:
  • 采样频率
    是设备一秒钟内对模拟信号的采样次数,在主流的采集卡上分为:(8Khz的电话采样率就可以达到人的对话程度)
22.05KHz:无线电广播; 
44.1KHz:音频 CD,MP3等; 
48KHz:miniDV、数字电视、DVD、电影和专业音频。
  • 采样位数
    它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。它的数值越大,分辨率也就越高,所发出声音的能力越强。
    在计算机中采样位数一般有8位和16位之分,8位不是说把纵坐标分成8份,而是分成2的8次方即256份; 同理16位是把纵坐标分成2的16次方65536份。

  • 声道数
    即声音的通道的数目。有单声道和立体声之分,单声道的声音只能使用一个喇叭发声(有的也处理成两个喇叭输出同一个声道的声音),立体声的PCM可以使两个喇叭都发声(一般左右声道有分工) ,更能感受到空间效果。
    单声道 采样数据为8位的短整数(short);
    双声道 采样数据为16位的整数,(int),高八位(左声道)和低八位(右声道)分别代表两个声道。

  • 存储量= (采样频率 * 采样位数 * 声道 * 时间)/8 (单位:字节数)。

[时长]s * [采样率]Hz * [采样位数]bit * [声道数] / 8 = [文件大小]byte 
某音频信号是采样率为8kHz、声道数、位宽为16bit,时长为1s,则音频数据的大小为: 
1 * 8000 * 16 *2 = 256000 bit / 8 = 32000 byte / 1024 = 31.25 KB

AudioRecorder

是 Android 中一种音频采集的方式,另外一种是 MediaRecorder

MediaRecorder:录制的音频文件是经过压缩后的,需要设置编码器。并且录制的音频文件可以用系统自带的Music播放器播放。
优点:官方提供 API
缺点: 不能实时处理音频,输出格式不多,且PCM可以处理生成
AudioRecord: 录制的是PCM格式的音频文件,需要用AudioTrack来播放。
优点:可以实时获取音频的数据做到边录边播放,可以对获取的音频做处理,压缩,传输等
缺点:输出的是原始数据 PCM 所以播放器不能播放,需要通过AudioTrack处理

开始采集

采集音频的步骤:
1.配置 AudioRecorder 构造函数的参数
2.初始化缓冲区
3.开始采集 ,子线程里将缓冲区的数据取出,写入文件流
4.停止采集,释放资源

从 AudioRecord 的构造函数开始

audioSource: 音频采集的输入源,可选的值以常量的形式定义在 MediaRecorder.AudioSource 类中,例如:MIC(由手机麦克风输入),VOICE_COMMUNICATION(用于VoIP应用)等等。
sampleRateInHz:采样率,注意,目前44100Hz是唯一可以保证兼容所有Android手机的采样率。
channelConfig: 通道数的配置,可选的值以常量的形式定义在 AudioFormat 类中,常用的是CHANNEL_IN_MONO(单通道),CHANNEL_IN_STEREO(双通道)
audioFormat: 返回的音频数据的格式,可选的值也是以常量的形式定义在 AudioFormat 类中,常用的是 ENCODING_PCM_16BIT(16bit),ENCODING_PCM_8BIT(8bit),注意,前者是可以保证兼容所有Android手机的。
bufferSizeInBytes: AudioRecord 内部的音频缓冲区的大小,该缓冲区的值不能低于一帧“音频帧”(Frame)的大小

  public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes)
    throws IllegalArgumentException {
        this((new AudioAttributes.Builder())
                    .setInternalCapturePreset(audioSource)
                    .build(),
                (new AudioFormat.Builder())
                    .setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,
                                        true/*allow legacy configurations*/))
                    .setEncoding(audioFormat)
                    .setSampleRate(sampleRateInHz)
                    .build(),
                bufferSizeInBytes,
                AudioManager.AUDIO_SESSION_ID_GENERATE);
    }
配置

参数配置:

    /**
     * 采样率。现在能够保证在所有设备上使用的采样率是44100Hz
     */
    public static final int SAMPLE_RATE_INHZ = 44100;
    /**
     * 输入声道数。CHANNEL_IN_MONO and CHANNEL_IN_STEREO. 其中CHANNEL_IN_MONO是可以保证在所有设备能够使用的。
     */
    public static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
    /**
     * 返回的音频数据的格式。 ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, and ENCODING_PCM_FLOAT.
     */
    public static final int ENCODING_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
缓冲区:

AudioRecord 提供了一个类为我们计算最小缓冲区,参数就是我们上面配置的 采样率,声道, 返回的音频数据的格式

 int minBufferSize = AudioRecord.getMinBufferSize(Config.SAMPLE_RATE_INHZ, Config.CHANNEL_CONFIG, Config.ENCODING_FORMAT);
    static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
        int channelCount = 0;
        switch (channelConfig) {
        case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
        case AudioFormat.CHANNEL_IN_MONO:
        case AudioFormat.CHANNEL_CONFIGURATION_MONO:
            channelCount = 1;
            break;
        case AudioFormat.CHANNEL_IN_STEREO:
        case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
        case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK):
            channelCount = 2;
            break;
        case AudioFormat.CHANNEL_INVALID:
        default:
            loge("getMinBufferSize(): Invalid channel configuration.");
            return ERROR_BAD_VALUE;
        }

        int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
        if (size == 0) {
            return ERROR_BAD_VALUE;
        }
        else if (size == -1) {
            return ERROR;
        }
        else {
            return size;
        }
    }
开始采集,子线程读数据写入文件流

直接调用创建好的 AudioRecorder 对象的 startRecording();

//开始采集
 mAudioRecorder.startRecording();
//需要再子线程里面调用(读和存)
AudioRecord.read(byte[] audioData, int offsetInBytes, int sizeInBytes);
FileOutputStream 的 write()

停止采集
mAudioTrack.stop();
mAudioRecorder.release();

AudioTrack

开始播放

开始播放步骤:
1.配置参数
2.配置缓冲区
3.开启子线程,把缓冲区读数据转换成输入流,再调用AudioTrack读 write()写入数据,最后调用 play()
4.结束释放资源

配置参数:

参数和 AudioRecorder 差不多,有区别的就是 AudioTrack 是输出声道,还要播放的类型,和播放的模式
streamType:播放的类型,都定义在 AudioManager 类中
mode: 播放的模式, MODE_STATIC, MODE_STREAM 两种
两者的区别

 /**   在播放之前必须把数据全部加载完成
     * Creation mode where audio data is transferred from Java to the native layer
     * only once before the audio starts playing.
     */
    public static final int MODE_STATIC = 0;
    /**
     *可以一边录音一边播放
     * Creation mode where audio data is streamed from Java to the native layer
     * as the audio is playing.
     */
    public static final int MODE_STREAM = 1;
 public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes, int mode)
    throws IllegalArgumentException {
        this(streamType, sampleRateInHz, channelConfig, audioFormat,
                bufferSizeInBytes, mode, AudioManager.AUDIO_SESSION_ID_GENERATE);
    }

缓冲区

和上面的 AudioRecorder 的配置一样

开启子线程,把缓冲区数据转换成输入流,再调用AudioTrack读 write()写入数据,最后调用 play()

int readCount = inStream.read(mBuffer);
mAudioTrack.write(mBuffer, 0, readCount);
 mAudioTrack.play();

停止释放资源

 mAudioTrack.stop();
 mAudioTrack.release();

Demo https://github.com/wubobo952/LearnAudio

你可能感兴趣的:(音视频 — AudioRecorder 和 AudioTrack)