(1)wavファイルのフォーマット
↓のサイトに分かり易く記述されています。
http://www.kk.iij4u.or.jp/~kondo/wave/
(2)wavファイルはそのまま使ってはならない
wavファイル(RIFF構造の一種)は、非常に汎用性の高いフォーマットです。
汎用性が高いということは、逆説すれば無駄(冗長)な情報が多いということです。
例えば、周波数、ビットレート、チャネル数などの情報は、VGS音源の場合は22050Hz、16bit、1chに固定されているので、ファイルに持たせるのはナンセンスです。そこで、wavファイルから必要な情報を抜き取り、独自ファイル(PCMファイル)のフォーマットに変換したものをリソースとして持つことにします。
なお、VGS音源にとって必要な情報は、以下の2種類です。
- パルス符号の数
- パルス符号データの塊
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; } |
(4)効果音はストアすること
AndroidやiPhoneの標準のAPIでは、wavファイルをそのまま発音する機能があります。
しかし、効果音というのは何回も繰り返し再生されるものなので、再生の都度、ファイルを読み込むというのは、処理効率が悪すぎます。
VGSの場合、全リソースデータ(ROMファイル)の内容を、起動時に一括でメモリ領域に展開していて、以降はメモリ領域を参照することでデータアクセスを行っています。これにより、リアルタイム性の高い効果音の発音処理を実現しています。ゲームにとっての効果音は、BGM以上に重要な存在なので、効果音データは必ず事前にストア済みの情報を使わなければなりません。
0 件のコメント:
コメントを投稿