使用AudioTrack播放PCM音频数据(android)

众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的。MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制。但是该类只能对完整的音频文件进行操作,而不能直接对纯PCM音频数据操作。假如我们通过解码得到PCM数据源,又当如何将它们播放?没错,就是用AudioTrack这个类(MediaPlayer内部也是调用该类进行真正的播放音频流操作)下面这个DEMO演示了如何使用AudioTrack来播放PCM音频数据

废话不多说,先上效果图:

工程代码结构也较为简单:

简单说下思路,先把PCM音频数据从指定的路径文件读到内存,然后给AudioPlayer设置数据源,音频参数等,最后执行播放,暂停,停止等操作

贴上部分类代码片段:

[java] view plaincopy

  1. public class AudioParam {
  2. int mFrequency;                 // 采样率
  3. int mChannel;                   // 声道
  4. int mSampBit;                   // 采样精度
  5. }

[java] view plaincopy

  1. public interface PlayState {
  2. public static final int MPS_UNINIT = 0;             // 未就绪
  3. public static final int MPS_PREPARE = 1;            // 准备就绪(停止)
  4. public static final int MPS_PLAYING = 2;            // 播放中
  5. public static final int MPS_PAUSE = 3;              // 暂停
  6. }

AudioPlayer代码片段如下:

[java] view plaincopy

  1. public class AudioPlayer implements IPlayComplete{
  2. private final static String TAG = "AudioPlayer";
  3. public final static int    STATE_MSG_ID = 0x0010;
  4. private Handler    mHandler;
  5. private AudioParam mAudioParam;                         // 音频参数
  6. private byte[]     mData;                               // 音频数据
  7. private AudioTrack mAudioTrack;                         // AudioTrack对象
  8. private boolean    mBReady = false;                     // 播放源是否就绪
  9. private PlayAudioThread mPlayAudioThread;               // 播放线程
  10. public AudioPlayer(Handler handler)
  11. {
  12. mHandler = handler;
  13. }
  14. public AudioPlayer(Handler handler,AudioParam audioParam)
  15. {
  16. mHandler = handler;
  17. setAudioParam(audioParam);
  18. }
  19. /*
  20. * 设置音频参数
  21. */
  22. public void setAudioParam(AudioParam audioParam)
  23. {
  24. mAudioParam = audioParam;
  25. }
  26. /*
  27. * 设置音频源
  28. */
  29. public void setDataSource(byte[] data)
  30. {
  31. mData = data;
  32. }
  33. /*
  34. *  就绪播放源
  35. */
  36. public boolean prepare()
  37. {
  38. if (mData == null || mAudioParam == null)
  39. {
  40. return false;
  41. }
  42. if (mBReady == true)
  43. {
  44. return true;
  45. }
  46. try {
  47. createAudioTrack();
  48. } catch (Exception e) {
  49. // TODO Auto-generated catch block
  50. e.printStackTrace();
  51. return false;
  52. }
  53. mBReady = true;
  54. setPlayState(PlayState.MPS_PREPARE);
  55. return true;
  56. }

[java] view plaincopy

  1. private boolean mThreadExitFlag = false;                        // 线程退出标志
  2. private int     mPrimePlaySize = 0;                             // 较优播放块大小
  3. private int     mPlayOffset = 0;                                // 当前播放位置
  4. private int     mPlayState = 0;                                 // 当前播放状态
  5. /*
  6. *  播放音频的线程
  7. */
  8. class PlayAudioThread extends Thread
  9. {
  10. @Override
  11. public void run() {
  12. // TODO Auto-generated method stub
  13. Log.d(TAG, "PlayAudioThread run mPlayOffset = " + mPlayOffset);
  14. mAudioTrack.play();
  15. while(true)
  16. {
  17. if (mThreadExitFlag == true)
  18. {
  19. break;
  20. }
  21. try {
  22. int size = mAudioTrack.write(mData, mPlayOffset, mPrimePlaySize);
  23. mPlayOffset += mPrimePlaySize;
  24. } catch (Exception e) {
  25. // TODO: handle exception
  26. e.printStackTrace();
  27. AudioPlayer.this.onPlayComplete();
  28. break;
  29. }
  30. if (mPlayOffset >= mData.length)
  31. {
  32. AudioPlayer.this.onPlayComplete();
  33. break;
  34. }
  35. }
  36. mAudioTrack.stop();
  37. Log.d(TAG, "PlayAudioThread complete...");
  38. }
  39. }

下面来剖析以下如何使用AudioTrack来播放PCM音频数据

首先要构建一个AudioTrack对象:(需要采样率,声道,采样精度参数)

[java] view plaincopy

  1. private void createAudioTrack() throws Exception
  2. {
  3. // 获得构建对象的最小缓冲区大小
  4. int minBufSize = AudioTrack.getMinBufferSize(mAudioParam.mFrequency,
  5. mAudioParam.mChannel,
  6. mAudioParam.mSampBit);
  7. mPrimePlaySize = minBufSize * 2;
  8. Log.d(TAG, "mPrimePlaySize = " + mPrimePlaySize);
  9. //               STREAM_ALARM:警告声
  10. //               STREAM_MUSCI:音乐声,例如music等
  11. //               STREAM_RING:铃声
  12. //               STREAM_SYSTEM:系统声音
  13. //               STREAM_VOCIE_CALL:电话声音
  14. mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
  15. mAudioParam.mFrequency,
  16. mAudioParam.mChannel,
  17. mAudioParam.mSampBit,
  18. minBufSize,
  19. AudioTrack.MODE_STREAM);
  20. //              AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。
  21. //              STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。
  22. //              这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
  23. //              这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。
  24. //              而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,
  25. //              后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
  26. //              这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。
  27. }

