加算による和音作成

 今回は、音の波形を「加算」して和音を作ってみます。音は重ね合わせることで「和音」になりその重ね合わせ処理も単純な波形同士の加算で行えるので(ただし、これは波形の表現と音の強さが一定の比例関係にある場合。Waveはどうなんだろう?)、3つの波形を生成し、順次重ねてみましょう。

和音の生成

 今回は波形の加算処理を行いやすくするために波形データを16ビット/8KHzで作成します。Waveの波形データは、8ビットだと符号なし、16ビットだと符号付で処理されるため、マイナスの値も含む波形データの加算には16ビットのほうが好都合なのです(8ビットだと、中央値を境に正負が変わるため中央値からの「絶対値」と「符号」を意識して処理する必要がある)。重ねる波形は、4オクターブのド・ミ・ソの3つ。16ビットだと−32768〜32767まで表現できるので、各波形の振幅は8000にしましょう。これなら3つ重なっても32000なので範囲内に収まります。

 和音を作成する前に、重ね合わせる音の波形を求めておきましょう。今回も単純な矩形派を使うことにして、最初にオクターブ3のラの音(220Hz)を元にオクターブ4のドミソの音の周波数を計算します。

  /* 半音一つの倍率 */
  lfRate=pow(2,1.0/12.0);

  /* オクターブ4のド、ミ、ソの周波数 */
  lfFreq[0]=220*pow(lfRate,3);         /* ド */
  lfFreq[1]=lfFreq[0]*pow(lfRate,4);   /* ミ */
  lfFreq[2]=lfFreq[1]*pow(lfRate,3);   /* ソ */

 音の周波数が求まったら、次は和音の作成。今回は、全体で3秒間24000個のデータを作成することにして、1秒毎にドミソの音を重ねて行きます。つまり、最初にドの音を3秒間持続する波形を生成し、その上に1秒後から2秒間ミの音を、さらに2秒後から1秒間ソの音を重ねるわけです。

 最初に、3秒間ドの音を作成しましょう。サンプリング形式が16ビット/8KHzなので、波形データはshort int型配列lpBuf[24000]に作成することにします。

  /* ドの音の波長計算 */
  l=(int)(8000.0/lfFreq[0]);

  for (i=0;i<24000;i++) { /* ドの音の波形生成 */

      if ((i % l)<l/2)
          lpBuf[i]=8000;
      else
          lpBuf[i]=-8000;

  }

 続いて、ドの音の波形の2〜3秒の場所にミの音を加算し重ね合わせます。サンプリング周波数が8KHzなので、2〜3秒というのはデータで言えば8001〜24000個目ですね。

  /* ミの音の波長計算 */
  l=(int)(8000.0/lfFreq[1]);

  for (i=8000;i<24000;i++) { /* ミの音を重ねる */

      if ((i % l)<l/2)
          lpBuf[i]+=8000;
      else
          lpBuf[i]-=8000;

  }

 最後に3秒目にソの音を重ねて、波形データの完成。

  /* ソの音の波長計算 */
  l=(int)(8000.0/lfFreq[2]);

  for (i=16000;i<24000;i++) { /* ソの音を重ねる */

      if ((i % l)<l/2)
          lpBuf[i]+=8000;
      else
          lpBuf[i]-=8000;

  }

 lpBufに波形データが出来たら、そのデータを16ビット/8KHzの波形データとして出力します。

  wf.wFormatTag=WAVE_FORMAT_PCM;
  wf.nChannels=1;
  wf.nSamplesPerSec=8000;
  wf.nAvgBytesPerSec=16000;
  wf.nBlockAlign=1;
  wf.wBitsPerSample=16;
  wf.cbSize=0;

  /* デバイスオープン */
  waveOutOpen(&hWOut,WAVE_MAPPER,&wf,(DWORD)hwnd,0,CALLBACK_WINDOW);

  wh.lpData=(char *)lpBuf;
  wh.dwBufferLength=48000;
  wh.dwBytesRecorded=0;
  wh.dwUser=0;
  wh.dwFlags=0;
  wh.dwLoops=1;
  wh.lpNext=NULL ;
  wh.reserved=0 ;

  waveOutPrepareHeader(hWOut,&wh,sizeof(WAVEHDR));
  waveOutWrite(hWOut,&wh,sizeof(WAVEHDR));

プログラム

 実行すると、WM_CREATEメッセージの応答で波形を作成し和音を演奏します。

プログラムソース表示


画像と音声処理実験室 > プログラミング資料庫