精华内容
下载资源
问答
  • Audio AudioFocus流程

    千次阅读 2018-09-26 09:24:17
    AudioFocus是Android引入的一个Audio协调机制,当多方需要使用Audio资源时,可以通过AudioFocus机制来协调配合,提高用户的体验。 该机制需要开发者主动去遵守,比如A应用没遵守该机制,则其它遵守了该机制的应用是...

         AudioFocus是Android引入的一个Audio协调机制,当多方需要使用Audio资源时,可以通过AudioFocus机制来协调配合,提高用户的体验。    该机制需要开发者主动去遵守,比如A应用没遵守该机制,则其它遵守了该机制的应用是完全没办法影响A应用的。    试想下后台在播放着音乐的时候你点开了某个视频,使得后台的音乐和视频的声音一起播放,毫无关联的声音一同播放会给用户带来极差的体验,此时我们就可以通过AudioFocus机制来解决这样的问题。

           使用AudioFocus机制主要是通过android.media.AudioManager这个类来进行的 public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) 方法请求获取焦点,

    如果获取成功,会返回int值AudioManager.AUDIOFOCUS_REQUEST_GRANTED,

    失败则返回AudioManager.AUDIOFOCUS_REQUEST_FAILED。

    通过abandonAudioFocus(OnAudioFocusChangeListener l)方法放弃焦点。 由于音频焦点是唯一的,所以可以在需要播放音乐时去申请音频焦点,如果获取到了则播放,同时正在播放的音频在失去音频焦点时停止播放或者调低音量,从而达到音频播放间的相互协调。

           对requestAudioFocus方法的参数进行解析:  OnAudioFocusChangeListener是一个接口,在这个接口里面只有一个方法需要实现 public void onAudioFocusChange(int focusChange);

    该方法会在焦点状态变化的时候被调用 参数focusChange代表变化后当前的状态,一共有以下四个值:

    AUDIOFOCUS_GAIN 重新获取到音频焦点时触发的状态。

    AUDIOFOCUS_LOSS 失去音频焦点时触发的状态,且该状态应该会长期保持,此时应当暂停音频并释放音频相关的资源。

    AUDIOFOCUS_LOSS_TRANSIENT 失去音频焦点时触发的状态,但是该状态不会长时间保持,此时应该暂停音频,且当重新获取音频焦点的时候继续播放。

    AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 失去音频焦点时触发的状态,在该状态的时候不需要暂停音频,但是应该降低音频的声音。

    streamType(STREAM_RING,STREAM_MUSIC,STREAM_ALARM,STREAM_NOTIFICATION,STREAM_BLUETOOTH_SCO,STREAM_DTMF,STREAM_TTS)

    durationHint用来表示获取焦点的时长,同时通知其它获取了音频焦点的OnAudioFocusChangeListener该如何相互配合,有以下几个值: AUDIOFOCUS_GAIN 代表此次申请的音频焦点需要长时间持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS。

    AUDIOFOCUS_GAIN_TRANSIENT 代表此次申请的音频焦点只需短暂持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT。按照官方注释:适用于短暂的音频,在接收到事件通知等情景时可使用该durationHint。

    AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 代表此次申请的音频焦点只需短暂持有,原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT。按照官方注释:在需要录音或语音识别等情景时可使用该durationHint。

    AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 代表此次申请不需要暂停其它申请的音频播放,应用跟其他应用共用焦点但播放的时候其他音频会降低音量。原本获取了音频焦点的OnAudioFocusChangeListener 接口将会回调onAudioFocusChange(int focusChange) 方法,传入的参数为AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK。

     

    在AudioManager的requestAudioFocus调用内部重载后的方法,根据传进来的streamType,构造了一个AudioAttributes对象向下传递,这个AudioAttributes主要是存储了一些音频流信息的属性. 这里面对falgs进行了与操作,由于之前传进来的是0,所以转换后的结果还是0.

    继续AudioManager.requestAudioFocus中 public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) 里面首先在registerAudioFocusRequest中注册所有的

    AudioFocusRequest private final ConcurrentHashMap<String, FocusRequestInfo> mAudioFocusIdListenerMap =         new ConcurrentHashMap<String, FocusRequestInfo>();        

     final FocusRequestInfo fri = new FocusRequestInfo(afr, (h == null) ? null :new ServiceEventHandlerDelegate(h).getHandler());

    其中mAudioFocusIdListenerMap根据AudioFocusRequest中OnAudioFocusChangeListener对象的来生成一个key,value则为根据afr生成的FocusRequestInfo ,存储在了mAudioFocusIdListenerMap对象中。 而之前所述的abandonAudioFocus中有unregisterAudioFocusRequest做的操作就是remove掉mAudioFocusIdListenerMap中的OnAudioFocusChangeListener 

    在AudioManager中还有一个电话相关的调用:

    public void requestAudioFocusForCall(int streamType, int durationHint)

     这个调用的也是AudioService中的requestAudioFocus并且往其中设置了一个clientId为AudioSystem.IN_VOICE_COMM_FOCUS_ID的状态。 继续往下就是进入到AudioService.requestAudioFocus中首先会进行权限检查,这里面就用到了AudioSystem.IN_VOICE_COMM_FOCUS_ID 也就是说如果clientId等于AudioSystem.IN_VOICE_COMM_FOCUS_ID,且要申请到MODIFY_PHONE_STATE的权限,否则会申请焦点失败。

    AudioService也只是做了中转,并没有做实际的操作,具体实现都是在MediaFocusControl.requestAudioFocus中大致过程如下:

    private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>(); 最主要的是对mFocusStack栈的操作,用来维护各client的申请和释放。

    1.判断mFocusStack.size() 不能超过100

    2.检查当前栈顶的元素是否是Phone应用占用,如果Phone处于占用状态,那么focusGrantDelayed = true。

    3. 压栈之前,需要检查当前栈中是否已经有这个应用的记录,如果有的话就删除掉。  

    如果mFocusStack不为空,并且栈顶的clientId与要申请焦点的clientId相同,得到栈顶元素即FocusRequester对象。如果申请的时长和flags都相同,则表示重复申请,直接返回成功,如果如果申请的时长和flags有一个不相同,则认为需要重新申请,此时如果focusGrantDelayed = false则需要将栈顶的元素出栈并将其释放

    4. // focus requester might already be somewhere below in the stack, remove it          

      removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);    

    移除可能在栈中(栈顶或者栈中)其他位置存在着相同clientId的元素

    a:如果要释放的应用是在栈顶,则释放之后,还需要通知先在栈顶应用,其获得了audiofocus;

    b:如果要释放的应用不是在栈顶,则只是移除这个记录,不需要更改当前audiofocus的占有情况。

    5.创建FocusRequester实例将请求包含的各种信息传入

    AudioAttributes aa, int focusChangeHint, IBinder cb,  IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, int sdk

    6.如果focusGrantDelayed = true,那么就会延迟申请,并把此次请求FocusRequester实例入栈,但是此时记录不是被压在栈顶,而是放在lastLockedFocusOwnerIndex这个位置,也就是打电话这个记录的后面;如果focusGrantDelayed = false即不需要延迟获得焦点,同样创建FocusRequester实例,但是先要通知栈里其他记录失去焦点,然后压入栈顶,最后通知自己获得焦点成功

    遍历mFocusStack,调用FocusRequester对象的handleExternalFocusGain方法 通知栈中其他元素丢失焦点流程. stackIterator.next()得到的是FocusRequester对象,因此查看FocusRequester中handleExternalFocusGain的代码

    主要关注两个变量gainRequest和mFocusLossReceived, mFocusLossReceived这个值在handleFocusLoss中进行赋值的,默认值是AudioManager.AUDIOFOCUS_NONE。

     * gainRequest这个是传进来的,例如

    AudioManager.AUDIOFOCUS_GAIN

     * return AudioManager.AUDIOFOCUS_LOSS

     * 若传进来的参数是 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT

     * 则return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT

    在handleFocusLoss中

    fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);

    通过mFocusDispatcher对象调用了dispatchAudioFocusChange方法,将mFocusLossReceived和mClientId传了进去。

    3.1 FocusRequester构造方法的第四个参数IAudioFocusDispatcher afl

    3.2 MediaFocusControl的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd。

    3.3 AudioService的requestAudioFocus方法的第四个参数IAudioFocusDispatcher fd 最终在AudioManager中实现。IAudioFocusDispatcher 的回调dispatchAudioFocusChange方法。

    在dispatchAudioFocusChange中通过Handler发送MSSG_FOCUS_CHANGE

    在ServiceEventHandlerDelegate 的创建handleMessage在其中根据MSSG_FOCUS_CHANGE来回调,而msg.arg1为focusChange即mFocusLossReceived

    listener.onAudioFocusChange(msg.arg1);

    最终app中实现onAudioFocusChange根据焦点的切换做相应的控制。

    展开全文
  • AudioPolicy&AudioFlinger初始化初始化概览总体框架启动步骤AudioPolicy初始化分析1、loadConfig()2、initialize() 初始化概览 总体框架 AudioFlinger和AudioPolicy两者是Android Audio框架层最主要的两个服务,...

    总体框架

    AudioFlinger和AudioPolicy两者是Android Audio框架层最主要的两个服务,他们两个是Android框架层的本地服务,在init.rc中启动;
    AudioPolicyManager负责音频策略定制者,说白了就相当于Audio系统的司令。
    AudioFlinger负责与底层audio alsa进行交互的实现者,那么它就是Audio系统的军官,干苦力的;
    总体框架:
    两个服务都属于audioserver进程,严格意义上来说audioserver通过init进程fork出来的,所以它是Linux系统中的一个进程。
    AudioFlinger:media.audio_flinger
    AudioPolicyService:media.audio_policy
    在这里插入图片描述

    初始化步骤简介

    1、通过init进程fork出来,从而开始各自服务的初始化
    2、首先初始化audioflinger服务
    3、其次初始化audiopolicyservice服务
    4、进一步通过audiopolicyservice和audioflinger完成音频hal层的初始化,这部分将是本文的重点难点分析。

    1、通过init进程fork出来,从而开始各自服务的初始化
    来,看下它是怎么定义:

    //frameworks/av/media/audioserver/audioserver.rc
    service audioserver /system/bin/audioserver
        class core
        user audioserver
        onrestart restart audio-hal-2-0
        ioprio rt 4 //设置io优先级
        disabled
    

    可以看到audioserver属于core类型,优于一般的main类型,也就是说它的启动是更早的。
    audioflinger&audiopolicyserver启动:

    frameworks/av/media/audioserver/main_audioserver.cpp
    int main(int argc __unused, char **argv)
    {
    	---
         android::hardware::configureRpcThreadpool(4, false /*callerWillJoin*/);
         sp<ProcessState> proc(ProcessState::self());
         sp<IServiceManager> sm = defaultServiceManager();
         ALOGI("ServiceManager: %p", sm.get());
         AudioFlinger::instantiate();
         AudioPolicyService::instantiate(); 
    	---
    }
    

    2、首先初始化audioflinger服务
    AudioFlinger初始化比较简洁,就是创建服务并将自身注册到systemserver中去,其次就是初始化部分通信组件以便后续与audio hal层进行通讯。如下图所示:
    在这里插入图片描述
    3、其次初始化audiopolicyservice服务
    AudioPolicyService的初始化就比audioflinger服务初始化复杂了,下图仅仅是audiopolicyservice与audiopolicymanager的初始化。主要就是创建出几个线程(AudioCommandThread类型的线程),以便后续与上层进行交互使用,上层调用的比如播放暂停的操作指令会进入这个线程队列,实现上层异步调用也可以防止底层耗时操作导致阻塞上层应用。接着便是创建AudioPolicyManager实例以及客户端等。大概流程如下图所示:
    在这里插入图片描述
    4、进一步通过audiopolicyservice和audioflinger完成音频hal层的初始化,这部分将是本文的重点难点分析。

    audiopolicyservice启动后,开始创建audiopolicymanager,并通过audiopolicymanager初始化audiopolicy策略,然后再进行对audio路由引擎(EngineInstance)进行初始化,初始化完路由引擎后便对audio hal 的so进行加载初始化,进一步通过加载后的so针对音频设备进行open操作,并默认打开主通道的输出音频流,最后将成功初始化的音频设备进行保存到audiopolicymanager以及audioflinger中,最后完成初始化。
    详细的初始化流程如下图所示:
    在这里插入图片描述

    初始化步骤详细流程分析

    从上面的初始大概流程可以知道,audio框架的初始化重点在audiopolicy部分的初始化,它不仅需要初始音频策略,还需针对加载的音频策略针对hal层的音频设备进行初始化,这部分还涉及到audioflinger部分,但以audiopolicy作为主线进行分析,下面将一步步对其进行分析。

    //frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
    AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
            : AudioPolicyManager(clientInterface, false /*forTesting*/)
    {
    	//1、加载audiopolicy的策略文件
        loadConfig();
        //2、针对加载的策略进行真正的初始化
        initialize();
    }
    

    可以看到AudioPolicyManager构造函数很简单,就两个调用:
    第一步:loadConfig()
    第二步:initialize()

    下面进入详细分析:

    1、loadConfig()

    很简单,就通过配置文件USE_XML_AUDIO_POLICY_CONF来控制是使用XML配置的策略文件还是使用传统旧config配置文件。这个变量的初始化可以通过配置文件进行选择。

    //frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
    void AudioPolicyManager::loadConfig() {
    #ifdef USE_XML_AUDIO_POLICY_CONF
    	//getConfig()这个很重要,为了后续的加载so做准备
        if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {
    #else
        if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, getConfig()) != NO_ERROR)
               && (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, getConfig()) != NO_ERROR)) {
    #endif
            ALOGE("could not load audio policy configuration file, setting defaults");
            getConfig().setDefault();
        }
    }
    

    1.1这个getConfig()得到的mConfig成员变量如下:

    mConfig(mHwModulesAll, mAvailableOutputDevices, mAvailableInputDevices, mDefaultOutputDevice, 
    static_cast<VolumeCurvesCollection*>(mVolumeCurves.get()))
    

    这些成员变量在解析配置文件(XML格式或者config格式)会得到初始化,这点很重要,后续的so加载会根据配置的module name来进行加载。其会通过Serializer.cpp进行XML文件的解析,这个是一个很繁重的任务,如需讲明其解析过程还需另起一个篇幅才能将其介绍,与初始化关系不大,一笔带过。
    XML的配置文件格式如下(简化版配置,这块涉及到音频路由,后续将会再写一篇详细介绍该配置文件):

    //frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml 
    <audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
        <globalConfiguration speaker_drc_enabled="true"/>
        <modules>
        	//编译后生成的so命名会根据module name 以及soc名字生成e.g. audio.[module name].[soc name]
        	//如IMX8的:audio.primary.imx8.so
            <module name="primary" halVersion="2.0">
                <attachedDevices>
                    <item>Speaker</item>
                </attachedDevices>
                <defaultOutputDevice>Speaker</defaultOutputDevice>
                <mixPorts>//输出混音线程
                    <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
                        <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000"
                        channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                    </mixPort>
                </mixPorts>
                <devicePorts>//输出设备节点
                    <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink" >
                    </devicePort>
                </devicePorts>
                <routes>
                	//音频路由
                    <route type="mix" sink="Speaker" sources="esai output,primary output"/>
                </routes>
            </module>
        </modules>
    </audioPolicyConfiguration>
    
    

    2、initialize()

    好了,上面的都是开胃菜,这个才是硬菜。
    来,看下这个大概步骤,心中有谱,码海不慌。
    主要是三个步骤:
    2.1初始音频路由引擎
    audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance();
    2.2、加载so 并且打开设备节点
    mpClientInterface->loadHwModule(hwModule->getName())
    2.3、打开输出流
    status_t status = outputDesc->open(nullptr, profileType, address, AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE,&output);
    怕你不信,所以贴了部分代码出来:

    //frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
    status_t AudioPolicyManager::initialize() {
    	//1、初始音频路由引擎
        // Once policy config has been parsed, retrieve an instance of the engine and initialize it.
        audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance();
        if (!engineInstance) {
            ALOGE("%s:  Could not get an instance of policy engine", __FUNCTION__);
            return NO_INIT;
        }
        // Retrieve the Policy Manager Interface
        mEngine = engineInstance->queryInterface<AudioPolicyManagerInterface>();
        if (mEngine == NULL) {
            ALOGE("%s: Failed to get Policy Engine Interface", __FUNCTION__);
            return NO_INIT;
        }
        mEngine->setObserver(this);
        status_t status = mEngine->initCheck();
    
          for (const auto& hwModule : mHwModulesAll) {
          	//2、加载so 并且打开设备节点
            hwModule->setHandle(mpClientInterface->loadHwModule(hwModule->getName()));
            mHwModules.push_back(hwModule);
            // open all output streams needed to access attached devices
            // except for direct output streams that are only opened when they are actually
            // required by an app.
            // This also validates mAvailableOutputDevices list
            for (const auto& outProfile : hwModule->getOutputProfiles()) {
                //经过一系列有效判断后 创建输出相关参数
                sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
                                                                                     mpClientInterface);
                const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
                const DeviceVector &devicesForType = supportedDevices.getDevicesFromType(profileType);
                String8 address = devicesForType.size() > 0 ? devicesForType.itemAt(0)->mAddress
                        : String8("");
                audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
                //3、打开输出流
                status_t status = outputDesc->open(nullptr, profileType, address,
                                               AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output);
    
                if (status != NO_ERROR) {
                    ALOGW("Cannot open output stream for device %08x on hw module %s",
                          outputDesc->mDevice,
                          hwModule->getName());
                } else {
                    for (const auto& dev : supportedDevices) {
                        ssize_t index = mAvailableOutputDevices.indexOf(dev);
                        // give a valid ID to an attached device once confirmed it is reachable
                        if (index >= 0 && !mAvailableOutputDevices[index]->isAttached()) {
                        	//这个很重要的变量,保存了可用的输出设备,后续会进一步说明
                            mAvailableOutputDevices[index]->attach(hwModule);
                        }
                    }
                    if (mPrimaryOutput == 0 &&
                            outProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
                        mPrimaryOutput = outputDesc;
                    }
                    addOutput(output, outputDesc);
                    setOutputDevice(outputDesc, profileType, true, 0,  NULL, address);
                }
             }//end inner for
            }//end out for
        }
        // make sure all attached devices have been allocated a unique ID
    

    好了,是不是也挺简单的,就三步。

    2.1、初始音频路由引擎

    2.1.1 创建路由
    audio_policy::EngineInstance
    这块有可配置路由和默认路由之分,音频流是根据路由策略进行打开相应的音频路由通路的。这部分内容需要领开一篇进行分析。这是一块很重要的内容,这里暂不展开分析。

    2.2、加载so 并且打开设备节点

    loadHwModule(hwModule->getName());
    参数是由上面步骤初始配置文件得到,hwModule->getName():如IMX8的根据配置文件:audio.primary.imx8.so
    会在vendor/lib/hw/加载文件,如果找不到会依次在system/lib/hw/进行查找。
    详细步骤如下:

    //注意这个返回值是audio_module_handle_t,这是个线程,这个很重要
    //因为后续的播放录音Track都是挂到这个audio_module_handle_t上去的,这个是个线程
    //frameworks/av/services/audioflinger/AudioFlinger.cpp
    audio_module_handle_t AudioFlinger::loadHwModule(const char *name){
    1==>进一步调用loadHwModule_l(name);
    2====> 再进一步调用到DevicesFactoryHal的openDevice方法打开驱动设备
    //frameworks/av/services/audioflinger/AudioFlinger.cpp
    int rc = mDevicesFactoryHal->openDevice(name, &dev);
    3=======>调用本地通讯方式的DeviceFactroy实现
    //frameworks\av\media\libaudiohal\2.0\DevicesFactoryHalLocal.cpp
    status_t DevicesFactoryHalLocal::openDevice(const char *name, sp<DeviceHalInterface> *device) 
    3.1=======>继续调用到load_audio_interface
    //frameworks\av\media\libaudiohal\2.0\DevicesFactoryHalLocal.cpp
    static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev){
        const hw_module_t *mod;
        int rc;
        rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
        if (rc) {
            ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__,
                    AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
            goto out;
        }
        rc = audio_hw_device_open(mod, dev);
        return rc;
    }
    4=========>最终会调用到audio.h的方法open,audio_hw_device_open方法会调用设备的open方法
    //hardware/libhardware/include/hardware/audio.h
    static inline int audio_hw_device_open(const struct hw_module_t* module,
                                           struct audio_hw_device** device){
        return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
                                     TO_HW_DEVICE_T_OPEN(device));
    }
    5==========>最后会调用到
    //最终会调用到各自厂商实现的hal层的open方法,代码路径就不放了
    static int adev_open(const hw_module_t* module, const char* name,
                         hw_device_t** device)
    

    到这里,音频设备打开就完毕了;
    额外说明一下:mDevicesFactoryHal的初始化:

    1// mDevicesFactoryHal初始化是在AudioFlinger初始化的时候进行的:
    //frameworks/av/services/audioflinger/AudioFlinger.cpp
    mDevicesFactoryHal = DevicesFactoryHalInterface::create();
    
    //frameworks/av/media/libaudiohal/DevicesFactoryHalInterface.cpp
    sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
        if (hardware::audio::V4_0::IDevicesFactory::getService() != nullptr) {
            return new V4_0::DevicesFactoryHalHybrid();
        }
        if (hardware::audio::V2_0::IDevicesFactory::getService() != nullptr) {
            return new DevicesFactoryHalHybrid();
        }
        return nullptr;
    }
    DevicesFactoryHalHybrid:Hybrid混合,包含了本地通讯方式,也包含了HIDL通讯方式:
    //frameworks/av/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp
    DevicesFactoryHalHybrid::DevicesFactoryHalHybrid(sp<IDevicesFactory> hidlFactory)
            : mLocalFactory(new DevicesFactoryHalLocal()),
              mHidlFactory(new DevicesFactoryHalHidl(hidlFactory)) {
    }
    
    status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp<DeviceHalInterface> *device) {
        if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0 &&
            strcmp(AUDIO_HARDWARE_MODULE_ID_HEARING_AID, name) != 0) {
            return mHidlFactory->openDevice(name, device);
        }
        return mLocalFactory->openDevice(name, device);
    }
    到这里,mDevicesFactoryHal的初始化介绍就完毕了。
    但是Android 10以后是mDevicesFactoryHal的初始化是这样实现的:
    //frameworks/av/media/libaudiohal/DevicesFactoryHalInterface.cpp
    sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
       return createPreferredImpl<DevicesFactoryHalInterface>(
                "android.hardware.audio", "IDevicesFactory");
    }
    通过是从服务中根据名称"android.hardware.audio", "IDevicesFactory"获取的,暂不深究。
    

    2.3、打开输出流

    //frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp  AudioPolicyManager::initialize()
     const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
                const DeviceVector &devicesForType = supportedDevices.getDevicesFromType(profileType);
                String8 address = devicesForType.size() > 0 ? devicesForType.itemAt(0)->mAddress
                        : String8("");
                audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
                status_t status = outputDesc->open(nullptr, profileType, address,
                                               AUDIO_STREAM_DEFAULT, AUDIO_OUTPUT_FLAG_NONE, &output);
                                               
    //frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
    status_t SwAudioOutputDescriptor::open(const audio_config_t *config,
                                           const DeviceVector &devices,
                                           audio_stream_type_t stream,
                                           audio_output_flags_t flags,
                                           audio_io_handle_t *output)
    {
        mDevices = devices;
        sp<DeviceDescriptor> device = devices.getDeviceForOpening();
        LOG_ALWAYS_FATAL_IF(device == nullptr,
                            "%s failed to get device descriptor for opening "
                            "with the requested devices, all device types: %s",
                            __func__, dumpDeviceTypes(devices.types()).c_str());
    
        audio_config_t lConfig;
        if (config == nullptr) {
            lConfig = AUDIO_CONFIG_INITIALIZER;
            lConfig.sample_rate = mSamplingRate;
            lConfig.channel_mask = mChannelMask;
            lConfig.format = mFormat;
        } else {
            lConfig = *config;
        }
    
        // if the selected profile is offloaded and no offload info was specified,
        // create a default one
        if ((mProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
                lConfig.offload_info.format == AUDIO_FORMAT_DEFAULT) {
            flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
            lConfig.offload_info = AUDIO_INFO_INITIALIZER;
            lConfig.offload_info.sample_rate = lConfig.sample_rate;
            lConfig.offload_info.channel_mask = lConfig.channel_mask;
            lConfig.offload_info.format = lConfig.format;
            lConfig.offload_info.stream_type = stream;
            lConfig.offload_info.duration_us = -1;
            lConfig.offload_info.has_video = true; // conservative
            lConfig.offload_info.is_streaming = true; // likely
        }
    
        mFlags = (audio_output_flags_t)(mFlags | flags);
    
        ALOGV("opening output for device %s profile %p name %s",
              mDevices.toString().c_str(), mProfile.get(), mProfile->getName().c_str());
    
        status_t status = mClientInterface->openOutput(mProfile->getModuleHandle(),
                                                       output,
                                                       &lConfig,
                                                       device,
                                                       &mLatency,
                                                       mFlags);
     mClientInterface:是在AudioPolicyManager调用的时候传进去的
     AudioPolicyClientInterface *mpClientInterface;  // audio policy client interface
    

    到了AudioPolicyClientInterface 这个就明朗了,调用如下:
    在这里插入图片描述
    大概流程就是AudioPolicyManager–>AudioPolicyService–>AudioFlinger–>audio hal层了。至此,output 输出流打开成功。
    最后将已经打开的输出流和输出设备保存到相应的数组里面,后续有流需要播出的时候直接进行查找,如果查找不到会尝试向底层查询,如果查询支持将会打开,如果查询不到,则会将其流类型改变并且将其加入到默认输出流里面进行播放处理。

    小结:
    这里的一切都是为音频播放做准备,后续将介绍音频路由部分。

    展开全文
  • Audio Unit

    2018-10-19 11:34:57
    Audio Unit 推荐先阅读Audio Unit Hosting Guide for iOS Audio Unit在框架中位置 Audio units通常在名为Audio Processing Graph的context中使用,如下所示: Audio units usually do their work in the context ...

    Audio Unit

    推荐先阅读Audio Unit Hosting Guide for iOS,部分翻译的文章可参考Audio Unit 基础

    Audio Unit在框架中位置
    Audio frameworks in iOS

    Audio Unit提供了音频快速的模块化处理,在以下的场景中更适合使用AudioUnit,而不是更高层次的音频框架:

    • Simultaneous audio I/O (input and output) with low latency, such as for a VoIP (Voice over Internet Protocol) application
      想使用低延迟的音频I/O,比如说VoIP的应用场景下
    • Responsive playback of synthesized sounds, such as for musical games or synthesized musical instruments
      多路声音的合成并且播放,比如游戏或者音乐合成器的应用
    • Use of a specific audio unit feature such as acoustic echo cancelation, mixing, or tonal equalization
      使用AudioUnit里面提供的特有功能,比如:回声消除、Mix两轨音频,以及均衡器、压缩器、混响器等效果器
    • A processing-chain architecture that lets you assemble audio processing modules into flexible networks. This is the only audio API in iOS offering this capability.

    Audio units通常在名为Audio Processing Graph的context中使用,如下所示:

    Audio units usually do their work in the context of an enclosing object called an audio processing graph, as shown in the figure. In this example, your app sends audio to the first audio units in the graph by way of one or more callback functions and exercises individual control over each audio unit. The output of the I/O unit—the last audio unit in this or any audio processing graph—connects directly to the output hardware.

    Audio Processing Graph

    APP持有的Audio Processing Graph容器中包含两个EQ Unit、一个Mixer Unit、一个I/O Unit,APP将磁盘或者网络中的两路流数据分别通过EQ Unit进行均衡处理,然后在Mixer Unit经过混音处理为一路,进入I/O Unit将此路数据送往硬件去播放。在这整个流程中,APP随时可以调整设置AU Graph及其中每个Unit的工作状态及参数,动态性的接入或者移出指定的Unit,并且保证线程安全。
    iOS Audio Unit(一)

    iOS 與 Mac OS X 的 Audio API 概觀一文中,对Audio Unit Processing Graph有这样的描述:

    Audio Unit Processing Graph 是一個可以處理播放與錄音的 API,這層把 audio 播放的處理過程,抽象化變成一個個的組件,我們可以透過組合這些組件創造我們想要的錄製與播放效果。Audio Unit Processing Graph API 裡頭大概有三個主要的角色:

    • AUNode 或 AudioComponent
    • AudioUnit
    • AUGraph

    我們不妨想像我們現在身處在演唱會的舞台上,有錄製歌聲與樂器的麥克風,而從麥克風到輸出到音響之間,還串接了大大小小的效果器,在這個過程中,無論是麥克風、音響或是效果器,都是不同的 AUNode。AUNode 是這些器材的實體,而我們要操控這些器材、改變這些器材的效果屬性,就會需要透過每個器材各自的操控介面,這些介面便是AudioUnit,最後構成整個舞台,便是 AUGraph。

    AUNode 與 AudioComponent 的差別在於,其實像上面講到的各種器材,除了可以放在 AUGraph 使用之外,也可以單獨使用,比方說我們有台音響,我們除了把音響放在舞台上使用外,也可以單獨拿這台音響輸出音樂。當我們要在 AUGraph 中使用某個器材,我們就要使用 AUNode 這種形態,單獨使用時,就使用 AudioComponent。但無論是操作 AUNode 或 AudioComponent,都還是得透過 AudioUnit 這一層操作介面。

    AUNode 與 AudioComponent 分成好幾類,包括輸入、輸出、混音、效果處理、格式轉換等等,彼此之間可以互相串接。輸入裝置包括像麥克風輸入或 MIDI 樂器,效果處理則包括像EQ 等化器、殘響(reverb)、改變音調(pitch)等;至於這邊所謂的格式轉換,是指在不同的 LPCM 格式之間轉換,在這一層 API 中只支援 LPCM 格式,但LPCM 之間又有很多種,不見得每個 node 都支援所有的 LPCM 格式,像 reverb效果的 effect node 就只支援浮點數,所以要讓音訊資料通過這個 node 之前,就需要先轉換成浮點數格式的 LPCM 資料。
    每個 AudioUnit 都有各自的輸入與輸出,在串接的時候,就是從某個 AudioUnit 的輸出,串接到另外一個 AudioUnit 的輸入,這種輸入輸出的端子叫做 bus,而每個 AudioUnit 最少會有一個輸入與輸出的 bus,也可能會有多個 bus。以 mixer 來說,就會有多個 bus,當我們從兩個輸入 bus 將資料送到同一個 mixer 上時,就可以產生混音效果。

    在 Mac OS X 上,我們通常會使用 default output 作為音訊播放的最終輸出的 AudioUnit,以 default input 作為錄音的起點。在 iOS 上則有一個特別的 AudioUnit,叫做 Remote IO,這個 AudioUnit 同時代表 iOS 的輸出與輸入裝置。Remote IO 有兩個 bus,bus 0 就是 iOS 的預設輸出,bus 1 則是輸入,所以我們在 iOS 上播放音樂,就是往 Remote IO 的 bus 0 傳送資料。
    Remote IO 的 bus 1 預設是關閉的,當我們要錄音的時候,我們必須先告訴 Remote IO 把 bus 1 變成 enable,但我們要做這件事情的時候,我們不但要獲得使用者給予我們使用麥克風的授權,還要設定正確的 Audio Session。我們會在後面說明 Audio Session。

    在使用 Audio Unit Processing Graph API 的時候,我們經常需要設定 render callback function。以錄音來說,當我們從 Remote IO 的 bus 1 收到資料後,想要儲存檔案,我們並沒有一種叫做「存檔」的 AudioUnit,而是我們要對某個 AudioUnit 設定 callback function,綁定某個 bus,在這個 function 中撰寫存檔的程式。以播放來說,當我們告訴 AUGraph 或 Remote IO 開始播放,我們也要設定 render callback function,提供用來播放用的資料。

    Audio Units in iOS

    iOS提供了4中类型的共7个的audio units,如下:

    Purpose Audio units
    Effect iPod Equalizer
    Mixing 3D Mixer ,Multichannel Mixer
    I/O Remote I/O ,Voice-Processing I/O ,Generic Output
    Format conversion Format Converter

    具体的identifiers可参考Identifier Keys for Audio Units

    在《音视频开发进阶指南》一书中对AudioUnit分类的具体介绍如下:

    1.Effect Units
    类型是kAudioUnitType_Effect,主要提供声音特效处理的功能,其子类型Effect Audio Unit Subtypes
    及其用途说明如下:

    • 均衡效果器:子类型是kAudioUnitSubType_NBandEQ,主要作用是未声音的某些频带增强或者减弱能量
    • 压缩效果器:子类型是kAudioUnitSubType_DynamicsProcessor,主要作用是当声音较小的时候,可以提供声音的能量,当声音的能量超过设置的阈值时,可以降低声音的能量…
    • 混响效果器:子类型是kAudioUnitSubType_Reverb2

    2.Mixer Units
    类型是kAudioUnitType_Mixer,主要提供Mix多路声音的功能,其子类型Mixer Audio Unit Subtypes
    及用途如下:

    • MultiChannelMixer: 子类型是kAudioUnitSubType_MultiChannelMixer,是多路声音混音的效果器,可以接受多路音频的输入,还可以分别调整每一路音频的增益与开关,并将多路音频合并成一路,该效果器在处理音频的图状结构中非常有用

    3.I/O Units
    类型是kAudioUnitType_Output,主要提供的是I/O的功能,其子类型说明如下:

    • RemoteIO: 子类型是kAudioUnitSubType_RemoteIO,用来采集音频与播放音频的,其实当开发者的应用场景中要使用麦克风及扬声器的时候回用到该AudioUnit
    • Generic Output:子类型是kAudioUnitSubType_GenericOutput,当开发者需要进行离线处理,或者说在AUGraph中不使用Speaker(扬声器)来驱动整个数据流,而是希望使用一个输出(可以放入内存队列或者进行磁盘I/O操作)来驱动数据流时,就使用该子类型

    4.Format Convert Units
    类型是kAudioUnitType_FormatConverter,主要用于提供格式转换功能,比如:采样格式由Float到SInt16的转换、交错和平铺的格式转换、单双声道的转换等,其子类型及用途说明如下:

    • AUConverter: 子类型是kAudioUnitSubType_AUConverter, 它将是本书要重点介绍的格式转换效果器, 当某些效果器对输入的音频格式有明确的要求时(比如3D Mixer Unit就必须使用UInt16格式的Sample), 或者开发者将音频数据输入给一些其他的编码器进行编码,又或者开发者想使用SInt16格式的PCM裸数据在其他CPU上进行音频算法计算等的场景下,就需要使用到这个ConvertNode了。一个典型例子,由FFmpeg解码出来的PCM数据是SInt16格式的, 因此不能直接输送给RemoteIO Unit进行播放, 所以需要构建一个ConvertNodeSInt16格式表示的数据转换为Float32格式表示的数据, 然后再输送给RemoteIO Unit , 最终才能正常播放出来

    • Time Pitch: 子类型是kAudioUnitSubType_NewTimePitch 即变速变调效果器, 这是一个很有意思的效果器, 可以对声音的音高, 速度进行调整, 像会说话的Tom猫类似的应用场景就可以使用这个效果器来实现

    5.Generator Units
    类型是kAudioUnitType_Generator, 在开发中我们经常使用它来提供播放器的功能. 其子类型及用途说明如下.

    • AudioUnitFilePlayer: 子类型kAudioUnitSubType_AudioFilePlayer, 在AudioUnit 里面, 如果我们的输入不是麦克风, 而希望其是一个媒体文件, 当然, 也可以类似于代码仓库中的AudioPlayer项目自行解码, 转换之后将数据输送给RemoteIO Unit播放出来, 但是其实还有一种更简单, 方便的方式. 那就是使用AudioFilePlayer这个 AudioUnit, 可以参考代码仓库中的AUPlayer项目,该项目就是使用AudioUnitFilePlayer作为输入数据源来提供数据的。需要注意的是, 必须在初始化AUGraph之后, 再去配置AudioFilerPlayer的数据源以及播放范围等属性, 否者就会出现错误,其实数据源还是会调用AudioFile的解码功能, 将媒体文件中的压缩数据解压成为PCM裸数据, 最终再交给AudioFilePlayer Unit进行后续处理.

    构建AudioUnit

    文档中介绍有2套API来创建AudioUnit

    为了在运行时找到一个audio unit,需要指定audio component description数据结构中type、subtype、manufacturer的key对应的值

    • type - AudioUnit的四大类型的type
    • subtype - 该大类型对应的子类型
    • manufacturer - 厂商,一般写成kAudioUnitManufacturer_Apple

    如下的例子:

    AudioComponentDescription ioUnitDescription;
     
    ioUnitDescription.componentType          = kAudioUnitType_Output;
    ioUnitDescription.componentSubType       = kAudioUnitSubType_RemoteIO;
    ioUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
    ioUnitDescription.componentFlags         = 0;
    ioUnitDescription.componentFlagsMask     = 0;
    

    上述代码创建了RemoteIO类型的AudioUnit描述的结构体,那么如何使用这个描述来创建真正的AudioUnit呢?

    1.使用audio unit API获取一个audio unit实例

    AudioComponent foundIoUnitReference = AudioComponentFindNext (
                                              NULL,
                                              &ioUnitDescription
                                          );
    AudioUnit ioUnitInstance;
    AudioComponentInstanceNew (
        foundIoUnitReference,
        &ioUnitInstance
    );
    

    2.使用 audio processing graph API获取一个audio unit实例

    // 声明并初始化一个AUGraph
    AUGraph processingGraph;
    NewAUGraph (&processingGraph);
     
    // Add an audio unit node to the graph, then instantiate the audio unit
    AUNode ioNode;
    AUGraphAddNode (
        processingGraph,
        &ioUnitDescription,
        &ioNode
    );
    AUGraphOpen (processingGraph); // indirectly performs audio unit instantiation
     
    // Obtain a reference to the newly-instantiated I/O unit
    AudioUnit ioUnit;
    AUGraphNodeInfo (
        processingGraph,
        ioNode,
        NULL,
        &ioUnit
    );
    

    Audio Units中的Scopes和Elements

    scope和elements

    When invoking a function to configure or control an audio unit, you specify the scope and element to identify the specific target of the function.
    当调用方法来配置or控制audio unit时,其实是指定通过指定scope和element来表示方法的target

    • scope - A scope is a programmatic context within an audio unit
    • element - An element is a programmatic context nested within an audio unit scope。当一个element是一个input或者output scope的组成部分时,它类似于音频设备中的 signal bus——由于这个原因,有时它也被称为bus。

    You specify an element (or bus) by its zero-indexed integer value. If setting a property or parameter that applies to a scope as a whole, specify an element value of 0.
    可以通过零索引整数值指定element(或bus)。如果要设置适用于scope的属性或参数,指定element值为0

    上图显示的是audio unit的通用架构,它显示的是在input和output中的elements数量是相同的。然而,不同的audio unit其架构是不同的。例如,mixer unit,可能会有多个input element,但只有一个output element。

    使用属性配置Audio Units

    An audio unit property is a key-value pair you can use to configure an audio unit.
    使用AudioUnitSetProperty方法来在任何audio unit设置属性:

    UInt32 busCount = 2;
     
    OSStatus result = AudioUnitSetProperty (
        mixerUnit,
        kAudioUnitProperty_ElementCount,   // the property key 属性的key
        kAudioUnitScope_Input,             // the scope to set the property on 设置属性所在的scope
        0,                                 // the element to set the property on 设置属性所在的element
        &busCount,                         // the property value 属性的值
        sizeof (busCount)
    );
    

    一些常用的属性:

    • kAudioOutputUnitProperty_EnableIO - 启用or禁用I/O unit的input or output 。默认情况下,output是启用的,input是禁用的
    • kAudioUnitProperty_ElementCount - 例如,配置mixer unit中的 input elements 的数量
    • kAudioUnitProperty_MaximumFramesPerSlice - for specifying the maximum number of frames of audio data an audio unit should be prepared to produce in response to a render call. For most audio units, in most scenarios, you must set this property as described in the reference documentation. If you don’t, your audio will stop when the screen locks.
    • kAudioUnitProperty_StreamFormat - for specifying the audio stream data format for a particular audio unit input or output bus.

    其它的一些方法:

    • AudioUnitGetPropertyInfo - To discover whether a property is available; if it is, you are given the data size for its value and whether or not you can change the value
    • AudioUnitGetProperty, AudioUnitSetProperty - To get or set the value of a property
    • AudioUnitAddPropertyListener, AudioUnitRemovePropertyListenerWithUserData - To install or remove a callback function for monitoring changes to a property’s value

    Audio Units 参数

    audio unit 参数是用户可以在音频生成的过程中更改,事实上,大部分参数可以在 audio unit 正在执行时实时调整的,例如音量。

    • AudioUnitGetParameter
    • AudioUnitSetParameter

    I/O Units的特点

    I/O unit包含2个elements,如下图所示:

     I/O unit

    RemoteIO是与硬件IO相关的一个Unit,它分为输入端和输出端(I代表Input,O代表Output)。输入端一般是指麦克风,输出端一般是指扬声器(Speaker)或者耳机。如果需要同时使用输入输出功能,即耳返(在唱歌或者说话时,耳机会将麦克风收录的声音播放处理,让用户能够听到自己的声音),则需要开发者做一些设置将它们连接起来

    RemoteIO Unit分为Element0 和 Element1,其中Element0控制输出端,Element1控制输入端,同时每个Element又分为Input Scope和Output Scope。如果开发者想使用扬声器的声音播放功能,那么必须将个Unit的Output Scope和Speaker进行连接。而如果开发者要使用麦克风的录音功能,那么必须将这个Unit的 Element1 的Input Scope和麦克风进行连接。

    使用Audio Processing Graphs来管理Audio Units

    一个audio processing graph是一个Core Foundation风格的不透明类型, AUGraph,可使用它来构建和管理一个audio unit processing链。一个graph能够利用多个audio units和多个render callback functions

    audio processing graph API使用另一个不透明类型AUNode,来表示graph上下文中的单个audio unit

    总的来说,构建audio processing graph需要三个任务:

    1. Adding nodes to a graph 添加node到graph
    2. Directly configuring the audio units represented by the nodes 直接配置nodes表示的audio units
    3. Interconnecting the nodes 互连node

    一个 Audio Processing Graph 只有一个I/O Unit

    Every audio processing graph has one I/O unit, whether you are doing recording, playback, or simultaneous I/O.

    Graphs let you start and stop the flow of audio by way of the AUGraphStart and AUGraphStop functions. These functions, in turn, convey the start or stop message to the I/O unit by invoking its AudioOutputUnitStart or AudioOutputUnitStop function. In this way, a graph’s I/O unit is in charge of the audio flow in the graph.

    Graphs允许你通过AUGraphStartAUGraphStop函数启动和停止音频流。反过来,这些函数通过调用其AudioOutputUnitStartAudioOutputUnitStop函数将开始或停止消息传递给I/O unit

    Audio Processing Graphs Provide Thread Safety

    以下是audio processing graph API支持的一些常见重新配置及其相关方法:

    • Adding or removing audio unit nodes (AUGraphAddNode, AUGraphRemoveNode) - 添加or移除audio unit node
    • Adding or removing connections between nodes (AUGraphConnectNodeInput, AUGraphDisconnectNodeInput) - 添加or移除node之间的连接
    • Connecting a render callback function to an input bus of an audio unit (AUGraphSetNodeInputCallback)

    Audio Flows Through a Graph Using “Pull”

    在audio processing graph中,consumer调用provider来提供更多的audio data。

    The pull mechanism of audio data flow

    每次请求data的过程叫做render call,或者叫做pull。上图中灰色的箭头表示的就是render call,render call请求的data被称为audio sample frames

    In turn, a set of audio sample frames provided in response to a render call is known as a slice.

    Render Callback Functions Feed Audio to Audio Units

    Understanding the Audio Unit Render Callback Function

    static OSStatus MyAURenderCallback (
        void                        *inRefCon,
        AudioUnitRenderActionFlags  *ioActionFlags,
        const AudioTimeStamp        *inTimeStamp,
        UInt32                      inBusNumber,
        UInt32                      inNumberFrames,
        AudioBufferList             *ioData
    ) { /* callback body */ }
    
    • inRefCon,参数指向回调附加到 audio unit 输入时指定的编程上下文
    • ioActionFlags,参数允许提供提示当没有音频数据处理时,例如当你的应用程序是合成吉他,用户当面没有播放,请执行此操作。当你想要输出静音,可以在回调主体中使用如下语句:*ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence,并且你必须明确地将 ioData 参数指向的缓冲区设置为0。
    • inTimeStamp,回调函数被触发时间,包含一个 AudioTimeStamp 结构体。
    • inBusNumber,参数指示调用回调的音频单元总线
    • inNumberFrames,当前调用的音频采样数
    • ioData,指向音频数据缓存区

    Audio Stream Formats Enable Data Flow

    AudioStreamBasicDescription的结构

    struct AudioStreamBasicDescription {
        Float64 mSampleRate;
        UInt32  mFormatID;
        UInt32  mFormatFlags;
        UInt32  mBytesPerPacket;
        UInt32  mFramesPerPacket;
        UInt32  mBytesPerFrame;
        UInt32  mChannelsPerFrame;
        UInt32  mBitsPerChannel;
        UInt32  mReserved;
    };
    typedef struct AudioStreamBasicDescription  AudioStreamBasicDescription;
    

    AudioStreamBasicDescription,可配置audio stream basic description (ASBD)来指定 linear PCM 格式和constant bit rate (CBR)

    In Core Audio, the following definitions apply:

    • An audio stream is a continuous series of data that represents a sound, such as a song.
      audio stream是连续的一系列data,表示一个sound

    • A channel is a discrete track of monophonic audio. A monophonic stream has one channel; a stereo stream has two channels.
      chanel是单声道音频的离散轨道。单声道流有一个频道;立体声流有两个通道。

    • A sample is single numerical value for a single audio channel in an audio stream.

    • A frame is a collection of time-coincident samples. For instance, a linear PCM stereo sound file has two samples per frame, one for the left channel and one for the right channel.
      frame是时间重合samples的集合。例如,线性PCM立体声声音文件每帧有两个sample,一个用于左声道,一个用于右声道。

    • A packet is a collection of one or more contiguous frames. A packet defines the smallest meaningful set of frames for a given audio data format, and is the smallest data unit for which time can be measured. In linear PCM audio, a packet holds a single frame. In compressed formats, it typically holds more; in some formats, the number of frames per packet varies.
      packet是frame的集合

    • The sample rate for a stream is the number of frames per second of uncompressed (or, for compressed formats, the equivalent in decompressed) audio.
      sample rate是每秒多个frame

    参考:iOS音频开发之AudioStreamBasicDescription

    首先,音频文件的产生是模拟信号->PCM以后的数字信号->压缩、编码以后的音频文件。
    PCM时采样频率叫做sample rate。
    每一次采样可以得到若干采样数据,对应多个channel。
    每一个采样点得到的若干采样数据组合起来,叫做一个frame。
    若干frame组合起来叫做一个packet。
    mSampleRate,就是采用频率
    mBitsPerChannel,就是每个采样数据的位数
    mChannelsPerFrame,可以理解为声道数,也就是一个采样时刻产生几个采样数据。
    mFramesPerPacket,就是每个packet的中frame的个数,等于这个packet中经历了几次采样间隔。
    mBytesPerPacket,每个packet中数据的字节数。
    mBytesPerFrame,每个frame中数据的字节数

    计算公式

    1.计算每个packet的持续时间

    duration = (1 / mSampleRate) * mFramesPerPacket
    
    展开全文
  • Android audio 一 源码路径 Android audioAudioRecord 分析上 Android audioAudioRecord 分析下 Android audio 四 AudioTrack 分析上 Android audio 五 AudioTrack 分析下 Android audioAudio...

    Android audio 一 源码路径

    Android audio 二 AudioRecord 分析上

    Android audio 三 AudioRecord 分析下

    Android audio 四 AudioTrack 分析上

    Android audio 五 AudioTrack 分析下

    Android audio 六 AudioRecord AudiTrack 拾音放音例子

    Android 采集音频类 AudioRecord

    文件:

    frameworks/base/media/java/android/media/AudioRecord.java

    frameworks/base/core/jni/android_media_AudioRecord.cpp

    frameworks/av/media/libmedia/AudioRecord.cpp

     

    在 APP 里创建一个拾音线程,先要实例化 AudioRecord 对象,下面从实例化对象 AudioRecord 开始分析 

    private AudioRecord audiorecord = new AudioRecord(......)

     

    AudioRecord 源码如下, 有三个构造函数 AudioRecord :

    第一个构造函数实例化 AudioRecord 对象, APP 调用。

    第二个 @SystemApi 是系统 API , 这个函数中调用了 native_setup ,实例化本地 AudioRecord 对象。

        // 调用 JNI ,创建本地 audiorecord 实例
        int initResult = native_setup(new WeakReference<AudioRecord>(this),
                                      mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
                                      mAudioFormat, mNativeBufferSizeInBytes,
                                      session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
    // frameworks/base/media/java/android/media/AudioRecord.java
    //---------------------------------------------------------
    // Constructor, Finalize
    //--------------------
    /**
     * Class constructor.
     * Though some invalid parameters will result in an {@link IllegalArgumentException} exception,
     * other errors do not.  Thus you should call {@link #getState()} immediately after construction
     * to confirm that the object is usable.
     * @param audioSource the recording source.
     *   See {@link MediaRecorder.AudioSource} for the recording source definitions.
     * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
     *   rate that is guaranteed to work on all devices, but other rates such as 22050,
     *   16000, and 11025 may work on some devices.
     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
     *   which is usually the sample rate of the source.
     *   {@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_IN_MONO} and
     *   {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
     *   to work on all devices.
     * @param audioFormat the format in which the audio data is to be returned.
     *   See {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT},
     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
     * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
     *   to during the recording. New audio data can be read from this buffer in smaller chunks
     *   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
     *   required buffer size for the successful creation of an AudioRecord instance. Using values
     *   smaller than getMinBufferSize() will result in an initialization failure.
     * @throws java.lang.IllegalArgumentException
     */
    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);
    }
    
    /**
     * @hide
     * Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
     * @param attributes a non-null {@link AudioAttributes} instance. Use
     *     {@link AudioAttributes.Builder#setAudioSource(int)} for configuring the audio
     *     source for this instance.
     * @param format a non-null {@link AudioFormat} instance describing the format of the data
     *     that will be recorded through this AudioRecord. 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 buffer where audio data is written
     *   to during the recording. New audio data can be read from this buffer in smaller chunks
     *   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
     *   required buffer size for the successful creation of an AudioRecord instance. Using values
     *   smaller than getMinBufferSize() will result in an initialization failure.
     * @param sessionId ID of audio session the AudioRecord 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
     */
    @SystemApi
    public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
                       int sessionId) throws IllegalArgumentException
    {
        mRecordingState = RECORDSTATE_STOPPED;
    
        if(attributes == null)
        {
            throw new IllegalArgumentException("Illegal null AudioAttributes");
        }
        if(format == null)
        {
            throw new IllegalArgumentException("Illegal null AudioFormat");
        }
    
        // remember which looper is associated with the AudioRecord instanciation
    	// 记住哪个线程与音频记录的安装相关
        if((mInitializationLooper = Looper.myLooper()) == null)
        {
            mInitializationLooper = Looper.getMainLooper();
        }
    
        // is this AudioRecord using REMOTE_SUBMIX at full volume?
        if(attributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX)
        {
            final AudioAttributes.Builder filteredAttr = new AudioAttributes.Builder();
            final Iterator<String> tagsIter = attributes.getTags().iterator();
            while(tagsIter.hasNext())
            {
                final String tag = tagsIter.next();
                if(tag.equalsIgnoreCase(SUBMIX_FIXED_VOLUME))
                {
                    mIsSubmixFullVolume = true;
                    Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume");
                }
                else     // SUBMIX_FIXED_VOLUME: is not to be propagated to the native layers
                {
                    filteredAttr.addTag(tag);
                }
            }
            filteredAttr.setInternalCapturePreset(attributes.getCapturePreset());
            mAudioAttributes = filteredAttr.build();
        }
        else
        {
            mAudioAttributes = attributes;
        }
    
        int rate = format.getSampleRate();
        if(rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED)
        {
            rate = 0;
        }
    
        int encoding = AudioFormat.ENCODING_DEFAULT;
        if((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0)
        {
            encoding = format.getEncoding();
        }
    
        audioParamCheck(attributes.getCapturePreset(), rate, encoding);
    
        if((format.getPropertySetMask()
                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0)
        {
            mChannelIndexMask = format.getChannelIndexMask();
            mChannelCount = format.getChannelCount();
        }
        if((format.getPropertySetMask()
                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0)
        {
            mChannelMask = getChannelMaskFromLegacyConfig(format.getChannelMask(), false);
            mChannelCount = format.getChannelCount();
        }
        else if(mChannelIndexMask == 0)
        {
            mChannelMask = getChannelMaskFromLegacyConfig(AudioFormat.CHANNEL_IN_DEFAULT, false);
            mChannelCount =  AudioFormat.channelCountFromInChannelMask(mChannelMask);
        }
    
        audioBuffSizeCheck(bufferSizeInBytes);
    
        int[] sampleRate = new int[] {mSampleRate};
        int[] session = new int[1];
        session[0] = sessionId;
        //TODO: update native initialization when information about hardware init failure
        //      due to capture device already open is available.
    	// 调用 native 方法,创建 audiorecord 实例
        int initResult = native_setup(new WeakReference<AudioRecord>(this),
                                      mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
                                      mAudioFormat, mNativeBufferSizeInBytes,
                                      session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/);
        if(initResult != SUCCESS)
        {
            loge("Error code "+initResult+" when initializing native AudioRecord object.");
            return; // with mState == STATE_UNINITIALIZED
        }
    
        mSampleRate = sampleRate[0];
        mSessionId = session[0];
    
        mState = STATE_INITIALIZED;
    }
    
    /**
     * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
     * the AudioRecordRoutingProxy subclass.
     * @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord
     * (associated with an OpenSL ES recorder). Note: the caller must ensure a correct
     * value here as no error checking is or can be done.
     */
    /*package*/ AudioRecord(long nativeRecordInJavaObj)
    {
        mNativeRecorderInJavaObj = 0;
        mNativeCallbackCookie = 0;
        mNativeDeviceCallback = 0;
    
        // other initialization...
        if(nativeRecordInJavaObj != 0)
        {
            deferred_connect(nativeRecordInJavaObj);
        }
        else
        {
            mState = STATE_UNINITIALIZED;
        }
    }

     

    在实例化 Audio Record 调用 native_setup 方法,进入 native 。

    本地方法接口如下列表:

    // frameworks/base/core/jni/android_media_AudioRecord.cpp
    static const JNINativeMethod gMethods[] = {
        // name,               signature,  funcPtr
        {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
        {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
        {"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
                                          (void *)android_media_AudioRecord_setup},
        {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
        {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
        {"native_read_in_byte_array",
                                 "([BIIZ)I",
                                         (void *)android_media_AudioRecord_readInArray<jbyteArray>},
        {"native_read_in_short_array",
                                 "([SIIZ)I",
                                         (void *)android_media_AudioRecord_readInArray<jshortArray>},
        {"native_read_in_float_array",
                                 "([FIIZ)I",
                                         (void *)android_media_AudioRecord_readInArray<jfloatArray>},
        {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
                                           (void *)android_media_AudioRecord_readInDirectBuffer},
        {"native_get_buffer_size_in_frames",
                                 "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},
        {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
        {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
        {"native_set_pos_update_period",
                                 "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
        {"native_get_pos_update_period",
                                 "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
        {"native_get_min_buff_size",
                                 "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
        {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
        {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
        {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
        {"native_disableDeviceCallback", "()V",
                                            (void *)android_media_AudioRecord_disableDeviceCallback},
        {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
                                           (void *)android_media_AudioRecord_get_timestamp},
    };

    从 JNI 的接口声明映射,知道

    native_setup  <-->  android_media_AudioRecord_setup

    接下来分析 android_media_AudioRecord_setup

    在 android_media_AudioRecord_setup 中先判断是否已经存在 nativeRecordInJavaObj 

    如果不存在 nativeRecordInJavaObj 

          lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));

    如果存在

         lpRecorder = (AudioRecord*)nativeRecordInJavaObj;

         把 long 类型指针,转换成 AudioRecord 对象指针,然后 setAudioRecord(env, thiz, lpRecorder);

    在 CPP 中一般使用  static_cast 和 reinterpret_cast 模板转换 type-id 类型。

    这里使用  (AudioRecord*)  强制指针类型转换。 

    // ----------------------------------------------------------------------------
    static jint
    android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
                                    jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
                                    jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
                                    jlong nativeRecordInJavaObj)
    {
    	......
        audio_attributes_t *paa = NULL;
        sp<AudioRecord> lpRecorder = 0;
        audiorecord_callback_cookie *lpCallbackData = NULL;
    
        jclass clazz = env->GetObjectClass(thiz);
        if(clazz == NULL)
        {
            ALOGE("Can't find %s when setting up callback.", kClassPathName);
            return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
        }
    
        // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one.
        if(nativeRecordInJavaObj == 0)
        {
    		......
    
            // create an uninitialized AudioRecord object
            lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
    
            // read the AudioAttributes values
            paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
            const jstring jtags =
                (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
            const char* tags = env->GetStringUTFChars(jtags, NULL);
            // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
            strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
            env->ReleaseStringUTFChars(jtags, tags);
            paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
            paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
            ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
    
            audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
            if(paa->flags & AUDIO_FLAG_HW_HOTWORD)
            {
                flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
            }
            // create the callback information:
            // this data will be passed with every AudioRecord callback
            lpCallbackData = new audiorecord_callback_cookie;
            lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
            // we use a weak reference so the AudioRecord object can be garbage collected.
            lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
            lpCallbackData->busy = false;
    
            const status_t status = lpRecorder->set(paa->source,
                                                    sampleRateInHertz,
                                                    format,        // word length, PCM
                                                    localChanMask,
                                                    frameCount,
                                                    recorderCallback,// callback_t
                                                    lpCallbackData,// void* user
                                                    0,             // notificationFrames,
                                                    true,          // threadCanCallJava
                                                    sessionId,
                                                    AudioRecord::TRANSFER_DEFAULT,
                                                    flags,
                                                    -1, -1,        // default uid, pid
                                                    paa);
    
            if(status != NO_ERROR)
            {
                ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
                      status);
                goto native_init_failure;
            }
        }
        else     // end if nativeRecordInJavaObj == 0)
        {
            lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
    
            // create the callback information:
            // this data will be passed with every AudioRecord callback
            lpCallbackData = new audiorecord_callback_cookie;
            lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
            // we use a weak reference so the AudioRecord object can be garbage collected.
            lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
            lpCallbackData->busy = false;
        }
    	......
        // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
        // of the Java object
        setAudioRecord(env, thiz, lpRecorder);
    
        // save our newly created callback information in the "nativeCallbackCookie" field
        // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
        env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
    
        return (jint) AUDIO_JAVA_SUCCESS;
    
        // failure:
        native_init_failure:
        env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
        env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
        delete lpCallbackData;
        env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
    
        // lpRecorder goes out of scope, so reference count drops to zero
        return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
    }
    

    接下来分析 setAudioRecord 函数,该函数返回 AudioRecord 对象的指针

    // frameworks/base/core/jni/android_media_AudioRecord.cpp
    // 结构体中的成员很重要, 把 AudioRecord 对象, 回调函数 和回调的音频数据保存到属性 jfielID,
    // 并保存到 java 层,提高 JAVA <--> CPP 相互调用的效率。
    struct audio_record_fields_t {
        // these fields provide access from C++ to the...
        jmethodID postNativeEventInJava; //... event post callback method
        jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
        jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
        jfieldID  nativeDeviceCallback;    // provides access to the JNIDeviceCallback instance
    };
    
    
    static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
    {
        Mutex::Autolock l(sLock);
        sp<AudioRecord> old =
                (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
        if (ar.get()) {
            ar->incStrong((void*)setAudioRecord);
        }
        if (old != 0) {
            old->decStrong((void*)setAudioRecord);
        }
        env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
        return old;
    }

     

    分析了 stepup ,接下来分析 start 和 stop 函数,发现在 JNI 接口里调用本地 AudioRecord 对象。

    主要的拾音业务逻辑在 AudioRecord.cpp 中。下一节分析 AudioRecord.cpp 。

    // frameworks/base/core/jni/android_media_AudioRecord.cpp
    // ----------------------------------------------------------------------------
    static jint
    android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
    {
        sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
        if (lpRecorder == NULL ) {
            jniThrowException(env, "java/lang/IllegalStateException", NULL);
            return (jint) AUDIO_JAVA_ERROR;
        }
    
        return nativeToJavaStatus(
                lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
    }
    
    
    // ----------------------------------------------------------------------------
    static void
    android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
    {
        sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
        if (lpRecorder == NULL ) {
            jniThrowException(env, "java/lang/IllegalStateException", NULL);
            return;
        }
    
        lpRecorder->stop();
        //ALOGV("Called lpRecorder->stop()");
    }

     

    展开全文
  • Audio System 二 之 Audio系统框架

    千次阅读 2019-09-29 10:16:02
    Audio System 二 之 Audio系统框架二、Linux Audio系统框架2.1 Application 层2.2 Framework 层2.3 Libraries 层2.4 HAL 层2.5 Tinyalsa 层2.6 Kernel部分2.7 Audio Devices 部分三、Qualcomm 平台 - Audio系统框架...
  • Android8.0 Audio系统之AudioFlinger

    千次阅读 2018-08-13 16:52:40
    继上一篇AudioTrack的分析,本篇我们来看AudioFlinger,AF主要承担音频混合输出,是Audio系统的核心,从AudioTrack来的数据最终都会在这里处理,并被写入到Audio的HAL。 frameworks\av\services\audioflinger\Audio...
  • AudioAudio Session

    千次阅读 2014-07-23 09:17:52
    在iOS开发中,音视频最重要的framework就是...要掌握音频的使用,首先要了解的就是Audio Session了。 1、什么是Audio Session? 先给出官方的英文解释: An audio session is the intermediary between
  • Android8.0 Audio系统之AudioPolicy

    千次阅读 2018-08-14 11:43:43
    上一篇我们跟踪分析了AudioFlinger,它是Audio系统的核心,但是AudioFlinger却不能脱离AudioPolicy工作。AudioPolicy模块承载着音频切换,音轨路由的重要工作,没有它,音频输出将乱套。在分析AudioFlinger的时候...
  • AudioSource 声音源: 常用的成员: Play Pause UnPause Stop PlayOneShot:使用该方法可以在一个AudioSource播放多个声音 PlayClipAtPoint:静态方法,可以在固定地点播放声音     AudioListener...
  • 从Android API AudioRecorder构造过程开始: 构造函数调用了native_setup native函数,在android_media_AudioRecorder.cpp jni文件中,基本的API接口都对应了native接口。 private native final int native_...
  • 理解 Audio 音频系统三 之 AudioFlinger三、AudioFlinger1. AudioFlinger 的启动2. AudioFlinger 构造函数 断断续续写了好多天,终于把《理解 Audio 音频系统二 之 audioserver & AudioPolicyService》 写好...
  • AudioRecord

    千次阅读 2016-11-08 01:14:14
    AudioRecord简介1.AudioRecord与MediaRecorder一样用来录制音频的 2.AudioRecord可以对录制的数据进行实时的处理,比如降噪,除杂,或者将音频进行实时传输,比如IP电话,对讲功能等操作。 3.AudioRecord比...
  • Audio Attributes

    千次阅读 2016-04-19 12:00:00
    Audio Attributes Audio players support attributes that define how the audio system handles routing, volume, and focus decisions for the specified source. Applications can attach attributes to an audi...
  • Unity的声音 —— AudioSource 和 AudioListener AudioSource AudioSource 是 Unity 中的 Audio 组件,其主要用来播放游戏场景中的 AudioClip,AudioClip 就是导入到 Unity 中的声音文件。Unity 可导入的音频...
  • Android Audio AudioEffect

    千次阅读 2011-10-14 07:23:48
    在看AudioSessionId相关代码的时候了解到,共用一个AudioSessionId的AudioTrack和MediaPlayer会共用一个AudioEffect。 今天就来看看AudioEffect是个什么东东。 看这个类的目的,主要是为了搞清楚AudioEffect
  • audio flinger

    千次阅读 2015-09-23 12:45:20
       audioflinger 2013-02-23 17:22 1648人阅读 评论(0) ...如果要转载请注明原创作者是蝈蝈OverViewAudio Policy and Audio Hardware ...1 Audio Hardware Device2 Audio Policy Service3 Audio Po
  • AudioAudio对象的方法和属性

    千次阅读 2016-03-29 17:23:21
    Audio是英文单词,有多种含义:Audio是AU格式一种经过压缩的数字声音格式的详写;Audio是音频的单词;Audio是听觉的单词。听觉声波作用于听觉器官,使其感受细胞兴奋并引起听神经的冲动发放传入信息,经各级听觉中枢...
  • AudioUnit播放

    2018-11-12 10:36:19
    AudioUnit播放 AudioUnit播放PCM 参考: Audio Unit播放PCM文件 AudioUnit实战
  • 右上角不显示音量图标,调节音量无效,设置中音量灰色,不可...$ sudo apt-get remove --purge alsa-base pulseaudio $ sudo apt-get install alsa-base pulseaudio $ sudo apt-get -f install && sudo a...
  • Audio笔记之AudioMixer

    千次阅读 2014-10-19 15:52:33
    // Ensure mConfiguredNames bitmask is initialized properly on all architectures. // The value of 1 = 32. ...AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTracks)  
  • Android Audio 3: Audio的实现

    千次阅读 2016-08-05 10:06:39
    1. 实现HALAudio HAL由三部分构成,这三部分必须实现。 hardware/libhardware/include/hardware/audio.h 表征了一个audio device的主要方法 hardware/libhar
  • E/AudioFlinger( 151): int android::load_audio_interface(const char*, audio_hw_device_t**) couldn't load audio hw module audio.primary (Invalid argument) I/AudioFlinger( 151): loadHwModule() error -22...
  • Android audio 一 源码路径 Android audioAudioRecord 分析上 Android audioAudioRecord 分析下 Android audio 四 AudioTrack 分析上 Android audio 五 AudioTrack 分析下 Android audioAudio...
  • Apowersoft_Audio Device 一个是Apowersoft Audio Device Apowersoft_Audio Device 前往文件夹/Library/Extensions和/System/Library/Extensions 删除Apowersoft_AudioDevice.kext Apowersoft Audio Device ...
  • 【Android Audio 入门 五】--- AudioSW介绍

    千次阅读 2019-09-19 21:42:37
    【Android Audio 入门 五】--- AudioSW介绍五、AudioSW介绍5.1 Audio SW 的作用5.2 Android Audio SW Architecture5.3 Voice Audio SW Architecture5.4 Audio Mixer 五、AudioSW介绍 5.1 Audio SW 的作用 简单...
  • Android是多任务系统,Audio系统是竞争资源。Android2.2之前,没有内建的机制来解决多个程序竞争Audio的问题,2.2引入了称作AudioFocus的机制来管理对Audio资源的竞争的管理与协调。本文主要讲解AudioFocus的使用。 ...
  • Audio笔记之AudioEffect

    千次阅读 2014-11-01 22:55:36
    public AudioEffect(UUID type, UUID uuid, int priority, int audioSession) throws IllegalArgumentException, UnsupportedOperationException, RuntimeException { int[] id = n
  • 理解 Audio 音频系统 一 之 Audio Policy一、学习思路介绍 前面学习了,也转载了挺多大神写的 Audio 文章,学习了比较久, 但看别人的文章终究学习的还是别人的思想,终究还只是看懂了, 估计不久后马上也会...
  • Android Audio 2: Audio相关术语

    千次阅读 2016-08-04 15:37:16
    Audio相关的术语包含广泛运用的通用术语和Android专属的术语。 1.通用术语 Audio通用术语具有传统和符合人们通用习惯的含义。 1.1 数字音频(Digital Audio) 数字音频相关的术语涉及处理那些使用数字形式编码的...
  • 2.2 Audio file 之 Audio信息获取

    千次阅读 2017-09-28 22:40:13
    引入框架 #import AudioToolbox/AudioToolbox.h> 在项目中拖入音频文件 NSString *filePath = [[NSBundle mainBundle]pathForResource:@”” ofType:@”“]; NSURL *audioUrl = [NSURL URLWithString:filePath];...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 78,294
精华内容 31,317
关键字:

audio