GLSL作曲講座

# 今回使うツール
## DemoEditor
### https://github.com/notargs/DemoEditor

- **作りました**
- ホットリロード機能付きGLSLEditor
- 動的に音楽/映像を**GLSLで**編集できる
- ビルドにpremake5が必要
- 好きなエディタでshaders直下のファイルを弄る（自分はVisual Studio Codeを使用）
- 今回のサンプルも入ってます

---

# シェーダーで曲を作る
- 4kb introなどで有効な手法
- プロシージャルなので当然mp3よりは圧倒的に軽い
- Math用の標準ライブラリをincludeする必要が無くなるため、その分ちょっと軽くできる

---

# 頑張って曲を作りました
- サンプルを流す

---

# わかったこと
- **DAW使って普通に作曲したほうが圧倒的にラク** :innocent: 
- 手探りでやってる部分が多いのでまだまだ改善できそう

---

# 音を鳴らす
![Temp.gif](https://qiita-image-store.s3.amazonaws.com/0/52576/9e8d8b64-50dd-4b7c-6590-4a5593158a72.gif)
- 空気の振動が耳に伝わって音が聞こえる
- スピーカーが振動して空気を振動させる
- スピーカーの振動周期を**GLSLで定義**する

---

# 音階の計算
```math
441\pi(s/ 12)^2
```

```glsl
float calcHertz(float scale)
{
    return 441.0 * pow(2.0, scale / 12.0) * PI;
}
```
『ラ』の音を基準とした音階を計算する

---

# 秘伝のタレその1
```glsl
#define A 0
#define B 2
#define C 3
#define D 5
#define E 7
#define F 8
#define G 10
```

- 音階を数値で入力するのは**人間には早すぎる**
- 英・米式表記は『ラ』を『A』として、ABCDEFGと呼ぶ

---

## ついでにCalcHealtzも拡張
```
float calcHertz(float octave, float note)
{
    return calcHertz(octave * 12 + note);
}
```

- オクターブを受け取る

---

## これが
```
Sin1(0, 8)
Sin1(0, 3)
Sin1(0, 10)
```

---

## こう書ける
```
Sin1(0, F)
Sin1(0, C)
Sin1(0, G)
```

- 多少慣れはいるが**圧倒的に読みやすい！！**

---

## ちなみに♯（半音上げ）や♭（半音下げ）はこう書く

```glsl
Sin1(0, F+1)
Sin1(0, F-1)
```

- わかりやすい

---

# 秘伝のタレその2
```glsl
#define Sin1(u, v) ret += clamp(sin(time * calcHertz(u, v)) * (1 - localTime + sin(time * 80.0) * 0.1), -0.3, 0.3);
#define Rect1(u, v) ret += rect(time * calcHertz(u, v)) * (1 - localTime);
```

- 自分でも読めない :weary: 
- でもコードはかなりスッキリする
- もっと綺麗な書き方がありそう

---

## これが

```glsl
ret += clamp(sin(time * calcHertz(0, F)) * (1 - localTime + sin(time * 80.0) * 0.1), -0.3, 0.3);
ret += clamp(sin(time * calcHertz(0, C)) * (1 - localTime + sin(time * 80.0) * 0.1), -0.3, 0.3);
ret += clamp(sin(time * calcHertz(0, G)) * (1 - localTime + sin(time * 80.0) * 0.1), -0.3, 0.3);
```

---

## こう書ける

```glsl
Sin1(0, F)
Sin1(0, C)
Sin1(0, G)
```

- 無いとシンドイ

---

# ループを作る
```glsl
float bassDrum(float time)
{
    float localTime = mod(time * 4, 2);
    float ret;
    ret += rect(max(0, 1 - localTime * 2) * localTime * 300) * max(0, 1 - localTime * 4);
    return ret;
}
```
- 音源ごとに1小節程度のループを作り、それを組み合わせていく

---

## ループ内で時間を定義
```glsl
float loopTime = mod(time, 8);
float localTime = mod(time * 2, 1.0);
```

- 時間を二つ定義
    - ノートの大まかな定義を行うための「loopTime」
    - ノートの中での減衰などを行うための「localTime」
- ここも雑な実装なのでもっと何かありそう

---

## ループ内でのメロディを定義
- 素直につらつらとif文を並べる
- 音楽やってる人なら割と読みやすい感じになっている

```glsl

float poly(float time)
{
    float loopTime = mod(time * 8, 8);
    float localTime = mod(time * 8, 1.0);
    float ret = 0;

    if (loopTime < 1)
    {
        Sin1(1, G)
    }
    else if (loopTime < 2)
    {
        Sin1(2, D)
    }
    else if (loopTime < 3)
    {
        Sin1(2, F)
    }
    else if (loopTime < 4)
    {
        Sin1(2, G)
    }
    else if (loopTime < 5)
    {
        Sin1(3, C)
    }
    else if (loopTime < 6)
    {
        Sin1(2, G)
    }
    else if (loopTime < 7)
    {
        Sin1(2, E)
    }
    else if (loopTime < 8)
    {
        Sin1(2, C)
    }
    return ret;
}
```

---

## 和音を鳴らす
- さっきからチラチラ出てたコード

```glsl
if (loopTime < 2)
{
    Sin1(0, F)
    Sin1(0, C)
    Sin1(0, G)
}
else if (loopTime < 4)
{
    Sin1(0, E)
    Sin1(0, C)
    Sin1(0, G)
}
else if (loopTime < 6)
{
    Sin1(0, D)
    Sin1(0, C) 
    Sin1(0, G)
}
else 
{
    Sin1(0, E) 
    Sin1(0, C) 
    Sin1(0, G)
}
```

---

# Mix
- タイムラインを用いてループを繋げていく
- コードだとややこしく見えるが、よくあるDAWのループ編集と同じことをやっている
- vec2を掛けることでボリューム調整、パン調整ができる

```glsl
vec2 mainSound(float time)
{
    vec2 sound = vec2(0.0);
    if (time < 16)
    {
        sound += strings(time) * vec2(0.55, 0.6);
    }
    else if (time < 32)
    {
        sound += strings(time) * vec2(0.55, 0.6);
        sound += base(time) * vec2(0.4, 0.38);
    }
    else if (time < 48)
    {
        sound += strings(time) * vec2(0.55, 0.6);
        sound += base(time) * vec2(0.4, 0.38);
        sound += poly(time) * vec2(0.2, 0.3);
    }
    else if (time < 64)
    {
        sound += strings(time) * vec2(0.55, 0.6);
        sound += base(time) * vec2(0.4, 0.38);
        sound += poly(time) * vec2(0.2, 0.3);
        sound += bassDrum(time) * 0.4;
    }
    else if (time < 80)
    {
        sound += strings(time) * vec2(0.55, 0.6);
        sound += base(time) * vec2(0.4, 0.38);
    }
    else if (time < 96)
    {
        sound += strings(time) * vec2(0.55, 0.6);
    }
    sound += noiseSound(time);
    sound = clamp(sound, -vec2(1), vec2(1));
    return vec2(sound);
}
```

---

# みんなもGLSLで作曲しよう！！！

---

# 質疑応答