Android -- Audio系统之AudioTrack内部实现简析(二)

AudioTrack播放流程 AudioTrack介绍 音频播放声音分为MediaPlayer和AudioTrack两种方案的。MediaPlayer可以播放多种格式的声音文件,例如MP3,WAV,OGG,AAC,MIDI等。然而AudioTrack只能播放PCM数据流。当然两者之间还是有紧密的联系,MediaPlayer在播放音频时,在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,最后由AudioFlinger进行混音,传递音频给硬件播放出来。利用AudioTrack播放只是跳过 阅读详情

Android -- Audio系统之AudioTrack内部实现简析(二)


在上一篇博文中,我们主要分析了Audio系统所依赖的Native服务的启动过程;说是启动,其实也就是分析它的初始化流程。而AudioTrack是Android提供的供应用使用的Audio API之一,它管理和实现了播放PCM制式音频的接口。AudioTrack采用“push”模式管理数据,我们需要调用write()方法主动将数据传输给AudioTrack对象。


AudioTrack可以工作在两种模式下:

  • STREAM模式:应用程序通过调用write()方法将连续的音频数据传输给AudioTrack对象。它的处理是阻塞的,只有当音频数据从Java层传入到Native层,并且加入到回放队列后,它才会返回。STREAM模式经常用于处理量较大的音频数据。
  • STATIC模式:当处理的音频数据量较小,能一次性填充到内存用以播放;且对播放时延要求较高时,会使用这种模式。

可能是MediaPlayer功能太完备,我们使用AudioTrack的机会不多。但借助它来分析Audio系统的上下层调用关系和实现流程,还是很有效的。希望在分析完AudioTrack的内部实现之后,我们都能对Android Audio模块有一些基本的了解和认识。


接下来,我们就借助一个STREAM模式下的代码Demo,一层层分析它的内部实现流程;了解AudioTrack是如何与AudioFlinger/AudioPolicyServcie这些Native服务交互的。


示例代码:

public void initAudioTrack() {

        //1、
        int bufsize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
                AudioFormat.ENCODING_PCM_16BIT);

        //2、指定音频流类型、采样率、声道、格式、工作模式等信息
        AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
                AudioFormat.ENCODING_PCM_16BIT, bufsize, AudioTrack.MODE_STREAM);

        //3、
        track.play();

        byte pkg[] = new byte[1024];//无效数据,只是用于分析;多数时候会在嵌套在某个循环之中

        //4、
        track.write(pkg, 0, pkg.length);

        //5、
        track.stop();

        //6、
        track.release();

    }
示例代码罗列了使用AudioTrack时所需的一些重要步骤。我们通过分解这些步骤,区分代码调用层次,一层层的从Java调用分析到JNI调用、Native调用等;最终达到我们的分析目的。


一、获取需要的最小Buffer大小


音频数据是具有很多属性的,比如采样率、音频格式以及声道数等等;而这些属性又跟我们的Audio Interface有关。我们配置不同的音频属性,底层播放这段数据所需的最小Buffer空间大小也会变化。所以,我们需要根据配置的音频信息,先得到它对应的最小Buffer空间;再继续后面的操作。


AudioTrack::getMinBufferSize()函数的定义如下:

