#GLSLで音楽
GLSLで音楽なんて何を考えているだ。ですよね。でも、これをクリアしないとdemoが書けないのです。こいつに手を出して、もうボチボチ2年近くになります。なんとなくイケるレベルに達したようだし、記事を書いてみます。しばらく連載してみます。記事を書いて頭の中を整理して、次のステップをみたいな。
GLSLで音楽といっても、たぶん入り口が、解らないと思うので、htmlのサンプルを書いておきます。transform feedbackを使ってます。こいつのvertex shaderの部分だけで音楽をつくります。
ソースの中にある
var duration = 120;
は、音楽の演奏時間を秒で表した数字です。
<html>
<body>
<script id="vs" type="x-shader/x-vertex">
#version 300 es
uniform float sampleRate;
out vec2 gain;
vec2 mainSound(float time)
{
return vec2( sin(6.2831*440.0*time)*exp(-3.0*time) );
}
void main() {
float time = float(gl_VertexID) / sampleRate;
gain = mainSound(time);
}
</script>
<script id="fs" type="x-shader/x-fragment">
#version 300 es
void main() {}
</script>
<script type="text/javascript" >
var duration = 120;
var audio = new AudioContext();
var sampleRate = audio.sampleRate;
var bufferSize = audio.sampleRate * duration;
var audioBuffer = audio.createBuffer(2, bufferSize, audio.sampleRate);
var node = audio.createBufferSource();
var canvas = document.createElement('canvas');
var gl = canvas.getContext("webgl2") || canvas.getContext("experimental-webgl2");
var compileShader = function(prog, src, type){
var sh = gl.createShader(type);
gl.shaderSource(sh, src.replace(/^\n/, ""));
gl.compileShader(sh);
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(sh));
}
gl.attachShader(prog, sh);
gl.deleteShader(sh);
};
var program = gl.createProgram();
compileShader(program, vs.text, gl.VERTEX_SHADER);
compileShader(program, fs.text, gl.FRAGMENT_SHADER);
gl.transformFeedbackVaryings(program, ['gain'], gl.SEPARATE_ATTRIBS);
gl.linkProgram(program);
gl.useProgram(program);
gl.uniform1f(gl.getUniformLocation(program, "sampleRate"), sampleRate);
var VBOs = [gl.createBuffer(),gl.createBuffer()];
for (var i = 0; i < 2; ++i) {
gl.bindBuffer(gl.ARRAY_BUFFER, VBOs[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(bufferSize*2), gl.STATIC_DRAW);
}
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, VBOs[0]);
gl.enable(gl.RASTERIZER_DISCARD);
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, bufferSize);
gl.endTransformFeedback();
gl.disable(gl.RASTERIZER_DISCARD);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
gl.bindBuffer(gl.ARRAY_BUFFER, VBOs[0]);
var aryBuffer = new ArrayBuffer(bufferSize*4*2);
var dataView = new DataView(aryBuffer);
gl.getBufferSubData(gl.ARRAY_BUFFER, 0, dataView);
var buf = new Float32Array(aryBuffer);
var data0 = audioBuffer.getChannelData(0);
var data1 = audioBuffer.getChannelData(1);
for(var i = 0; i < bufferSize; i++) {
data0[i] = buf[i*2];
data1[i] = buf[i*2+1];
}
node.buffer = audioBuffer;
node.loop = false;
node.connect(audio.destination);
node.start(0);
</script>
</body>
</html>
#音楽を作るという事は
音源とシーケンスをshaderで書く。これだけだ。きっと。
#まず音源
サンプルの中のvertex shaderに書いてある
sin(6.2831*440.0*time)
これが音源です。440Hzの純音と呼ばれてるやつです。Aのキーで、なんか基準の周波数らしい。
exp(-3.0*time)
エンベロープと呼ばれてます。時間で自然衰減する関数です。ほっておけば音は小さくなる、当たり前のこと。この2つを乗算して使うが基本です。
#シーケンスについて
MIDIの規格で、ノートオフまで衰減しないモノもあるけど、shaderでそれをする事は放棄しました。放棄すればshaderでシーケンスに道がみえてきます。
#少しずつ書いてきます
今、シーケンスを文字数の少ない関数に納めました。だけど、これを見せても多分、理解して貰えないでしょう。単純にコマンドとして使えるし問題ないけど、説明も入れていきます。これをする理由は、久々に自分のshaderを読んで読み解けなかったからです。もう一回、最初から構築しながら記事を書いていこうと思います。
#GLSLで音楽の記事
GLSLで音楽(まずは、ドラムだ)
GLSLで音楽(メロディーいってみます)
GLSLで音楽(和音を使ってみる)
GLSLで音楽(今までの応用の一つ)