Android AudioTrack

来源:互联网 发布:桌面软件编程语言 编辑:程序博客网 时间:2024/06/04 19:54


1、Android AudioTrack简介

          在Android中播放声音可以用MediaPlayer和AudioTrack两种方案的,但是两种方案是有很大区别的,MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。而AudioTrack只能播放PCM数据流。

           事实上,两种本质上是没啥区别的,MediaPlayer在播放音频时,在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,最后由AudioFlinger进行混音,传递音频给硬件播放出来。利用AudioTrack播放只是跳过Mediaplayer的解码部分而已。Mediaplayer的解码核心部分是基于OpenCORE 来实现的,支持通用的音视频和图像格式,codec使用的是OpenMAX接口来进行扩展。因此使用audiotrack播放mp3文件的话,要自己加入一个音频解码器,如libmad。否则只能播放PCM数据,如大多数WAV格式的音频文件。

参考上一篇博文。

http://blog.csdn.net/conowen/article/details/7727145


2、使用Mediaplayer的不足

     MediaPlayer提供了5个setDataSource方法,如其中一个,虽然可以设置文件流起始地址与文件流长度。

public void setDataSource(FileDescriptor fd, long offset, long length)

Since: API Level 1

Sets the data source (FileDescriptor) to use. The FileDescriptor must be seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility to close the file descriptor. It is safe to do so as soon as this call returns.

Parameters
fdthe FileDescriptor for the file you want to playoffsetthe offset into the file where the data to be played starts, in byteslengththe length in bytes of the data to be played
Throws
IllegalStateExceptionif it is called in an invalid stateIOException IllegalArgumentException 





      但是对于实时地播放加密过的音频文件却是束手无策。虽然对于一些加密过的音频文件,可以采用Audiotrack与Libmad结合的方式解决。


3、简单Demo程序

    下面提供一个Audiotrack播放mp3的demo,mp3没有经过加密的,解码部分是由libmad完成。(若是要播放加密音频文件,可以操作的libmad解码文件流即可。)

直接贴代码,代码的大意已经在注释说明了。




