Help us understand the problem. What is going on with this article?

GLSL作曲講座

More than 3 years have passed since last update.

GLSL作曲講座

by notargs
1 / 22

今回使うツール

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で作曲しよう!!!


質疑応答

virtualcast
VRシステム(バーチャルキャスト)の開発、運営、企画
https://virtualcast.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした