美文网首页
音视频 — AudioRecorder 和 AudioTrack

音视频 — AudioRecorder 和 AudioTrack

作者: 波波维奇c | 来源:发表于2019-06-17 23:22 被阅读0次

    基础知识

    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

          本文链接:https://www.haomeiwen.com/subject/hlqzfctx.html