这个用例很简单,但其中会有一些重要概念,应注意理解。
注意:要了解AudioTrack Java API的具体信息,需要仔细阅读Android API中的相关文档。阅读API文档,是一个能快速掌握相关知识的好方法。
**AudioTrackAPI使用例子(Java层)**
~~~
//① 根据音频数据的特性来确定所要分配的缓冲区的最小size
int bufsize =
AudioTrack.getMinBufferSize(8000,//采样率:每秒8K个点
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//声道数:双声道
AudioFormat.ENCODING_PCM_16BIT//采样精度:一个采样点16比特,相当于2个字节
);
//② 创建AudioTrack
AudioTrack trackplayer = new AudioTrack(
AudioManager.STREAM_MUSIC,//音频流类型
8000,AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
AudioFormat.ENCODING_PCM_16BIT, bufsize,
AudioTrack.MODE_STREAM//数据加载模式);
//③ 开始播放
trackplayer.play() ;
......
//④ 调用write写数据
trackplayer.write(bytes_pkg, 0,bytes_pkg.length) ;//往track中写数据
......
//⑤ 停止播放和释放资源
trackplayer.stop();//停止播放
trackplayer.release();//释放底层资源
~~~
上面的用例引入了两个新的概念,一个是数据加载模式,另一个是音频流类型。下面进行详细介绍。
1. AudioTrack的数据加载模式
AudioTrack有两种数据加载模式:MODE_STREAM和MODE_STATIC,它们对应着两种完全不同的使用场景。
- MODE_STREAM:在这种模式下,通过write一次次把音频数据写到AudioTrack中。这和平时通过write系统调用往文件中写数据类似,但这种工作方式每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引入延时。为解决这一问题,AudioTrack就引入了第二种模式。
* MODE_STATIC:这种模式下,在play之前只需要把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用量较小,延时要求较高的文件。但它也有一个缺点,就是一次write的数据不能太多,否则系统无法分配足够的内存来存储全部数据。
这两种模式中以MODE_STREAM模式相对常见和复杂,我们的分析将以它为主。
>[info]**注意**:如果采用STATIC模式,须先调用write写数据,然后再调用play。
2. 音频流的类型
在AudioTrack构造函数中,会接触到AudioManager.STREAM_MUSIC这个参数。它的含义与Android系统对音频流的管理和分类有关。
Android将系统的声音分为好几种流类型,下面是几个常见的:
- STREAM_ALARM:警告声
- STREAM_MUSIC:音乐声,例如music等
- STREAM_RING:铃声
- STREAM_SYSTEM:系统声音,例如低电提示音,锁屏音等
- STREAM_VOCIE_CALL:通话声
* * * * *
**注意**:上面这些类型的划分和音频数据本身并没有关系。例如MUSIC和RING类型都可以是某首MP3歌曲。另外,声音流类型的选择没有固定的标准,例如,铃声预览中的铃声可以设置为MUSIC类型。
* * * * *
音频流类型的划分和Audio系统对音频的管理策略有关。其具体作用,在以后的分析中再做详细介绍。在目前的用例中,把它当做一个普通数值即可。
3. Buffer分配和Frame的概念
在用例中碰到的第一个重要函数就是getMinBufferSize。这个函数对于确定应用层分配多大的数据Buffer具有重要指导意义。先回顾一下它的调用方式:
**AudioTrackAPI使用例子(Java层)**
~~~
//注意这些参数的值。想象我们正在一步步的Trace,这些参数都会派上用场
AudioTrack.getMinBufferSize(8000,//每秒8K个点
AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
AudioFormat.ENCODING_PCM_16BIT);
~~~
来看这个函数的实现:
**AudioTrack.java**
~~~
static public int getMinBufferSize(intsampleRateInHz, int channelConfig,
intaudioFormat) {
int channelCount = 0;
switch(channelConfig) {
case AudioFormat.CHANNEL_OUT_MONO:
caseAudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
channelCount = 2;//目前最多支持双声道
break;
default:
return AudioTrack.ERROR_BAD_VALUE;
}
//目前只支持PCM8和PCM16精度的音频数据
if((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
&& (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
return AudioTrack.ERROR_BAD_VALUE;
}
//对采样频率也有要求,太低或太高都不行。
if( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) )
return AudioTrack.ERROR_BAD_VALUE;
/*
调用Native函数,先想想为什么,如果是简单计算,那么Java层做不到吗?
原来,还需要确认硬件是否支持这些参数,当然得进入Native层查询了
*/
int size = native_get_min_buff_size(sampleRateInHz,
channelCount,audioFormat);
if((size == -1) || (size == 0)) {
return AudioTrack.ERROR;
}
else {
return size;
}
}
~~~
Native的函数将查询Audio系统中音频输出硬件HAL对象的一些信息,并确认它们是否支持这些采样率和采样精度。
说明:HAL对象的具体实现和硬件厂商有关系,如果没有特殊说明,我们则把硬件和HAL作为一种东西讨论。
来看Native的native_get_min_buff_size函数。它在android_media_track.cpp中。
**android_media_track.cpp**
~~~
/*
注意我们传入的参数是:
sampleRateInHertz = 8000,nbChannels = 2
audioFormat = AudioFormat.ENCODING_PCM_16BIT
*/
static jintandroid_media_AudioTrack_get_min_buff_size(
JNIEnv*env, jobject thiz,
jintsampleRateInHertz, jint nbChannels, jint audioFormat)
{
intafSamplingRate;
intafFrameCount;
uint32_t afLatency;
/*
下面这些调用涉及了AudioSystem,这个和AudioPolicy有关系。这里仅把它们看成是
信息查询即可
*/
//查询采样率,一般返回的是所支持的最高采样率,例如44100
if(AudioSystem::getOutputSamplingRate(&afSamplingRate) != NO_ERROR) {
return -1;
}
//① 查询硬件内部缓冲的大小,以Frame为单位。什么是Frame?
if(AudioSystem::getOutputFrameCount(&afFrameCount) != NO_ERROR) {
return -1;
}
//查询硬件的延时时间
if(AudioSystem::getOutputLatency(&afLatency) != NO_ERROR) {
return -1;
}
......
~~~
这里有必要插入内容,因为代码中出现了音频系统中的一个重要概念:Frame(帧)。
说明:Frame是一个单位,经多方查寻,最终在ALSA的wiki中找到了对它的解释。Frame直观上用来描述数据量的多少,例如,一帧等于多少字节。1单位的Frame等于1个采样点的字节数×声道数(比如PCM16,双声道的1个Frame等于2×2=4字节)。
我们知道,1个采样点只针对一个声道,而实际上可能会有一或多个声道。由于不能用一个独立的单位来表示全部声道一次采样的数据量,也就引出了Frame的概念。Frame的大小,就是一个采样点的字节数×声道数。另外,在目前的声卡驱动程序中,其内部缓冲区也是采用Frame作为单位来分配和管理的。
OK,继续native_get_min_buff_size函数。
~~~
......
// minBufCount表示缓冲区的最少个数,它以Frame作为单位
uint32_t minBufCount = afLatency / ((1000 *afFrameCount)/afSamplingRate);
if(minBufCount < 2) minBufCount = 2;//至少要两个缓冲
//计算最小帧个数
uint32_tminFrameCount =
(afFrameCount*sampleRateInHertz*minBufCount)/afSamplingRate;
//下面根据最小的FrameCount计算最小的缓冲大小
intminBuffSize = minFrameCount //计算方法完全符合我们前面关于Frame的介绍
* (audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1)
* nbChannels;
returnminBuffSize;
}
~~~
getMinBufSize会综合考虑硬件的情况(诸如是否支持采样率,硬件本身的延迟情况等)后,得出一个最小缓冲区的大小。一般我们分配的缓冲大小会是它的整数倍。
好了,介绍完一些基本概念后,开始要分析AudioTrack了。
- 前言
- 第1章 阅读前的准备工作
- 1.1 系统架构
- 1.1.1 Android系统架构
- 1.1.2 本书的架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.3 工具介绍
- 1.3.1 Source Insight介绍
- 1.3.2 Busybox的使用
- 1.4 本章小结
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 学习JNI的实例:MediaScanner
- 2.3 Java层的MediaScanner分析
- 2.3.1 加载JNI库
- 2.3.2 Java的native函数和总结
- 2.4 JNI层MediaScanner的分析
- 2.4.1 注册JNI函数
- 2.4.2 数据类型转换
- 2.4.3 JNIEnv介绍
- 2.4.4 通过JNIEnv操作jobject
- 2.4.5 jstring介绍
- 2.4.6 JNI类型签名介绍
- 2.4.7 垃圾回收
- 2.4.8 JNI中的异常处理
- 2.5 本章小结
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 属性服务
- 3.3 本章小结
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 关于zygote的总结
- 4.3 SystemServer分析
- 4.3.1 SystemServer的诞生
- 4.3.2 SystemServer的重要使命
- 4.3.3 关于 SystemServer的总结
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService发送请求
- 4.4.2 有求必应之响应请求
- 4.4.3 关于zygote分裂的总结
- 4.5 拓展思考
- 4.5.1 虚拟机heapsize的限制
- 4.5.2 开机速度优化
- 4.5.3 Watchdog分析
- 4.6 本章小结
- 第5章 深入理解常见类
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初识影子对象
- 5.2.2 第二板斧--由弱生强
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 轻量级的引用计数控制类LightRefBase
- 5.2.5 题外话-三板斧的来历
- 5.3 Thread类及常用同步类分析
- 5.3.1 一个变量引发的思考
- 5.3.2 常用同步类
- 5.4 Looper和Handler类分析
- 5.4.1 Looper类分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步关系
- 5.4.4 HandlerThread介绍
- 5.5 本章小结
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函数
- 6.2.2 独一无二的ProcessState
- 6.2.3 时空穿越魔术-defaultServiceManager
- 6.2.4 注册MediaPlayerService
- 6.2.5 秋风扫落叶-StartThread Pool和join Thread Pool分析
- 6.2.6 你彻底明白了吗
- 6.3 服务总管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服务的注册
- 6.3.3 ServiceManager存在的意义
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查询ServiceManager
- 6.4.2 子承父业
- 6.5 拓展思考
- 6.5.1 Binder和线程的关系
- 6.5.2 有人情味的讣告
- 6.5.3 匿名Service
- 6.6 学以致用
- 6.6.1 纯Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小结
- 第7章 深入理解Audio系统
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介绍
- 7.2.2 AudioTrack(Java空间)分析
- 7.2.3 AudioTrack(Native空间)分析
- 7.2.4 关于AudioTrack的总结
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的诞生
- 7.3.2 通过流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 关于AudioFlinger的总结
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的创建
- 7.4.2 重回AudioTrack
- 7.4.3 声音路由切换实例分析
- 7.4.4 关于AudioPolicy的总结
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 题外话
- 7.6 本章小结
- 第8章 深入理解Surface系统
- 8.1 概述
- 8.2 一个Activity的显示
- 8.2.1 Activity的创建
- 8.2.2 Activity的UI绘制
- 8.2.3 关于Activity的总结
- 8.3 初识Surface
- 8.3.1 和Surface有关的流程总结
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI层分析
- 8.3.4 Surface和画图
- 8.3.5 初识Surface小结
- 8.4 深入分析Surface
- 8.4.1 与Surface相关的基础知识介绍
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface对象的创建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介绍
- 8.4.7 深入分析Surface的总结
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的诞生
- 8.5.2 SF工作线程分析
- 8.5.3 Transaction分析
- 8.5.4 关于SurfaceFlinger的总结
- 8.6 拓展思考
- 8.6.1 Surface系统的CB对象分析
- 8.6.2 ViewRoot的你问我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小结
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理与机制分析
- 9.2.1 Netlink和Uevent介绍
- 9.2.2 初识Vold
- 9.2.3 NetlinkManager模块分析
- 9.2.4 VolumeManager模块分析
- 9.2.5 CommandListener模块分析
- 9.2.6 Vold实例分析
- 9.2.7 关于Vold的总结
- 9.3 Rild的原理与机制分析
- 9.3.1 初识Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 关于Rild main函数的总结
- 9.3.6 Rild实例分析
- 9.3.7 关于Rild的总结
- 9.4 拓展思考
- 9.4.1 嵌入式系统的存储知识介绍
- 9.4.2 Rild和Phone的改进探讨
- 9.5 本章小结
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模块分析
- 10.2.2 MSS模块分析
- 10.2.3 android.process.media媒体扫描工作的流程总结
- 10.3 MediaScanner分析
- 10.3.1 Java层分析
- 10.3.2 JNI层分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 关于MediaScanner的总结
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介绍
- 10.4.2 我问你答
- 10.5 本章小结