ソフトシンセ(の基本)

注意:ここではある程度デモの話,C言語の基礎,WinAPIがわかるという前提で話を進めていきます。
その辺の話からよくわからない方はそういうサイトを回ってからの方が良いと思われます。
あと、VisualC++(以下VC)限定なので他の方にはあまり有効でないかもしれません。



さて今回はソフトシンセについてですが、デモのグラフィック部分にくらべて
あんがい奥が深いのがソフトシンセです。
グラフィックコードはコードを書いたら書いた文だけ映像に反映されますが
サウンドはいじる部分によっては、かなり変わることもあれば、あまり変わらない
こともあります。とりあえず、今回は基本的な音の再生ということについてのみ
触れて生きたいと思います。


●基本はWAVEファイル
すべての基本はWAVEファイルです。WAVEファイルというと、あの*.wavファイルです。
ソフトシンセの基本的な方針は、このWAVEファイルを生成して再生をするという方式です。
WAVEファイルが何かわからない人はちょっとその辺を検索しながらさまよってください。
まぁWAVEファイルは簡単に説明すると、ファイル先頭にWAVEについての情報ヘッダーがあり
以下、サウンドの波形(WAVE)データが並んでいます。
早い話、ヘッダーは後から考えるとして、後ろの部分の波形データの作成が重要になってきます。


●波形データ
WAVEファイルの波形データは16bitの44100Hzとか8bitの22050Hzとかになっていることが多いと
思いますが・・・(何のことを言ってるのかわからない人はマジでWAVEファイルについて調べてください)
つまり、前者のフォーマットで1秒間分のデータなら、16bit整数(short型)[-32768〜32767]が44100個ある
ということですね?(モノラル時。ステレオなら88200個)

 long t;
 short* wave_data=(short*)GlobalAlloc(GPTR,sizeof(short)*44100);
 for(t=0; t<44100; t++){
     if( (t%200)<100 ) wave_data[t] =  32767;  
     else              wave_data[t] = -32767;  
 }
まぁこんなかんじでやると44100/200≒220Hzの矩形波音がで出るというわけです。


●WAVEデバイス
案外簡単ですね。
さて、あとはこのWAVEのバッファに基づいてヘッダーを作ってやります。
(注:実際のWAVEヘッダーではないです。WAVEデバイスのためのフォーマット情報です。)
 WAVEFORMATEX wf;                              //WAVEFORMATEX 構造体
 wf.wFormatTag=WAVE_FORMAT_PCM;                //これはこのまま
 wf.nChannels=1;                               //モノラル ステレオなら'2'
 wf.nSamplesPerSec=44100;                      //44100Hz
 wf.wBitsPerSample=16;                         //16ビット
 wf.nBlockAlign=wf.nChannels*wf.wBitsPerSample/8;       //計算
 wf.nAvgBytesPerSec=wf.nSamplesPerSec*wf.nBlockAlign;   //計算
 wf.cbSize=0;                                           //計算
 HWAVEOUT hWOut;
 waveOutOpen(&hWOut, WAVE_MAPPER, &wf, 0, 0, CALLBACK_NULL);
まぁこんな感じでWAVEFORMATEXをつくり、WAVEデバイスをオープンします。

●WAVE情報
でもって後は
WAVEHDR wh;
wh.lpData = (LPSTR)wave_data;
wh.dwBufferLength = sizeof(short)*44100;
wh.dwFlags = 0;
wh.dwLoops = 1;//1回だけ再生
wh.dwBytesRecorded=0;
wh.dwUser=0;
wh.lpNext=NULL;
wh.reserved=0;
こんな感じで再生情報のためのWAVEHDRをつくってやります。
赤字のところは、上で作った波形データ(wave_data[44100])による値です。


waveOutPrepareHeader(hWOut , &wh , sizeof(WAVEHDR));
waveOutWrite(hWOut , &wh , sizeof(WAVEHDR));
まぁあとはこれで再生♪


●後処理
後処理ですが、waveOutCloseなどでちゃんと後処理をしても良いのですが
  ExitProcess(0);    
まぁいっそのこと思い切って強制終了って形のほうが、サイズ的にも
動作もいいかんじになるので・・・(w
とりあえずNT系のOSならこれでちゃんとうまくいくので・・・


再生の基本はコレだけです。
とりあえず、音が鳴ってしまえば、あとは気合と根性です。
上のwave_dataのサイズを大きくして、そこにさまざまな波形データを
入れてしまえば、さまざまな音が鳴ります。
とはいっても、そこがまた難しいわけですが・・・
とりあえず、ここまでやったことがサンプルに・・・これのほぼコピペですが・・・。
(ちょっとボリュームが大きめなので気をつけてください。)


●まとめ
一気にソフトシンセをやろうと思うと、かなり無理があるので
ちょっとずついこうと思います。でも、とりあえず基本はコレだけです。
後は、元の波形を作ることに全力を尽くすだけです。
そこにはAPIの知識とか下手な小細工はありません。(ないわけではないが・・・)
基本波(sin,saw,square,triangle...など)の知識とフィルターの知識があれば
そいつをプログラムして叩き込むだけです。
そうして作った波形に、音階や和音のプログラムをくっつけてしまえば
それは、もう立派なソフトシンセとなることでしょう。
まぁ道のりは長いですが、最初の一歩はこんなもんでしょう。
waveOut***な関数を知りたい方は検索するなりして調べてみてください。
個人的にはWisdomSoftさんマルチメディアAPIなんかがおすすめです。



感想・質問・間違い指摘はBBSまで・・・