/**
     * Returns the estimated minimum buffer size required for an AudioTrack
     * object to be created in the {@link #MODE_STREAM} mode.
     * The size is an estimate because it does not consider either the route or the sink,
     * since neither is known yet.  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.
     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
     * @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},
     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
     * @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 (!isMultichannelConfigSupported(channelConfig)) {
                loge("getMinBufferSize(): Invalid channel configuration.");
                return ERROR_BAD_VALUE;
            } else {
                channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
            }
        }

        if (!AudioFormat.isPublicEncoding(audioFormat)) {//判断音频格式
            loge("getMinBufferSize(): Invalid audio format.");
            return ERROR_BAD_VALUE;
        }

        // sample rate, note these values are subject to change
        // Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed
        if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
                (sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {//判断音频采样率是否合法,是否在人贰可识别的频率范围内
            loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");
            return ERROR_BAD_VALUE;
        }

        int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);//因为采样率、声道数等信息都跟硬件的支持有关,需要通过JNI查询硬件信息
        if (size <= 0) {
            loge("getMinBufferSize(): error querying hardware");
            return ERROR;
        }
        else {
            return size;
        }
    }
根据代码中的注释,首先会进行传参的有效性检测;最后通过JNI调用将函数处理带入Native层。


这里重要的一步调用是:native_get_min_buff_size().


二、创建AudioTrack对象


得到最小的Buffer大小后,我们就会去创建AudioTrack实例。AudioTrack的构造函数定义是:

//--------------------------------------------------------------------------
    // Constructor, Finalize
    //--------------------
    /**
     * Class constructor.
     * @param streamType the type of the audio stream. See
     *   {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
     *   {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC},
     *   {@link AudioManager#STREAM_ALARM}, and {@link AudioManager#STREAM_NOTIFICATION}.
     * @param sampleRateInHz the initial source sample rate expressed in Hz.
     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
     *   which is usually the sample rate of the sink.
     *   {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
     * @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},
     *   {@link AudioFormat#ENCODING_PCM_8BIT},
     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
     * @param bufferSizeInBytes the total size (in bytes) of the internal buffer where audio data is
     *   read from for playback. This should be a nonzero multiple of the frame size in bytes.
     *   <p> If the track's creation mode is {@link #MODE_STATIC},
     *   this is the maximum length sample, or audio clip, that can be played by this instance.
     *   <p> If the track's creation mode is {@link #MODE_STREAM},
     *   this should be the desired buffer size
     *   for the <code>AudioTrack</code> to satisfy the application's
     *   latency requirements.
     *   If <code>bufferSizeInBytes</code> is less than the
     *   minimum buffer size for the output sink, it is increased to the minimum
     *   buffer size.
     *   The method {@link #getBufferSizeInFrames()} returns the
     *   actual size in frames of the buffer created, which
     *   determines the minimum frequency to write
     *   to the streaming <code>AudioTrack</code> to avoid underrun.
     *   See {@link #getMinBufferSize(int, int, int)} to determine the estimated minimum buffer size
     *   for an AudioTrack instance in streaming mode.
     * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM}
     * @throws java.lang.IllegalArgumentException
     */
    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);
    }
最终的调用是:
    /**
     * Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
     * @param attributes a non-null {@link AudioAttributes} instance.
     * @param format a non-null {@link AudioFormat} instance describing the format of the data
     *     that will be played through this AudioTrack. See {@link AudioFormat.Builder} for
     *     configuring the audio format parameters such as encoding, channel mask and sample rate.
     * @param bufferSizeInBytes the total size (in bytes) of the internal buffer where audio data is
     *   read from for playback. This should be a nonzero multiple of the frame size in bytes.
     *   <p> If the track's creation mode is {@link #MODE_STATIC},
     *   this is the maximum length sample, or audio clip, that can be played by this instance.
     *   <p> If the track's creation mode is {@link #MODE_STREAM},
     *   this should be the desired buffer size
     *   for the <code>AudioTrack</code> to satisfy the application's
     *   latency requirements.
     *   If <code>bufferSizeInBytes</code> is less than the
     *   minimum buffer size for the output sink, it is increased to the minimum
     *   buffer size.
     *   The method {@link #getBufferSizeInFrames()} returns the
     *   actual size in frames of the buffer created, which
     *   determines the minimum frequency to write
     *   to the streaming <code>AudioTrack</code> to avoid underrun.
     *   See {@link #getMinBufferSize(int, int, int)} to determine the estimated minimum buffer size
     *   for an AudioTrack instance in streaming mode.
     * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM}.
     * @param sessionId ID of audio session the AudioTrack must be attached to, or
     *   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at construction
     *   time. See also {@link AudioManager#generateAudioSessionId()} to obtain a session ID before
     *   construction.
     * @throws IllegalArgumentException
     */
    public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
            int mode, int sessionId)
                    throws IllegalArgumentException {
        super(attributes);
        // mState already == STATE_UNINITIALIZED

        if (format == null) {
            throw new IllegalArgumentException("Illegal null AudioFormat");
        }

        // remember which looper is associated with the AudioTrack instantiation
        Looper looper;
        if ((looper = Looper.myLooper()) == null) {
            looper = Looper.getMainLooper();
        }

        int rate = format.getSampleRate();
        if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
            rate = 0;
        }

        int channelIndexMask = 0;
        if ((format.getPropertySetMask()
                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0) {
            channelIndexMask = format.getChannelIndexMask();
        }
        int channelMask = 0;
        if ((format.getPropertySetMask()
                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0) {
            channelMask = format.getChannelMask();
        } else if (channelIndexMask == 0) { // if no masks at all, use stereo
            channelMask = AudioFormat.CHANNEL_OUT_FRONT_LEFT
                    | AudioFormat.CHANNEL_OUT_FRONT_RIGHT;
        }
        int encoding = AudioFormat.ENCODING_DEFAULT;
        if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0) {
            encoding = format.getEncoding();
        }
        audioParamCheck(rate, channelMask, channelIndexMask, encoding, mode);
        mStreamType = AudioSystem.STREAM_DEFAULT;

        audioBuffSizeCheck(bufferSizeInBytes);

        mInitializationLooper = looper;

        if (sessionId < 0) {
            throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
        }

        int[] sampleRate = new int[] {mSampleRate};
        int[] session = new int[1];
        session[0] = sessionId;
        // native initialization
        int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
                sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
                mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);//传入了构造的参数信息,重要地,我们传入了一个AudioTrack类型的弱引用对象
        if (initResult != SUCCESS) {
            loge("Error code "+initResult+" when initializing AudioTrack.");
            return; // with mState == STATE_UNINITIALIZED
        }

        mSampleRate = sampleRate[0];
        mSessionId = session[0];

        if (mDataLoadMode == MODE_STATIC) {
            mState = STATE_NO_STATIC_DATA;
        } else {
            mState = STATE_INITIALIZED;
        }
    }
