多角形の波アニメーション
話題になってたアニメーションが気になって、久々の JavaScript の練習って事で真似してみた。
- http://app.awm.jp/waves/waves.html (押すと音も鳴ります)
ファイル
- https://github.com/yoya/misc/blob/master/js/waves.html
- https://github.com/yoya/misc/blob/master/js/waves.js
機能
- 左側に多角形を表示して一定の回転速度で辺をなぞる
- 円形と四角形と六角形。ついでに三角形を増やす
- 多角形をなぞった時のY方向の値を波として右方向にスクロールする
- 対応する Canvas を押されたら波形に対応する音を出力。
設計
実装
- 始めに1024分割で波形データを計算する
- 円は単純に cos, sin を x, y 座標にする
function make_wavetable_circle(waveDataX, waveDataY) { var theta = 0; var theta_delta = 2 * Math.PI / wave_length; for (var i = 0 ; i < wave_length ; i++) { waveDataX[i] = Math.cos(theta); waveDataY[i] = Math.sin(theta); theta += theta_delta; } }
-
- 四角形は左右上下で処理を分岐させて、移動は tan で算出
for (var i = 0 ; i < wave_length; i++) { if ((theta < theta_8) || (theta > theta_8*7)) { // right x = 1; y = Math.tan(theta); } else if (theta < theta_8 * 3) { // top x = - Math.tan(theta - theta_8*2); y = 1; } else if (theta < theta_8 * 5) { // left x = - 1; y = - Math.tan(theta - theta_8*4); } else { // bottom x = Math.tan(theta - theta_8*6); y = - 1; } waveDataX[i] = x; waveDataY[i] = y; theta += theta_delta; }
-
- 六角形は垂直線の座標を tan で出して、残りの5辺は回転行列で計算
function rotateXY(x, y, t) { var x2 = x * Math.cos(t) - y * Math.sin(t); var y2 = x * Math.sin(t) + y * Math.cos(t); return [x2, y2]; }
-
- 三角形も、六角形と同じ要領で。
- 定期的にこの波形データに対応する表示をして、アニメーションする
- マウスイベントを補足して
- offsetX, offsetY で場所を把握
- 対応する音は、波形を元に AudioBufferSource で作成。
- offsetX, offsetY で場所を把握
var buf = audio_ctx.createBuffer(1, wave_length, sampleRate); var data = buf.getChannelData(0); for (var j = 0; j < wave_length ; j++) { data[j] = wave.waveDataY[j] * 1000; } wave.audioBuffer = buf;
-
-
- 上下方向が音量。Gain を挟んで、その value に設定。
- 左右方向が音程。playbackRate に周波数比を設定。
-
function getToneRatio(x, wave) { var tone = x * 2 + 20; return tone / (sampleRate / wave_length) } function getVolume(y, wave) { return (wave.height - y) / wave.height / 1000; }
-
- 押したまま動かした場合、場所に応じて音量と音程を設定し直す
- 話したら音を消す。stop と disconnect 、null 代入。
- マウスが離れても音を消す。(でないと押したままCanvasの外に出ると鳴りっぱなしになる)
ハマり
- var のつけ忘れで for の中で読んだ先の function で i が上書きされる問題に結構ハマった。
- canvas はピクセルグリッドの左上を座標にしてるので、x, y に0.5 を足さないとpixelを跨る線を表現しようとしてボヤける。
- NaN の値を canvas に渡すとエラーを出さずに単に描写しないだけなので、たまに困る。
- BufferSource > Destination に直で繋いでた時はシグナル値が -1 < s < 1 でそれっぽい音が鳴ったけど、BufferSource > Gain > Destination として Gain を挟んだら -100 < s < 100 位にしないと正弦波に余計な 成分が入ってダメだった。多分、量子化ノイズ。
- 念のため、-1000 < s < 1000 にしておいた。
課題
- スクロールが力ずく過ぎるので、リングバッファで書き直したい。
- 六角形、三角形を実装してたら n 角形に一般化できるの分かったので、気が向いたら作る。