Android Audio Architecture

Android Audio Architecture

Android Audio Framework Architecture

Android Audio框架介绍

Android Audio System(1):Linux && Android Audio 系统框架

音频基础知识

声音有哪些重要属性呢?

1. 响度(Loudness)
响度就是人类可以感知到的各种声音的大小,也就是音量。响度与声波的振幅有直接关系。

2. 音调(Pitch)
音调与声音的频率有关系,当声音的频率越大时,人耳所感知到的音调就越高,否则就越低。

3. 音色(Quality)
同一种乐器,使用不同的材质来制作,所表现出来的音色效果是不一样的,这是由物体本身的结构特性所决定的。

如何将各种媒体源数字化呢?

image

音频采样

将声波波形信号通过ADC转换成计算机支持的二进制的过程叫做音频采样(Audio Sampling)。采样(Sampling)的核心是把连续的模拟信号转换成离散的数字信号。

图一:

image

图二:

image

样本(Sample)

这是我们进行采样的初始资料,比如一段连续的声音波形。

  • 声道数(channels)

由于音频的采集和播放是可以叠加的,因此,可以同时从多个音频源采集声音,并分别输出到不同的扬声器,故声道数一般表示声音录制时的音源数量或回放时相应的扬声器数量。

单声道(Mono)和双声道(Stereo)比较常见,顾名思义,前者的声道数为1,后者为2

  • 音频帧(frame)

这个概念在应用开发中非常重要,网上很多文章都没有专门介绍这个概念。

音频跟视频很不一样,视频每一帧就是一张图像,而从上面的正玄波可以看出,音频数据是流式的,本身没有明确的一帧帧的概念,在实际的应用中,为了音频算法处理/传输的方便,一般约定俗成取2.5ms~60ms为单位的数据量为一帧音频。(视频和音频应该差不多吧???)

这个时间被称之为“采样时间”,其长度没有特别的标准,它是根据编解码器和具体应用的需求来决定的,我们可以计算一下一帧音频帧的大小:

假设某音频信号是采样率为8kHz、双通道、位宽为16bit,20ms一帧,则一帧音频数据的大小为:

int size = 8000 x 2 x 16bit x 0.02s = 5120 bit = 640 byte

采样器(Sampler)

采样器是将样本转换成终态信号的关键。它可以是一个子系统,也可以指一个操作过程,甚至是一个算法,取决于不同的信号处理场景。理想的采样器要求尽可能不产生信号失真。

  • 采样率(samplerate)

采样就是把模拟信号数字化的过程,不仅仅是音频需要采样,所有的模拟信号都需要通过采样转换为可以用0101来表示的数字信号,示意图如下所示:

图三:

image

蓝色代表模拟音频信号,红色的点代表采样得到的量化数值。

采样频率越高,红色的间隔就越密集,记录这一段音频信号所用的数据量就越大,同时音频质量也就越高。

根据奈奎斯特理论,采样频率只要不低于音频信号最高频率的两倍,就可以无损失地还原原始的声音。

通常人耳能听到频率范围大约在20Hz~20kHz之间的声音,为了保证声音不失真,采样频率应在40kHz以上。常用的音频采样频率有:8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz、96kHz、192kHz等。

  • 奈奎斯特采样理论

“当对被采样的模拟信号进行还原时,其最高频率只有采样频率的一半”。
换句话说,如果我们要完整重构原始的模拟信号,则采样频率就必须是它的两倍以上。

比如人的声音范围是2~ 20kHZ,那么选择的采样频率就应该在40kHZ左右,数值太小则声音将产生失真现象,而数值太大也无法明显提升人耳所能感知的音质。

量化(Quantization)

采样后的值还需要通过量化,也就是将连续值近似为某个范围内有限多个离散值的处理过程。因为原始数据是模拟的连续信号,而数字信号则是离散的,它的表达范围是有限的,所以量化是必不可少的一个步骤。

  • 量化精度(位宽)