AudioAttributes类是Parcelable接口的子类,它是一个对Audio设置信息进行保存的数据包类;我们只需注意它的数据封装作用就行了。构造函数中的处理也很直观,除去一些初始化操作外,重要的调用是:native_setup(),此时的处理也会转入到JNI中。


三、启动播放


AudioTrack对象创建完成后,我们会启动播放,调用play()方法:

/---------------------------------------------------------
    // Transport control methods
    //--------------------
    /**
     * Starts playing an AudioTrack.
     * <p>
     * If track's creation mode is {@link #MODE_STATIC}, you must have called one of
     * the write methods ({@link #write(byte[], int, int)}, {@link #write(byte[], int, int, int)},
     * {@link #write(short[], int, int)}, {@link #write(short[], int, int, int)},
     * {@link #write(float[], int, int, int)}, or {@link #write(ByteBuffer, int, int)}) prior to
     * play().
     * <p>
     * If the mode is {@link #MODE_STREAM}, you can optionally prime the data path prior to
     * calling play(), by writing up to <code>bufferSizeInBytes</code> (from constructor).
     * If you don't call write() first, or if you call write() but with an insufficient amount of
     * data, then the track will be in underrun state at play().  In this case,
     * playback will not actually start playing until the data path is filled to a
     * device-specific minimum level.  This requirement for the path to be filled
     * to a minimum level is also true when resuming audio playback after calling stop().
     * Similarly the buffer will need to be filled up again after
     * the track underruns due to failure to call write() in a timely manner with sufficient data.
     * For portability, an application should prime the data path to the maximum allowed
     * by writing data until the write() method returns a short transfer count.
     * This allows play() to start immediately, and reduces the chance of underrun.
     *
     * @throws IllegalStateException if the track isn't properly initialized
     */
    public void play()
    throws IllegalStateException {
        if (mState != STATE_INITIALIZED) {
            throw new IllegalStateException("play() called on uninitialized AudioTrack.");
        }
        baseStart();
        synchronized(mPlayStateLock) {
            native_start();
            mPlayState = PLAYSTATE_PLAYING;
        }
    }
处理很简单,启动播放的操作切换到JNI中:native_start().


四、写入数据


