首页 > 其他分享 >Android音频开发之AudioTrack

Android音频开发之AudioTrack

时间:2023-04-26 14:56:55浏览次数:53  
标签:int AudioTrack 音频 MODE 缓冲区 Android 播放

原文地址 www.jianshu.com

在前两节中分享了Android音频开发之音频基本概念Android音频开发之音频采集,本文分享的是如何使用 AudioTrack 来播放 使用AudioRecord 采集后的 PCM 数据。

  1. 构造 AudioTrack 实例

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) 

在采样 pcm 音频数据需要设置对应的采样率,采样精度,采样的通道数和采样的缓冲区大小,如果播放的音频是使用 AudioRecord 录制的,那么这些参数配置信息需要和 AudioRerord一致,不然播放就会出现奇怪的问题。

1.1 AudioTrack 播放音频时会有两种方式:

音频播放的方式,有两种方式 MODE_STATIC 或者 MODE_STREAM 。

  • MODE_STATIC 预先将需要播放的音频数据读取到内存中,然后才开始播放。
  • MODE_STREAM 边读边播,不会将数据直接加载到内存

1.2 MODE_STREAM 的方式构建 AudioTrack 实例

/**
* 构建 AudioTrack 实例对象
*/
private void createStreamModeAudioTrack() {
    if (audioTrack == null) {
        bufferSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
    }
} 

1.3 MODE_STATIC 的方式构建 AudioTrack 实例

/**
* 构建 AudioTrack 实例对象
*/
//file 就是需要播放的音频文件,这里的buffersize就是文件的大小
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
        44100, AudioFormat.CHANNEL_OUT_STEREO,
        AudioFormat.ENCODING_PCM_16BIT, (int) file.length(), AudioTrack.MODE_STATIC); 
  1. 写入数据

不间断通过 write 方法的写数据给 AudioTrack .

注意: 对于 MODE_STREAM 写入数据,会阻塞,直到写入的数据都传输给 AudioTrack。
对于 MODE_STATIC 会将数据拷贝到缓冲区中,并在该方法返回后执行 play() 方法播放音频数据。

  • int write (byte[] audioData, int offsetInBytes,int sizeInBytes)
  • int write (short[] audioData, int offsetInShorts, int sizeInShorts)
  • int write (float[] audioData, int offsetInFloats, int sizeInFloats,int writeMode)

该方法的返回值:

  • 正确:>=0 该值表示写入的数据量。

  • 错误:<0

    • ERROR_INVALID_OPERATION
    • ERROR_BAD_VALUE
    • ERROR_DEAD_OBJECT
    • ERROR

2.1 两种方式写入数据的区别

  • MODE_STATIC

在 AudioTrack 创建之处,会初始化一个与其相关联的 buffer 缓冲区,这个缓冲区的大小是在构造方法指定的。这个大小表示 AudioTrack 可以播放多久。对于 MODE_STATIC 这种模式下,这个 buffer 的大小就是需要播放的文件或者流的大小。

//写入数据大小 array 就是预先将音频数据加载到array数组中
int writeResult = audioTrack.write(array, 0, array.length);
//检查写入的结果,如果是异常情况,则直接需要释放资源
if (writeResult == AudioTrack.ERROR_INVALID_OPERATION || writeResult == AudioTrack.ERROR_BAD_VALUE
        || writeResult == AudioTrack.ERROR_DEAD_OBJECT || writeResult == AudioTrack.ERROR) {
    //出异常情况
    isPlaying = false;
    release();
    return;
} 
  • MODE_STREAM

使用这种方式是通过将数据写入到缓冲区中,而需要注意写入到这个缓冲区的数据大小,需要确保小于或者等于这个构造 AudioTrack 的缓冲区大小。

