`

Android开发:使用AudioTrack播放PCM音频数据【附源码】

 
阅读更多

http://www.linuxidc.com/Linux/2012-06/63186.htm

 

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

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


 

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


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

贴上部分类代码片段:

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

 

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

 

AudioPlayer代码片段如下:

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

 

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

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

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

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

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

  1.   
  2. private   void  startThread()  
  3. {  
  4.      if  (mPlayAudioThread ==  null )  
  5.     {  
  6.         mThreadExitFlag =  false ;  
  7.         mPlayAudioThread =  new  PlayAudioThread();  
  8.         mPlayAudioThread.start();  
  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操作而导致的,其它的不多说了,觉得有用的童鞋自己写代码看吧。。。喜欢就顶一下吧!

  • 大小: 38.8 KB
  • 大小: 30.6 KB
分享到:
评论
2 楼 u011250186 2015-01-23  
多谢楼主分享,感激不尽
1 楼 nicky12 2013-04-01  
这个真的不错   谢了   linux公社那个 ftp上面没有了

相关推荐

    android AudioTrack播放PCM音频文件 rm格式视频.rar

    android基于AudioTrack播放PCM音频文件的源码实例,播放扩展名为rm格式的文件,不使用android 内置的Audio和video来播放PCM音频数据,通过通过解码得到PCM数据源,使用AudioTrack类实现音频文件的播放,本源码就是...

    android使用AudioTrack低延迟播放音频文件.rar

    `AudioTrack`类是Android提供的一种高级音频播放接口,它允许开发者直接控制音频缓冲区,从而实现更低的延迟播放。下面将详细探讨如何使用`AudioTrack`来实现这一目标。 首先,`AudioTrack`的创建需要几个关键参数...

    Android 音频降噪源码

    在降噪过程中,通常会使用AudioRecord来捕获原始音频数据,然后通过自定义的降噪算法对数据进行处理,最后使用AudioTrack将处理后的音频数据播放出来。 2. **WebRTC的音频引擎** WebRTC的音频引擎(Audio Engine)...

    Android代码-录音播放和音频格式转换pcm-binbin-pcmpcm-wav.zip

    5. **源码实现**:这个压缩包中的源码示例很可能展示了如何使用Android SDK中的`AudioRecord`和`AudioTrack`类进行录音和播放操作,并且可能包含了处理PCM、BIN、ADPCM和WAV格式转换的函数或类。例如,它可能包含...

    AudioDemo:音频录制播放demo

    AudioDemoAndroid 音视频开发(二):使用 AudioRecord 采集音频PCM并保存到文件:Android 音视频开发(三):使用 AudioTrack 播放PCM音频:使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放,并实现...

    android 音频录音(pcm),播放(pcm,bin)转换(pcm-bin,pcm-wav,bin-pcm)

    在Android上,播放PCM或BIN音频文件通常需要使用`AudioTrack`类。对于PCM数据,开发者需要确保采样率、位深度和通道数与录音时保持一致。而对于BIN文件,由于可能缺少元数据,可能需要额外的处理步骤来确定正确的...

    Android+FFmpeg+OpenSL ES音频解码播放

    - 解码音频:使用libavcodec对音频流进行解码,生成原始的PCM数据。 - 数据转换:根据OpenSL ES的要求,可能需要将解码后的PCM数据转换为特定的格式和采样率。 - 填充缓冲区:将转换后的PCM数据填充到OpenSL ES的...

    android 录音机 源码

    8. **播放音频**:通过`AudioTrack.write()`写入数据,然后调用`AudioTrack.play()`开始播放。 9. **停止播放**:调用`AudioTrack.stop()`停止播放,释放资源。 在实际开发中,还需要关注权限问题。确保在...

    安卓对讲机录音机相关-录音播放和音频格式转换pcm-binbin-pcmpcm-wav.zip

    2. **音频播放**:对于PCM数据的播放,Android提供了`AudioTrack`类。你需要创建一个`AudioTrack`实例,传入相应的PCM数据、采样率、位深度和声道数等信息,然后调用`play()`方法开始播放。注意,PCM数据通常是裸...

    Android 媒体播放源码

    AudioTrack用于将PCM数据推送到音频硬件进行播放,而AudioRecord则用于从麦克风捕获音频数据。 八、性能优化 为了优化媒体播放性能,开发者可以考虑使用硬件解码、异步准备、预缓存策略等技术。同时,注意处理好...

    Android音频实时传输与播放-客户端

    编码过程涉及将原始PCM音频数据转换为AMR格式,这通常通过Android的MediaCodec API实现。 2. **音频录制**:Android提供了AudioRecord类,用于从麦克风捕获音频流。开发者需要设置适当的采样率、通道数和位深度,...

    安卓Android源码——语音压缩,android开发语音功能较多使用的时候,压缩大小50%.zip

    在Android开发中,音频文件通常使用不同的编码格式进行压缩,如PCM(脉冲编码调制)、AAC(高级音频编码)、MP3等。这些编码方式能够有效减少音频数据的大小,同时保持可接受的声音质量。 2. Android MediaCodec ...

    Android 电子钢琴源码_电子钢琴_android模拟钢琴小程序源码_android源码_钢琴代码_钢琴_

    同时,音高和音量的控制也是关键,可能涉及到对PCM音频数据的处理。 此外,音效的实时合成也是一个挑战。开发者可能会使用OpenSL ES,这是一个高性能的音频API,能够处理复杂的音频处理任务,如音频合成、滤波等。...

    安卓Android源码——语音压缩,android开发语音功能较多使用的时候,压缩大小50%.rar

    本资源"安卓Android源码——语音压缩,android开发语音功能较多使用的时候,压缩大小50%"提供了一套针对语音数据进行压缩的解决方案,其主要目标是减少音频文件的大小,以便更高效地存储和传输。 首先,让我们了解...

    android音频编解码事例Demo

    LAME(Low-Average-Mean Error)是一个高效的MP3编码器,它能够将原始的音频数据(如PCM)转换为高质量的MP3音频文件。LAME库提供了一套API,使得开发者可以在不同平台上,包括Android,方便地集成MP3编码功能。 2...

    Android应用源码之audio1(_audio.zip

    1. **AudioTrack**: 这是Android SDK提供的一个类,用于播放音频数据。开发者可以创建AudioTrack实例,传入音频缓冲区,然后调用play()方法来播放声音。源码中可能涉及到设置音频格式(如采样率、位深度、通道数)、...

    Android高级应用源码-Android电子麦克风.zip

    3. **AudioTrack使用**:与AudioRecord相对,AudioTrack负责将音频数据播放出来。同样需要设置音频格式参数,如采样率、通道配置和音频格式(如PCM)。调用write()方法将音频数据写入播放队列,然后调用play()方法...

    android使用speex库对音频流进行编解码

    同样需要初始化解码器状态,然后将编码数据输入解码器,得到解码后的PCM数据,最后通过`AudioTrack`类播放音频。 5. **VoIP通信** 在实现VoIP(Voice over IP)通话时,编解码后的音频数据通常会通过网络发送到另...

    Android应用源码之语音压缩,android开发语音功能较多使用的时候,压缩大小50%.zip

    5. **MediaRecorder配置**:使用MediaRecorder时,开发者需要设置音频源、输出格式、编码器、采样率、声道数等参数。例如,如果选择AAC编码,可以设定`setAudioEncoder(MediaRecorder.AudioEncoder.AAC)`。 6. **...

Global site tag (gtag.js) - Google Analytics