GLSLで音楽（まずは、ドラムだ）

#ドラムのmusic shader
ドラムのシーケンスから始めていきます。
とりあえずサンプルのshaderを書きました。バスドラムの音源は自力で見つけるこどが出来ずに、[ここ](https://github.com/FMS-Cat/20180310-glsl-music/blob/master/3-4_kick.glsl)から、勝手にお借りしてます。ごめんなさい。

```glsl
#define A 0.125

float noise(float time)
{
    return  (fract(sin(time*99.0)*50000.0)*2.0-1.0);
}

float kick(float time) {
    return sin(6.283 * 50.0 * time - 10.0 * exp( -70.0 * time ))*exp(-time*1.6); 
}

float snare(float time) {
  return noise(time)*max(0.0,1.0-min(0.85,time*4.25)-(time-0.25)*0.3);
}

float hihat(float time) {
  return noise(time)*exp(-time*150.0);
}

float sequence(int s,float time)
{
  float n =mod(time,A);
  for(int i=0;i<16;i++){
    if((s>>(int(time/A)-i)%16&1)==1)break;
    n+=A;
  }
  return n;
}

vec2 mainSound( float time )
{
    return vec2(
                 0.5 * kick(sequence(0x0581,time))+
                 0.3 * snare(sequence(0x1010,time))+
                 0.2 * hihat(sequence(0x5555,time))
                 );
}
```

この音は[shader toy](https://www.shadertoy.com/view/XsVBzt)で聞けます。
先日書いた[記事](https://qiita.com/gaziya5/items/e5d058c20bc75c72b5a2)のhtmlサンプルの中のvertex shaderの中の

```glsl
vec2 mainSound(float time)
{
  return vec2( sin(6.2831*440.0*time)*exp(-3.0*time) );
}
```
これを上のサンプルと書き換えても使えます。この辺りはshadertoyのsound shaderを意識してつくってます。以後、サンプルはshadertoyのsound shaderで音がでるように書いていきます。





#シーケンス
バスドラム、ハイハット、スネアドラム音源の説明は後日にして、シーケンスについて説明していきます。音源の関数は、なんとなくわかると思いますが、

 ```
sequence(0x5555,time)
```
この数字ですね。これは、いわゆるデータです。ノートオンするタイミングが書いてあります。
0x5555これを2進数に変換すると101010101010101 こうなります。
なんとなくhihatのリズムが見えてきたと思います。

#sequence()関数の簡単な説明

webGL2.0からshaderでビット演算が使えるようになりました。ビット演算の右シフトとANDを使って、各桁の数字を取り出すスキルがあります。

```
0x5555 >> 2 & 1
```
こんな感じです。使い方としては

```glsl

int bitShift(int data, int order)
{
  return data >> order & 1;
}

########
bitShift(0x5555, 2); // ==> output 1;

```
0x5555を2進数(0b101010101010101)にして下位から3つ目の数字は 1 だってこと。

これを利用してシーケンスをしていきます。
8ビートは、1小節に８分音符のハイハットを８回って意味でいいのかな？この辺の認識に自信が無いですが、そういう事で話を進めます。間違えていたら教えてください。
世間のドラム打ち込みをみると、16分音符を最小リズムにしてるようなので、それにならってみます。一小節に16個のon-offが必要です。GLSLのintは32bitなので十分にたります。
BMPを使って表す一小節の秒数は 240/BPM。それを更に16で割り、16分音符の秒数は 15/BPM。
今回のサンプルでは、BPM = 120 にしたので、16分音符の秒数は 0.125秒。
こんな感じをベースにして、sequence()関数を作りました。
他の拍子とかにも応用できると思います。

#データ制作について
0x0581とかは、人様のドラム打ち込みの画像を見ながら、onには1をoffには0と並べて2進数を作り、webの16進数変換アプリを使わせてもらいました。色々試したが、これが一番楽でした。注意点として、ビットシフトを使っているので、右から左にbitを並べていってください。

#追記
自力で変換するのは大変なのでpythonでツールを作りました。

```python
import sys
from PyQt5 import QtCore,QtWidgets,QtGui

class Window(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
 
        self.setGeometry(500, 200, 500, 350)
        self.setWindowTitle('Binary Convert')
        
        font = QtGui.QFont()
        font.setBold(True)
         
        w = 50
        self.CheakBoices=[]
        for i in range(16):
            if i%4 == 0: w += 20
            label = QtWidgets.QLabel(format(i,'x'),self)
            label.setFont(font)
            label.move(w+5, 20)
            cb = QtWidgets.QCheckBox(self)
            cb.move(w, 40)
            self.CheakBoices.append(cb)
            w += 20
            
        binaryLabel = QtWidgets.QLabel("Binary",self)
        hexLabel = QtWidgets.QLabel("Hex",self)
        decimalLabel = QtWidgets.QLabel("Decimal",self)
        binaryLabel.move(50, 90)
        hexLabel.move(50, 130)
        decimalLabel.move(50, 170)
        binaryLabel.setFont(font)
        hexLabel.setFont(font)
        decimalLabel.setFont(font)
        
        self.binaryEdit = QtWidgets.QLineEdit(self)
        self.hexEdit = QtWidgets.QLineEdit(self)
        self.decimalEdit = QtWidgets.QLineEdit(self)
        self.binaryEdit.move(120, 90)
        self.hexEdit.move(120, 130)
        self.decimalEdit.move(120, 170)
        self.binaryEdit.setFont(font)
        self.hexEdit.setFont(font)
        self.decimalEdit.setFont(font)
        self.binaryEdit.setReadOnly(True)
        self.hexEdit.setReadOnly(True)
        self.decimalEdit.setReadOnly(True)
        
        convertButton = QtWidgets.QPushButton("Convert",self)
        convertButton.setFont(font)
        convertButton.move(360, 160)
        
        convertButton.clicked.connect(self.convert)
        

        numberLabel = QtWidgets.QLabel("Number",self)
        numberLabel.move(50, 260)
        numberLabel.setFont(font)
        self.numberEdit = QtWidgets.QLineEdit("0",self)
        self.numberEdit.move(120, 260)
        self.numberEdit.setFont(font)

        numberButton = QtWidgets.QPushButton("Check",self)
        numberButton.setFont(font)
        numberButton.move(360, 260)
        
        convertButton.clicked.connect(self.convert)
        numberButton.clicked.connect(self.cheak)

    def convert(self):
        n=0
        for i in range(16):
            if self.CheakBoices[i].isChecked():
                n += 1<<i
        
        self.binaryEdit.setText(format(n, '#016b'))
        self.hexEdit.setText(format(n, '#04x'))
        self.decimalEdit.setText(str(n))
    
    def cheak(self):
        for i in range(16):
            self.CheakBoices[i].setCheckState([QtCore.Qt.Unchecked, QtCore.Qt.Checked][int(self.numberEdit.text(),0)>>i&1])


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

```
チェックボックスにリズムパターンを入れて"convert"ボタンで`int`が出てきます。
"number"に`int`を入れて"cheack"ボタンでチェックボックスにリズムパターンがでます。

#追記
データを別アプリで作るのも、無理があると気付きshaderの中で作る奴にしました。2019/12/09
[Snake Knot](https://www.shadertoy.com/view/WsX3R4)

```glsl
#define BPM 140.
#define A (15./BPM)

float adsr(float t, float a, float d, float s, float r, float gt)
{  
    return max(0.0,
    	min(1.0, t/max(1e-4, a)) 
        - min((1.0 - s) ,max(0.0, t - a)*(1.0 - s)/max(1e-4, d))
        - max(0.0, t - gt)*s/max(1e-4, r));
}

float noise(float t)
{
    return fract(sin(t*45678.0)*1234.5)*2.0-1.0;
}

float square(float f)
{
	return sign(fract(f)-0.5);
}

float kick(float t){
	return sin(315.0*t-10.0*exp(-50.0*t))*adsr(t,0.0, 0.3, 0.0, 0.0, 0.0);
			+0.2*square(50.0*t)* adsr(t,0.0, 0.05, 0.0, 0.0, 0.0);
}

float snare(float t)
{
    return noise(t)*adsr(t,0.01, 0.1, 0.0, 0.0, 0.0);
}

float closeHihat(float t)
{
    return noise(t)*adsr(t,0.0, 0.03, 0.0, 0.0, 0.0);
}

float openHihat(float t)
{
    return noise(t)*adsr(t,0.0, 0.05, 0.5, 0.03, 0.03);
}

float sequence(int s,float t)
{
  float n =mod(t,A);
  for(int i=0;i<16;i++){
    if((s>>(int(t/A)-i)%16&1)==1)break;
    n+=A;
  }
  return n;
}

#define Rhythm2Int(v,a)v=0;for(int i=0;i<16;i++)v+=a[i]<<i;

vec2 mainSound( float time )
{   
    int[4] r_kick;       // int[](0x0c05,0x0c05,0x0405,0x0c0c)
    int[4] r_snare;      // int[](0x9290,0x9290,0x4290,0x4292)
    int[4] r_closeHihat; // int[](0x5555,0x5555,0x5555,0x5155)
    int[4] r_openHihat;  // int[](0x0000,0x0000,0x0000,0x0400)
    int[4] r_velocity;   // int[](0x3030,0x3030,0x3030,0x3030)
        
    Rhythm2Int( r_kick[0],       int[]( 1,0,1,0, 0,0,0,0, 0,0,1,1, 0,0,0,0 ))
    Rhythm2Int( r_snare[0],      int[]( 0,0,0,0, 1,0,0,1, 0,1,0,0, 1,0,0,1 ))
    Rhythm2Int( r_closeHihat[0], int[]( 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0 ))
    Rhythm2Int( r_openHihat[0],  int[]( 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 ))
    Rhythm2Int( r_velocity[0],   int[]( 0,0,0,0, 1,1,0,0, 0,0,0,0, 1,1,0,0 ))
        
    Rhythm2Int( r_kick[1],       int[]( 1,0,1,0, 0,0,0,0, 0,0,1,1, 0,0,0,0 ))
    Rhythm2Int( r_snare[1],      int[]( 0,0,0,0, 1,0,0,1, 0,1,0,0, 1,0,0,1 ))
    Rhythm2Int( r_closeHihat[1], int[]( 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0 ))
    Rhythm2Int( r_openHihat[1],  int[]( 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 ))
    Rhythm2Int( r_velocity[1],   int[]( 0,0,0,0, 1,1,0,0, 0,0,0,0, 1,1,0,0 ))

    Rhythm2Int( r_kick[2],       int[]( 1,0,1,0, 0,0,0,0, 0,0,1,0, 0,0,0,0 ))
    Rhythm2Int( r_snare[2],      int[]( 0,0,0,0, 1,0,0,1, 0,1,0,0, 0,0,1,0 ))
    Rhythm2Int( r_closeHihat[2], int[]( 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0 ))
    Rhythm2Int( r_openHihat[2],  int[]( 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 ))
    Rhythm2Int( r_velocity[2],   int[]( 0,0,0,0, 1,1,0,0, 0,0,0,0, 1,1,0,0 ))

    Rhythm2Int( r_kick[3],       int[]( 0,0,1,1, 0,0,0,0, 0,0,1,1, 0,0,0,0 ))
    Rhythm2Int( r_snare[3],      int[]( 0,1,0,0, 1,0,0,1, 0,1,0,0, 0,0,1,0 ))
    Rhythm2Int( r_closeHihat[3], int[]( 1,0,1,0, 1,0,1,0, 1,0,0,0, 1,0,1,0 ))
    Rhythm2Int( r_openHihat[3],  int[]( 0,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,0 ))
    Rhythm2Int( r_velocity[3],   int[]( 0,0,0,0, 1,1,0,0, 0,0,0,0, 1,1,0,0 ))
    
    int i = int(floor(time/(A*16.)))&3;
    int velocity = r_velocity[i]>>(int(floor(time/A))&15)&1;
    float vol = 0.2 *(1.0+0.5*float(velocity));
    return vec2(
        0.0
		+0.4 * kick(sequence(       r_kick[i],       time))
       	+0.3 * snare(sequence(      r_snare[i],      time))
       	+vol * closeHihat(sequence( r_closeHihat[i], time))
        +vol * openHihat(sequence(  r_openHihat[i],  time))
    );
}
```


<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Amen Breakの打ち込みをしてみたのですが、誰か添削してくれませんかね？<br>もしくは、これをドラム用の雛形にしてもいいです。<a href="https://t.co/X9YKC6U1bl">https://t.co/X9YKC6U1bl</a> <br> <a href="https://twitter.com/hashtag/shaderMusic%E9%9F%B3%E6%BA%90?src=hash&amp;ref_src=twsrc%5Etfw">#shaderMusic音源</a></p>&mdash; gaz (@gaziya5) <a href="https://twitter.com/gaziya5/status/1072495394364551168?ref_src=twsrc%5Etfw">2018年12月11日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">いっそのこと、shader musicの音源を共有ってのはどうですか？<a href="https://twitter.com/hashtag/shaderMusic%E9%9F%B3%E6%BA%90?src=hash&amp;ref_src=twsrc%5Etfw">#shaderMusic音源</a></p>&mdash; gaz (@gaziya5) <a href="https://twitter.com/gaziya5/status/1071410046808903681?ref_src=twsrc%5Etfw">2018年12月8日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

なんてのを企てました。参加おねがいします。

#GLSLで音楽の記事
[GLSLで音楽（はじめに）](https://qiita.com/gaziya5/items/e5d058c20bc75c72b5a2)
[GLSLで音楽（メロディーいってみます）](https://qiita.com/gaziya5/items/67ebe54f4bc02bd31bd4)
[GLSLで音楽（和音を使ってみる）](https://qiita.com/gaziya5/items/0a3ef4b96a6d173f0895)
[GLSLで音楽（今までの応用の一つ）](https://qiita.com/gaziya5/items/27ba6dedfc3028782f2f)