上图三中,每一个红色的采样点,都需要用一个数值来表示大小,这个数值的数据类型大小可以是:4bit、8bit、16bit、32bit等等,位数越多,表示得就越精细,声音质量自然就越好,当然,数据量也会成倍增大。

常见的位宽是:8bit 或者 16bit

编码(Coding)

计算机的世界里,所有数值都是用二进制表示的,因而我们还需要把量化值进行二进制编码。这一步通常与量化同时进行。

  • 常见的音频编码方式有哪些?

上面提到过,模拟的音频信号转换为数字信号需要经过采样和量化,量化的过程被称之为编码,根据不同的量化策略,产生了许多不同的编码方式,常见的编码方式有:PCM 和 ADPCM,这些数据代表着无损的原始数字音频信号,添加一些文件头信息,就可以存储为WAV文件了,它是一种由微软和IBM联合开发的用于音频数字存储的标准,可以很容易地被解析和播放。

我们在音频开发过程中,会经常涉及到WAV文件的读写,以验证采集、传输、接收的音频数据的正确性。

  • 常见的音频压缩格式有哪些?

首先简单介绍一下音频数据压缩的最基本的原理:因为有冗余信息,所以可以压缩。

(1) 频谱掩蔽效应: 人耳所能察觉的声音信号的频率范围为20Hz~20KHz,在这个频率范围以外的音频信号属于冗余信号。

(2) 时域掩蔽效应: 当强音信号和弱音信号同时出现时,弱信号会听不到,因此,弱音信号也属于冗余信号。

下面简单列出常见的音频压缩格式:

MP3,AAC,OGG,WMA,Opus,FLAC,APE,M4A,AMR,等等

录制过程

  1. 音频采集设备(比如Microphone)捕获声音信息。
  2. 模拟信号通过模数转换器(ADC)处理成计算机能接受的二进制数据。
  3. 根据需求进行必要的渲染处理,比如音效调整、过滤等等。
  4. 处理后的音频数据理论上已经可以存储到计算机设备中了,比如硬盘、USB设备等等。不过由于这时的音频数据体积相对庞大,不利于保存和传输,通常还会对其进行压缩处理。比如我们常见的mp3音乐,实际上就是对原始数据采用相应的压缩算法后得到的。压缩过程根据采样率、位深等因素的不同,最终得到的音频文件可能会有一定程度的失真,另外,音视频的编解码既可以由纯软件完成,也同样可以借助于专门的硬件芯片来完成。

回放过程

  1. 从存储设备中取出相关文件,并根据录制过程采用的编码方式进行相应的解码。
  2. 音频系统为这一播放实例选定最终匹配的音频回放设备。
  3. 解码后的数据经过音频系统设计的路径传输。
  4. 音频数据信号通过数模转换器(DAC)变换成模拟信号。
  5. 模拟信号经过回放设备,还原出原始声音。

总结(音频处理和播放过程):

图四:

image

Android Audio 系统架构图

图一:

image

图二:

[图片上传失败...(image-5d41d5-1565571781143)]

图二:

image

图三:

[图片上传失败...(image-244252-1565571781143)]

APP

音乐播放器软件等等。

Java Framework

frameworks/base/media/java/android/media

该层使用 android.media API提供的功能开实现与audio硬件设备的交互

Android提供了两个功能相似的类,即AudioTrack和AudioRecorder,MediaPlayerService内部的实现就是通过它们来完成的, 只不过MediaPlayer/MediaRecorder提供了更强大的控制功能,相比前者也更易于使用。

除此以外,Android系统还为我们控制音频系统提供了AudioManager、AudioService及AudioSystem类。这些都是framework为便利上层应用开发所设计的。在内部,此代码调用JNI层与Audio相关的native代码来访问音频硬件。

JNI

frameworks/base/core/jni
frameworks/base/media/jni

生成库libandroid_runtime.so,Audio 的JNI是其中的一个部分

Libraries

framework只是向应用程序提供访问Android库的桥梁,具体功能实现放在库中完成。

