当前位置:   article > 正文

Android Audio知识梳理 看完这一篇就够了!

android audio

文章目录
前言
一、Audio基础
1.音频基础属性
2.音频格式
3.音频处理
二、整体架构
1.概述
2.Audio架构
三、重要模块
1.概述
2.AudioTrack
3.AudioRecord
4.AudioManager
5.AudioService
6.AudioSystem
7.AudioPolicyService
8.AudioFlinger
四、项目实例(汽车)
1.前言
2.CarAudio
3.CarAudioManager
4.CarAudioService
总结
前言


Audio是安卓里面非常重要的模块,对于学习安卓开发不管是做APP或是系统层以及BSP的同行都可以进行学习,拓宽知识维度,为以后的工作提供便利。
本文语言通俗易懂,内容由简入繁,看不懂的地方可以反复琢磨,多多动脑思考。

坚持看完哦!

一、Audio基础
1.音频基础属性
音频指人耳可以听到的声音频率在 20HZ~20kHz 之间的声波。

声音的三要素:

1、音量(Volume)
也叫做响度(Loudness),人耳对声音强弱的主观感觉就是响度,响度和声波 振动的幅度有关。

2、音调(Pitch)
人耳对声音高低的感觉称为音调(也叫音频),音调主要与声波的频率有关。声波的频率高,则音调也高。

3、音色(Quality)
不同的发声体由于其材料、结构不同,则发出声音的音色也不同。

2.音频格式
音频格式是指要在计算机内播放或是处理音频文件,是对声音文件进行数、模转换的过程。

常见音频格式:

1、WAV
是微软公司专门为Windows开发的一种标准数字音频文件,该文件能记录各种单声道或立体声的声音信息,并能保证声音不失真。但是,占用空间太大。

2、MIDI
MIDI是Musical Instrument Digital Interface的缩写,又称作乐器数字接口,是数字音乐/电子合成乐器的统一国际标准。

MIDI 传输的不是声音信号, 而是音符、控制参数等指令, 它指示MIDI 设备要做什么,怎么做, 如演奏哪个音符、多大音量等。它们被统一表示成MIDI 消息(MIDI Message) 。

3、MP3
MP3是利用人耳对高频声音信号不敏感的特性,将时域波形信号转换成频域信号,并划分成多个频段,对不同的频段使用不同的压缩率,对高频加大压缩比(甚至忽略信号)对低频信号使用小压缩比,保证信号不失真。从而将声音用1∶10甚至1∶12的压缩率压缩。

这种压缩方式的全称叫MPEG Audio Player3,简称为MP3。

4、AAC
出现于1997年,基于MPEG-2的音频编码技术。由Fraunhofer IIS、杜比实验室、AT&T、索尼等公司共同开发,目的是取代MP3格式。

AAC,全称Advanced Audio Coding,是一种专为声音数据设计的文件压缩格式。与MP3不同,它采用了全新的算法进行编码,更加高效,具有更高的“性价比”。利用AAC格式,可使人感觉声音质量没有明显降低的前提下,更加小巧。苹果ipod、诺基亚手机支持AAC格式的音频文件。

3.音频处理
日常生活中我们听到的声波波形信号都是时间连续的,我们称这种信号为模拟信号,模拟信号需要量化成数字信号(离散、不连续的)以后才能被我们的计算机识别。

1、音频的量化
可以简单分为五个步骤:

(1)模拟信号采集
现实生活中的声音表现为连续的、平滑的波形,其横坐标为时间轴,纵坐标表示声音的强弱。

(2)采样
按照一定的时间间隔在连续的波上进行采样取值。

(3)量化
将采样得到的值进行量化处理,也就是给纵坐标定一个刻度,记录下每个采样的纵坐标的值。

(4)编码
将每个量化后的样本值转换成二进制编码,可以看到模拟信号经过采样、量化、编码后形成的二进制序列就是数字音频信号。

(5)数字信号
将所有样本二进制编码连起来存储在计算机上就形成了数字信号。


