ads by Amazon

ラベル OS共通 の投稿を表示しています。 すべての投稿を表示
ラベル OS共通 の投稿を表示しています。 すべての投稿を表示

2013年4月12日金曜日

15. 蛙の歌を歌わせてみる

前の記事で作成したtoneテーブル(tone.c)のテストを兼ねて、test06.cppをベースにして蛙の歌をBGMとして再生するプログラム(発音テスト)を実装してみることにします。なお、この段階では、まだオペレーションを実装していないので、ダイレクトにテーブルの波形情報をバッファリングする形で実装します。

(1)toneテーブルのextern宣言
まず、tone.cで宣言しているtoneテーブル(TONE1、TONE2、TONE3、TONE4)をextern宣言でアクセスできるようにします。
/* トーンテーブル */ extern "C" { extern short* TONE1[85]; /* 三角波 */ extern short* TONE2[85]; /* ノコギリ波 */ extern short* TONE3[85]; /* 矩形波 */ extern short* TONE4[85]; /* ノイズ */ } 

(2)発音処理の実装
mkbuf関数の、効果音を発音後の部分に、次の発音テストコードを実装します。
/* 音源エミュレータテスト */ { static int hz; static int ply[7]={39,41,43,44,43,41,39}; static int p1; // 発音位置 static int p2; // 波形位置 for(i=0;i<size;i+=2) { bp=(short*)(&buf[i]); wav=*bp; wav+=TONE3[ply[p1]][1+p2]*64; if(32767<wav) wav=32767; else if(wav<-32768) wav=-32768; (*bp)=(short)wav; p2+=2; if(TONE3[ply[p1]][0]<=p2) p2=0; hz++; if(11025<=hz) { p1++; if(7<=p1) p1=0; p2=0; hz=0; } } }

上記のテストコードでは、11025Hzの間隔(=0.5秒間隔)で、「ドレミファミレド」の発音を繰り返しています。別にフルコーラスでも良いのですが、単なるテストコードなので簡単に。なお、TONE3(矩形波)のテーブルを使っていますが、この部分をTONE1にすれば三角波、TONE2にすればノコギリ波、TONE4にすればノイズが奏でられるようになります。

これで実装完了です。
簡単ですね。

この実装を施したtest07.cppはコチラからダウンロードできます。
test07.cppのコンパイルは、次のコマンドでOKです。
CL /MT test07.cpp tone.c user32.lib dsound.lib dxguid.lib

BGM(蛙の歌)を鳴らした状態で、効果音の発音をすることができることも確認してください。
これで、BGMと効果音の両方を鳴らすことができました。

まぁ、ここまでは簡単です。
本格的に難しくなるのは、ここから先です

・・・ここからが地獄だ・・・

恐らく、ここまでの記事では脱落者は少ないものと推測しています。そもそも、このブログで扱っているネタに興味を持たれる時点で、それなりに高いプログラミング能力をお持ちの方が大半だろうと思われるので。しかし、ここから先は、脱落者続出になるかもしれません。

誰も読まないブログ?それもまた一興。

14. toneテーブルの準備

8. PSG音源と波形メモリ音源の違い」で解説したように、これから作成する音源モジュールは、全音ストア方式の音源モジュールです。そこで、楽器毎に全音階分の基礎波形データの音色テーブル(toneテーブル)を準備する必要があります。

(1)テーブル形式
基礎波形データのテーブル形式は、音階毎に1Hz分のパルス符号数と符号付16bit整数のパルス符号を持つ可変長テーブルとします。ごちゃごちゃ言ってますが、要するにshort型配列の塊です。
1Hz分のパルス符号数(n)は、音階によって異なります。
音は、低い音の周期(n1)が長く、高い音の周期(n2)が短い特性があります。
つまり、配列サイズはn2<n1となります。
したがって、可変長配列として準備するのが妥当だと言えます。

(2)テーブル作成作業
それでは、上記の理論を踏まえて、三角波、矩形波、ノコギリ波、ノイズのtoneテーブルを手書きで作成・・・などと愚かなことをやる人は、このブログの読者には居ないと思います。我々プログラマは、そういったバカバカしい作業を効率的にこなすのが得意なので。

私の場合、下記のプログラムを組んで、toneテーブルを一瞬で作成しました。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char* argv[]) { const char *n1[12]={"A","A#","B","C","C#","D","D#","E","F","F#","G","G#"}; const char *n2[12]={"A","AS","B","C","CS","D","DS","E","F","FS","G","GS"}; float cnt[85]={ 55.0000 /* A 0 /00 */ , 58.2705 /* A# 0 /01 */ , 61.7354 /* B 0 /02 */ , 65.4064 /* C 1 /03 */ , 69.2957 /* C# 1 /04 */ , 73.4162 /* D 1 /05 */ , 77.7817 /* D# 1 /06 */ , 82.4069 /* E 1 /07 */ , 87.3071 /* F 1 /08 */ , 92.4986 /* F# 1 /09 */ , 97.9989 /* G 1 /10 */ , 103.8262 /* G# 1 /11 */ , 110.0000 /* A 1 /12 */ , 116.5409 /* A# 1 /13 */ , 123.4798 /* B 1 /14 */ , 130.8128 /* C 2 /15 */ , 138.5913 /* C# 2 /16 */ , 146.8324 /* D 2 /17 */ , 155.5635 /* D# 2 /18 */ , 164.8138 /* E 2 /19 */ , 174.6141 /* F 2 /20 */ , 184.9972 /* F# 2 /21 */ , 195.9977 /* G 2 /22 */ , 207.6523 /* G# 2 /23 */ , 220.0000 /* A 2 /24 */ , 233.0819 /* A# 2 /25 */ , 246.9417 /* B 2 /26 */ , 261.6256 /* C 3 /27 */ , 277.1826 /* C# 3 /28 */ , 293.6648 /* D 3 /29 */ , 311.1270 /* D# 3 /30 */ , 329.6276 /* E 3 /31 */ , 349.2282 /* F 3 /32 */ , 369.9944 /* F# 3 /33 */ , 391.9954 /* G 3 /34 */ , 415.3047 /* G# 3 /35 */ , 440.0000 /* A 3 /36 */ , 466.1638 /* A# 3 /37 */ , 493.8833 /* B 3 /38 */ , 523.2511 /* C 4 /39 */ , 554.3653 /* C# 4 /40 */ , 587.3295 /* D 4 /41 */ , 622.2540 /* D# 4 /42 */ , 659.2551 /* E 4 /43 */ , 698.4565 /* F 4 /44 */ , 739.9888 /* F# 4 /45 */ , 783.9909 /* G 4 /46 */ , 830.6094 /* G# 4 /47 */ , 880.0000 /* A 4 /48 */ , 932.3275 /* A# 4 /49 */ , 987.7666 /* B 4 /50 */ , 1046.5023 /* C 5 /51 */ , 1108.7305 /* C# 5 /52 */ , 1174.6591 /* D 5 /53 */ , 1244.5079 /* D# 5 /54 */ , 1318.5102 /* E 5 /55 */ , 1396.9129 /* F 5 /56 */ , 1479.9777 /* F# 5 /57 */ , 1567.9817 /* G 5 /58 */ , 1661.2188 /* G# 5 /59 */ , 1760.0000 /* A 5 /60 */ , 1864.6550 /* A# 5 /61 */ , 1975.5332 /* B 5 /62 */ , 2093.0045 /* C 6 /63 */ , 2217.4610 /* C# 6 /64 */ , 2349.3181 /* D 6 /65 */ , 2489.0159 /* D# 6 /66 */ , 2637.0205 /* E 6 /67 */ , 2793.8259 /* F 6 /68 */ , 2959.9554 /* F# 6 /69 */ , 3135.9635 /* G 6 /70 */ , 3322.4376 /* G# 6 /71 */ , 3520.0000 /* A 6 /72 */ , 3729.3101 /* A# 6 /73 */ , 3951.3101 /* B 6 /74 */ , 4186.0090 /* C 7 /75 */ , 4434.9221 /* C# 7 /76 */ , 4698.6363 /* D 7 /77 */ , 4978.0317 /* D# 7 /78 */ , 5274.0409 /* E 7 /79 */ , 5587.6517 /* F 7 /80 */ , 5919.9108 /* F# 7 /81 */ , 6271.9270 /* G 7 /82 */ , 6644.8752 /* G# 7 /83 */ , 7040.0000 /* A 7 /84 */ }; float fhz; int hz; int i; int tone=0; float n,m; printf("/*\n"); printf(" *--------------------------------------------------\n"); printf(" * 三角波(SANKAKU)\n"); printf(" *--------------------------------------------------\n"); printf(" */\n"); for(tone=0;tone<85;tone++) { fhz=88200/cnt[tone]; hz=(int)fhz; printf("/* #%02d: tone %s oct %d */\n",tone,n1[tone%12],1+tone/12); m=64.0f / (fhz/4); printf("short TRI_%s%d[%d]={%d",n2[tone%12],1+tone/12,hz+1,hz); for(n=0,i=0;i<hz/4;i++,n+=m) { printf(",%d",(int)n); } for(i=0;i<hz/2;i++,n-=m) { printf(",%d",(int)n); } for(i=0;i<hz/4;i++,n+=m) { printf(",%d",(int)n); } printf("};\n\n"); } printf("short* TONE1[85]={"); for(tone=0;tone<85;tone++) { if(tone)printf(","); printf("TRI_%s%d",n2[tone%12],1+tone/12); } printf("};\n\n"); printf("/*\n"); printf(" *--------------------------------------------------\n"); printf(" * ノコギリ波(NOKOGIR)\n"); printf(" *--------------------------------------------------\n"); printf(" */\n\n"); for(tone=0;tone<85;tone++) { fhz=88200/cnt[tone]; hz=(int)fhz; printf("/* #%02d: tone %s oct %d */\n",tone,n1[tone%12],1+tone/12); m=64.0f / (fhz/2); printf("short NOC_%s%d[%d]={%d",n2[tone%12],1+tone/12,hz+1,hz); for(n=0,i=0;i<hz/2;i++,n+=m) { printf(",%d",(int)n); } for(n=-64.0f;i<hz;i++,n+=m) { printf(",%d",(int)n); } printf("};\n\n"); } printf("short* TONE2[85]={"); for(tone=0;tone<85;tone++) { if(tone)printf(","); printf("NOC_%s%d",n2[tone%12],1+tone/12); } printf("};\n\n"); printf("/*\n"); printf(" *--------------------------------------------------\n"); printf(" * 矩形波(KUKEI)\n"); printf(" *--------------------------------------------------\n"); printf(" */\n\n"); for(tone=0;tone<85;tone++) { fhz=88200/cnt[tone]; hz=(int)fhz; printf("/* #%02d: tone %s oct %d */\n",tone,n1[tone%12],1+tone/12); printf("short KUK_%s%d[%d]={%d",n2[tone%12],1+tone/12,hz+1,hz); for(i=0;i<hz/2;i++) { printf(",%d",48); } for(;i<hz;i++) { printf(",%d",-48); } printf("};\n\n"); } printf("short* TONE3[85]={"); for(tone=0;tone<85;tone++) { if(tone)printf(","); printf("KUK_%s%d",n2[tone%12],1+tone/12); } printf("};\n\n"); printf("/*\n"); printf(" *--------------------------------------------------\n"); printf(" * ノイズ\n"); printf(" *--------------------------------------------------\n"); printf(" */\n\n"); for(tone=0;tone<85;tone++) { fhz=88200/cnt[tone]; hz=(int)fhz; printf("/* #%02d: tone %s oct %d */\n",tone,n1[tone%12],1+tone/12); printf("short NIZ_%s%d[%d]={%d",n2[tone%12],1+tone/12,hz+1,hz); for(i=0;i<hz/2;i++) { printf(",%d",rand()%64); } for(;i<hz;i++) { printf(",%d",-(rand()%64)); } printf("};\n\n"); } printf("short* TONE4[85]={"); for(tone=0;tone<85;tone++) { if(tone)printf(","); printf("NIZ_%s%d",n2[tone%12],1+tone/12); } printf("};\n\n"); return 0; } 
上記プログラムをコンパイルして実行すれば、音色テーブルの配列を宣言するC言語のソースコードが標準出力されるので、それをリダイレクトで保存します。(一種のCGIプログラムです)