比如上面的AudioTrack、AudioRecorder、MediaPlayer和MediaRecorder等等在库中都能找到相对应的类。

1、frameworks/av/media/libmedia【libmedia.so】
2、frameworks/av/services/audioflinger【libaudioflinger.so】
3、frameworks/av/media/libmediaplayerservice【libmediaplayerservice.so】

+ Native framework

frameworks/base/include/media/
frameworks/base/media/libmedia/

提供了与android.media*包同样的功能的底层实现,该模块使用Binder IPC代理来获取media server与audio相关的具体服务

这部分内容被编译成库libmedia.so,实现Audio系统的核心框架,同时提供了IAudioFlinger 类接口。

在这个类中,可以获得IAudioTrack 和IAudioRecorder 两个接口,分别用于声音的播放和录制。AudioTrack 和AudioRecorder 分别调用IAudioTrack 和IAudioRecorder 来实现。IAudioFlinger.hIAudioTrack.hIAudioRecorder.h这三个接口通过下层来实现。AudioSystem.hAudioTrack.hAudioRecorder.h 是对上层提供的接口,它们既供本地程序调用,也可以通过JNI 向Java 层提供接口。

从功能上看
AudioSystem负责的是Audio系统的综合管理功能
AudioTrack 和AudioRecorder 分别负责音频数据的输出和输入,即播放和录制
另外一个接口是IAudioFlingerClient,它作为向IAudioFlinger中注册的监听器,相当于使用回调函数获取IAudioFlinger运行时信息。

+ Binder IPC

frameworks/av/media/libmedia

Binder IPC代理提供了不同进程间的通信功能, 位于frameworks/av/media/libmedia, 以I字母开头

+ Android Audio Manager(Audio管理策略,AudioPolicyService)

Audio policy service is a service responsible for all actions that require a policy decision to be made first, such as opening a new I/O audio stream, re-routing after a change, and stream volume management. This audio policy source code is located in the below path.

  • frameworks/av/services/audiopolicy