2、音频处理参数

(1)采样率
单位时间内对模拟信号的采样次数,也就是采样频率,采样频率越高,声音的还原就越真实越自然,当然数据量就越大。

我们日常生活中常见的采样率:

5kHz:仅能满足人们讲话的声音质量

8KHz:电话所用采样率, 对于人的说话已经足够

22.05KHz:达到 FM 广播的声音品质(适用于语音和中等品质的音乐)

44.1KHz:最常见的采样率标准,理论上的 CD 音质界限,可以达到很好的听觉效果

48KHz:比 CD 音质更加精确一些

对于高于 48KHz 的采样频率人耳已无法辨别出来了,所以在电脑上没有多少使用价值。

(2)采样位数
每个采样点能够表示的数据范围,用多少个 bit 表示。采样位数通常有 8 bits 或 16 bits 两种,采样位数越大,所能记录声音的变化度就越细腻,相应的数据量就越大。8 位字长量化(低品质)和 16 位字长量化(高品质),16 bit 是最常见的采样精度。

采样位数也被叫做采样精度、量化级、量化数据位数等。

比较一下,一段相同的音乐信息,16位声卡能把它分为64K个精度单位进行处理,而8位声卡只能处理256个精度单位, 造成了较大的信号损失,最终的采样效果自然是无法相提并论的。

(3)通道数
为了播放声音时能够还原真实的声场,在录制声音时在前后左右几个不同的方位同时获取声音,每个方位的声音就是一个声道。声道数是声音录制时的音源数量或回放时相应的扬声器数量,有单声道、双声道、多声道。

(4)码率
码率高低直接影响音质,码率高音质好,码率低音质差。

码率就是数据传输时单位时间传送的数据位数,一般我们用的单位是kbps即千位每秒。

就是一种音乐每秒播放的数据量,单位用bit表示,也就是二进制位。 bps就是比特率。一个字节相当于8个二进制位。也就是说128bps的4分钟的歌曲的文件大小是这样计算的:

码率 = 采样率 * 采样位数 * 声道数
(128/8)460=3840kB=3.8MB

(5)帧
音频数据是流式的,本身没有明确的一帧帧的概念,在实际的应用中,为了音频算法处理/传输的方便,一般约定俗成取 2.5ms~60ms 为单位的数据量为一帧音频。这个时间被称之为 “采样时间”,其长度没有特别的标准,它是根据编解码器和具体应用的需求来决定的。

音频帧的播放时间 = 一个AAC帧对应的采样样本的个数 / 采样频率(单位为s)。

音频在量化得到二进制的码字后,需要进行变换,而变换(MDCT)是以块为单位(block)进行的,一个块由多个(120或128)样本组成。而一帧内会包含一个或者多个块。帧的常见大小有960、1024、2048、4096等。一帧记录了一个声音单元,它的长度是样本长度和声道数的乘积。

例如,最常见的音频格式 MP3 的数据通常由两部分组成,一部分为 “ID3” 用来存储歌名、演唱者、专辑、音轨数等信息,另一部分为音频数据。音频数据部分以帧(frame)为单位存储,每个音频都有自己的帧头,每个帧头中存储了采样率等解码必须的信息,所以每一个帧都可以独立于文件存在和播放,这个特性加上高压缩比使得 MP3 文件成为了音频流播放的主流格式。

最后给出一个公式:
文件时长 = (文件总大小 - 头信息)/ (采样频率 * 采样位数 * 通道数 / 8)

3、音频编码
从信息论的观点来看,描述信源的数据是信息和数据冗余之和,即:数据=信息+数据冗余。音频信号在时域和频域上具有相关性,也即存在数据冗余。将音频作为一个信源,音频编码的实质是减少音频中的冗余。

根据编码方式的不同,音频编码技术分为三种:波形编码、参数编码和混合编码。

一般来说,波形编码的话音质量高,但编码率也很高;参数编码的编码率很低,产生的合成语音的音质不高;混合编码使用参数编码技术和波形编码技术,编码率和音质介于它们之间。


