2013年12月26日 星期四

[android]AudioTrack使用,用來完成PCM Buffer To Play,並提供Play、Pause、Resume與Stop的說明。

近期,在研究如何用播放buffer PCM,作一個底層的播放,自己控制,不透過MediaPlayer class來控制這些流程,因為工作需要,所以就不說明了,重點放在怎麼做這樣的事情。

1.先init()這個AudioTrack,一開始它會要求你這個AudioTrack的buffer大小,這個取決於你的應用,另外重要的是AudioTrack.MODE_STATIC,這個是說明你這個buffer是一次寫入還是分批寫入,如果你是一次寫入的話那就用MODE_STATIC,分批寫入的話就用MODE_STREAM,但是兩邊的用法不一樣,如果你是想要用MODE_STREAM來完成這樣的事情,那我這篇內文就不適用了。

2.playAudioTrack(short[] data, int offset, int length),這就是播放的function,不過因為我這邊使用的是MODE_STATIC mode,所以offset在這裡都不適用,因為不需要向後加data,所以就只要提供data buf 跟data size即可,注意編碼問題(big little endian),如果你的測試音檔從windows上copy過來,請注意它的編碼,會跟Android不一樣。

3.playpause(),這個就是]pause與resume的function,沒啥好說的,大致上就是按下pause,記住你現在的位置(getPlaybackHeadPosition),resume播放這個位置(setPlaybackHeadPosition)。

4.playstop(),很奇怪的設計,要先pause()後,才可以stop(),直接stop沒作用,似乎下stop指令只有改PLAYSTATE_STOPPED為1而已,要清楚還是要去看他這邊stop後做了哪些事情,我是沒再去看這邊的內容。

5.如果你需要Playback的話,請記得加上OnPlaybackPositionUpdateListener 它會提供兩個function給你。
onMarkerReached:大概的意思當你設定位置到了,它會通知你,透過setNotificationMarkerPosition來設定,這大概的想法就是拿來做播放完畢的通知,通知UI層核時播放完畢

onPeriodicNotification:大概的意思當你設定位置的n倍就會通知你,例如你設定frame=1000,那它1000、2000、3000....都會通知你。

6.這個範例大致上說明了怎麼使用,但不完整,但是該點到的都有點到,但是沒包裝。

AndroidManifest.xml 開權限:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

Source Code:
public class JWavePlayer implements OnPlaybackPositionUpdateListener {
private static final boolean DBG = true;
private static final String TAG = "JWavePlayer";

private int audioFrequency;
private int audioChannel; // 8k or 16k
private int audioEncoding; // Mono or STEREO
private int audioTrackSize;// 288000/16000=18(s) 每一個TrackSize容納18秒
AudioTrack mAudioTrack; // 8bit or 16bit

public short[] g_data;
public int g_offset;
public int g_length;
public int g_frame;
public JWavePlayer() {
audioFrequency = 16000;
audioChannel = AudioFormat.CHANNEL_OUT_MONO;
audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
audioTrackSize=288000;
}
public void init() {
if (mAudioTrack != null) {
release();
}
int minBufSize = AudioTrack.getMinBufferSize(audioFrequency,
audioChannel, audioEncoding);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, audioFrequency,
audioChannel, audioEncoding, audioTrackSize, AudioTrack.MODE_STATIC);
mAudioTrack.setPlaybackPositionUpdateListener(this);
}

public void playAudioTrack(short[] data, int offset, int length) {
if (data == null || data.length == 0 && mAudioTrack.getPlayState()==mAudioTrack.PLAYSTATE_STOPPED) {
return;
}
try {

mAudioTrack.reloadStaticData();
mAudioTrack.write(data, offset, length);
mAudioTrack.setNotificationMarkerPosition(length/2);
mAudioTrack.play();
} catch (Exception e) {
// TODO: handle exception
Log.i("MyAudioTrack", "catch exception..."+e.getMessage());
}
}
public void playpause(){
try {
if (mAudioTrack.getPlayState()==mAudioTrack.PLAYSTATE_PLAYING){
g_frame=mAudioTrack.getPlaybackHeadPosition();
mAudioTrack.pause();
}else if (mAudioTrack.getPlayState()==mAudioTrack.PLAYSTATE_PAUSED){
mAudioTrack.setPlaybackHeadPosition(g_frame);
mAudioTrack.play();
}
} catch (Exception e) {
Log.i("MyAudioTrack", "catch exception..."+e.getMessage());
}
Log.e("MyAudioTrack",String.valueOf(mAudioTrack.getPlayState()) );
}
public void playStop(){
mAudioTrack.pause();
mAudioTrack.stop();
Log.e("MyAudioTrack",String.valueOf(mAudioTrack.getPlayState()) );
}
public void state(){
Log.d(TAG,"getPlayState:"+mAudioTrack.getPlayState());
//PLAYSTATE_STOPPED 1
//PLAYSTATE_PAUSED 2
//PLAYSTATE_PLAYING 3
Log.d(TAG,"getState:"+mAudioTrack.getState());
//STATE_UNINITIALIZED 0
//STATE_INITIALIZED 1
//STATE_NO_STATIC_DATA 2
//Log.d(TAG,"getStreamType:"+mAudioTrack.getStreamType());
}

public void release() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
}
}
@Override
public void onMarkerReached(AudioTrack track) {
// TODO Auto-generated method stub
Log.e(TAG,"MarkerReached State:"+track.getState());

}
@Override
public void onPeriodicNotification(AudioTrack track) {
// TODO Auto-generated method stub
Log.e(TAG,"PeriodicNotification State:"+track.getState());
}

}

沒有留言:

張貼留言