The main roles of audio policy service are,

  • keep track of current system state (removable device connections, phone state, user requests etc.,
  • System state changes and user actions are notified to audio policy manager with methods of the audio_policy.

This service loads the audio_policy.conf file from system/etc/ during start which is used to declare the audio devices present on your product. Below is an example of audio_policy.conf file with different supported devices.

global_configuration {
  attached_output_devices AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_SPEAKER
  default_output_device AUDIO_DEVICE_OUT_AUX_DIGITAL
  attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_REMOTE_SUBMIX
}

audio_hw_modules {
  primary {
    outputs {
      primary {
        sampling_rates 48000
        channel_masks AUDIO_CHANNEL_OUT_STEREO
        formats AUDIO_FORMAT_PCM_16_BIT
        devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_ALL_SCO
        flags AUDIO_OUTPUT_FLAG_PRIMARY
      }
      hdmi {
        sampling_rates dynamic
        channel_masks dynamic
        formats AUDIO_FORMAT_PCM_16_BIT
        devices AUDIO_DEVICE_OUT_AUX_DIGITAL
        flags AUDIO_OUTPUT_FLAG_DIRECT
      }
    }
    inputs {
      primary {
        sampling_rates 8000|11025|16000|22050|24000|32000|44100|48000
        channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO
        formats AUDIO_FORMAT_PCM_16_BIT
        devices AUDIO_DEVICE_IN_USB_DEVICE|AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_ALL_SCO
      }
    }
  }
}

From Android 7.0, ASOP introduces a new audio policy configuration file format (XML) for describing the audio topology. Refer this link audio_config.xml file for example XML format config file. Note: Android 7.0 preserves support for using audio_policy.conf; this legacy format is used by default. To use the XML file format, include the build option USE_XML_AUDIO_POLICY_CONF := 1 in device makefile.

+ Media Server & The Audio Flinger

frameworks/av/servers/audioflinger

media server里面包含audio services, 这里包含与HAL层实现实际交互的代码, 这部分内容被编译成库libaudioflinger.so,它是Audio系统的本地服务部分

可以使用以下命令从init.rc文件中启动Media service

service media /system/bin/mediaserver

HAL

hardware/libhardware/include/hardware
hardware/libhardware_legacy/include/hardware_legacy

HAL定义了音频服务调用的标准接口,您必须实现该接口才能使您的音频硬件正常运行。

Android’s audio Hardware Abstraction Layer (HAL) connects the higher-level, audio-specific framework APIs in android.media to the underlying audio driver and hardware.The audio HAL interfaces are located inhardware/libhardware/include/hardware. This section explains how to implement the audio Hardware Abstraction Layer (HAL) for different hard wares.The audio HAL mainly contain two interfaces,

  1. hardware/libhardware/include/hardware/audio.h. Represents the main functions of an audio device.
  2. hardware/libhardware/include/hardware/audio_effect.h. Represents effects that can be applied to audio such as downmixing, echo cancellation, or noise suppression.

Device vendor must implement all interfaces to support audio HAL. Audio Hardware mainly contains basic functionality for:

  • Opening/closing audio input/output streams
  • Audio devices enabling/disabling, like EARPIECE, SPEAKER, and BLUETOOTH_SCO, etc.
  • Set volume for voice call and Mute Mic.
  • Change audio modes including AUDIO_MODE_NORMAL for standard audio playback, AUDIO_MODE_IN_CALL when a call is in progress, AUDIO_MODE_RINGTONE when a ringtone is playing.

Each input/output stream should provide function for

  • data write/read.
  • Get/set Sample rate.
  • Get channels, buffer size information of current stream.
  • Get/set current format, eg. AUDIO_FORMAT_PCM_16_BIT.
  • Put stream into standby mode.
  • Add/remove audio effect on the stream.
  • Get/set parameter to switch audio path.

The audio HAL should implement all the below represented function callbacks.

static int adev_open(const hw_module_t* module, const char* name, hw_device_t** device)
{
    struct audio_device *adev;
    int ret = 0;
    int i,j,k;
    bool found;

    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
        return -EINVAL;

    adev = calloc(1, sizeof(struct udio_device));
    if (!adev)
        return -ENOMEM;

    /* Set HAL API Version*/

    adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
    adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
    adev->hw_device.common.module = (struct hw_module_t *) module;
    adev->hw_device.common.close = adev_close;

    /* Function callbacks*/

    adev->hw_device.init_check = adev_init_check;
    adev->hw_device.set_voice_volume = adev_set_voice_volume;
    adev->hw_device.set_master_volume = adev_set_master_volume;
    adev->hw_device.set_mode = adev_set_mode;
    adev->hw_device.set_mic_mute = adev_set_mic_mute;
    adev->hw_device.get_mic_mute = adev_get_mic_mute;
    adev->hw_device.set_parameters = adev_set_parameters;
    adev->hw_device.get_parameters = adev_get_parameters;
    adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size;
    adev->hw_device.open_output_stream = adev_open_output_stream;
    adev->hw_device.close_output_stream = adev_close_output_stream;
    adev->hw_device.open_input_stream = adev_open_input_stream;
    adev->hw_device.close_input_stream = adev_close_input_stream;
    adev->hw_device.dump = adev_dump;

    /* Sampling rate */
    adev->default_rate = adev->mm_rate;
    pcm_config_mm_out.rate = adev->mm_rate;
    pcm_config_mm_in.rate = adev->mm_rate;
    pcm_config_hdmi_multi.rate = adev->mm_rate;
    pcm_config_esai_multi.rate = adev->mm_rate;

    *device = &adev->hw_device.common;

    return 0;
}

从设计上来看,硬件抽象层是AudioFlinger直接访问的对象。

这说明了两个问题,
AudioFlinger并不直接调用底层的驱动程序;
AudioFlinger上层模块只需要与它进行交互就可以实现音频相关的功能了。

因而我们可以认为AudioFlinger是Android音频系统中真正的“隔离板”,无论下面如何变化,上层的实现都可以保持兼容。

音频方面的硬件抽象层主要分为两部分,即AudioFlinger和AudioPolicyService。

实际上后者并不是一个真实的设备,只是采用虚拟设备的方式来让厂商可以方便地定制出自己的策略。抽象层的任务是将AudioFlinger/AudioPolicyService真正地与硬件设备关联起来,但又必须提供灵活的结构来应对变化——特别是对于Android这个更新相当频繁的系统。

比如以前Android系统中的Audio系统依赖于ALSA- lib,但后期就变为了tinyalsa,这样的转变不应该对上层造成破坏。

因而Audio HAL提供了统一的接口来定义它与AudioFlinger/AudioPolicyService之间的通信方式,这就是audio_hw_deviceaudio_stream_inaudio_stream*out等等存在的目的,这些Struct数据类型内部大多只是函数指针的定义,是一些“壳”。

AudioFlinger/AudioPolicyService初始化时,它们会去寻找系统中最匹配的实现(这些实现驻留在以audio.primary.*,audio.a2dp._为名的各种库中)来填充这些“壳”。

根据产品的不同,音频设备存在很大差异,在Android的音频架构中,这些问题都是由HAL层的audio.primary等等库来解决的,而不需要大规模地修改上层实现。换句话说,厂商在定制时的重点就是如何提供这部分库的高效实现了。

AudioRcorder和AudioTrack是Audio系统对外提供API类;
AudioRcorder主要用于完成音频数据的采集;
AudioTrack则是负责音频数据的输出;
AudioFlinger管理着系统中的输入输出音频流,并承担着音频数据的混合,通过读写Audio硬件实现音频数据的输入输出功能;
AudioPolicyService是Audio系统的策略控制中心,掌管系统中声音设备的选择和切换、音量控制等。

Tinyalsa

源码在external/tinyalsa目录下
Tinyalsa:tinyplay/tinycap/tinymix,这些用户程序直接调用 alsa 用户库接口来实现放音、录音、控制

Kernel Driver

Audio驱动与硬件以及HAL层实现的交互。你可以选择在此级别使用ALSA, OSS, 或者自定义的驱动程序。HAL是与驱动程序无关的。

注意:如果您选择ALSA,我们建议使用external / tinyalsa作为驱动程序的用户部分,因为它具有兼容的许可(标准用户模式库是GPL许可的)。

+ ALSA 和 ASoC

Native ALSA Application:tinyplay/tinycap/tinymix,这些用户程序直接调用 alsa 用户库接口来实现放音、录音、控制
ALSA Library API:alsa 用户库接口,常见有 tinyalsa、alsa-lib
ALSA CORE:alsa 核心层,向上提供逻辑设备(PCM/CTL/MIDI/TIMER/…)系统调用,向下驱动硬件设备(Machine/I2S/DMA/CODEC)
ASoC CORE:asoc 是建立在标准 alsa core 基础上,为了更好支持嵌入式系统和应用于移动设备的音频 codec 的一套软件体系
Hardware Driver:音频硬件设备驱动,由三大部分组成,分别是 Machine、Platform、Codec

+ ASoC

ASoC被分为Machine、Platform和Codec三大部分。

其中的Machine驱动负责Platform和Codec之间的耦合和设备或板子特定的代码。

Platform驱动的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DAI)把音频数据传送给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音信信号。

  • Machine

