LoginSignup
6

More than 5 years have passed since last update.

GLSLで音楽(はじめに)

Last updated at Posted at 2018-06-25

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
  3. You can use dark theme
What you can do with signing up
6