search
LoginSignup
6

More than 3 years have passed since last update.

posted at

updated at

GLSLで音楽(はじめに)

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で音楽(今までの応用の一つ)

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6