AudioTrack中有几个重载的write()函数定义,主要的区别是它们对音频数据转换的数组类型要求不同,从而导致了不同的重载版本的调用。JNI层对这种重载形式也做了区分:

    private native final int native_write_byte(byte[] audioData,
                                               int offsetInBytes, int sizeInBytes, int format,
                                               boolean isBlocking);

    private native final int native_write_short(short[] audioData,
                                                int offsetInShorts, int sizeInShorts, int format,
                                                boolean isBlocking);

    private native final int native_write_float(float[] audioData,
                                                int offsetInFloats, int sizeInFloats, int format,
                                                boolean isBlocking);

    private native final int native_write_native_bytes(Object audioData,
            int positionInBytes, int sizeInBytes, int format, boolean blocking);
定义了不同版本的JNI函数,但在JNI实现中,它们最终的函数实现却基本一致。这里我们就不看AudioTrack中的write()函数定义了,它的函数定义最终也是调用这些native 函数,进入JNI。


这一步的重要调用:native_write_byte()/native_write_short()...


五、停止播放


如果需要停止播放,我们则会调用AudioTrack::stop()函数:

   /**
     * Stops playing the audio data.
     * When used on an instance created in {@link #MODE_STREAM} mode, audio will stop playing
     * after the last buffer that was written has been played. For an immediate stop, use
     * {@link #pause()}, followed by {@link #flush()} to discard audio data that hasn't been played
     * back yet.
     * @throws IllegalStateException
     */
    public void stop()
    throws IllegalStateException {
        if (mState != STATE_INITIALIZED) {
            throw new IllegalStateException("stop() called on uninitialized AudioTrack.");
        }

        // stop playing
        synchronized(mPlayStateLock) {
            native_stop();
            mPlayState = PLAYSTATE_STOPPED;
            mAvSyncHeader = null;
            mAvSyncBytesRemaining = 0;
        }
    }
它的处理很直观,转入JNI中;这里重要的函数调用是:natvie_stop().


六、释放资源


当我们停止播放后,还需要释放AudioTrack相关联的各种系统资源,此时会调用AudioTrack::release():

    /**
     * Releases the native AudioTrack resources.
     */
    public void release() {
        // even though native_release() stops the native AudioTrack, we need to stop
        // AudioTrack subclasses too.
        try {
            stop();
        } catch(IllegalStateException ise) {
            // don't raise an exception, we're releasing the resources.
        }
        baseRelease();
        native_release();
        mState = STATE_UNINITIALIZED;
    }
与stop()的处理类似,通过native_release()调用进入JNI中处理。

分析完AudioTrack Java层的实现,发现它真的是太简单了,所有的复杂处理都转入到JNI中,从而再转入到Native层进行最终处理。


我们整理下之前分析得出的6个重要调用:

  • native_get_min_buff_size():获取最小Buffer大小
  • native_setup():完成AudioTrack的创建
  • native_start():AudioTrack启动播放
  • native_write_byte()/native_write_short()...:往AudioTrack写入音频数据
  • natvie_stop():AudioTrack停止播放
  • native_release():释放AudioTrack绑定的系统资源

看起来Java AudioTrack把复杂处理的锅都通过JNI甩给Native层了,我们下一步就要在JNI中分析这几个函数调用,看下它们是如何处理Java AudioTrack的请求的。

【初学音频】Android的Audio系统之AudioTrack 深入理解Android之AudioTrack的破解 阅读详情

相关推荐

Android 9 Audio系统笔记:音频路由实现——从AudioTrack到audiohal