PCM:Pulse Code Modulation,脉冲编码调制。
ADPCM:Adaptive Differential Pulse Code Modulation,自适应差分脉冲编码调制。
SB-ADPCM:Sub band Adaptive Differential Pulse Code Modulation,子带-自适应差分脉冲编码调制。
LPC:Linear Predictive Coding,线性预测编码。
CELPC:Code Excited Linear Predictive Coding,码激励线性预测编码。
VSELPC:Vector Sum Excited Linear Predictive Coding,矢量和激励线性预测编码。
RPE-LTP:Regular Pulse Excited-Long Term Predictive,规则脉冲激励长时预测。
LD-CELP:Low Delay-Code Excited Linear Predictive,低时延码激励线性预测。
MPE:Multi-Pulse Excited,多脉冲激励。
PSTN:Public Switched Telephone Network,公共交换电话网。
ISDN:Integrated Services Digital Network,综合业务数字网。

二、整体架构
1.概述
Audio系统在Android中负责音频方面的数据流传输和控制功能,也负责音频设备的管理。这个部分作为Android的Audio系统的输入/输出层次,一般负责播放PCM声音输出和从外部获取PCM声音,以及管理声音设备和设置,注意,解码功能不在这里实现,在android系统里音频视频的解码是 opencore 或 stagefright 完成的,在解码之后才调用音频系统的接口,创建音频流并播放。

1、功能
·音频管理:负责音量调节、音频设备选择、响铃模式选择等;
·声音播放:负责一个音频流的创建、参数设置、播放、暂停、释放;
·声音录音:负责一个录音音轨的创建、管理;
·声音音效:负责控制声音的效果。

(Android 系统对audio的实现是比较复杂的,但实现的方法还是对音频系统的抽象)

2、服务
Audio是安卓系统中一个非常重要的模块,所有音频的操作都要经过它。它不仅为用户提供服务,而且还为应用开发者提供服务。要时刻记住所做的一切开发工作都是为了别人能更好的使用这个模块,服务好别人,并且保证我们的应用和系统能够稳定运行,但做到这些也不是一件简单的事。

从系统开机的那一刻起,Audio系统服务就随之启动,伴随其运行。当被需要时,要求实时做出回应来,给底层和用户一个合理的“交代”。

比如说:用户听歌时觉得音量过小,就要按手机上音量+键,Audio系统需要处理好用户的这次请求,做到准确,迅速。

再比如说,汽车正在播放歌曲,来了导航播报,多媒体需要和导航混音并且降低音量,然后再来蓝牙电话,多媒体和导航都需要停止播放,这些都是需要Audio系统来协调处理。

2.Audio架构

 

android 音频播放从app开始:

在framework层创建播放器

在audio library层做音频流和输出流控制

在Hal层将音频数据写入到输出设备进行声音输出。

其中audio library层是音频处理的核心。

App --> Frameworks --> Audio Library --> HAL

音频播放/录音流程图:


Audio系统的各层次介绍:
1、JAVA接口层

·AudioManager.java
为APP提供音量控制和响铃模式控制等功能,是音频管理器。

·AudioTrack .java
为APP提供管理和播放一个音频流的功能(必须是PCM流,因为不提供解码功能)。

·AudioRecord.java
为APP提供录音功能。

·AudioEffect.java
为APP提供控制音效的功能。

2、JAVA服务层
音频在java层的服务只有AudioService,他实现了IAudioService接口,AudioManager 是这个服务的客户端。

AudioService服务向下调用AudioSystem.java,AudioSystem.java再通过JNI调用到本地层。

Q1:AudioTrack 、AudioRecord和AudioEffect的调用情况?

他们是直接调用本地接口的。

Q2:为什么AudioTrack 、AudioRecord和AudioEffect没有java服务层?

这是因为一般的音频文件比如mp3、ogg等都是压缩的音频流,需要经过解码才能得到pcm数据流,解码是opencore或stagefright完成的,一般是播放器的服务,比如MediaPlayer,先解码音频文件,然后去创建AudioTrack、设置音效等,然后播放音乐。这些内容的实现一般是在本地服务层,所以AudioTrack 、AudioRecord和AudioEffect的服务层在本地层的AudioFlinger里实现就够了。

3、JNI层
Android的Audio部分通过JNI向Java层提供接口,在Java层可以通过JNI接口完成Audio系统的大部分操作。Audio JNI部分的代码路径为:frameworks/base/core/jni。

其中,主要实现的3个文件为:android_media_AudioSystem.cpp、android_media_Audio Track.cpp和android_media_AudioRecord.cpp,它们分别对应了Android Java框架中的3个类的支持:

·android.media.AudioSystem:负责Audio系统的总体控制;

·android.media.AudioTrack:负责Audio系统的输出环节;

·android.media.AudioRecorder:负责Audio系统的输入环节。

提示:
    现在基本都用HIDL代替了。

4、NATIVE层
Native层的代码和逻辑是整个音频模块的核心!!!

Android的Audio系统的核心框架在media库中提供,对上面主要实现AudioSystem、AudioTrack和AudioRecorder三个类,这其实是Audio系统的本地接口层。

AudioFlinger提供了IAudioFlinger类接口,在这个类中,可以获得IAudioTrack和IAudioRecorder两个接口,分别用于声音的播放和录制。AudioTrack和AudioRecorder分别通过调用IAudioTrack和IAudioRecorder来实现。

Native层采用了C/S的架构方式,AudioTrack是属于Client端的,AudioFlinger和AudioPolicyService是属于服务端的,AudioFlinger是唯一可以调用HAL层接口的地方,而AudioPolicyService主要是对一些音频策略的选择和一些逻辑调用的中转。

5、硬件抽象层(HAL)
HAL 定义了音频服务会调用且您必须实现才能使音频硬件正常运行的标准接口

android audio framework中的audio flinger是通过操作audio hal层对间接的对底层设备进行操作的。(音频数据的读写以及各种参数的设定)。

音频 HAL 由以下接口组成:

hardware/libhardware/include/hardware/audio.h 表示音频设备的主函数。

hardware/libhardware/include/hardware/audio_effect.h 表示可应用于音频的效果,如缩混、回音或噪音抑制。

    现在HIDL用的很广泛了,HAL层也不用做一些重复的开发了,减轻了一些负担。

6、驱动层
Audio的驱动是基于Linux标准的音频驱动:OSS(Open Sound System)或者ALSA(Advanced Linux Sound Architecture)驱动程序来实现的。

手机上用的也有用TINYALSA,是ALSA的精简版,主要功能都在。

三、重要模块
1.概述
之前了解了安卓整个Audio系统的分层,整体架构是怎么样的,大概在心里也已经有了一些初步的轮廓。现在开始细化,挑选出几个重要的模块,逐个分析,找出其中的联系,并且知道其工作原理,目的就算达到了。

在这几个重要的类中,有提供给apk开发者使用的接口,还有核心的几个Audio系统服务。

当我们要使用Audio的API时,要回想一下第一章提到过的那几个音频参数的具体含义。

2.AudioTrack
AudioTrack是最灵活、高级的音频API,用它可以实现将原始音频数据直接送到硬件的操作。是管理和播放单一音频资源的类,仅仅能播放已经解码的PCM流,用于PCM音频流的回放。

想使用好这个API播放音频数据,可以分为五个步骤:
1、 配置基本参数
(1)StreamType音频流类型:最主要的几种STREAM

        AudioManager.STREAM_MUSIC:用于音乐播放的音频流。    
        AudioManager.STREAM_SYSTEM:用于系统声音的音频流。    
        AudioManager.STREAM_RING:用于电话铃声的音频流。
        AudioManager.STREAM_VOICE_CALL:用于电话通话的音频流。
        AudioManager.STREAM_ALARM:用于警报的音频流。
        AudioManager.STREAM_NOTIFICATION:用于通知的音频流。
        AudioManager.STREAM_BLUETOOTH_SCO:用于连接到蓝牙电话的手机音频流。
        AudioManager.STREAM_SYSTEM_ENFORCED:在某些国家实施的系统声音的音频流。
        AudioManager.STREAM_DTMF:DTMF音调的音频流。
        AudioManager.STREAM_TTS:文本到语音转换(TTS)的音频流。    

