/********************************************************************************************
* author:conowen@大钟
* E-mail:conowen@hotmail.com
* http://blog.csdn.net/conowen
* 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。
********************************************************************************************/
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
Throws
|
但是对于实时地播放加密过的音频文件却是束手无策。虽然对于一些加密过的音频文件,可以采用Audiotrack与Libmad结合的方式解决。
3、简单Demo程序
下面提供一个Audiotrack播放mp3的demo,mp3没有经过加密的,解码部分是由libmad完成。(若是要播放加密音频文件,可以操作的libmad解码文件流即可。)
直接贴代码,代码的大意已经在注释说明了。
@LibmadActivity.Java
- /*
- * author:conowen
- * date:2012.7.29
- */
- package com.conowen.libmad;
- import android.app.Activity;
- import android.media.AudioFormat;
- import android.media.AudioManager;
- import android.media.AudioTrack;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.Toast;
- public class LibmadActivity extends Activity {
- private Thread mThread;
- private short[] audioBuffer;
- private AudioTrack mAudioTrack;
- private Button btnPlay, btnPauseButton;
- private int samplerate;
- private int mAudioMinBufSize;
- private int ret;
- private NativeMP3Decoder MP3Decoder;
- private boolean mThreadFlag;
- private String filePath = "/sdcard/test.mp3";
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- btnPlay = (Button) findViewById(R.id.buttonPlay);
- btnPauseButton = (Button) findViewById(R.id.buttonPause);
- MP3Decoder = new NativeMP3Decoder();
- ret = MP3Decoder.initAudioPlayer(filePath, 0);
- if (ret == -1) {
- Log.i("conowen", "Couldn't open file '" + filePath + "'");
- } else {
- mThreadFlag = true;
- initAudioPlayer();
- audioBuffer = new short[1024 * 1024];
- mThread = new Thread(new Runnable() {
- @Override
- public void run() {
- // TODO Auto-generated method stub
- while (mThreadFlag) {
- if (mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PAUSED) {
- // ****从libmad处获取data******/
- MP3Decoder.getAudioBuf(audioBuffer,
- mAudioMinBufSize);
- mAudioTrack.write(audioBuffer, 0, mAudioMinBufSize);
- } else {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- });
- mThread.start();
- }
- btnPlay.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- if (ret == -1) {
- Log.i("conowen", "Couldn't open file '" + filePath + "'");
- Toast.makeText(getApplicationContext(),
- "Couldn't open file '" + filePath + "'",
- Toast.LENGTH_SHORT).show();
- } else {
- if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {
- //mThreadFlag = true;// 音频线程开始
- mAudioTrack.play();
- // mThread.start();
- } else if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) {
- //mThreadFlag = true;// 音频线程开始
- mAudioTrack.play();
- } else {
- Toast.makeText(getApplicationContext(),
- "Already in play", Toast.LENGTH_SHORT).show();
- }
- }
- }
- });
- btnPauseButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- if (ret == -1) {
- Log.i("conowen", "Couldn't open file '" + filePath + "'");
- Toast.makeText(getApplicationContext(),
- "Couldn't open file '" + filePath + "'",
- Toast.LENGTH_SHORT).show();
- } else {
- if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
- mAudioTrack.pause();
- } else {
- Toast.makeText(getApplicationContext(), "Already stop",
- Toast.LENGTH_SHORT).show();
- }
- }
- }
- });
- }
- private void initAudioPlayer() {
- // TODO Auto-generated method stub
- samplerate = MP3Decoder.getAudioSamplerate();
- System.out.println("samplerate = " + samplerate);
- samplerate = samplerate / 2;
- // 声音文件一秒钟buffer的大小
- mAudioMinBufSize = AudioTrack.getMinBufferSize(samplerate,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO,
- AudioFormat.ENCODING_PCM_16BIT);
- mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, // 指定在流的类型
- // STREAM_ALARM:警告声
- // STREAM_MUSCI:音乐声,例如music等
- // STREAM_RING:铃声
- // STREAM_SYSTEM:系统声音
- // STREAM_VOCIE_CALL:电话声音
- samplerate,// 设置音频数据的采样率
- AudioFormat.CHANNEL_CONFIGURATION_STEREO,// 设置输出声道为双声道立体声
- AudioFormat.ENCODING_PCM_16BIT,// 设置音频数据块是8位还是16位
- mAudioMinBufSize, AudioTrack.MODE_STREAM);// 设置模式类型,在这里设置为流类型
- // AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。
- // STREAM方式表示由用户通过write方式把数据一次一次得写到audiotrack中。
- // 这种方式的缺点就是JAVA层和Native层不断地交换数据,效率损失较大。
- // 而STATIC方式表示是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,
- // 后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。
- // 这种方法对于铃声等体积较小的文件比较合适。
- }
- static {
- System.loadLibrary("mad");
- }
- @Override
- protected void onDestroy() {
- // TODO Auto-generated method stub
- super.onDestroy();
- mAudioTrack.stop();
- mAudioTrack.release();// 关闭并释放资源
- mThreadFlag = false;// 音频线程暂停
- MP3Decoder.closeAduioFile();
- }
- }
@NativeMP3Decoder.java
- package com.conowen.libmad;
- public class NativeMP3Decoder {
- private int ret;
- public NativeMP3Decoder() {
- }
- public native int initAudioPlayer(String file,int StartAddr);
- public native int getAudioBuf(short[] audioBuffer, int numSamples);
- public native void closeAduioFile();
- public native int getAudioSamplerate();
- }
@main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="AudioTrack"
- />
- <Button
- android:id = "@+id/buttonPlay"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="PLAY"
- />
- <Button
- android:id = "@+id/buttonPause"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="PAUSE"
- />
- </LinearLayout>