@LibmadActivity.java

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * author:conowen 
  3.  * date:2012.7.29 
  4.  */  
  5.   
  6. package com.conowen.libmad;  
  7.   
  8. import android.app.Activity;  
  9. import android.media.AudioFormat;  
  10. import android.media.AudioManager;  
  11. import android.media.AudioTrack;  
  12. import android.os.Bundle;  
  13. import android.util.Log;  
  14. import android.view.View;  
  15. import android.view.View.OnClickListener;  
  16. import android.widget.Button;  
  17. import android.widget.Toast;  
  18.   
  19. public class LibmadActivity extends Activity {  
  20.   
  21.   
  22.     private Thread mThread;   
  23.     private short[] audioBuffer;  
  24.     private AudioTrack mAudioTrack;  
  25.     private Button btnPlay, btnPauseButton;  
  26.       
  27.   
  28.     private int samplerate;  
  29.     private int mAudioMinBufSize;  
  30.     private int ret;  
  31.     private NativeMP3Decoder MP3Decoder;  
  32.   
  33.     private boolean mThreadFlag;  
  34.   
  35.     private String filePath = "/sdcard/test.mp3";  
  36.   
  37.     /** Called when the activity is first created. */  
  38.     @Override  
  39.     public void onCreate(Bundle savedInstanceState) {  
  40.         super.onCreate(savedInstanceState);  
  41.         setContentView(R.layout.main);  
  42.         btnPlay = (Button) findViewById(R.id.buttonPlay);  
  43.         btnPauseButton = (Button) findViewById(R.id.buttonPause);  
  44.         MP3Decoder = new NativeMP3Decoder();  
  45.         ret = MP3Decoder.initAudioPlayer(filePath, 0);  
  46.         if (ret == -1) {  
  47.             Log.i("conowen""Couldn't open file '" + filePath + "'");  
  48.   
  49.         } else {  
  50.             mThreadFlag = true;  
  51.             initAudioPlayer();  
  52.   
  53.             audioBuffer = new short[1024 * 1024];  
  54.             mThread = new Thread(new Runnable() {  
  55.   
  56.                 @Override  
  57.                 public void run() {  
  58.                     // TODO Auto-generated method stub  
  59.                     while (mThreadFlag) {  
  60.                         if (mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PAUSED) {  
  61.                             // ****从libmad处获取data******/  
  62.                             MP3Decoder.getAudioBuf(audioBuffer,  
  63.                                     mAudioMinBufSize);  
  64.                             mAudioTrack.write(audioBuffer, 0, mAudioMinBufSize);  
  65.   
  66.                         } else {  
  67.                             try {  
  68.                                 Thread.sleep(1000);  
  69.                             } catch (InterruptedException e) {  
  70.                                 // TODO Auto-generated catch block  
  71.                                 e.printStackTrace();  
  72.                             }  
  73.                         }  
  74.                     }  
  75.   
  76.                 }  
  77.             });  
  78.             mThread.start();  
  79.   
  80.         }  
  81.         btnPlay.setOnClickListener(new OnClickListener() {  
  82.   
  83.             @Override  
  84.             public void onClick(View v) {  
  85.                 // TODO Auto-generated method stub  
  86.                 if (ret == -1) {  
  87.                     Log.i("conowen""Couldn't open file '" + filePath + "'");  
  88.                     Toast.makeText(getApplicationContext(),  
  89.                             "Couldn't open file '" + filePath + "'",  
  90.                             Toast.LENGTH_SHORT).show();  
  91.                 } else {  
  92.                     if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {  
  93.                         //mThreadFlag = true;// 音频线程开始  
  94.                         mAudioTrack.play();  
  95.                         // mThread.start();  
  96.                     } else if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {  
  97.                         //mThreadFlag = true;// 音频线程开始  
  98.                         mAudioTrack.play();  
  99.   
  100.                     } else {  
  101.                         Toast.makeText(getApplicationContext(),  
  102.                                 "Already in play", Toast.LENGTH_SHORT).show();   
  103.                     }  
  104.                 }  
  105.             }  
  106.         });  
  107.         btnPauseButton.setOnClickListener(new OnClickListener() {  
  108.   
  109.             @Override  
  110.             public void onClick(View v) {  
  111.                 // TODO Auto-generated method stub  
  112.                 if (ret == -1) {  
  113.                     Log.i("conowen""Couldn't open file '" + filePath + "'");  
  114.                     Toast.makeText(getApplicationContext(),  
  115.                             "Couldn't open file '" + filePath + "'",  
  116.                             Toast.LENGTH_SHORT).show();  
  117.                 } else {  
  118.                     if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {  
  119.                         mAudioTrack.pause();  
  120.                                           
  121.                     } else {  
  122.                         Toast.makeText(getApplicationContext(), "Already stop",  
  123.                                 Toast.LENGTH_SHORT).show();  
  124.   
  125.                     }  
  126.   
  127.                 }  
  128.             }  
  129.         });  
  130.     }  
  131.   
  132.     private void initAudioPlayer() {  
  133.         // TODO Auto-generated method stub  
  134.         samplerate = MP3Decoder.getAudioSamplerate();  
  135.         System.out.println("samplerate = " + samplerate);  
  136.         samplerate = samplerate / 2;  
  137.         // 声音文件一秒钟buffer的大小  
  138.         mAudioMinBufSize = AudioTrack.getMinBufferSize(samplerate,  
  139.                 AudioFormat.CHANNEL_CONFIGURATION_STEREO,  
  140.                 AudioFormat.ENCODING_PCM_16BIT);  
  141.   
  142.         mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, // 指定在流的类型  
  143.                 // STREAM_ALARM:警告声  
  144.                 // STREAM_MUSCI:音乐声,例如music等  
  145.                 // STREAM_RING:铃声  
  146.                 // STREAM_SYSTEM:系统声音  
  147.                 // STREAM_VOCIE_CALL:电话声音  
  148.                   
  149.                 samplerate,// 设置音频数据的采样率  
  150.                 AudioFormat.CHANNEL_CONFIGURATION_STEREO,// 设置输出声道为双声道立体声  
  151.                 AudioFormat.ENCODING_PCM_16BIT,// 设置音频数据块是8位还是16位  
  152.                 mAudioMinBufSize, AudioTrack.MODE_STREAM);// 设置模式类型,在这里设置为流类型  
  153.         // AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。  
  154.         // STREAM方式表示由用户通过write方式把数据一次一次得写到audiotrack中。  
  155.         // 这种方式的缺点就是JAVA层和Native层不断地交换数据,效率损失较大。  
  156.         // 而STATIC方式表示是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,  
  157.         // 后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。  
  158.         // 这种方法对于铃声等体积较小的文件比较合适。  
  159.     }  
  160.   
  161.     static {  
  162.         System.loadLibrary("mad");  
  163.   
  164.     }  
  165.     @Override  
  166.     protected void onDestroy() {  
  167.         // TODO Auto-generated method stub  
  168.         super.onDestroy();  
  169.         mAudioTrack.stop();  
  170.         mAudioTrack.release();// 关闭并释放资源          
  171.         mThreadFlag = false;// 音频线程暂停  
  172.         MP3Decoder.closeAduioFile();  
  173.     }  
  174. }  


