ads by Amazon

2013年4月11日木曜日

12. 波形合成

前の記事では、波形合成を行わず、スロット毎(効果音の種類毎)にプライオリティを設けて、ひとつの効果音だけ発音する効果音の発音システムを実装しました。しかし、昨今のゲームは通常、複数の効果音を同時に発音することができます。

この記事では、前の記事で作成したtest05.cppをベースにして、複数の効果音を同時に発音するプログラムを作成してみたいと思います。

(1)波形合成の考え方
複数の効果音を同時に発音することは、波形の合成を行うことで実現できます。
波形の合成というと、何やら難しい定理でも登場しそうな気がするかもしれませんが、波形の合成は、単純に鳴らす全ての波形データを足し算するだけで実現できます。(合成された波形を分解するには、フーリエ解析が必要になるので若干難しいですが、合成するのは簡単です)

(2)オーバフロー対策
波形データを足し算する場合、オーバフロー対策が必要になります。
波形データは16bitの整数(-32,768~32,767)ですが、波形の足し算を行った結果、-32,769以下または32,768以上になるとオーバーフローしてしまいます。そこで、合成を行う時は32bitの数値に合成し、バッファリングする時に16bitの範囲に丸める必要があります。

(3)実装
以上の2点を踏まえて、mkbuf関数を次のように改造します。
/* バッファリング処理(合成版) */
static void mkbuf(char* buf,size_t size)
{
    int i,j;
    int cs;
    int wav;
    short* bp;

    /* 無音状態にする */
    memset(buf,0,size);

    /* 合成しながらバッファリング */
    for(i=0;i<256;i++) {
        if(_eff[i].flag) {
            if(1<_eff[i].flag) {
                _eff[i].pos=0;
                zflag((unsigned char)i);
                aflag((unsigned char)i);
            }
            /* コピーサイズの計算 */
            cs=_eff[i].size-_eff[i].pos;
            if(size<(size_t)cs) {
                cs=(int)size;
            }
            /* バッファリング */
            for(j=0;j<cs;j+=2) {
                bp=(short*)(&buf[j]);
                wav=*bp;
                wav+=*((short*)&(_eff[i].dat[_eff[i].pos+j]));
                if(32767<wav) wav=32767;
                else if(wav<-32768) wav=-32768;
                (*bp)=(short)wav;
            }
            /* ポジション・チェンジ */
            _eff[i].pos+=cs;
            if(_eff[i].size<=_eff[i].pos) {
                /* 発音終了 */
                zflag((unsigned char)i);
            }
        } else {
            _eff[i].pos=0;
        }
    }
}
これで完成です。簡単ですね。
一応、完成版のtest06.cppはコチラからダウンロードできます。

理論はこんなに簡単であるにも関わらず、効果音の合成が長らく実現できていない時期があった原因は、test05.cppのバッファリング処理(memcpy)と比べて演算コストが高いためです。

0 件のコメント:

コメントを投稿