(2)MODE模式(static和stream两种)
AudioTrack.MODE_STREAM:
STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到AudioTrack中。

AudioTrack.MODE_STATIC:
STATIC就是数据一次性交付给接收方。

(3)采样率:mSampleRateInHz
采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)

(4)通道数目:mChannelConfig
声道设置:android支持双声道立体声和单声道。MONO单声道,STEREO立体声.

CHANNEL_OUT_MONO;
CHANNEL_OUT_STEREO。

(5)音频量化位数:mAudioFormat(只支持8bit和16bit两种)
ENCODING_PCM_16BIT;
ENCODING_PCM_8BIT;

采样大小越大,那么信息量越多,音质也越高,现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit足够了。

2、获取最小缓冲区大小
根据采样率,采样精度,单双声道来得到frame的大小。

计算最小缓冲区:
AudioTrack.getMinBufferSize(mSampleRateInHz,mChannelConfig, mAudioFormat);

注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。**

3、创建AudioTrack对象
取到mMinBufferSize后,我们就可以创建一个AudioTrack对象了。它的构造函数原型是:
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode);

构造函数里面的参数我们之前都讲过了,所以创建时根据需求自行指定即可。

4、获取PCM文件,转成DataInputStream
根据存放PCM的路径获取到PCM文件。

    File file = new File(path);
    mDis = new DataInputStream(new FileInputStream(file));

5、开启/停止播放
(1)开始播放
先把音频数据放到AudioTrack获取到的缓冲中。
readCount = mDis.read(MinBufferSize);
先调用AudioTrack.play();
之后就可以写数据了,AudioTrack.write(MinBufferSize, 0, readCount);

(2)停止播放
停止播放音频数据,如果是STREAM模式,会等播放完最后写入buffer的数据才会停止。如果立即停止,要调用pause()方法,然后调用flush方法,会舍弃还没有播放的数据。

(3)释放资源
AudioTrack.release();

3.AudioRecord
看完AudioTrack了,再看AudioRecord的话就会非常简单。
一般情况下录音实现的简单流程如下:(实现不难,不具体说了)
1、创建一个数据流。
2、构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。
3、初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。
4、开始录音。
5、从AudioRecord中读取声音数据到初始化buffer,将buffer中数据导入数据流。
6、停止录音。
7、关闭数据流。

4.AudioManager
AudioManager实现了AudioSystem类与JAVA层系统服务AudioService的封装,主要作用为向应用程序层提供Audio系统的控制接口。提供了丰富的API让开发者对应用的音量和铃声模式等进行控制以及访问。主要内容涉及到音频流、声音、蓝牙、扩音器、耳机等等。

常用功能:
1、音量控制
调音量有两个比较重要的接口。

(1)adjustStreamVolume(int streamType, int direction, int flags);
这个方法主要是用于按键调音,音量+/-。按照规定好的步进值和方向调整指定流的音量大小。

streamType:是你要调节的流。比如说,音乐,铃声,闹钟等。

direction:调节的方向,也就是增大/减小,或者保持不变。(ADJUST_LOWER;
ADJUST_RAISE;ADJUST_SAME)。

flags:常用就是是否在调节音量时显示UI。

(2)setStreamVolume(int streamType, int index, int flags);

这函数是可以直接设置特定流的音量值,主要是用在系统设置中进行调节。

这个函数和上一个就一个参数不同,但是很简单。

index代表的就是设置的音量值。

2、静音控制

(1) setMasterMute(boolean mute, int flags);

这个函数的作用是设置全局静音,一直设置到BSP。