保存したファイル(tone.c)はコチラに置いておきます。

(3)tone.c
このソースコードでは、TONE1、TONE2、TONE3、TONE4というshort型配列に、三角波、ノコギリ波、矩形波、ノイズの1Hz×85音階分の配列を宣言しています。各音階配列は、第一要素がパルス符号の数で、第二要素以降がパルス符号データになっています。

(4)補足1
なお、上記の説明を読んでいて、「基礎波形のパルス符号はshort型よりsigned char型の方が良いのでは?」という点に気づけた方が居るとすれば、素晴らしいです。その方がメモリ容量を1/2に節約できるので、品質(効率性)が良くなります。
しかし、波形情報は基本的に16bitで計算するので、基数変換のコストを削るために、short型にしておきました。ついでに、配列の最初の要素にパルス符号数を格納しているので、char型だと収まりきらないという事情があり、short型にしておきました。
でも、結局計算する時は32bitにしている訳だし、後者の件は構造体にすれば問題無いので、signed charの方が良いかもしれません。

(5)補足2
あと、実際の音源モジュールのサンプリング周波数は22050Hzですが、波形データはそれより細かい粒度で作成しています。これは、周波数の誤差を少なくするために必要な処置です。(割り切れない数値が誤差となり、誤差が大きいと若干音痴になってしまうため、基礎波形は粒度を細かくすることで、誤差を少なくしています)

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)と比べて演算コストが高いためです。