然后开一个子线程从缓存区里分块取数据然后写入硬件设备进行播放

[java] view plaincopy

  1. private void startThread()
  2. {
  3. if (mPlayAudioThread == null)
  4. {
  5. mThreadExitFlag = false;
  6. mPlayAudioThread = new PlayAudioThread();
  7. mPlayAudioThread.start();
  8. }
  9. }

AudioTrack里有三个重要方法:

void play()

int write(byte[] audioData, int offsetInBytes, int sizeInBytes) (该方法是阻塞的)

void stop()

从前面那个线程代码可以看出,我们在写数据之前需要先执行 play(),然后才能进行write操作,当数据播放完毕或是线程被外部终止的时候最后调用stop()停止写数据;若执行了play操作但后面却没有执行write操作的话,或是write操作结束后没有调用stop,观察logcat会不断打印提示信息,这是提示我们对以上三个方法的调用要规范

只要大家设置的音频参数和音频数据都是正确的,就能顺畅的播放出声音,本例已经附带了用于测试的音频文件以及参数说明(已测试通过),具体看工程里音频数据这个文件夹下的readme.txt即可.网上有些童鞋反应说audiotrack播放音频不顺畅,如果数据源没问题的话估计是他们的demo里没有连续地执行write操作而导致的,其它的不多说了,觉得有用的童鞋自己写代码看吧。。。喜欢就顶一下吧!

代码链接如下:

https://github.com/dongweiq/study/AudioPlayerDemo

本文着重介绍audiotrack的使用,关于其底层原理,且看这位仁兄的文章:

http://www.cnblogs.com/innost/archive/2011/01/09/1931457.html

我的github地址:https://github.com/dongweiq/study

欢迎关注,欢迎star o(∩_∩)o 。有什么问题请邮箱联系 [email protected] qq714094450

时间: 2024-07-29 11:53:01

使用AudioTrack播放PCM音频数据(android)的相关文章

Android 音视频深入 二 AudioTrack播放pcm(附源码下载)

本篇项目地址,名字是录音和播放PCM,求starhttps://github.com/979451341/Audio-and-video-learning-materials 1.AudioTrack官方说明AudioTrack允许PCM音频缓冲器流到音频接收器进行回放.这是通过"推"的数据对象的信号使用 write(byte[], int, int) and write(short[], int, int) 方法.一个信号可以在两种模式下运行:静态或流.在流模式中,应用程序写一个连续

libvlc —— 播放器示例程序[C++代码实现攫取 RGB图像 和 PCM音频 数据功能]