@NativeMP3Decoder.java

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.conowen.libmad;  
  2.   
  3. public class NativeMP3Decoder {  
  4.   
  5.     private int ret;  
  6.   
  7.     public NativeMP3Decoder() {  
  8.   
  9.     }  
  10.   
  11.     public native int initAudioPlayer(String file,int StartAddr);  
  12.   
  13.     public native int getAudioBuf(short[] audioBuffer, int numSamples);  
  14.   
  15.     public native void closeAduioFile();  
  16.   
  17.     public native int getAudioSamplerate();  
  18.   
  19.   
  20.   
  21. }  

@main.xml

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7.       
  8.     <TextView    
  9.     android:layout_width="fill_parent"   
  10.     android:layout_height="wrap_content"   
  11.     android:text="AudioTrack"  
  12.     />  
  13.         
  14.     <Button    
  15.     android:id = "@+id/buttonPlay"  
  16.     android:layout_width="wrap_content"   
  17.     android:layout_height="wrap_content"   
  18.     android:text="PLAY"  
  19.     />  
  20.       
  21.     <Button    
  22.     android:id = "@+id/buttonPause"  
  23.     android:layout_width="wrap_content"   
  24.     android:layout_height="wrap_content"   
  25.     android:text="PAUSE"  
  26.     />     
  27.       
  28. </LinearLayout>  

1、Android AudioTrack简介

          在Android中播放声音可以用MediaPlayer和AudioTrack两种方案的,但是两种方案是有很大区别的,MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。而AudioTrack只能播放PCM数据流。

           事实上,两种本质上是没啥区别的,MediaPlayer在播放音频时,在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,最后由AudioFlinger进行混音,传递音频给硬件播放出来。利用AudioTrack播放只是跳过Mediaplayer的解码部分而已。Mediaplayer的解码核心部分是基于OpenCORE 来实现的,支持通用的音视频和图像格式,codec使用的是OpenMAX接口来进行扩展。因此使用audiotrack播放mp3文件的话,要自己加入一个音频解码器,如libmad。否则只能播放PCM数据,如大多数WAV格式的音频文件。

参考上一篇博文。

http://blog.csdn.net/conowen/article/details/7727145


2、使用Mediaplayer的不足

     MediaPlayer提供了5个setDataSource方法,如其中一个,虽然可以设置文件流起始地址与文件流长度。

public void setDataSource(FileDescriptor fd, long offset, long length)

Since: API Level 1

Sets the data source (FileDescriptor) to use. The FileDescriptor must be seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility to close the file descriptor. It is safe to do so as soon as this call returns.

Parameters
fdthe FileDescriptor for the file you want to playoffsetthe offset into the file where the data to be played starts, in byteslengththe length in bytes of the data to be played
Throws
IllegalStateExceptionif it is called in an invalid stateIOException IllegalArgumentException 





      但是对于实时地播放加密过的音频文件却是束手无策。虽然对于一些加密过的音频文件,可以采用Audiotrack与Libmad结合的方式解决。


3、简单Demo程序

    下面提供一个Audiotrack播放mp3的demo,mp3没有经过加密的,解码部分是由libmad完成。(若是要播放加密音频文件,可以操作的libmad解码文件流即可。)

直接贴代码,代码的大意已经在注释说明了。




