More than 1 year has passed since last update.

今回使うツール

DemoEditor

https://github.com/notargs/DemoEditor

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

シェーダーで曲を作る

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

頑張って曲を作りました

  • サンプルを流す

わかったこと

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

音を鳴らす

Temp.gif
- 空気の振動が耳に伝わって音が聞こえる
- スピーカーが振動して空気を振動させる
- スピーカーの振動周期をGLSLで定義する


音階の計算

441\pi(s/ 12)^2
float calcHertz(float scale)
{
    return 441.0 * pow(2.0, scale / 12.0) * PI;
}

『ラ』の音を基準とした音階を計算する


秘伝のタレその1

#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)
  • 多少慣れはいるが圧倒的に読みやすい!!

ちなみに♯(半音上げ)や♭(半音下げ)はこう書く

Sin1(0, F+1)
Sin1(0, F-1)
  • わかりやすい

秘伝のタレその2

#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:
  • でもコードはかなりスッキリする
  • もっと綺麗な書き方がありそう

これが

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);

こう書ける

Sin1(0, F)
Sin1(0, C)
Sin1(0, G)
  • 無いとシンドイ

ループを作る

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小節程度のループを作り、それを組み合わせていく

ループ内で時間を定義

float loopTime = mod(time, 8);
float localTime = mod(time * 2, 1.0);
  • 時間を二つ定義
    • ノートの大まかな定義を行うための「loopTime」
    • ノートの中での減衰などを行うための「localTime」
  • ここも雑な実装なのでもっと何かありそう

ループ内でのメロディを定義

  • 素直につらつらとif文を並べる
  • 音楽やってる人なら割と読みやすい感じになっている
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;
}

和音を鳴らす

  • さっきからチラチラ出てたコード
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を掛けることでボリューム調整、パン調整ができる
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で作曲しよう!!!


質疑応答

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.