参数一个是mute的布尔值,另一个flags之前说过。

(2)setStreamMute(int streamType, boolean state);
设置指定流静音,其内部静音实现就是在FW把该流的音量设置为0。

3、音频模式/状态
AudioMode用于描述整个Audio部分的当前状态。

            MODE_NORMAL:正常状态(默认)。
            MODE_RINGTONE:响铃状态(来电)。
            MODE_IN_CALL:呼叫电话状态(拨出未接通)。
            MODE_IN_COMMUNICATION:电话状态(接通)。

这些状态的改变会影响音频数据在Audio各输出设备中的路由选择。
还有各种铃声状态也都很常用:

            RINGER_MODE_NORMAL:正常模式,可以听到铃声。
            RINGER_MODE_SILENT:静音模式,无铃声,无震动。
            RINGER_MODE_VIBRATE:震动模式,无铃声,有震动。

4、音频焦点
(1)目的:
AudioFocus机制是为解决系统中Audio资源的竞争问题而引入的。

(2)要求:
需要所有参与Audio竞争的竞争者主动去遵循,Audio Focus机制才能更好的工作。

(3)申请焦点:
现在只说安卓8.0以上的接口。
public int requestAudioFocus(@NonNull AudioFocusRequest focusRequest);

可以看到参数只有一个AudioFocusRequest,那么重点肯定就在这里边了,其中一定设置了许多参数,包括音源类型、监听器等。

所以先看这个AudioFocusRequest。

AudioFocusRequest类是一个封装有关音频焦点请求的信息的类。
看个例子更加直观:

         mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                            .setWillPauseWhenDucked(true)
                            .setAcceptsDelayedFocusGain(true)
                                .setOnAudioFocusChangeListener(mListener, mHandler)
                                .setAudioAttributes(mAttribute)
                            .build();
                            
    AUDIOFOCUS_GAIN:当前声音的唯一的音频来源,持续多长时间未知,结束后也不希望恢复另一个音频流。
    AUDIOFOCUS_GAIN_TRANSIENT:应用程序暂时从当前所有者那里获取焦点,音频播放完后恢复以前的音频播放, 表示请求焦点时已经有人在持有焦点
    AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:和上面的一样临时获取焦点,但他可以在拥有焦点期间,另一个应用可以降低音量继续播放,俗称 躲闪,
    AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:也是临时请求焦点,在拥有焦点期间不希望设备播放任何内容。就是在持有焦点期间,在有请求焦点时禁止把焦点分给其他人。

setWillPauseWhenDucked():声明应用程序在音频回避方面的预期行为,也就是说如果我不希望系统自动给我降低音量,而是想自己暂停音频相关的工作,可以调这个函数取消系统的默认行为。这样通过监听音频焦点变化,来自己处理。

setAcceptsDelayedFocusGain():这个是为了能够延迟获取到焦点的必须条件,但是同时也必须要设置AudioManager.OnAudioFocusChangeListener才能得知何时获取到焦点。

setOnAudioFocusChangeListener():设置音频焦点变化监听器。值得一提的是这个方法有个重载的方法,有一个重载方法有两个参数,第二个参数为Handler对象,看到Handler应该明白了,是为了使用它的消息队列来顺序处理这个回调。

setAudioAttributes(): 这个方法是用来描述app的使用情况。这方法需要传入一个AudioAttributes对象,这个对象也是使用Builder模式来构造。

例如使用AudioAttributes.Builder.setUsage()来描述使用这个音频来干什么,我们可以传入一个AudioAttributes.USAGE_MEDIA来表明用这个音频来作为媒体文件来播放,也可以传入一个AudioAttributes.USAGE_ALARM来表明用这个来作为闹铃。

(4)释放焦点:
public int abandonAudioFocus(OnAudioFocusChangeListener l);
参数就是之前说过的音频焦状态变化监听器。

5.AudioService
AudioService这个系统服务包含或者使用了几乎所有与音频有关的内容,AudioService是音频系统在java层的大本营