这里写目录标题一、动态路由的初始化1、获取路由策略2、向AudioPolicyManager注册路由策略二、动态路由的路由流程,以AudioTrack创建为例 [上一篇介绍了AudioFlinger的初始化](https://blog.youkuaiyun.com/biandan1231/article/details/105718091),接下来对音频路由进行介绍。注意,本文主要介绍动态路由,即汽车音频路由的常规方式。针对Android原生路由策略不做深入分析。 什么是音频路由?如何实现音频路由?如何定制音频路由?这

biandan1231的专栏 1万+

深入剖析Android音频之AudioTrack

播放声音可以用MediaPlayer和AudioTrack,两者都提供了java API供应用开发者使用。虽然都可以播放声音,但两者还是有很大的区别的。其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。而AudioTrack只能播放已经解码的PCM流,如果是文件的话只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能

深入剖析Android系统 3万+

Android Audio基础——AudioTrack音频控制(二十)

AudioTrack 是用于播放音频数据的类,可以使用 set 方法设置音频数据,然后使用 start 来启动播放。而 pause 和 stop 则是用于控制播放过程的方法。不管 APP 使用什么播放音频文件,最终都是由 AudioTrack 将数据传递下去。

小旭的专栏 1877

Android音频开发(3):如何播放一帧音频

本文重点关注如何在Android平台上播放一帧音频数据。阅读本文之前,建议先读一下《Android音频开发(1):基础知识》,因为音频开发过程中,经常要涉及到这些基础知识,掌握了这些重要的概念后,开发过程中的很多参数和流程就会更加容易理解。Android SDK 提供了3套音频播放的API,分别是:MediaPlayer,SoundPool,AudioTrack,关于它们的区别可以看这篇文章:《I...

weixin_34268169的博客 855

Android Audio音频系统之深入浅出

音频分析

zjy764219923的专栏 5699

Win10家庭版中文用户名改英文全攻略:从注册表到环境变量一步不落

本文详细介绍了Windows 10家庭版中文用户名改为英文的完整流程,包括注册表编辑、环境变量调整等关键步骤。通过激活管理员账户、重命名用户文件夹、修改注册表键值等操作,彻底解决因中文路径导致的软件兼容性问题,并提供常见错误解决方案和系统维护建议。

weixin_29227425的博客 427

Android Audio——使用 AudioTrack 播放音频

Android SDK 提供了3套音频播放的API,分别是:MediaPlayer,SoundPool,AudioTrack。简单来说,MediaPlayer 更加适合在后台长时间播放本地音乐文件或者在线的流式资源;SoundPool 则适合播放比较短的音频片段,比如游戏声音、按键声、铃声片段等等,它可以同时播放多个音频;而 AudioTrack 则更接近底层,提供了非常强大的控制能力,支持低延迟播放,适合流媒体和VoIP 语音电话等场景。

liuning1985622的博客 6210

vue微前端qiankun框架学习到项目实战,基座登录动态菜单及权限控制

技术无关,独立开发,独立部署,增量升级,独立运行,微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用。

前端开发工程师 9747

Android 11 Audio框架探索之AudioTracK(二)

在上一篇介绍了关于AudioPolicyService与AudioFlinger服务的启动及初始化。这里探索一下AudioTrack与AudioFlinger做了那些事情。 音频文件在Media模块解码出来的原始数据格式为PCM,当上层需要播放一个文件的时候就会创建AudioTrack对象来承担放音的工作,如果对media模块感兴趣的朋友可以研究一下,下面我们开始进入正题。 先上代码在分析: AudioTtack.java /** ...

weixin_43240438的博客 5267

【QGIS】PyCharm集成pyqgis的processing工具箱:环境配置与初始化实战

本文详细介绍了如何在PyCharm中配置QGIS的Python环境并成功集成其强大的Processing工具箱。文章核心解决了独立环境下导入processing模块失败、算法无法调用等常见难题,通过手动添加插件路径和执行关键初始化步骤,实现了在PyCharm中丝滑调用QGIS地理处理算法,从而将复杂的空间分析流程自动化。

weixin_28831399的博客 391

从零构建机械臂规划栈:Pinocchio、OMPL与Ruckig的工程化融合实践

本文详细介绍了如何从零构建机械臂规划栈,通过融合Pinocchio、OMPL和Ruckig三大核心组件,实现高性能的机械臂运动规划与轨迹生成。文章深入探讨了技术选型、工程架构设计及实战优化技巧,特别针对逆解、路径规划和轨迹平滑等关键环节提供了实用解决方案,适用于工业场景中的高实时性需求。

weixin_29072299的博客 433

Android音频系统之AudioTrack(二)

1.1.1 AudioPolicyService的路由实现我们在AudioPolicyService小节曾将其比作是一个“路由器”,不过还没有深入解析它是如何完成路由选择的。这部分的功能与使用者——AudioTrack有很大关联,所以我们特别将它的实现原理剖析放在这里,以使读者可以综合起来理解。路由器功能由如下几个部分组成:l  与发送方(AudioTrack)的接口就好像路由器首先要接收到一个I

林学森的技术专栏 1万+

数字经济新质生产力数字化转型数据要素流通交易解决方案:数据要素化演进路径与管理、公共数据授权运营平台体系、数据底座、典型应用场景

数字经济的发展依赖于数据要素的有效流通与交易,数字化转型的核心在于数据的全生命周期管理和价值释放。数据要素化通过数据资源化、资产化和资本化的“三化”过程,实现其经济与社会价值。数据管理涵盖采集、存储、加工、分析等环节,支持数据的度量与交换。公共数据、企业数据和个人数据通过场内、场外及二级市场交易实现资本化。公共数据开发利用面临隐私泄露、监督管理难等痛点,目标是构建授权运营平台体系,确保数据流通的安全与效率。平台体系包括统一门户、授权管理、运营管理、合规监督等核心功能,支持明文、密文和可信交换流通方式。数据底

weixin_44094929的博客 1003

Android Audio音频系统

Audio硬件抽象层的实现在各个系统中可能是不同的,需要使用代码去继承相应的类并实现它们,作为Android系统本地框架层和驱动程序接口AudioFlinger继承了libmedia.so(Audio本地框架类)里面的接口,上层调用的只是libmedia.so部分的接口,但实际上调用的内容是libaudioflinger.so,使用JNI和Java对上层提供接口,JNI部分通过调用libmedia.so库提供的接口来实现。虽然有的类是在同一个库中实现的,但并不代表它们会在同一个进程中被调用。

sdkdlwk的博客 2967

深入解析report_timing:从基础参数到高级时序路径分析技巧

本文深入解析了静态时序分析(STA)中`report_timing`命令的核心用法与高级技巧。作为芯片设计的关键诊断工具,它能根据SDC约束精准定位时序路径问题。文章从基础参数过滤讲起,详细介绍了如何通过`-from`、`-through`、`-to`等选项定位关键路径,并深入讲解了`-input_pins`、`-nets`、`-transition_time`等高级选项如何揭示延迟细节,以及`-nworst`、`-slack_lesser_than`等过滤排序技巧,旨在帮助工程师高效解读时序报告,优化设计

h6i7j8的博客 1108

AMEsim不连续处理机制:仿真精度与效率的平衡艺术

本文深入解析AMEsim不连续处理机制在系统仿真中的应用,探讨如何平衡仿真精度与效率。通过液压系统仿真案例,详细介绍了事件检测算法、变步长求解器自适应策略及典型场景优化方法,帮助工程师有效应对仿真中的不连续性问题,提升仿真效率和准确性。

v4w5x6的博客 440

Android Audio System深入探究之AudioTrack(基于Android9)

文章目录1. 音频系统概述2. 放音流程分析2.1 getMinBufferSize2.2 AudioTrack2.3 AudioTrack (Native空间) set分析2.3.1 Cblk 在AF端初始化2.3.2 IAudioTrack和AT、AF的关系2.3.3 共享内存与cblk2.4 play和write分析2.5 obtainBuffer 和releaseBuffer2.6 delete AudioTrack2.7 stop分析AudioTrack流程总结 基于Android 9 1. 音

虎哥LoveDroid 3075

ESD防护设计宝典(十九):元器件选型宝典——共模电感

然而,这位“大师”的脾气也颇为古怪,若设计者不解其性、用之不当,它非但不能抑制噪声,反而会引入信号完整性恶化、电源压降等新问题,甚至让您的系统EMC性能“雪上加霜”,导致项目在测试阶段功亏一篑。本文将带您深入共模电感的内在世界,从本质解析到参数详解,从设计陷阱到实战选型,全方位领悟其平衡艺术,掌握其应用核心,规避那些足以让项目翻车的致命陷阱。:这是存在于电路板内部的“家贼”。共模电感实质上是一个四端器件,它通过磁场的“加法”与“减法”运算,巧妙地实现了对共模噪声的抑制,同时为差模信号大开绿灯。

m0_56451176的博客 421

Android_P_Audio_系统(2) — AudioTrack

AudioTrack 用于 Android 平台音频数据输出,属于 Audio 系统对外提供的 API 类,因此它在 Java 与Native层均有对应的源码实现。先从 Java 层的一个用例了解下 AudioTrack 工作流程。// 1 根据音频数据特征确定所要分配的缓存区的最小 sizeAudioTrack.getMinBufferSize(8000, // 采样率:每秒 8000 个采样点AudioFormat.CHANNEL_OUT_STEREO, // 声道数:双声道。

sdkdlwk的博客 1462

钢轨伤损人工智能图像识别APP—小轨当家_easy-railway.zip

钢轨伤损人工智能图像识别APP—小轨当家_easy-railway

BLE基带芯片设计及soc系统集成-综合文档

BLE基带芯片设计及soc系统集成

Android -- Audio系统之AudioTrack内部实现简析(三)

Android -- Audio系统之AudioTrack内部实现简析(三) 上一篇中,我们介绍了一个使用AudioTrack的简单示例,并初步分析了使用AudioTrack API时每一步的Java调用实现;承接之前的内容,我们对应Demo中的示例,分析Java AudioTrack实现,最后得出了6个重要的函数调用: native_get_min_buff_size():获

To be or not to be, it's a question 4036

航天最新版《禁(限)用工艺目录(2021版)》.pdf

航天最新版《禁(限)用工艺目录(2021版)》.pdf

Android audio 五 AudioTrack 分析下

Android audio 一 源码路径 Android audio 二 AudioRecord 分析上 Android audio 三 AudioRecord 分析下 Android audio 四 AudioTrack 分析上 Android audio 五 AudioTrack 分析下 Android audio 六 AudioRecord AudiTrack 拾音放音例子 该小...

qq_38907791的博客 1387

高数各章综合测试题及答案.doc

高数各章综合测试题及答案.doc

Android的Audio系统

<br /> <br />Android的Audio 系统<br />第一部分 Audio系统综述<br />第二部分 Audio系统和上层接口<br />第三部分 Audio的硬件抽象层<br />第一部分 Audio系统综述<br />Audio系统在Android中负责音频方面的数据流传输和控制功能,也负责音频设备的管理。<br />Audio系统主要的分成几个层次:<br />1. media中库提供的Audio系统的上层接口<br />2. AudioFlinger作为Audio系统的中枢<br

gowyz的专栏 2万+

Android 框架层AudioTrack

java层的AudioTrack只是java语言的api(包皮公司),我们对java层AudioTrack的api调用,实际都会传递到native层的AudioTrack去执行;native层的AudioTrack在初始化的时候,会跨进程的调用AudioFlinger的createTrack方法,并返回一个IAudioTrack的对象,native层的AudioTrack以及AudioFlinger通过这个对象进行交互;

qq_48709036的博客 1318

framework之Audio系统

Audio 系统 AudioTrack audio_track_cblk_t AudioFlinger AudioPolicyService AudioPolicyManagerBase DuplicatingThread

ahjxly的专栏 3287

Android Audio System 之一:AudioTrack如何与AudioFlinger交换音频数据

stepUser()和stepServer的作用是调整当前偏移的位置,可以看到,他们仅仅是把成员变量user或server的值加上需要移动的数量,user和server的值并不考虑FIFO的边界问题,随着数据的不停写入和读出,user和server的值不断增加,只要处理得当,user总是出现在server的后面,因此frameAvalible()和frameReady()中的算法才会一直成立。这时候u在s的前面,用上面的公式计算就会错误,但是android使用了一些技巧,保证了上述公式一直成立。

xioayuner1的专栏 649

Android深入浅出之Audio 第一部分 AudioTrack分析

一 目的 本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等。 分析的流程是: l         先从API层对应的某个类开始,用户层先要有一个简单的使用流程。 l         根据这个流程,一步步进入到JNI,服务层。在此过程中,碰到不熟悉或者第一次见到的类或者方法,都会解释。也

hlglinglong的专栏 870
上一篇: Android -- Audio Native服务之启动流程分析(一)
下一篇: Android -- Audio系统之AudioTrack内部实现简析(三)
第一序列丶
博客等级 码龄11年 249粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值