iアプリでPCM再生

iアプリでSDカードからwavファイルをストリーミング的に読み出して再生するプログラムを作っています。
Ga氏作成のWSX2音源のコードを利用させてもらっています。
PCエンジンエミュレータを音対応にする前段階としての、実験プログラムです。ただファイルからPCMデータを読み出して再生するだけ、のプログラムなんですがこれがなかなかうまくいきませんでした。
wsx2のplayメソッドを短い周期で呼びすぎて、最初は音がでない!とハマリました。1/60秒周期でplayメソッドを呼ぶのは間隔が短すぎたようです。とりあえずエミュレータで使うことは忘れて、1秒周期で呼ぶようにしたら、音がでました。
でも音が途切れます。かなりブチブチです。
各処理の処理時間を計測してみたところ、SDカードからデータを読むのがやたら遅いみたいです。全然再生処理のタイミングに間に合ってないです。32000Hz16bitPCMなので、readShortで1サンプルずつデータを読んでいたのですが、細かく読むと遅いようです。バイト配列にまとめてドン!と読むようにしたらかなり速くなりました。これでとりあえずは、目立つ途切れはなくなりましたが、若干、切り替えの部分がノイズのように聞こえます。

iアプリの音声再生関係の仕様は、着メロファイルをただ再生するだけ、のことしか前提にないみたいで、プログラムから音を動的にリアルタイムで作ることはかなり面倒、というより、かなりの裏技的な実装を強いられます。
その困難をなんとか乗り越えたのがWSX2で、着メロファイル形式のイメージをメモリ上に動的に作って再生します。しかし、着メロファイル形式のイメージを作る処理と音を再生する処理は同時にできません(着メロファイルイメージに書き込みつつ再生はできない)。そのため、着メロファイルイメージを再生するためのオーディオプレゼンタを2個作って、それを交互に切り替えながら再生しています。片方のオーディオプレゼンタで再生している間に、もう片方に音データをセットしてやるわけです。
ダブルバッファ的な方法です。普通、ストリーム再生するためのサウンドバッファはリングバッファにすることが多い(DirectSoundもそう)のですが、書き込みと再生を一緒にできないので、2個にわける必要があるわけです。
そんなわけで、片方のオーディオプレゼンタが再生を終える瞬間に、もう片方のオーディオプレゼンタの再生を開始する必要があり、タイミングが遅れると音が途切れて聞こえる結果になります。今はこの部分で、ノイズが発生しているようです。

ノイズ無く再生するためには、2つのオーディオプレゼンタを切り替えて再生するタイミングをきっちりあわせる必要があります。既存のエミュレータの場合、エミュレータのループが60FPSをキープする前提で再生タイミングをとっているようです。
なので、エミュレータの処理が重くなったりして60FPSがキープできないと、音が途切れます。

再生タイミングがエミュレーションのループに依存する実装だと、よっぽど携帯の処理速度が速くないと途切れる可能性が高くなってしまいます。音が途切れないようにするには、タイミングを別につくってやるのがよさそうです。タイマで一定周期のインターバルで再生メソッドを呼び出すようにしたら、音が途切れることはなくなるんじゃないだろうか?と思ってやってみました。
このやり方はうまくいったみたいで、音の途切れやノイズはほとんど判らないレベルになりました。

SDカードから読み出したwavファイルを再生するという、単純な応用の場合は、タイマイベントで音源を駆動する実装でうまいぐあいに音が途切れずなめらかに再生できました。しかし、これをエミュレータに組み込むにはまだ問題があります。
エミュレータでは、処理が遅くて60FPSをキープできない場合、音楽のテンポがゆっくりになるように実装しないと、結局はPCMのサンプル数が再生時間に対して足りなくなって音の途切れが発生してしまうので、そこの実装をうまくやる必要があります。

WSX2も若干ながら最適化してみました。G721(ADPCM)のエンコード処理で、量子化テーブルをリニアサーチしている部分を、テーブル一発で値が取れるようにしてみました。負荷がどれぐらい変わったかは、ちゃんと計測したらまた書きます…