1、介绍
(1)音频系统在java层中基本上不参与数据流的,AudioService这个系统服务包含或者使用了几乎所有与音频有关的内容,所以说AudioService是音频系统在java层的大本营。

(2)AudioManager拥有AudioService的Bp端,是AudioService在客户端的一个代理,几乎所有客户端对AudioManager进行的请求,最终都会交由AudioService实现。

(3)AudioService的功能实现依赖于AudioSystem类,AudioSystem无法实例化,它是java层到native层的代理,AudioService通过它与AudioPolicyService以及AudioFlinger进行通信。

2、音量管理
(1)AudioService音量管理的核心是VolumeStreamState,它保存了一个流类型所有的音量信息。

(2)VolumeStreamState保存了运行时的音量信息,而音量的生效则是在底层AudioFlinger完成的。

所以进行音量设置需要做两件事情:更新VolumeStreamState存储的音量值,设置音量到Audio底层系统。

6.AudioSystem
AudioSystem在Audio系统中属于一个承上启下的类,在java和native都有它,所以我们在上层调用的很多功能都经过它,再经过它流转到native服务。

1、介绍
(1)AudioSystem是Audio子系统面向framework的接口,这里面有很多一竿子戳到底的函数。同样,Audio子系统内部也往往使用AudioSystem进行通信,比如AF和APM。

(2)AudioService通过对AudioSystem进行函数调用与Audio系统进行通信。

(3)AudioSystem和AudioFlinger以及AudioPolicyService的双向通信机制。

7.AudioPolicyService
我们知道,在 Audio系统中,核心的两块是 AudioPolicy 和 AudioFlinger,其中 AudioPolicy 相当于军师的角色,专门来制定 Audio 播放时的相关的策略及设定相关的参数,而 AudioFlinger 相当于将军,会根据军师的策略来执行。
所以重点都在这两个服务上,那么就先看AudioPolicyService这个负责“指挥“的服务,最好的办法就是看它启动时都分别干了什么。

启动流程分析:
1、AudioServer
(1)main_audioserver.cpp会编译成可执行文件audioserver,在主线程中,主要是监控子进程的状态,而在它的子进程中分别调用 AudioFlinger::instantiate() 和 AudioPolicyService::instantiate(),分别对AudioFlinger 和 AudioPolicyService 进行初始化。

(2)同样会被它初始化的还有语音识别 SoundTriggerHwService::instantiate()和 VR 语音模块 VRAudioServiceNative::instantiate(),不过这些不是我们目前的重点,后续再看。

具体代码如下:
xref: /frameworks/av/media/audioserver/main_audioserver.cpp

