ウチツクシ

ゲームをしたり作ったり

リアルタイムにサウンドを生成・再生する

f:id:K-s:20160925193304p:plain
Mk-Synth、サウンドの再生にwaveOut系のAPIを使っているのだけど、スクリプトがHSPTV部門用に詰めてて自分でも分かりにくいので、再生部分だけでも分かりやすくなるようにサンプル書いた。
実行するといきなり音が鳴るので音量注意!マウス左ボタンで波形の形を変えれる。

; HSP3.5b4
#include "winmm.as"

  ; 基になる波形の準備
  bwsize = 100 ; 一周期の長さ
  ddim bwbuf, bwsize
  repeat bwsize
    if (cnt < bwsize / 2) {
      bwbuf(cnt) = 0.2
    } else {
      bwbuf(cnt) = -0.2
    }
  loop
  bwpos = 0
  
  boxsize = 3
  asprate = 0.5
  winsx = bwsize * boxsize
  winsy = asprate * winsx
  screen 0, winsx, winsy
  gosub *drawBaseWave

  channels = 1
  bits = 16
  samples = 44100
  blockalign = channels * bits / 8
  wbufnum = 5
  wbufblock = 1000
  wbufsize = wbufblock * blockalign ; 小さい程反応が早いが音が途切れやすい
  
  dim wfx, 4 ; WAVEFORMATEX
  wfx(0) = 1 | (channels << 16) ; WAVE_FORMAT_PCM
  wfx(1) = samples
  wfx(2) = samples * blockalign
  wfx(3) = blockalign | (bits << 16)
  
  ; デバイス オープン
  dim hwo
  waveOutOpen varptr(hwo), $ffffffff, varptr(wfx), 0, 0, 0 ; WAVE_MAPPER
  if stat {
    dialog "デバイスのオープンに失敗しました"
    end
  }
  
  ; マルチバッファリング用のバッファ準備
  sdim wavBuf, wbufsize * wbufnum
  dim whd, 8, wbufnum  ; WAVEHDR
  ptwhd = varptr(whd)
  repeat wbufnum
    whd(0, cnt) = varptr(wavBuf) + wbufsize * cnt, wbufsize
    pt = ptwhd + cnt * 32
    waveOutPrepareHeader hwo, pt, 32
    waveOutWrite hwo, pt, 32
  loop
  nowbuf = 0
  
  onexit goto *bye
  
*mainLoop
  await 1
  stick key, 256
  if (key & 256) { ; マウス左ボタン
    bwbuf(mousex / boxsize) = double((mousey - winsy / 2) / boxsize) * boxsize / winsy * 2
    gosub *drawBaseWave
  }
  if (whd(4, nowbuf) & 1) { ; WHDR_DONE(1): バッファの再生が終了している
    gosub *writeWave
  }
  goto *mainLoop

*drawBaseWave
  redraw 0
  color 255, 255, 255
  boxf
  color 0, 0, 0
  repeat bwsize
    x = cnt * boxsize
    y = (bwbuf(cnt) + 1) / 2 * winsy
    boxf x, y, x + boxsize - 1, y + boxsize - 1
  loop
  redraw 1
  return
  
*writeWave
  p = nowbuf * wbufsize
  repeat wbufblock
    wpoke wavBuf, cnt * 2 + p, int(bwbuf(bwpos) * $7fff)
    bwpos = (bwpos + 1) \ bwsize
  loop
  waveOutWrite hwo, ptwhd + nowbuf * 32, 32
  nowbuf = (nowbuf + 1) \ wbufnum
  return
  
*bye
  waveOutReset hwo
  repeat wbufnum
    waveOutUnprepareHeader hwo, ptwhd + cnt * 32, 32
  loop
  waveOutClose hwo
  end
  end

サウンドのバッファを複数用意しておいて再生が終わったバッファに波形を書くの繰り返し。再生が終わったのを知るのは oncmd で MM_WOM_DONE を受け取る方法もある(こっちが普通?)けど、今回は WAVEHDR構造体 の dwFlags をループでチェックしてる。

波形の作り方で色々音が変わるのが結構楽しい。キーマニ専コンで弾きたかった(Win10に更新したら動かなくなった…