10. wavファイルの構造と変換

効果音用のPCMデータは、wavファイルから取得することにします。

(1)wavファイルのフォーマット
↓のサイトに分かり易く記述されています。
http://www.kk.iij4u.or.jp/~kondo/wave/

(2)wavファイルはそのまま使ってはならない
wavファイル(RIFF構造の一種)は、非常に汎用性の高いフォーマットです。
汎用性が高いということは、逆説すれば無駄(冗長)な情報が多いということです。
例えば、周波数、ビットレート、チャネル数などの情報は、VGS音源の場合は22050Hz、16bit、1chに固定されているので、ファイルに持たせるのはナンセンスです。そこで、wavファイルから必要な情報を抜き取り、独自ファイル(PCMファイル)のフォーマットに変換したものをリソースとして持つことにします。
なお、VGS音源にとって必要な情報は、以下の2種類です。
  • パルス符号の数
  • パルス符号データの塊
(3)変換コマンド(vgswav)
VGSでは、22050Hz、16bit、1chのwavファイルを独自形式(PCM)に変換するvgswavというコマンドを提供しています。vgswavコマンドのソースコードは次のような感じになっています。

■vgswav.c
/* WAVEを独自形式のPCMデータに変換する */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 情報ヘッダ */
struct DatHead {
    char riff[4];
    unsigned int fsize;
    char wave[4];
    char fmt[4];
    unsigned int bnum;
    unsigned short fid;
    unsigned short ch;
    unsigned int sample;
    unsigned int bps;
    unsigned short bsize;
    unsigned short bits;
    char data[4];
    unsigned int dsize;
};

