AudioTrack在Android系统中是用于PCM数据的混音、播放,并不涉及到音频的解码。因此MP3这类经过编码的音频格式文件不能直接通过AudioTrack正确地播放,AudioTrack只能播放PCM格式的音频数据,如wav格式的音频。

AudioTrack播放音频的实例如下:

    AudioTrack audio = new AudioTrack(
AudioManager.STREAM_MUSIC, // stream mode
32000, // sample rate
AudioFormat.CHANNEL_OUT_STEREO, // single or stereo
AudioFormat.ENCODING_PCM_16BIT, // bit format
AudioTrack.MODE_STREAM
);
audio.start();
byte[] buffer = new buffer[4096];
int count;
while(true)
{
audio.write(buffer, 0, 4096);
}

共有三个步骤:

  1. 构建AudioTrack对象,并且把PCM的参数传到对象里面
  2. 调用start
  3. 调用write

1. 构建AudioTrack对象

在AudioTrack构造方法内部仅调用了一个set函数

AudioTrack::AudioTrack(
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
const sp<IMemory>& sharedBuffer,
audio_output_flags_t flags,
callback_t cbf,
void* user,
int notificationFrames,
int sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
int uid)
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT)
{
mStatus = set(streamType, sampleRate, format, channelMask,
0 /*frameCount*/, flags, cbf, user, notificationFrames,
sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, uid);
}

set方法内部主要做了什么?

status_t AudioTrack::set(
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
int frameCountInt,
audio_output_flags_t flags,
callback_t cbf,
void* user,
int notificationFrames,
const sp<IMemory>& sharedBuffer,
bool threadCanCallJava,
int sessionId,
transfer_type transferType,
const audio_offload_info_t *offloadInfo,
int uid)
{ audio_io_handle_t output = AudioSystem::getOutput(
streamType,
sampleRate, format, channelMask,
flags,
offloadInfo); if (cbf != NULL) {
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
} // create the IAudioTrack
status_t status = createTrack_l(streamType,
sampleRate,
format,
frameCount,
flags,
sharedBuffer,
output,
0 /*epoch*/);

除了对参数的预处理以及保存之外,中间最主要的功能有三个:

  1. getOutput,获取输出设备并打开该设备
  2. 创建AudioTrackThread,并执行该线程
  3. 创建音轨,实际上是创建用户与Android系统的共享内存,用于传输音频数据

由于getOutput与createTrack_l的篇幅较大,我会在后面的章节在分析,这里分析一下AudioTrackThread。

AudioTrackThread主要用于反馈处理,会根据不同的buffer状态进行不同的操作,其中buffer状态包括下面几种:

  1. buffer的position有增长,说明buffer正在被填充;
  2. remainFrames减少说明buffer正在被消耗,即buffer内的Audio数据已被混音;
  3. loop_cycle说明buffer需要循环播放;
  4. loop_final说明buffer已经播放完毕;
  5. etc.

对于不同的状态,会调用到回调函数mCbf,该函数在set的时候被传入。回调函数mCbf的作用还是很大的,比如说能通知应用程序当前的播放状态,也能根据当前的状态继续进行下一步的操作。像AwesomePlayer中的音频处理模块AudioPlayer就能通过mCbf的回调自动填充buffer。

bool AudioTrack::AudioTrackThread::threadLoop()
{ nsecs_t ns = mReceiver.processAudioBuffer(this); } // ------------------------------------------------------------------------- nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
{ mCbf(...); }

2. 调用start

start方法是触发混音线程对当前音轨进行混音并输出,篇幅较大,会在后面章节分析

3. 调用write

write方法需要获得刚刚所创建的音轨,并且把PCM数据往音轨写入。由于音轨的大小有限,write也很有可能一次性不能写入全部数据,因此需要循环调用write方法

// -------------------------------------------------------------------------

ssize_t AudioTrack::write(const void* buffer, size_t userSize)
{ while (userSize >= mFrameSize) {
audioBuffer.frameCount = userSize / mFrameSize; status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); memcpy(audioBuffer.i8, buffer, toWrite);
}
}

需要注意的就是AudioTrack除了初始化之外,write只是往音轨内写入PCM数据,这是Audio数据流动的第一步。

