Edited at

GLSL作曲入門


自己紹介

128.png


  • 名前: notargs


作ったもの


  • LGTM Shaders

LGTM


  • CRT Shader

CRT



今回話す内容


?「Game Jamでプログラマーのみのチームになってしまった…」


?「Game Jamでプログラマーのみのチームになってしまった…」

?「音楽素材どうしよう…」


?「Game Jamでプログラマーのみのチームになってしまった…」

?「音楽素材どうしよう…」

@「それ、シェーダーで作れますよ!!」



メリット


  • プログラマーだけで楽曲制作が完結できる

  • OpenGLとPCMにさえ対応していれば動くため、移植性が高い

  • プロシージャルなので圧縮率が高い



デメリット


  • 高度な知識と実装力が必要

  • めんどくさい



手法



Transform Feedback


  • 頂点シェーダでの計算結果を頂点バッファオブジェクトに再度格納する機能

  • CPUからアクセス可能



Transform Feedback


  • 頂点シェーダでの計算結果を頂点バッファオブジェクトに再度格納する機能

  • CPUからアクセス可能

  • GPGPUできる!!



シェーダーの中身



  • gl_vertexIDを元に、そのタイミングでの波形を返す関数

  • 戻り値はステレオに対応したvec2型

void main() 

{
float time = gl_VertexID / 44100.0;
out_sample = mainSound(time) * 0.5;
}



再生処理

waveOutOpenとかで適当に流す

waveOutOpen(&h_wave_out, WAVE_MAPPER, &wave_format, reinterpret_cast<DWORD_PTR>(hWnd), 0, CALLBACK_WINDOW);

waveOutPrepareHeader(h_wave_out, &wave_hdr, sizeof(wave_hdr));
waveOutWrite(h_wave_out, &wave_hdr, sizeof(wave_hdr));



音階と周波数


SnapCrab_NoName_2017-1-25_11-20-6_No-00.png


  • みんな大好きsinカーブ

  • 濁りの無い音


SnapCrab_NoName_2017-1-24_5-8-6_No-00.png

- 横幅が周波数(音の高さ)で、1秒間の振幅数、hz(ヘルツ)で表す

- 縦幅が音量に対応している


SnapCrab_NoName_2017-1-25_11-17-52_No-00.png

- 間隔が狭ければ高い音に


SnapCrab_NoName_2017-1-25_11-19-21_No-00.png


  • 間隔が大きければ低い音になります



音階の計算方法


  • ラ(A)の音が440hz

  • 周波数が二倍になると、音階が1オクターブ上がる

  • 1オクターブは12音なので、下記の式で特定の音についての周波数が計算できる

float calcHertz(float scale)

{
return 440.0 * pow(2.0, scale / 12.0);
}



バスドラムの音を作る


  • 低音でちょっとずつ音量を下げていく

  • 音量とともに周波数も下げても面白い

SnapCrab_NoName_2017-1-25_14-4-45_No-00.png



ハイハット


  • 一瞬だけ乱数を鳴らしてフェードアウトさせる

SnapCrab_NoName_2017-1-25_14-12-51_No-00.png



矩形波


  • サインカーブと比べると少しトゲのある音

SnapCrab_NoName_2017-1-25_14-16-7_No-00.png



ストリングス(和音)


  • 複数の音を鳴らすには単純に足し算すればいい

  • 下の例文はAm7のコードを鳴らすコード

sound += rect(time * calcHertz(24.0)); // ラ(A)

sound += rect(time * calcHertz(28.0)); // ド(C)
sound += rect(time * calcHertz(31.0)); // ミ(E)
sound += rect(time * calcHertz(35.0)); // ソ(G)



ストリングス(和音)


  • グラフで見るとこんな感じ
    SnapCrab_NoName_2017-1-25_14-23-59_No-00.png



ミキシング


  • それぞれの音を関数として定義

  • main直下で足し算することでミキシング

  • 0.5などの数値を掛け算してボリュームを変更

  • vec2を掛けることでパン(音の低位)を振る

vec2 mainSound(float time)

{
float sound = 0.0;
sound += bassDrum(time) * 0.5;
sound += snereDrum(time) * 0.5;
sound += hiHat(time) * 0.5;
sound += strings(time) * 0.2;
sound += bass(time) * 0.2;
if (abs(sound) > 1.0) sound /= abs(sound);
return vec2(sound);
}



最終的な曲のグラフ

SnapCrab_NoName_2017-1-25_14-27-2_No-00.png



実演

https://www.shadertoy.com/view/4l3GD2

※ このサンプルで使っているShaderToyではTransform Feedbackを使っていません