用于描述设备组件信息和特定的控制如耳机/外放等。

是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出Machine几乎是不可重用的,每个Machine上的硬件实现可能都不一样,CPU不一样,Codec不一样,音频的输入、输出设备也不一样,Machine为CPU、Codec、输入输出设备提供了一个载体。

这一部分将平台驱动和Codec驱动绑定在一起,描述了板级的硬件特征。

主要负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码。

Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。ASoC的一切都从Machine驱动开始,包括声卡的注册,绑定Platform和Codec驱动等等

  • Playtfrom

用于实现平台相关的DMA驱动和音频接口等。

一般是指某一个SoC平台,比如pxaxxx,s3cxxxx,omapxxx等等,与音频相关的通常包含该SoC中的时钟、DMA、I2S、PCM等等,只要指定了SoC,那么我们可以认为它会有一个对应的Platform,它只与SoC相关,与Machine无关,这样我们就可以把Platform抽象出来,使得同一款SoC不用做任何的改动,就可以用在不同的Machine中。实际上,把Platform认为是某个SoC更好理解。

这一部分只关心CPU本身,不关心Codec。主要处理两个问题:DMA引擎和SoC集成的PCM、I2S或AC ‘97数字接口控制。主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DAI)把音频数据传送给Codec进行处理,最终由Codec输出驱动耳机或者是喇叭的音信信号。在具体实现上,ASoC有把Platform驱动分为两个部分:snd_soc_platform_driver和snd_soc_dai_driver。其中,platform_driver负责管理音频数据,把音频数据通过dma或其他操作传送至cpu dai中,dai_driver则主要完成cpu一侧的dai的参数配置,同时也会通过一定的途径把必要的dma等参数与snd_soc_platform_driver进行交互。

  • Codec