int main(int argc,char* argv[])
{
    FILE* fpR=NULL;
    FILE* fpW=NULL;
    int rc=0;
    struct DatHead dh;
    char* data=NULL;
    char mh[4];

    /* 引数チェック */
    rc++;
    if(argc<3) {
        fprintf(stderr,"usage: vgswav input(wav) output(pcm)\n");
        goto ENDPROC;
    }

    /* 読み込みファイルをオープン */
    rc++;
    if(NULL==(fpR=fopen(argv[1],"rb"))) {
        fprintf(stderr,"ERROR: Could not open: %s\n",argv[1]);
        goto ENDPROC;
    }

    /* 情報ヘッダを読み込む */
    rc++;
    if(sizeof(dh)!=fread(&dh,1,sizeof(dh),fpR)) {
        fprintf(stderr,"ERROR: Invalid file header.\n");
        goto ENDPROC;
    }

    /* 形式チェック */
    rc++;
    if(0!=strncmp(dh.riff,"RIFF",4)) {
        fprintf(stderr,"ERROR: Not RIFF format.\n");
        goto ENDPROC;
    }
    rc++;
    if(0!=strncmp(dh.wave,"WAVE",4)) {
        fprintf(stderr,"ERROR: Not WAVE format.\n");
        goto ENDPROC;
    }
    rc++;
    if(0!=strncmp(dh.fmt,"fmt ",4)) {
        fprintf(stderr,"ERROR: Invalid format.\n");
        goto ENDPROC;
    }
    rc++;
    if(0!=strncmp(dh.data,"data",4)) {
        fprintf(stderr,"ERROR: Invalid data.\n");
        goto ENDPROC;
    }

    printf("Header of %s:\n",argv[1]);
    printf(" - Format: %d\n",dh.fid);
    printf(" - Channel: %dch\n",dh.ch);
    printf(" - Sample: %dHz\n",dh.sample);
    printf(" - Transform: %dbps\n",dh.bps);
    printf(" - Block-size: %dbyte\n",(int)dh.bsize);
    printf(" - Bit-rate: %dbit\n",(int)dh.bits);
    printf(" - PCM: %dbyte\n",(int)dh.dsize);


    rc++;
    if(22050!=dh.sample) {
        fprintf(stderr,"ERROR: Sampling rate is not 22050Hz.\n");
        goto ENDPROC;
    }
    rc++;
    if(1!=dh.ch) {
        fprintf(stderr,"ERROR: Sampling channel is not 1(mono).\n");
        goto ENDPROC;
    }
    rc++;
    if(16!=dh.bits) {
        fprintf(stderr,"ERROR: Sampling bit rate is not 16bit.\n");
        goto ENDPROC;
    }
    rc++;
    if(dh.sample*2!=dh.bps) {
        fprintf(stderr,"ERROR: Invalid transform-rate(byte/sec).\n");
        goto ENDPROC;
    }

    /* 波形データを読む込む領域を確保する */
    rc++;
    if(NULL==(data=(char*)malloc(dh.dsize))) {
        fprintf(stderr,"ERROR: Memory allocation error.\n");
        goto ENDPROC;
    }

    /* 波形データを読み込む */
    rc++;
    if(dh.dsize!=fread(data,1,dh.dsize,fpR)) {
        fprintf(stderr,"ERROR: Could not read PCM data.\n");
        goto ENDPROC;
    }

    /* 書き込みファイルをオープン */
    rc++;
    if(NULL==(fpW=fopen(argv[2],"wb"))) {
        fprintf(stderr,"ERROR: Could not open: %s\n",argv[2]);
        goto ENDPROC;
    }

    /* ヘッダ書き込み */
    rc++;
    strcpy(mh,"EFF");
    if(4!=fwrite(mh,1,4,fpW)) {
        fprintf(stderr,"ERROR: Could not write header.\n");
        goto ENDPROC;
    }

    /* サイズ情報書き込み(Big-endian) */
    rc++;
    mh[0]=((dh.dsize & 0xFF000000) >> 24) & 0xFF;
    mh[1]=((dh.dsize & 0x00FF0000) >> 16) & 0xFF;
    mh[2]=((dh.dsize & 0x0000FF00) >> 8) & 0xFF;
    mh[3]=dh.dsize & 0xFF;
    if(4!=fwrite(mh,1,4,fpW)) {
        fprintf(stderr,"ERROR: Could not write size.\n");
        goto ENDPROC;
    }

    /* PCM書き込み */
    if(dh.dsize!=fwrite(data,1,dh.dsize,fpW)) {
        fprintf(stderr,"ERROR: Could not write data.\n");
        goto ENDPROC;
    }

    rc=0;

    /* 終了処理 */
ENDPROC:
    if(data) {
        free(data);
    }
    if(fpR) {
        fclose(fpR);
    }
    if(fpW) {
        fclose(fpW);
    }
    return rc;
}
このコマンドを実行すれば、wav形式のファイルが、先頭32bit(4バイト)にアイキャッチ、次の32bit(4バイト)にサイズ、残りがPCMデータという非常にシンプルなデータ構造に変換されます。ちなみに、サイズ情報をビッグエンディアンに変換しているのはナンセンスです。私の自前のツールの仕様の関係で、そういう仕様にしています。(PCMデータそのものはリトルエンディアンです)