在我以前的实际项目中,曾利用 libvlc 去解码音视频媒体数据(如 RTSP.本地文件 等),通过其提供的回调函数接口,攫取 RGB图像 进行图像分析,如 人脸识别.运动检测 等一类的产品应用.除此之外,只要提供适当的 MRL,配合选项参数,VLC 还可以进行屏幕录制.摄像头图像采集.麦克风音频采集 等功能. 我在网上参看过很多人提供的示例源码,实现流程都很初潜,只适合当作学习的 Demo 来看,与实际的项目应用还有很多问题要解决.为此,在这里公开我封装 libvlc 的 C++ 类,方便TA

使用WindowsAPI播放PCM音频

这一篇文章同上一篇<使用WindowsAPI获取录音音频>原理具有相似之处,不再详细介绍函数与结构体的参数 1. waveOutGetNumDevs 2. waveOutGetDevCaps 3. waveOutOpen 回调函数 void CALLBACK PlayCallback(HWAVEOUT hwaveout, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); 4. waveOutPrepareHeader 5

使用WindowsAPI实现播放PCM音频的方法

这篇文章主要介绍了使用WindowsAPI实现播放PCM音频的方法,很实用的一个功能,需要的朋友可以参考下 本文介绍了使用WindowsAPI实现播放PCM音频的方法,同前面一篇使用WindowsAPI获取录音音频的方法原理具有相似之处,这里就不再详细介绍具体的函数与结构体的参数,相同的部分加以省略,只介绍主要的功能部分代码.如下所示: 1. waveOutGetNumDevs 2. waveOutGetDevCaps 3. waveOutOpen 回调函数: ? 1 void CALLBACK

【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上)

音视频学习入门技术文章连载: 技术开发故事会连载 [音视频连载-001]基础学习篇-SDL 介绍以及工程配置 [音视频连载-002]基础学习篇-SDL 创建窗口并显示颜色 [音视频连载-003]基础学习篇-SDL 消息循环和事件响应 [音视频连载-004]基础学习篇-SDL 加载图片并显示 [音视频连载-005]基础学习篇-SDL 加载 YUV 文件并显示 [音视频连载-006]基础学习篇-SDL 播放 YUV 视频文件]) 在前面的文章中已经能够利用 SDL 去播放 YUV 视频文件了,接下来

11.3、Libgdx的音频之播放PCM音频

(官网:www.libgdx.cn) audio模块可以提供对音频硬件的直接访问. 音频硬件是通过AudioDevice接口进行的抽象. 以下创建一个新的AudioDevice实例: AudioDevice device = Gdx.audio.newAudioDevice(44100, true); 如果设备无法创建,将返回一个GdxRuntimeException异常. 需要注意的是:在所有的Android设备中延时都非常高,许多设备达到400毫秒的延时. 销毁通过如下代码实现: devic

DirectSound播放PCM(可播放实时采集的音频数据)

前言 该篇整理的原始来源为http://blog.csdn.net/leixiaohua1020/article/details/40540147.非常感谢该博主的无私奉献,写了不少关于不同多媒体库的博文.让我这个小白学习到不少.现在将其整理是为了收录,以备自己查看. 一.DirectSound简介 DirectSound是微软所开发DirectX的组件之一,可以在Windows 操作系统上录音,并且记录波形音效(waveform sound).目前DirectSound 是一个成熟的API ,

最简单的视音频播放示例8:DirectSound播放PCM

本文记录DirectSound播放音频的技术.DirectSound是Windows下最常见的音频播放技术.目前大部分的音频播放应用都是通过DirectSound来播放的.本文记录一个使用DirectSound播放PCM的例子.注:一位仁兄已经提醒我DirectSound已经计划被XAudio2取代了.后来考证了一下发现确有此事.因此在下次更新中考虑加入XAudio2播放PCM的例子.本文仍然记录一下DirectSound这位“元老”. DirectSound简介 DirectSound是微软所

最简单的视频播放示例8:DirectSound播放PCM

本文记录DirectSound播放音频的技术.DirectSound是Windows下最常见的音频播放技术.目前大部分的音频播放应用都是通过DirectSound来播放的.本文记录一个使用DirectSound播放PCM的例子.注:一位仁兄已经提醒我DirectSound已经计划被XAudio2取代了.后来考证了一下发现确有此事.因此在下次更新中考虑加入XAudio2播放PCM的例子.本文仍然记录一下DirectSound这位"元老". DirectSound简介 DirectSoun