int main(int argc __unused, char **argv)
{
    // TODO: update with refined parameters
    limitProcessMemory(
        "audio.maxmem", /* "ro.audio.maxmem", property that defines limit */
        (size_t)512 * (1 << 20), /* SIZE_MAX, upper limit in bytes */
        20 /* upper limit as percentage of physical RAM */);

    signal(SIGPIPE, SIG_IGN);

    bool doLog = (bool) property_get_bool("ro.test_harness", 0);

    pid_t childPid;
    // FIXME The advantage of making the process containing media.log service the parent process of
    // the process that contains the other audio services, is that it allows us to collect more
    // detailed information such as signal numbers, stop and continue, resource usage, etc.
    // But it is also more complex.  Consider replacing this by independent processes, and using
    // binder on death notification instead.
    if (doLog && (childPid = fork()) != 0) {
        // media.log service
        //prctl(PR_SET_NAME, (unsigned long) "media.log", 0, 0, 0);
        // unfortunately ps ignores PR_SET_NAME for the main thread, so use this ugly hack
        strcpy(argv[0], "media.log");
        sp<ProcessState> proc(ProcessState::self());
        MediaLogService::instantiate();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
           ......
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm = defaultServiceManager();
        ALOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        AudioPolicyService::instantiate();

        // AAudioService should only be used in OC-MR1 and later.
        // And only enable the AAudioService if the system MMAP policy explicitly allows it.
        // This prevents a client from misusing AAudioService when it is not supported.
        aaudio_policy_t mmapPolicy = property_get_int32(AAUDIO_PROP_MMAP_POLICY,
                                                        AAUDIO_POLICY_NEVER);
        if (mmapPolicy == AAUDIO_POLICY_AUTO || mmapPolicy == AAUDIO_POLICY_ALWAYS) {
            AAudioService::instantiate();
        }

        SoundTriggerHwService::instantiate();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }

2、AudioPolicyService::instantiate()
首先注册一个名为 media.audio_policy 的服务,在注册服务过程中会首先调用 AudioPolicyService::onFirstRef() 函数,它是我们 AudioPolicySerivce 启动时的核心代码。

3、AudioPolicyService::onFirstRef()
在该代码中,主要工作如下:
(1)创建了三个AudioCommandThread,名字分别为"ApmTone",“ApmAudio”,“ApmOutput”。

(2)实例化 AudioPolicyClient 对象

(3)初始化 AudioPolicyManager ,传参就是 AudioPolicyClient对象。

(4)AudioPolicyEffects 音效初始化。

void AudioPolicyService::onFirstRef()
{
    {
        Mutex::Autolock _l(mLock);

        // start tone playback thread,用于播放tone音,tone是音调的意思
        mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
        // start audio commands thread,用于执行audio命令
        mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
        // start output activity command thread,用于执行audio输出命令
        mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
        //实例化AudioPolicyClient对象
        mAudioPolicyClient = new AudioPolicyClient(this);
        //实例化AudioPolicyManager对象
        mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
    }
    // load audio processing modules
    //初始化音效相关
    sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
    {
               mAudioPolicyEffects = audioPolicyEffects;
    }

}

4、在 AudioPolicyManager 初始化过程中
(1)解析Audio 配置文件,audio_policy.xml 和 audio_policy.conf,解析配置文件中当前系统所支持的输出设备mAvailableOutputDevices、输入设备mAvailableInputDevices、默认输出设备mDefaultOutputDevice。

(2)调用 config.setDeafult() 初始化 mHwModules ,配置 Module 的名字为 “primary”,配置默认输出设备为 AUDIO_DEVICE_OUT_SPEAKER,默认输入设备为 AUDIO_DEVICE_IN_BUILTIN_MIC。

(3)通过 getName() 加载模块,调用loadHwModule_l 函数初始化 HwModule 模块,调用 openDevice 初始化 hardware 层的audio配置,hardware 层会返回给上层 Audio 操作方法,及初始化 输入链表 streams_input_cfg_list 和输出流链表 streams_output_cfg_list ,

(4)初始化默认音量解级参数,包括 通话、系统、铃声、闹钟、通知、蓝牙、拨号盘等音量等级。

(5)给每一个输入 和 输出设备,都分配一个线程,及对应的输入 和 输出 stream 流,根据 audio类型调用不同的函数(Playback 、 OFFLOAD 、 DIRECT 类型等。

(6) 最终所有的信息保存在 HwModule[ ] 中,将所有的输出设备保存在 mDeviceForStrategy[ ] 中。

    至此,所有的输入设备和输出设备 都有对应的stream及单独的 thread 。
1
5、AudioPolicyEffects 音效初始化

解析audio_effects.conf 文件,得到并加载 系统支持的音效库。初始化各个音效对应的参数,将各音效和 对应的输入 和输出流绑定在一起,这样,当上层要初始化使用音效时,就会在对应的threadloop中调用process_l音效处理函数。创建一个AudioFlinger客户端,将 effect 和 AudioFlinger 客户端绑定在一起。

    注:
    声音通路配置,sources 是声音输信端口,sink 是声音输出端口    
    播放:source(MixerPort)->sink(DevicePort)
    录音:source(DevicePort)->sink(MixerPort)

(借用几张图片)
加载完系统定义的所有音频接口,并生成相应的数据对象,如

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/988364
推荐阅读
相关标签