【HSP】正弦波の音を出す【モジュール】

【HSP】正弦波の音を出す【モジュール】

最近、音楽の調に興味が出てきて、自分は音感どれくらいあるのかなって思って、その手のサイト探したら、上級者向けだと 0.5 ヘルツぐらいしか違わない音を聞き分けるヤツとかあって、それはさすがに無理だから、もうちょっと幅を広げて確かめてみたいなと思って、音を作成するプログラムを書きました。
そのためにググってみたら、数年前に自分が書いた記事が出てきた…。このころはインデントや改行をちゃんと使ってなかったから、プログラムがすごい見にくい…orz
しょうがないので一から作り直しました。あのころは C の関数とかよくわかってなかったから大変だったけど、今では割とスラスラかけるようになった。わーい。
まずはモジュールから。
※2014/11/13 少しだけ修正
#ifndef _WAVEOUT_H_
#define _WAVEOUT_H_

#include "winmm.as"

#module _WAVEOUT_M_

// 
// モジュール初期化
// 
#deffunc local init

// WAVEFORMATEX 構造体
wBytesPerSample = 2      // 量子化バイト数

wFormatTag      = 0x0001 // WAVE_FORMAT_PCM
nChannels       = 2      // ステレオ
nSamplesPerSec  = 44100  // サンプルレート
wBitsPerSample  = wBytesPerSample * 8
nBlockAlign     = nChannels * wBytesPerSample
nAvgBytesPerSec = nSamplesPerSec * nBlockAlign
cbSize          = 0

wfex = wFormatTag | (nChannels << 16), nSamplesPerSec, nAvgBytesPerSec, nBlockAlign | (wBitsPerSample << 16), cbSize

// オープン
hWaveOut = 0
waveOutOpen varptr(hWaveOut), -1, varptr(wfex), 0, 0, 0 // WAVE_MAPPER, CALLBACK_NULL

return

// 
// モジュール終了処理
// 
#deffunc exit onexit

// クローズ
waveOutReset hWaveOut
waveOutUnprepareHeader hWaveOut, varptr(whdr), 32 // sizeof(WAVEHDR)
waveOutClose hWaveOut

return

// 
// wave 出力
// 
// _wave      : 波形データの入った変数
// _nAvgBytes : 波形データのバイト数
// 
#deffunc waveOut var _wave, int _nAvgBytes

// WAVEHDR 構造体
lpData          = varptr(_wave)
dwBufferLength  = _nAvgBytes
dwBytesRecorded = 0
dwUser          = 0
dwFlags         = 12 // WHDR_BEGINLOOP | WHDR_ENDLOOP
dwLoops         = 1  // ループ回数
lpNext          = 0
reserved        = 0

whdr = lpData, dwBufferLength, dwBytesRecorded, dwUser, dwFlags, dwLoops, lpNext, reserved

// 再生

waveOutReset hWaveOut
waveOutPrepareHeader hWaveOut, varptr(whdr), 32 // sizeof(WAVEHDR)
waveOutWrite hWaveOut, varptr(whdr), 32 // sizeof(WAVEHDR)

return

// 
// サイン波出力
// 
// f : 周波数 (ヘルツ単位)
// a : 振幅   (0.0 ~ 1.0)
// t : 時間   (秒単位)
// 
#deffunc waveOutSine double f, double a, double t

// 波形データ
nSamples = int(t * nSamplesPerSec) // 全体のサンプル数
nAvgBytes = nSamples * nBlockAlign // 全体のバイト数

dim wave, (nAvgBytes + 3) / 4
repeat nSamples
// この部分は nChannels や wBytesPerSample によって書き換えが必要
wpoke wave, cnt * 4    , int(sin(2.0 * M_PI * f * cnt / nSamplesPerSec) * 0x7FFF * a) & 0xFFFF
wpoke wave, cnt * 4 + 2, int(sin(2.0 * M_PI * f * cnt / nSamplesPerSec) * 0x7FFF * a) & 0xFFFF
loop

// 再生
waveOut wave, nAvgBytes

return

#global

init@_WAVEOUT_M_

#endif

そしてサンプル。

#include "hspmath.as"

#define NOTE_DIFF 1.05946309436

// 
// メイン
// 

melody = -9, -7, -5, -4, -2, 0, 2

repeat
waveOutSine 440.0 * pow(NOTE_DIFF, melody(cnt \ 7) + (cnt / 7 \ 4 - 1) * 12), 0.25, 0.5
await 500
loop

以前のプログラムだと、ドレミファソラシドの周波数を一つずつ指定してたけど、半音が 2 の 1/12 乗であることと、ラの音を基準に決められていることを知ったので、それを基準に作りました。
HSP で 構造体を扱うとき、4バイト区切りなら 整数型配列 にするだけで「これで万時OKだわ」なんだけど、2 バイトのとかあるから、 poke 系 で書き込むかどうか迷った。とりあえず ビット演算して代入することにした。
ほんとはピアノの鍵盤のプログラム作ろうかと思ったけど、デザインが面倒くさそうなので保留。
mml の再生とかもしてみたいなー。
では、またいつか~。
↓ブログランキング投票  (クリック) お願いします~。