@LibmadActivity.java

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * author:conowen 
  3.  * date:2012.7.29 
  4.  */  
  5.   
  6. package com.conowen.libmad;  
  7.   
  8. import android.app.Activity;  
  9. import android.media.AudioFormat;  
  10. import android.media.AudioManager;  
  11. import android.media.AudioTrack;  
  12. import android.os.Bundle;  
  13. import android.util.Log;  
  14. import android.view.View;  
  15. import android.view.View.OnClickListener;  
  16. import android.widget.Button;  
  17. import android.widget.Toast;  
  18.   
  19. public class LibmadActivity extends Activity {  
  20.   
  21.   
  22.     private Thread mThread;   
  23.     private short[] audioBuffer;  
  24.     private AudioTrack mAudioTrack;  
  25.     private Button btnPlay, btnPauseButton;  
  26.       
  27.   
  28.     private int samplerate;  
  29.     private int mAudioMinBufSize;  
  30.     private int ret;  
  31.     private NativeMP3Decoder MP3Decoder;  
  32.   
  33.     private boolean mThreadFlag;  
  34.   
  35.     private String filePath = "/sdcard/test.mp3";  
  36.   
  37.     /** Called when the activity is first created. */  
  38.     @Override  
  39.     public void onCreate(Bundle savedInstanceState) {  
  40.         super.onCreate(savedInstanceState);  
  41.         setContentView(R.layout.main);  
  42.         btnPlay = (Button) findViewById(R.id.buttonPlay);  
  43.         btnPauseButton = (Button) findViewById(R.id.buttonPause);  
  44.         MP3Decoder = new NativeMP3Decoder();  
  45.         ret = MP3Decoder.initAudioPlayer(filePath, 0);  
  46.         if (ret == -1) {  
  47.             Log.i("conowen""Couldn't open file '" + filePath + "'");  
  48.   
  49.         } else {  
  50.             mThreadFlag = true;  
  51.             initAudioPlayer();  
  52.   
  53.             audioBuffer = new short[1024 * 1024];  
  54.             mThread = new Thread(new Runnable() {  
  55.   
  56.                 @Override  
  57.                 public void run() {  
  58.                     // TODO Auto-generated method stub  
  59.                     while (mThreadFlag) {  
  60.                         if (mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PAUSED) {  
  61.                             // ****从libmad处获取data******/  
  62.                             MP3Decoder.getAudioBuf(audioBuffer,  
  63.                                     mAudioMinBufSize);  
  64.                             mAudioTrack.write(audioBuffer, 0, mAudioMinBufSize);  
  65.   
  66.                         } else {  
  67.                             try {  
  68.                                 Thread.sleep(1000);  
  69.                             } catch (InterruptedException e) {  
  70.                                 // TODO Auto-generated catch block  
  71.                                 e.printStackTrace();  
  72.                             }  
  73.                         }  
  74.                     }  
  75.   
  76.                 }  
  77.             });  
  78.             mThread.start();  
  79.   
  80.         }  
  81.         btnPlay.setOnClickListener(new OnClickListener() {  
  82.   
  83.             @Override  
  84.             public void onClick(View v) {  
  85.                 // TODO Auto-generated method stub  
  86.                 if (ret == -1) {  
  87.                     Log.i("conowen""Couldn't open file '" + filePath + "'");  
  88.                     Toast.makeText(getApplicationContext(),  
  89.                             "Couldn't open file '" + filePath + "'",  
  90.                             Toast.LENGTH_SHORT).show();  
  91.                 } else {  
  92.                     if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {  
  93.                         //mThreadFlag = true;// 音频线程开始  
  94.                         mAudioTrack.play();  
  95.                         // mThread.start();  
  96.                     } else if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {  
  97.                         //mThreadFlag = true;// 音频线程开始  
  98.                         mAudioTrack.play();  
  99.   
  100.                     } else {  
  101.                         Toast.makeText(getApplicationContext(),  
  102.                                 "Already in play", Toast.LENGTH_SHORT).show();   
  103.                     }  
  104.                 }  
  105.             }  
  106.         });  
  107.         btnPauseButton.setOnClickListener(new OnClickListener() {  
  108.   
  109.             @Override  
  110.             public void onClick(View v) {  
  111.                 // TODO Auto-generated method stub  
  112.                 if (ret == -1) {  
  113.                     Log.i("conowen""Couldn't open file '" + filePath + "'");  
  114.                     Toast.makeText(getApplicationContext(),  
  115.                             "Couldn't open file '" + filePath + "'",  
  116.                             Toast.LENGTH_SHORT).show();  
  117.                 } else {  
  118.                     if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {  
  119.                         mAudioTrack.pause();  
  120.                                           
  121.                     } else {  
  122.                         Toast.makeText(getApplicationContext(), "Already stop",  
  123.                                 Toast.LENGTH_SHORT).show();  
  124.   
  125.                     }  
  126.   
  127.                 }  
  128.             }  
  129.         });  
  130.     }  
  131.   
  132.     private void initAudioPlayer() {  
  133.         // TODO Auto-generated method stub  
  134.         samplerate = MP3Decoder.getAudioSamplerate();  
  135.         System.out.println("samplerate = " + samplerate);  
  136.         samplerate = samplerate / 2;  
  137.         // 声音文件一秒钟buffer的大小  
  138.         mAudioMinBufSize = AudioTrack.getMinBufferSize(samplerate,  
  139.                 AudioFormat.CHANNEL_CONFIGURATION_STEREO,  
  140.                 AudioFormat.ENCODING_PCM_16BIT);  
  141.   
  142.         mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, // 指定在流的类型  
  143.                 // STREAM_ALARM:警告声  
  144.                 // STREAM_MUSCI:音乐声,例如music等  
  145.                 // STREAM_RING:铃声  
  146.                 // STREAM_SYSTEM:系统声音  
  147.                 // STREAM_VOCIE_CALL:电话声音  
  148.                   
  149.                 samplerate,// 设置音频数据的采样率  
  150.                 AudioFormat.CHANNEL_CONFIGURATION_STEREO,// 设置输出声道为双声道立体声  
  151.                 AudioFormat.ENCODING_PCM_16BIT,// 设置音频数据块是8位还是16位  
  152.                 mAudioMinBufSize, AudioTrack.MODE_STREAM);// 设置模式类型,在这里设置为流类型  
  153.         // AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。  
  154.         // STREAM方式表示由用户通过write方式把数据一次一次得写到audiotrack中。  
  155.         // 这种方式的缺点就是JAVA层和Native层不断地交换数据,效率损失较大。  
  156.         // 而STATIC方式表示是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,  
  157.         // 后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。  
  158.         // 这种方法对于铃声等体积较小的文件比较合适。  
  159.     }  
  160.   
  161.     static {  
  162.         System.loadLibrary("mad");  
  163.   
  164.     }  
  165.     @Override  
  166.     protected void onDestroy() {  
  167.         // TODO Auto-generated method stub  
  168.         super.onDestroy();  
  169.         mAudioTrack.stop();  
  170.         mAudioTrack.release();// 关闭并释放资源          
  171.         mThreadFlag = false;// 音频线程暂停  
  172.         MP3Decoder.closeAduioFile();  
  173.     }  
  174. }  


