スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

AudioTrackをAudioQueueのように使う

前回Androidでオーディオ処理をおこなうのは少々きついようなことを書きましたが、Android SDKではおそらくAudioTrackを使うのが一番無難だと思います。
このAudioTrack、仕組みがCore AudioのAudioQueueによく似てるんですよね。いわゆるキュー方式というやつです。iPhoneアプリの開発をしてたときは、かなりこのAudioQueueにお世話になってましたw

AudioQueueでオーディオ再生をおこなうときは、まず何フレームかのバッファ(オーディオデータを入れる箱のようなもの。私は1024フレームくらいにしてました)を再生する前に3つほどエンキュー(再生をしてもらうための順番待ち)しておきます。再生され終わったバッファ(へのポインタ)はコールバック関数の引数として渡されます。そこで新たなオーディオデータを入れ、再度エンキューする形でバッファは使い回されます。
Appleが提供するAudioQueueProgrammingGuideにはだいたいそのようなことが書かれています。

ということは、若干AudioQueueとは異なりますがAudioTrackもこのように利用するのがベターなんじゃないかと思ったのです。
ですが、ネットの様々な方のブログ等を拝見させていただくと、必ずしもAudioQueueのように利用してらっしゃらないみたいです。そこで、AudioTrackをAudioQueue流に使用したコードを書いてみました。


public class AudioTrackTest extends Activity
implements AudioTrack.OnPlaybackPositionUpdateListener,View.OnClickListener {

private Button play;
private Button stop;
private AudioTrack track;
short[] buf = new short[1024];
double phase = 0.0;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

//再生、停止ボタンの準備
play = (Button)this.findViewById(R.id.button1);
play.setOnClickListener(this);
stop = (Button)this.findViewById(R.id.button2);
stop.setOnClickListener(this);

//AudioTrackのインスタンス生成
track = new AudioTrack(AudioManager.STREAM_MUSIC, // 音楽再生用のオーディオストリーム
44100, // サンプリングレート
AudioFormat.CHANNEL_OUT_MONO, // モノラル
AudioFormat.ENCODING_PCM_16BIT, // 16bit PCM
AudioTrack.getMinBufferSize(44100,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT),// 合計バッファサイズ
AudioTrack.MODE_STREAM); // ストリームモード

//コールバック通知先の指定
track.setPlaybackPositionUpdateListener(this);

//1024フレーム分再生ごとに通知をおこなう
track.setPositionNotificationPeriod(1024);
}

//無視
public void onMarkerReached(AudioTrack track) {

}

//コールバック内で1024フレーム分のバッファをエンキュー
public void onPeriodicNotification(AudioTrack track) {
sinWave(buf,440);
track.write(buf,0,buf.length);
}

//ボタンをタッチしたときの動作
public void onClick(View view) {
//再生ボタンタッチ時
if (view == play & track.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {
//1024サンプル分のバッファを3つエンキュー
int i;
for(i=0;i<3;i++){
sinWave(buf,440);
track.write(buf,0,buf.length);
}
// 再生開始
track.play();
}
//停止ボタンタッチ時
else if (view == stop& track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
//停止
track.stop();
}
}

//サイン波の生成
void sinWave(short data[],double freq) {
for (int i = 0; i < data.length; i++) {
data[i] = (short)(Short.MAX_VALUE * Math.sin(phase));
phase += 2.0 * Math.PI * freq / 44100;
}
}
}
(すいません、フツーにコピペしたらインデントがなくなっちゃいました…)
上のコードは440Hzのサイン波(44.1kHz,16bit モノラル)を鳴らすアプリです。

重要なのは、再生する前に1024フレーム分のバッファをあらかじめ3つwrite()しておいてからplay()することです。またAudioQueueのようなコールバック関数ではないのですが、1024フレーム分再生される度にonPeriodicNotification()というメソッドが呼び出されるよう登録し、そのなかで新しいオーディオデータをバッファに入れ、write()するようにしています。細切れにwrite()しているので、ずーっとブロックされっぱなしってことはないと思うのですが、どうなんでしょうか。その辺はちょっと良く分かりません。

1024フレーム×3で理論的には約69.7ms、AudioTrackで遅延が生じる計算になります。一応私のスマートフォンではちゃんと再生できましたが、最初にエンキューしておくバッファを減らしたり、フレーム数を減らしたりすると、とたんに鳴らなくなってしまいました。どうやらこれがぎりぎりのようです。

なにせjavaは初心者なものですから、もし間違いなどございましたら是非ご指摘いただければ助かります。
スポンサーサイト

コメントの投稿

非公開コメント

FC2ブログランキング

FC2Blog Ranking

カウンター
プロフィール

ibanez_320tzs

Author:ibanez_320tzs
人と音の間にあるインタラクション、音絡みのメディアアートについて研究している大学院生です。
LAN経由で音をやりとりするiPhoneの楽器アプリなど作ってます。
「音・人・空間をつなぐ未来の音楽」が広まればいいな。

カテゴリ
Android関連書籍
Twitter
最新記事
月別アーカイブ
リンク
RSSリンクの表示
ブロとも申請フォーム

この人とブロともになる

最新コメント
最新トラックバック
検索フォーム
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。