AudioTrack 不是 final 类型,也就是说可以使用继承实现自己的功能,但是官方文档表示不建议这样做。

 //边读边播
 byte[] buffer = new byte[bufferSize];
 while (fis.available() > 0) {
     int readCount = fis.read(buffer);
     if (readCount == -1) {
         Log.e(TAG, "没有更多数据可以读取了");
         break;
     }
     int writeResult = audioTrack.write(buffer, 0, readCount);
     if (writeResult >= 0) {
         //success
     } else {
         //fail
         //丢掉这一块数据
         continue;
     }
 } 

这个缓冲区大小可以通过 AudioTrack.getMinBufferSize 来获取

bufferSize = AudioTrack.getMinBufferSize(44000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT); 
  1. 状态判断

3.1 AudioTrack 状态判断

检测一个已经创建好的 AudioTrack 的状态,确保操作在正确初始化之后进行。当需要进行播放前,校验 AudioTrack 是否处于正确的状态。

int getState () 

返回值介绍:

  • STATE_INITIALIZED 表示 AudioTrack 已经是可以使用了。
  • STATE_UNINITIALIZED 表示 AudioTrack 创建时没有成功地初始化。
  • STATE_NO_STATIC_DATA 表示当前是使用 MODE_STATIC ,但是还没往缓冲区中写入数据。当接收数据之后会变为 STATE_INITIALIZED 状态。
//播放时,状态校验
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
    Log.e(TAG, "不能播放,当前播放器未处于初始化状态..");
    return;
} 

3.2 AudioTrack 播放状态

int getPlayState() 
  • PLAYSTATE_STOPPED 停止
  • PLAYSTATE_PAUSED 暂停
  • PLAYSTATE_PLAYING 正在播放
  1. 播放 play

  • 对于 MODE_STATIC 模式,必须要调用 write(...) 相关方法将数据写入到对应的缓冲区中,然后才可以调用 paly(...) 方法进行播放操作。
//先将所有的数据写入到缓冲区
write(...)
//然后在播放
play(..) 
  • 对于 MODE_STREAM 模式
paly(...)

new Thread() {
    public void run() {
        //一系列的 write 操作
        `write(...)`
    }
    
}.start(); 
  1. AudioTrack 状态

5.1 停止播放

对于 MODE_STREAM 模式,如果单是调用 stop 方法, AudioTrack 会等待缓冲的最后一帧数据播放完毕之后,才会停止,如果需要立即停止,那么就需要调用 pause 然后调用 flush 这两个方法,那么 AudioTrack 就是丢缓冲区中剩余的数据。

void stop () 

5.2 暂停

暂停播放,但是缓冲区中没有被播放的数据不会被舍弃,调用 play 方法即可接着播放,

void pause () 

5.3 刷新

刷新正在排队播放的音频数据,调用该方法会将写入到缓冲区但没有被播放的音频数据都会被丢弃。如果是非 STREAM 或者没有执行 pasuse 或者 stop 将不会有任何效果。

void flush() 

5.4 释放

释放本地 AudioTrack 对象。

void release () 

示例代码

public void stop() { 
    if ((audioTrack != null) && (audioTrack.getState() == AudioTrack.STATE_INITIALIZED)) {
        if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_STOPPED) {
            audioTrack.flush();
            audioTrack.stop();
        } 
} 
  1. AudioTrack 和 MediaPlayer 的区别?

  • AudioTrack 只能播放 pcm 原始数据,不能播放视频。

  • MediaPlayer 可以播放视频和音频。

  • AudioTrack 只支持 pcm 原始音频数据。

  • MediaPlayer 支持 mp3,wav,aac...

  • MediaPlayer 在底层会创建指定的格式的解码器,将音频数据转化为 pcm 然后再交给 pcm 去播放。MediaPlayer底层会创建 AudioTrack,将解码后的数据交给 AudioTrack 播放。

  • 每一个音频流对应着一个AudioTrack类的一个实例,
    每个AudioTrack会在创建时注册到 AudioFlinger中,
    由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中进行播放,目前Android同时最多可以创建32个音频流,也就是说,Mixer最多会同时处理32个AudioTrack的数据流。

  1. 参考文档

标签:int,AudioTrack,音频,MODE,缓冲区,Android,播放
From: https://www.cnblogs.com/cps666/p/17356055.html

