播放PCM

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




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




简单说下思路,先把PCM音频数据从指定的路径文件读到内存,然后给AudioPlayer设置数据源,音频参数等,最后执行播放,暂停,停止等操作
贴上部分类代码片段:
public   class  AudioParam {  
  
     int  mFrequency;                  // 采样率   
      
     int  mChannel;                    // 声道   
      
     int  mSampBit;                    // 采样精度   
      
}  
 
public   interface  PlayState {  
      
     public   static   final   int  MPS_UNINIT =  0 ;              // 未就绪   
      
     public   static   final   int  MPS_PREPARE =  1 ;             // 准备就绪(停止)   
      
     public   static   final   int  MPS_PLAYING =  2 ;             // 播放中   
      
     public   static   final   int  MPS_PAUSE =  3 ;               // 暂停   
}  
 
AudioPlayer代码片段如下:
public   class  AudioPlayer  implements  IPlayComplete{  
  
     private   final   static  String TAG =  "AudioPlayer" ;  
      
     public   final   static   int     STATE_MSG_ID =  0x0010 ;  
      
     private  Handler    mHandler;  
      
     private  AudioParam mAudioParam;                          // 音频参数   
      
     private   byte []     mData;                                // 音频数据   
      
     private  AudioTrack mAudioTrack;                          // AudioTrack对象   
      
     private   boolean     mBReady =  false ;                      // 播放源是否就绪   
      
     private  PlayAudioThread mPlayAudioThread;                // 播放线程   
      
     public  AudioPlayer(Handler handler)  
    {  
        mHandler = handler;  
    }  
      
     public  AudioPlayer(Handler handler,AudioParam audioParam)  
    {  
        mHandler = handler;  
        setAudioParam(audioParam);    
    }  
      
     /*  
     * 设置音频参数  
     */   
     public   void  setAudioParam(AudioParam audioParam)  
    {  
        mAudioParam = audioParam;  
    }  
      
     /*  
     * 设置音频源  
     */   
     public   void  setDataSource( byte [] data)  
    {  
        mData = data;         
    }  
      
      
     /*  
     *  就绪播放源  
     */   
     public   boolean  prepare()  
    {  
         if  (mData ==  null  || mAudioParam ==  null )  
        {  
             return   false ;  
        }  
          
         if  (mBReady ==  true )  
        {  
             return   true ;  
        }  
          
         try  {  
            createAudioTrack();  
        }  catch  (Exception e) {  
             // TODO Auto-generated catch block   
            e.printStackTrace();  
             return   false ;  
        }  
                  
          
        mBReady =  true ;  
          
        setPlayState(PlayState.MPS_PREPARE);  
          
         return   true ;  
    }  
 
private   boolean  mThreadExitFlag =  false ;                         // 线程退出标志   
      
     private   int      mPrimePlaySize =  0 ;                              // 较优播放块大小   
      
     private   int      mPlayOffset =  0 ;                                 // 当前播放位置   
      
