播放聲音可以用MediaPlayer和AudioTrack,兩者都提供了java API供應用開發者使用。雖然都可以播放聲音,但兩者還是有很大的區別的。
其中最大的區別是MediaPlayer可以播放多種格式的聲音檔案,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer會在framework層建立對應的音訊解碼器。
而AudioTrack只能播放已經解碼的PCM流,如果是檔案的話只支援wav格式的音訊檔案,因為wav格式的音訊檔案大部分都是PCM流。AudioTrack不建立解碼器,所以只能播放不需要解碼的wav檔案。
當然兩者之間還是有緊密的聯絡的,MediaPlayer在framework層還是會建立AudioTrack,把解碼後的PCM數流傳遞給AudioTrack,AudioTrack再傳遞給AudioFlinger進行混音,然後才傳遞給硬體播放。
所以是MediaPlayer包含了AudioTRack。
通過檢視API可以知道,MediaPlayer提供了5個setDataSource方法,分為三類,一類是傳遞播放檔案的字串路徑作為引數,例如直接取sd卡里mp3檔案的路徑,一類是傳遞播放檔案的FileDescriptor檔案描述符作為播放的id,例例如從db中查詢的音訊檔案的id,就可以直接賦給MediaPlayer進行播放。還有一類是Uri型別的資原始檔,用於播放content uri檔案。
下面是一個用MediaPlayer播放音樂的示例,音樂檔案是從資料庫中取出的。
if (mMediaPlayer == null) { mMediaPlayer = new MediaPlayer(); // 建立MediaPlayer物件 } mMediaPlayer.reset(); String dataSource = getDataByPosition(mCursor, mPlayPosition); mMediaPlayer.setDataSource(dataSource); // 設定需要播放的資料來源 mMediaPlayer.prepare(); // 準備播放,如果是流媒體需要呼叫prepareAsync進行非同步準備 if (Common.PLAY_MODE_SINGLE_LOOP == mPlayMode) { mMediaPlayer.setLooping(true); // 單曲迴圈 } else { mMediaPlayer.setLooping(false); // 不迴圈播放 } mMediaPlayer.start(); // 開始播放,如果是播放流媒體,需要等到流媒體準備完成才能播放(在prepare的callback函式中呼叫start進行播放) // 根據位置來獲取歌曲位置 public String getDataByPosition(Cursor c, int position) { c.moveToPosition(position); int dataColumn = c.getColumnIndex(MediaStore.Audio.Media.DATA); String data = c.getString(dataColumn); return data; }
AudioTrack播放聲音時不能直接把wav檔案傳遞給AudioTrack進行播放,必須傳遞buffer,通過write函式把需要播放的緩衝區buffer傳遞給AudioTrack,然後才能播放。
AudioTrack使用的例子參考下面:
此示例轉自:http://samyou.iteye.com/blog/1125872
public class AndroidTest extends Activity implements View.OnClickListener,SurfaceHolder.Callback { private SurfaceHolder surfaceHolder = null; private SurfaceView surfaceView = null; private AudioTrack audioTrack = null; private Thread writePCMThread = null; private File audioFile = null; private FileInputStream fileInputStream = null; private byte buffer[] = new byte[16*10000]; // The Handler that gets information back from the other threads private final Handler msgHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { default: break; } } }; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); surfaceView = (SurfaceView) findViewById(R.id.surface); SurfaceHolder surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); findViewById(R.id.button1).setOnClickListener(this); findViewById(R.id.button2).setOnClickListener(this); } public void finish() { super.finish(); System.out.println("finish"); try { writePCMThread.interrupt(); } catch (Exception e) { } try { fileInputStream.close(); } catch (Exception e) { } try { audioTrack.stop(); audioTrack.release(); } catch (Exception e) { } } protected void onResume() { super.onResume(); System.out.println("back on!!!!!!!!!!!"); initAudioTrack(); audioFile = new File(Environment.getExternalStorageDirectory(),"test.wav"); System.out.println(audioFile.length()); try { fileInputStream = new FileInputStream(audioFile); fileInputStream.skip(0x2c); } catch (Exception e) { } writePCMThread = new Thread(new Runnable(){ public void run() { try { while(fileInputStream.read(buffer)>=0) { System.out.println("write pcm data"); audioTrack.write(buffer, 0, buffer.length); } } catch (Exception e) { e.printStackTrace(); } } }); } private void initAudioTrack() { int minBufferSize = AudioTrack.getMinBufferSize(0xac44, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); System.out.println("minBufferSize = "+minBufferSize); audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 0xac44, AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize*2,AudioTrack.MODE_STREAM); audioTrack.setStereoVolume(1.0f, 1.0f);// 設定當前音量大小 System.out.println("initAudioTrack over"); audioTrack.play(); } public void onClick(View v) { switch (v.getId()) { case R.id.button1: writePCMThread.start(); break; case R.id.button2: break; default: break; } } public void surfaceCreated(SurfaceHolder holder) { System.out.println("surfaceCreated()"); this.surfaceHolder = holder; } public void surfaceDestroyed(SurfaceHolder holder) { } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { this.surfaceHolder = holder; } public void onActivityResult(int requestCode, int resultCode, Intent data) { } }
注:PCM和WAV
以下摘自:http://www.erji.net/read.php?tid=227570
簡單來說,pcm是一種資料編碼格式,CD唱盤上燒錄的就直接用pcm格式編碼的資料檔案; wav是一種聲音檔案格式,wav裡面包含的聲音資料可以是採用pcm格式編碼的聲音資料,也可以是採用其它格式編碼的聲音資料,但目前一般採用pcm編碼的聲音資料 兩者區別就是這些
pcm是一個通訊上的概念,脈衝編碼調製。wav是媒體概念,體現的是封裝。wav檔案可以封裝pcm編碼資訊,也可以封裝其他編碼格式,例如mp3等。