热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android系统Audio框架介绍

音频基础知识声音有哪些重要属性呢?响度(Loudness)响度就是人类可以感知到的各种声音的大小,也就是音量。响度与声波的振幅有直接关系。音调(Pitch)音调与声音的频率有关系,当声音的频率越大时,

音频基础知识

声音有哪些重要属性呢?

  1. 响度(Loudness)

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

  1. 音调(Pitch)

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

  1. 音色(Quality)

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

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

音频采样

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

  1. 样本(Sample)

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

  1. 采样器(Sampler)

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

  1. 量化(Quantization)

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

  1. 编码(Coding)

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

 

奈奎斯特采样理论

“当对被采样的模拟信号进行还原时,其最高频率只有采样频率的一半”。

换句话说,如果我们要完整重构原始的模拟信号,则采样频率就必须是它的两倍以上。比如人的声音范围是2~ 20kHZ,那么选择的采样频率就应该在40kHZ左右,数值太小则声音将产生失真现象,而数值太大也无法明显提升人耳所能感知的音质。

录制过程

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

回放过程

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

Audio框架

  1. APP

厂商根据特定需求自己写的一个音乐播放器软件等等。

  1. Framework

Android也提供了另两个相似功能的类,即AudioTrack和AudioRecorder,MediaPlayerService内部的实现就是通过它们来完成的,只不过MediaPlayer/MediaRecorder提供了更强大的控制功能,相比前者也更易于使用。除此以外,Android系统还为我们控制音频系统提供了AudioManager、AudioService及AudioSystem类。这些都是framework为便利上层应用开发所设计的。

  1. Libraries

framework只是向应用程序提供访问Android库的桥梁,具体功能实现放在库中完成。比如上面的AudioTrackAudioRecorderMediaPlayerMediaRecorder等等在库中都能找到相对应的类。

1、frameworks/av/media/libmedia【libmedia.so】

2、frameworks/av/services/audioflinger【libaudioflinger.so】

3、frameworks/av/media/libmediaplayerservice【libmediaplayerservice.so】

    4.   HAL

从设计上来看,硬件抽象层是AudioFlinger直接访问的对象。这说明了两个问题,一方面AudioFlinger并不直接调用底层的驱动程序;另一方面,AudioFlinger上层模块只需要与它进行交互就可以实现音频相关的功能了。因而我们可以认为AudioFlinger是Android音频系统中真正的“隔离板”,无论下面如何变化,上层的实现都可以保持兼容。

音频方面的硬件抽象层主要分为两部分,即AudioFlinger和AudioPolicyService。实际上后者并不是一个真实的设备,只是采用虚拟设备的方式来让厂商可以方便地定制出自己的策略。抽象层的任务是将AudioFlinger/AudioPolicyService真正地与硬件设备关联起来,但又必须提供灵活的结构来应对变化——特别是对于Android这个更新相当频繁的系统。比如以前Android系统中的Audio系统依赖于ALSA-lib,但后期就变为了tinyalsa,这样的转变不应该对上层造成破坏。因而Audio HAL提供了统一的接口来定义它与AudioFlinger/AudioPolicyService之间的通信方式,这就是audio_hw_device、audio_stream_in及audio_stream_out等等存在的目的,这些Struct数据类型内部大多只是函数指针的定义,是一些“壳”。当AudioFlinger/AudioPolicyService初始化时,它们会去寻找系统中最匹配的实现(这些实现驻留在以audio.primary.*,audio.a2dp.*为名的各种库中)来填充这些“壳”。根据产品的不同,音频设备存在很大差异,在Android的音频架构中,这些问题都是由HAL层的audio.primary等等库来解决的,而不需要大规模地修改上层实现。换句话说,厂商在定制时的重点就是如何提供这部分库的高效实现了。

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

 

Audio 系统代码:

(1)Audio 的Java 部分

frameworks/base/media/java/android/media

与Audio 相关的Java包是android.media,主要包含AudioManager和Audio 系统的几个类。

(2)Audio 的JNI 部分

frameworks/base/core/jni

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

(3)Audio 的框架部分

frameworks/base/include/media/

frameworks/base/media/libmedia/

这部分内容被编译成库libmedia.so,实现Audio系统的核心框架,同时提供了IAudioFlinger 类接口。在这个类中,可以获得IAudioTrack 和IAudioRecorder 两个接口,分别用于声音的播放和录制。AudioTrack 和AudioRecorder 分别调用IAudioTrack 和IAudioRecorder 来实现。IAudioFlinger.h、IAudioTrack.h 和IAudioRecorder.h 这三个接口通过下层来实现。AudioSystem.h、AudioTrack.h 和AudioRecorder.h 是对上层提供的接口,它们既供本地程序调用,也可以通过JNI 向Java 层提供接口。从功能上看,AudioSystem 负责的是Audio 系统的综合管理功能,而AudioTrack 和AudioRecorder 分别负责音频数据的输出和输入,即播放和录制。另外一个接口是IAudioFlingerClient,它作为向IAudioFlinger中注册的监听器,相当于使用回调函数获取IAudioFlinger运行时信息。

(4)Audio Flinger

frameworks/base/libs/audioflinger

这部分内容被编译成库libaudioflinger.so,它是Audio系统的本地服务部分。

(5)Audio 的硬件抽象层接口

hardware/libhardware_legacy/include/hardware/

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系统,输出输入两个类负责数据通道。在各个层次之间具有对应关系:

在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;
};

通过上表比较可以看出,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硬件抽象层。


推荐阅读
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • Android native层服务例子Bp和Bn
    转入android阵地,被各种权限所阻挠,app写个jni各种没有权限,只能开个native服务,本来android的服务& ... [详细]
author-avatar
mikewuhan_689
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有