[Android] AudioTrack实例的更多相关文章

  1. Android HTTP实例 使用GET方法和POST方法发送请求

    Android HTTP实例 使用GET方法和POST方法发送请求 Web程序:使用GET和POST方法发送请求 首先利用MyEclispe+Tomcat写好一个Web程序,实现的功能就是提交用户信息 ...

  2. Android HTTP实例 发送请求和接收响应

    Android HTTP实例 发送请求和接收响应 Android Http连接 实例:发送请求和接收响应 添加权限 首先要在manifest中加上访问网络的权限: <manifest ... & ...

  3. Android图像处理实例教程

    Android图像处理实例教程 原始出处 http://vaero.blog.51cto.com/4350852/856750

  4. 一个简单的Android小实例

    原文:一个简单的Android小实例 一.配置环境 1.下载intellij idea15 2.安装Android SDK,通过Android SDK管理器安装或卸载Android平台   3.安装J ...

  5. android本地数据库,微信数据库WCDB for Android 使用实例

    android本地数据库,微信数据库WCDB for Android 使用实例 Home · Tencent/wcdb Wikihttps://github.com/Tencent/wcdb/wiki ...

  6. 【转】 Android常用实例—Alert Dialog的使用

    Android常用实例—Alert Dialog的使用 AlertDialog的使用很普遍,在应用中当你想要用户做出“是”或“否”或者其它各式各样的选择时,为了保持在同样的Activity和不改变用户 ...

  7. JSON的android应用实例

    JSON的android应用实例 Json在线解析器 下面是直接通过JUnit来测试直接通过API来解析Json数据 1.普通键值对象 2.Json数组对象 3.Json数组对象

  8. Android开发实例之miniTwitter登录界面的实现

    原文: http://www.jizhuomi.com/android/example/134.html 本文要演示的Android开发实例是如何完成一个Android中的miniTwitter登录界 ...

  9. android 教程实例系列

    用户界面部分学起来还真是无处下手哇,总不能一个控件发一篇文吧,略有点费时间啊...这个难道不是边用边学才给力吗..所以我打算从最实用的Button开始下手. 先贴几个链接,好东西: android用户 ...

随机推荐

  1. 代码对齐 分类: C#小技巧 2014-04-17 14:45 166人阅读 评论(0) 收藏

    开发项目时,为了是代码层次清晰.美观,常常需要调整多行,使之对齐.在网上也看到一些方法,感觉不好用,偶尔发现一个小技巧. (1)多行代码同时右移 同时选中几行,按"Tab"键,就会 ...

  2. Ubuntu中使用终端运行Hadoop程序

    接上一篇<Ubuntu Kylin系统下安装Hadoop2.6.0> 通过上一篇,Hadoop伪分布式基本配好了. 下一步是运行一个MapReduce程序,以WordCount为例: 1. ...

  3. Java学习的随笔(一)对象概念、this指针、权限修饰符

    最近在看<Java编程思想>,下面按照最近看书的顺序梳理一下心得,由于是初次学习,大部分心得是摘抄自书中: 1. Java中,每个变量都是一个对象. 在创建时首先在内存的堆栈中创建一个该对 ...

  4. url传递中文的解决方案

    本文转载:http://www.cnblogs.com/ghd258/archive/2005/10/23/260241.html url传递中文的解决方案 1.设置web.config文件. < ...

  5. Textview 文本旋转,倾斜

    有时候Android自带的控件无法满足我们的某些要求,这时就需要我们自定义控件来实现这些功能.比如需要一个TextView里的字倾斜一定的角度,就需要自定义TextView. 代码如下: ? 1 2 ...

  6. 【下载分】C语言for循环语句PK自我活动

    想了解自己C语言for语句的掌握程度吗?敢和自己PK较量一番吗?參加"C语言for循环语句PK自我活动",仅仅要成绩70分以上.就可赢得CSDN下载分. 12道题目题库动态读取,每 ...

  7. select菜单实现二级联动

    <tr> <th>执行人<b>*</b></th> <td> <select name="jdcld.ZXDW& ...

  8. Python访问sqlite3数据库取得dictionary的正路!

    [引子] 很多人都知道,Python里是内置了很好用的sqlite3的.但这个库有个缺陷,在执行fetchall()/fetchone()等方法后,得到的是一个tuple.以前吧,做自己的小项目,tu ...

  9. PHPMailer中文说明

    PHPMailer中文说明 A开头: $AltBody --属性出自:PHPMailer ::$AltBody文件:class.phpmailer .php说明:该属性的设置是在邮件正文不支持HTML ...

  10. POJ 2049 Finding Nemo

    Finding Nemo Time Limit: 2000MS   Memory Limit: 30000K Total Submissions: 8631   Accepted: 2019 Desc ...