相关文章

  • Android音频开发之音频采集
    原文地址www.jianshu.comAndroid音频开发之音频采集在Android系统中,一般使用AudioRecord或者MediaRecord来采集音频。AudioRecord是一个比较偏底层的API,它可以获取到一帧帧PCM数据,之后可以对这些数据进行处理。而MediaRecorder是基于AudioRecorder的API(最......
  • 资源解析器!支持喜马拉雅VIP和付费音频解析
    购买了喜马拉雅VIP会员,结果发现收听的资源不支持下载到本地,这就导致自己想用其他设备进行收听VIP音频就受到很大的限制。 比如我女儿使用的毛毛虫点读笔,如果我想让她用毛毛虫点读笔播放喜马拉雅上的资源,就成了一个难题。 如果想解决音频下载的问题,就得借助下面这个音频解析......
  • Android 开发中打印日志
    在Android开发过程中,我们常常需要打印堆栈来定位问题或者调试问题,这里记录一下相关的方法。1.MTK平台1.1preloader 1.2LK 2.高通平台2.1sbl 2.2xbl 2.3abl  3.kernel  4.native层4.1打印堆栈 Android.bpcc_binary{name:"print_s......
  • Xamarin.Android给 EditText 添加焦点的扩展方法
    ///<summary>///定义了一个扩展方法SetFocus,作用是设置EditText控件的焦点,并可以选择全部选中或者仅选中最后一个字符///给EditText添加焦点的扩展方法,可选择是否全部选中///</summary>///<paramname="ed"></param>......
  • Android开发之一:10.0 USB弹窗权限流程解析
    1.新建activity,获取UsbManagerusbManager=(UsbManager)getSystemService(Context.USB_SERVICE)2.获取所以的USB设备HashMap<String,UsbDevice>map=usbManager.getDeviceList()3.过滤别的USB设备,拿到自己USB的USBDevice类,然后请求USB权限,usbManager.requestPermission(us......
  • 音频处理库性能对比:计算mel频谱的速度哪个更快?
    介绍音频信号处理在各种应用中都发挥着重要的作用,如语音识别、音乐信息检索、语音合成等。其中,Mel频谱是一种常用的频域特征表示方法,用于描述人类听觉系统对频率的敏感程度。在深度学习音频领域,mel频谱是最常用的音频特征。在本文中,我们将对四个常用的音频处理库——audioflux、......
  • 直播平台软件开发,一个简单的Android登录实现demo
    直播平台软件开发,一个简单的Android登录实现demo一、登录活动 packagecom.example.login; importandroid.content.Intent;importandroid.os.Bundle;importandroid.text.TextUtils;importandroid.view.View;importandroid.widget.Button;importandroid.widget.EditText......
  • Android源码在线查看网站
    一、aospxrefhttp://aospxref.com/优点:更新速度快缺点:历史版本较少二、androidxrefhttp://androidxref.com/优点:历史版本较多缺点:更新速度慢两者可搭配使用。非常便利三、Google在线源码上面两个的平台存在如下几点问题:搜索关键字困难且不精确,特别是对有括号和“_”的......
  • Android编译系统
    一.概述在Android7.0之前,Android编译系统使用GNUMake描述和shell来构建编译规则,模块定义都使用Android.mk进行定义,Android.mk的本质就是Makefile,但是随着Android的工程越来越大,模块越来越多,Makefile组织的项目编译时间越来越长。因此,在Android7.0开始,Google采用ninja来代......
  • GB/T28181-2022相对2016版“基于TCP协议的视音频媒体传输要求“规范解读和技术实现
    规范解读GB/T28181-2022和GB/T28181-2016规范,有这么一条“更改了附录D基于TCP协议的视音频媒体传输要求(见附录D,2016年版的附录L)。”。本文主要是针对GB/T28181-2022里面提到的“基于TCP协议的视音频媒体传输要求”做相应的接口适配,在此之前,我们先回顾下规范里面针对这部分......