用于实现平台无关的功能,如寄存器读写接口,音频接口,各widgets的控制接口和DAPM的实现等

字面上的意思就是编解码器,Codec里面包含了I2S接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line- in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line- out),Codec和Platform一样,是可重用的部件,同一个Codec可以被不同的Machine使用。嵌入式Codec通常通过I2C对内部的寄存器进行控制。

这一部分只关心Codec本身,与CPU平台相关的特性不由此部分操作。在移动设备中,Codec的作用可以归结为4种,分别是:

1、对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号。
2、对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号。
3、对音频通路进行控制,比如播放音乐,收听调频收音机,又或者接听电话时,音频信号在codec内的流通路线是不一样的。
4、对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等。

ASoC对Codec的这些功能都定义好了一些列相应的接口,以方便地对Codec进行控制。ASoC对Codec驱动的一个基本要求是:驱动程序的代码必须要做到平台无关性,以方便同一个Codec的代码不经修改即可用在不同的平台上。

image

ASoC对于Alsa来说,就是分别注册PCM/CONTROL类型的snd_device设备,并实现相应的操作方法集。图中DAI是数字音频接口,用于配置音频数据格式等。

☁ Codec驱动向ASoC注册snd_soc_codec和snd_soc_dai设备。
☁ Platform驱动向ASoC注册snd_soc_platform和snd_soc_dai设备。
☁ Machine驱动通过snd_soc_dai_link绑定codec/dai/platform。

Widget是各个组件内部的小单元。处在活动通路上电,不在活动通路下电。ASoC的DAPM正是通过控制这些Widget的上下电达到动态电源管理的效果。

☁ path描述与其它widget的连接关系。
☁ event用于通知该widget的上下电状态。
☁ power指示当前的上电状态。
☁ control实现空间用户接口用于控制widget的音量/通路切换等。

对驱动开者来说,就可以很好的解耦了:

☁ codec驱动的开发者,实现codec的IO读写方法,描述DAI支持的数据格式/操作方法和Widget的连接关系就可以了;
☁ soc芯片的驱动开发者,Platform实现snd_pcm的操作方法集和DAI的配置如操作 DMA,I2S/AC97/PCM的设定等;
☁ 板级的开发者,描述Machine上codec与platform之间的总线连接, earphone/Speaker的布线情况就可以了。

+ DAPM

DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。

DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc core中完成。

用户空间的应用程序无需对代码做出修改,也无需重新编译,DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或关闭。

+ DPCM

Dynamic PCM

Audio devices

具体的Audio硬件设备。

总结