@NativeMP3Decoder.java

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. package com.conowen.libmad;  
  2.   
  3. public class NativeMP3Decoder {  
  4.   
  5.     private int ret;  
  6.   
  7.     public NativeMP3Decoder() {  
  8.   
  9.     }  
  10.   
  11.     public native int initAudioPlayer(String file,int StartAddr);  
  12.   
  13.     public native int getAudioBuf(short[] audioBuffer, int numSamples);  
  14.   
  15.     public native void closeAduioFile();  
  16.   
  17.     public native int getAudioSamplerate();  
  18.   
  19.   
  20.   
  21. }  

@main.xml

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7.       
  8.     <TextView    
  9.     android:layout_width="fill_parent"   
  10.     android:layout_height="wrap_content"   
  11.     android:text="AudioTrack"  
  12.     />  
  13.         
  14.     <Button    
  15.     android:id = "@+id/buttonPlay"  
  16.     android:layout_width="wrap_content"   
  17.     android:layout_height="wrap_content"   
  18.     android:text="PLAY"  
  19.     />  
  20.       
  21.     <Button    
  22.     android:id = "@+id/buttonPause"  
  23.     android:layout_width="wrap_content"   
  24.     android:layout_height="wrap_content"   
  25.     android:text="PAUSE"  
  26.     />     
  27.       
  28. </LinearLayout>  
0 0
原创粉丝点击