     private   int      mPlayState =  0 ;                                  // 当前播放状态   
      
      
     /*  
     *  播放音频的线程  
     */   
     class  PlayAudioThread  extends  Thread  
    {  
  
          
         @Override   
         public   void  run() {  
             // TODO Auto-generated method stub   
  
              
            Log.d(TAG,  "PlayAudioThread run mPlayOffset = "  + mPlayOffset);  
              
            mAudioTrack.play();   
              
                 while ( true )  
                {                                             
                      
                     if  (mThreadExitFlag ==  true )  
                    {  
                         break ;  
                    }                     
                      
                     try  {  
                          
                         int  size = mAudioTrack.write(mData, mPlayOffset, mPrimePlaySize);                 
                        mPlayOffset += mPrimePlaySize;        
                          
                    }  catch  (Exception e) {  
                         // TODO: handle exception   
                        e.printStackTrace();  
                        AudioPlayer. this .onPlayComplete();  
                         break ;  
                    }  
              
                     if  (mPlayOffset >= mData.length)  
                    {             
                        AudioPlayer. this .onPlayComplete();  
                         break ;  
                    }  
                      
                      
                }  
                  
            mAudioTrack.stop();   
                  
            Log.d(TAG,  "PlayAudioThread complete..." );                        
        }  
      
          
          
    } 
下面来剖析以下如何使用AudioTrack来播放PCM音频数据
首先要构建一个AudioTrack对象:(需要采样率,声道,采样精度参数)
private   void  createAudioTrack()  throws  Exception  
    {  
          
         // 获得构建对象的最小缓冲区大小   
         int  minBufSize = AudioTrack.getMinBufferSize(mAudioParam.mFrequency,   
                                                    mAudioParam.mChannel,  
                                                    mAudioParam.mSampBit);  
          
          
        mPrimePlaySize = minBufSize *  2 ;  
        Log.d(TAG,  "mPrimePlaySize = "  + mPrimePlaySize);  
          
//               STREAM_ALARM:警告声   
//               STREAM_MUSCI:音乐声,例如music等   
//               STREAM_RING:铃声   
//               STREAM_SYSTEM:系统声音   
//               STREAM_VOCIE_CALL:电话声音   
        mAudioTrack =  new  AudioTrack(AudioManager.STREAM_MUSIC,  
                                    mAudioParam.mFrequency,   
                                    mAudioParam.mChannel,   
                                    mAudioParam.mSampBit,   
                                    minBufSize,  
                                    AudioTrack.MODE_STREAM);  
//              AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。   
//              STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。   
//              这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。   
//              这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。   
//              而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,   
//              后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。   
//              这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。   
          
          
  
    }  
然后开一个子线程从缓存区里分块取数据然后写入硬件设备进行播放
  
private   void  startThread()  
{  
     if  (mPlayAudioThread ==  null )  
    {  
        mThreadExitFlag =  false ;  
        mPlayAudioThread =  new  PlayAudioThread();  
        mPlayAudioThread.start();  
    }  
}  
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操作而导致的,其它的不多说了,觉得有用的童鞋自己写代码看吧。。。喜欢就顶一下吧!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Linux 上播放 PCM 音频,可以使用 ALSA(Advanced Linux Sound Architecture)库。以下是一个简单的 C 代码示例,演示如何使用 ALSA 播放 PCM 音频: ```c #include <alsa/asoundlib.h> int main() { int err; snd_pcm_t *pcm_handle; snd_pcm_hw_params_t *params; unsigned int sample_rate = 44100; unsigned int channels = 2; unsigned int buffer_size = 1024; unsigned int period_size = 256; char *buffer; // 打开 PCM 设备 err = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0); if (err < 0) { printf("Error opening PCM device: %s\n", snd_strerror(err)); return 1; } // 分配硬件参数结构体 snd_pcm_hw_params_alloca(&params); // 初始化硬件参数结构体 err = snd_pcm_hw_params_any(pcm_handle, params); if (err < 0) { printf("Can't initialize hardware parameter structure: %s\n", snd_strerror(err)); return 1; } // 设置采样率、通道数和 PCM 格式 err = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); err |= snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE); err |= snd_pcm_hw_params_set_channels(pcm_handle, params, channels); err |= snd_pcm_hw_params_set_rate_near(pcm_handle, params, &sample_rate, 0); if (err < 0) { printf("Can't set hardware parameters: %s\n", snd_strerror(err)); return 1; } // 设置缓冲区大小和周期大小 err = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, params, &buffer_size); err |= snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &period_size, 0); if (err < 0) { printf("Can't set buffer/period size: %s\n", snd_strerror(err)); return 1; } // 应用硬件参数 err = snd_pcm_hw_params(pcm_handle, params); if (err < 0) { printf("Can't set hardware parameters: %s\n", snd_strerror(err)); return 1; } // 分配缓冲区 buffer = (char *) malloc(period_size * channels * 2); // 播放 PCM 音频 while (1) { // 从文件或其他来源读取 PCM 数据到缓冲区 // ... // 写入缓冲区中的 PCM 数据到 PCM 设备 err = snd_pcm_writei(pcm_handle, buffer, period_size); if (err < 0) { printf("Error writing PCM data: %s\n", snd_strerror(err)); break; } } // 关闭 PCM 设备 snd_pcm_close(pcm_handle); return 0; } ``` 在上面的代码中,我们首先打开 PCM 设备,然后初始化硬件参数结构体,并设置采样率、通道数和 PCM 格式等参数。接着,我们设置缓冲区大小和周期大小,并应用硬件参数。最后,我们分配缓冲区,并在一个循环中不断从文件或其他来源读取 PCM 数据到缓冲区,然后将缓冲区中的 PCM 数据写入 PCM 设备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值