1、Audio使用JNI和Java对上层提供接口,JNI部分通过调用libmedia库提供的接口来实现。
2、 Audio 本地框架类是libmedia.so的一个部分,这些Audio框架类对上层提供接口,由下层的本地代码去实现。
3、AudioFlinger继承libmeida中的接口,提供实现库libaudiofilnger.so。这部分内容没有自己的对外头文件,上层调用的只是libmedia本部分的接口,但实际调用的内容是libaudioflinger.so。
4、Audio的硬件抽象层提供到硬件的接口,供AudioFlinger调用。Audio的硬件抽象层实际上是各个平台开发过程中需要主要关注和独立完成的部分。

在Android的Audio系统中,无论上层还是下层,都使用一个管理类和输出输入两个类来表示整个Audio系统,输出输入两个类负责数据通道。在各个层次之间具有对应关系:

image

在libhardware_legacy中定义的音频相关的硬件抽象层数据结构legacy_audio_device、legacy_stream_out、legacy_stream_in如下:

音频设备描述符:

struct legacy_audio_device {
    struct audio_hw_device device;
    struct AudioHardwareInterface *hwif;
};

音频输出描述符:

struct legacy_stream_out {
    struct audio_stream_out stream;
    AudioStreamOut *legacy_out;
};

音频输入描述符:

struct legacy_stream_in {
    struct audio_stream_in stream;
    AudioStreamIn *legacy_in;
};
image

通过上表比较可以看出,audio_hw_device和AudioHardwareInterface、audio_stream_out和AudioStreamOut、audio_stream_in和AudioStreamIn定义的接口基本一致,这是为了兼容Android先前版本。

AudioHardwareInterface.cpp负责实现基础类和管理,而AudioHardwareGeneric.cpp、AudioHardwareStub.cpp、AudioDumpInterface.cpp和A2dpAudioInterface.cpp各自代表一种Auido硬件抽象层的实现。

  1. AudioHardwareGeneric.cpp:实现基于特定驱动的通用Audio硬件抽象层,这是一个真正能够使用的Audio硬件抽象层,但是它需要Android的一种特殊的声音驱动程序的支持。
  2. AudioHardwareStub.cpp:实现Audio硬件抽象层的一个桩,这个实现不操作实际的硬件和文件,它所进行的是空操作。
  3. AudioDumpInterface.cpp:实现输出到文件的Audio硬件抽象层,支持Audio的输出功能,不支持输入功能。
  4. A2dpAudioInterface.cpp:实现蓝牙音频的Audio硬件抽象层。

Qualcomm平台 - Audio系统框架

由于接下来的一系列Android && kernel 源码分析都是基于Qualcomm 平台的,十分有必要介绍Qualcomm 平台的Audio 系统框架。硬件平台及软件版本:
☁ Kernel - 3.18
☁ SoC - Qualcomm snapdragon
☁ CODEC - WCD9335
☁ Machine - msm8996
☁ Userspace - tinyalsa

Qualcomm Audio系统总体框架图

image

ASoC driver

ALSA 片上系统 (ASoC) 驱动程序将音频系统分为四个组成部分Machine driver、Platform driver、CPU driver、Codec driver。

Machine driver

将平台、CPU 和编解码驱动程序整合在一起
kernel/sound/soc/msm/.c
定义Frontend (FE) and Backend (BE), Digital Audio Interface (DAI) links

Platform driver

包含用于流数据传输与路由的平台特定的控件(control), 细分为 FE 和 BE 平台驱动程序

  • FE

Audio – 实例化 PCM 播放和录制会话;借助 ASM 接口,将 PCM 数据从用户空间传输到 DSP 进行播放
以及从 DSP 传输到用户空间进行录制 – 在 kernel/sound/soc/msm-pcm-q6-v2.c 中实现
Voice – 初始化/取消初始化语音呼叫设置 – 在 kernel/sound/soc/msm-pcm-voice-v2.c 中实现
VoIP – 初始化/取消初始化 MVS 接口以传输自/至 DSP 的 PCM 数据 – 在kernel/sound/soc/msm- pcmvoip-v2.c中实现
Compressed offload – 支持将压缩数据发送到 DSP 进行压缩分流播放 – 在 kernel/sound/soc/msm- compress-q6-v2.c 中实现

  • BE