(4)効果音はストアすること
AndroidやiPhoneの標準のAPIでは、wavファイルをそのまま発音する機能があります。
しかし、効果音というのは何回も繰り返し再生されるものなので、再生の都度、ファイルを読み込むというのは、処理効率が悪すぎます。
VGSの場合、全リソースデータ(ROMファイル)の内容を、起動時に一括でメモリ領域に展開していて、以降はメモリ領域を参照することでデータアクセスを行っています。これにより、リアルタイム性の高い効果音の発音処理を実現しています。ゲームにとっての効果音は、BGM以上に重要な存在なので、効果音データは必ず事前にストア済みの情報を使わなければなりません。

2013年4月10日水曜日

7. 440Hz(A)の矩形波を鳴らし続けてみる

それでは、「4. 無音を鳴らし続ける処理の実装(Windows編)」で示したtest03.cpp(無音を鳴らし続けるプログラム)を少し改造して、440HzのA(ラ)を鳴らし続けるプログラムを作成してみたいと思います。なお、他所の解説サイトでは、サイン波を例に示している所が多いみたいですが、「サイン波よりも矩形波の方がコンピュータ向き」ということを体感して頂く為、ここでは矩形波を例に説明します。

(1)処理の考え方
test03.cppの場合、1秒の長さは22050Hzです。
つまり、出力する波形の周期は、22050Hz÷440Hzということで、だいたい50Hzのインターバルで発音してあげれば良いという計算になります。正確には50.11363636363636ですが、先ずは、そこまで厳密に考慮せず、ゆとり式に「だいたい50Hz」ということで処理を実装することにします。どうせテスト目的のものですし。なお、50Hzの矩形波(交流波)を出力するには、25Hzで「正」と「負」を交互に書き込みます。

(2)矩形波バッファリングの実装
それでは、サウンドスレッド(sndmain)に対して、だいたい440Hzの矩形波をバッファリングする処理を実装していきたいと思います。

先ずは、矩形波の算出をするための変数を宣言します。

■変数宣言の追加
    /* 矩形波バッファリング用の変数*/
    int i;
    int hz=0;
    short val=4096;

そして、memsetでバッファを0クリアしていた部分に以下の赤字のような形に変更します。

■バッファリング処理の実装
    /* 要求待ちループ */
    while(1) {
        /* READY状態の間、バッファの音を鳴らし続ける */
        while(SND_READY==_SndCTRL) {
            /* バッファを440Hzの矩形波で埋める */
            for(i=0;i<SAMPLE_BUFS/2;i++) {
                ((short*)buf)[i]=val;
                hz++;
                if(25==hz) {
                    hz=0;
                    val=-val;
                }
            }

これで、50Hz周期で、振幅4096(÷32768)の矩形波がバッファリングされるようになりました。このプログラム(test04.cpp)をコンパイルして実行すれば、だいたい440HzのAっぽい音が鳴り続けます。