Android 4.4KitKat AudioTrack 流程分析

Android Audio 系统的主要内容:

  • AudioManager:这个主要是用来管理Audio系统的,需要考虑整个系统上声音的策略问题,例如来电话铃声,短信铃声等,主要是策略上的问题。
  • AudioTrack:这个主要是用来播放声音的
  • AudioRecord:这个主要是用来录音的

当前分析AudioTrack的文章较多,先以AudioTrack为例进行分析。

JAVA层的AudioTrack class:framework\base\media\java\android\media\AudioTrack.java中。

AudioTrack的使用方法实例:

 1 //根据采样率,采样精度,单双声道来得到frame的大小。
 2 int bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点
 3 AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
 4 AudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节
 5 //注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。
 6 //创建AudioTrack
 7 AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
 8 AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
 9 AudioFormat.ENCODING_PCM_16BIT,bufsize,AudioTrack.MODE_STREAM);//
10 trackplayer.play() ;//开始
11 trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据
12 ….
13 trackplayer.stop();//停止播放
14 trackplayer.release();//释放底层资源。

  AudioTrack.MODE_STREAMAudioTrack中有MODE_STATIC和MODE_STREAM两种分类。STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。

  StreamType:这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。Android将系统的声音分为以下几类常见的(未写全):

  • STREAM_ALARM:警告声
  • STREAM_MUSCI:音乐声,例如music等
  • STREAM_RING:铃声
  • STREAM_SYSTEM:系统声音
  • STREAM_VOCIE_CALL:电话声音

  为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。

  从AudioTrack的使用实例来逐个分析其中用到的方法,首先是getMinBufferSize:

  getMinBufferSize

/**
     * Returns the minimum buffer size required for the successful creation of an AudioTrack
     * object to be created in the {@link #MODE_STREAM} mode. Note that this size doesn't
     * guarantee a smooth playback under load, and higher values should be chosen according to
     * the expected frequency at which the buffer will be refilled with additional data to play.
     * For example, if you intend to dynamically set the source sample rate of an AudioTrack
     * to a higher value than the initial source sample rate, be sure to configure the buffer size
     * based on the highest planned sample rate.
     * @param sampleRateInHz the source sample rate expressed in Hz.
     * @param channelConfig describes the configuration of the audio channels.
     *   See {@link AudioFormat#CHANNEL_OUT_MONO} and
     *   {@link AudioFormat#CHANNEL_OUT_STEREO}
     * @param audioFormat the format in which the audio data is represented.
     *   See {@link AudioFormat#ENCODING_PCM_16BIT} and
     *   {@link AudioFormat#ENCODING_PCM_8BIT}
     * @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,
     *   or {@link #ERROR} if unable to query for output properties,
     *   or the minimum buffer size expressed in bytes.
     */
    static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
        int channelCount = 0;
        switch(channelConfig) {
        case AudioFormat.CHANNEL_OUT_MONO:
        case AudioFormat.CHANNEL_CONFIGURATION_MONO:
            channelCount = 1;
            break;
        case AudioFormat.CHANNEL_OUT_STEREO:
        case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
            channelCount = 2;
            break;
        default:
            if ((channelConfig & SUPPORTED_OUT_CHANNELS) != channelConfig) {
                // input channel configuration features unsupported channels
                loge("getMinBufferSize(): Invalid channel configuration.");
                return ERROR_BAD_VALUE;
            } else {
                channelCount = Integer.bitCount(channelConfig);
            }
        }
      //目前只支持PCM8和PCM16精度的音频
        if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
            && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
            loge("getMinBufferSize(): Invalid audio format.");
            return ERROR_BAD_VALUE;
        }
     //ft,对采样频率也有要求,太低或太高都不行,人耳分辨率在20HZ到40KHZ之间
        // sample rate, note these values are subject to change
        if ( (sampleRateInHz < SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > SAMPLE_RATE_HZ_MAX) ) {
            loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");
            return ERROR_BAD_VALUE;
        }
     //调用native函数
        int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
        if (size <= 0) {
            loge("getMinBufferSize(): error querying hardware");
            return ERROR;
        }
        else {
            return size;
        }
    }

View Code

相关推荐:

Python Web框架怎么选_Django与Flask在不同场景下的适用分析

Django适合中型业务系统,因其开箱即用的admin、ORM、auth等组件;Flask适合轻量、嵌入式或高度定制场景,如微服务或IoT网关。 什么时候该用Django而不是Flask 如果你要快速上线一个带用户管理、后台编辑、数据库迁移、API和权限控制的中型业务系统,Django是更省力的选择...

PyTube 400 Bad Request 错误的根源分析与稳定替代方案

pytube因youtube后端接口变更频繁触发http400错误,根本原因在于其请求结构已过时;升级至最新版可临时缓解,但长期推荐迁移到维护更活跃、兼容性更强的yt-dlp或pytubefix。 pytube因youtube后端接口变更频繁触发http400错误,根本原因在于其请求结构已过时;升级...

PyTube 400 Bad Request 错误的根源分析与稳定解决方案

pytube因youtube后端接口变更频繁触发http400错误,本质是请求结构失效;升级至最新版pytube可快速修复,但长期应转向更活跃维护的替代方案(如pytubefix或yt-dlp)并增强错误处理机制。 pytube因youtube后端接口变更频繁触发http400错误,本质是请求结构失...

如何在Python中进行地理空间数据的聚类分析_使用PySAL与Geopandas

PySAL与geopandas组合是地理感知聚类的务实选择,能尊重地球曲率、构建空间权重、识别空间自相关;sklearn直接对经纬度聚类会因纬度差异导致距离失真,结果不可解释。 PySAL和geopandas的组合,是做真正地理感知聚类(而非简单把经纬度当平面坐标扔进sklearn.DBSCAN)的...

Python中Scikit-learn如何构建机器学习流水线_使用Pipeline简化流程

Pipeline的核心作用是防止数据泄露、避免训练/预测不一致、减少重复代码;其fit()在每次交叉验证fold内仅用当前fold训练子集拟合scaler,杜绝跨fold信息泄露。 直接说结论:用Pipeline不是为了“看起来更专业”,而是为了堵住数据泄露、避免训练/预测不一致、减少重复代码这三类...