路由 – 执行音频路由任务 – 在 /kernel/sound/soc/msm-pcm-routing-v2.c 中实现

CPU driver

  • FE

向 ASoC 框架提供关于 FE PCM 设备的信息
ASoC 框架与平台驱动程序提供的路由表共同将 PCM 播放/捕获从 FE 传递至 BE
没有针对播放和录制的内置逻辑
定义 FE CPU DAI – 在 kernel/sound/soc/msm/msm-dai-fe.c 中实现

  • BE

要在初始化 PCM 播放/捕获时激活所需音频硬件端口,则配置 DSP AFE 模块
定义 BE CPU DAI – 在 kernel/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c 中实现

Codec driver

与平台无关,其中包含音频控制、音频接口功能、编解码器 DAPM 定义以及编解码器输入输出功能
此外,实现 MBHC 状态机,用于检测有线耳机插入/拔出、附件类型、连接器类型和多按钮检测

DSP driver

image

ASM(Audio Stream Manager)

 用于与 DSP ASM 模块通信的接口
 提供将 PCM 数据路由至 DSP 的机制,支持按数据流进行后期处理/预处理

ADM(Audio Device Manager)

 允许在 DSP 中使用 ADM 服务
 配置 COPP 和路由矩阵
 与音频校准数据库 (ACDB) 进行通信,使用正确的校准数据配置 COPP
 将 ASM 会话 ID 路由至 ADM 会话

AFE(Audio Front-End)

 允许在 DSP 中使用 AFE 服务
 激活/禁用音频硬件端口
 子系统管理器 – 发生 MDSP 复位事件时,通知音频和语音驱动程序关闭待处理
会话、执行清理操作并等待一个指示 MDSP 已启动的事件

APR(Asynchronous Packet Router)

 为处理器间通信提供异步框架
 用于与 Hexagon 和调制解调器处理器进行通信
 Image loader PIL – 载入 MDSP 图像

User Space

Audio Hardware Abstraction Layer (AHAL)

 通过 tinyALSA 将 AudioFlinger调用映射至ASoC 驱动程序的硬件抽象层。

ACDB loader

 检索特定设备的校准信息,并写入 PMEM。ACDB 驱动程序在启动过程中分配该 PMEM。在设备切换时,此校准将被发送到 DSP。

tinyALSA

 连接至内核 ASoC 驱动程序的接口,供音频 HAL 使用。提供用于音频流和设备管理的基本 PCM 和混音控件 API。

Audio route

 此模块会从一个 .xml 文件读取 ALSA 混音控件,并根据音频HAL 所选的设备设置混音控件。

Concurrency Manager

 在MSM8x10中,视频解码和编码在DSP中完成; 因此,有对可支持的并发性有一些限制。
 MSM8x10中引入的并发管理器管理并发性可以支持涉及语音和音频的不同用例

Multimedia framework – Stagefright

 支持标准音频格式的播放/录制
 与解码器/编码器库以及 OpenMAX IL 组件通信,以便进行解码和编码

Audio service

 由系统服务器启动并由服务管理器管理的运行时服务之一
 意图注册;当从各种应用程序(HDMI、蓝牙等)接收到这些意图时,通知音频系统

AudioFlinger

 通过 libaudio 接口、蓝牙 A2DP 接口管理所有音频输出/输入设备
 将多个音频流处理为单一的 PCM 音频;混合后的输出被传送到输出设备
 播放音乐流时的音量

Audio Policy Manager (APM)

 定义多个音频用例之间的并发规则
 用例示例 – 电话通话、音乐播放、系统声音和通知
 定义播放的音频(例如:语音、播放、铃声)以及播放的设备(蓝牙、扬声器
和耳机)
APM 用途
 管理各种输入输出设备接口
 管理各种输入输出设备,例如:麦克风、扬声器、耳机、听筒、A2DP、蓝牙 SCO
 基于音频流、模式和方法选择和定义适当的路由策略
 管理每个音频流的音量/静音设置(在它们激活或禁用时)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容