シェーダで音楽を作るmusic shaderの紹介

この記事は、シェーダアドベントカレンダー13日目の記事です。
https://qiita.com/advent-calendar/2019/shader-advent-calender-2019

#### シェーダで音楽を作るとはどういうことか

まずはこのページを開いて楽曲をお聞きください。（自作のヤツで恐縮ですが）
https://www.shadertoy.com/view/3tX3D8

興味を持った方はShadertoy上で作ってみるか、
もしくはは下記ページの「曲書くためのツール」から落とせるツールで、
シェーダ使って曲を書くことができます。

https://machiaworx.net/?p=332

これらは、シェーダで音楽を作るという一風変わったアプローチが実践されている光景です。
グラフィック作るシェーダでどうやって音楽作るんだよ！という意見はごもっともなのでこれから内容を記載していきます。

#### まず、どうやって音を作るの

楽曲における音は、波を与えてスピーカ等に振動を伝えることで成立します。
すなわち、時間における波形を計算して、最終的にスピーカに出力できれば音が鳴ります。

例：サイン波
```GLSL:test.fs
vec2 mainSound( float time )
{
    // A 440 Hz wave that attenuates quickly overt time
    return vec2( sin(6.2831*440.0*time)*exp(-3.0*time) );
}
```
（上記の式はShadertoyからの引用のため、timeがfloat型になっております）

ソフトウェアシンセサイザーもこの方式で作れます。
（勿論シェーダ以外で書く場合は文法が割と違いますけど）

#### どうやってシェーダで音を出すの

以下にフローを記載します。
主にPC上で動くアプリケーション作成を想定してます。

* バッファをVBOとしてGPUに領域を確保する
* transform feedbackで計算結果をVBOに格納する準備
* シェーダで波形生成を行う
* VBOをクライアント側のメモリに格納する
* バッファの計算結果をWAVとして再生する

##### VBOとしてGPUに領域を確保

VBO→パーティクルとかでおなじみ、頂点データを格納するGPU側のメモリに置かれたバッファになります。
パーティクルだったらVBOに頂点座標を格納する変数をバインドしますが、今回は2*（曲の長さ*44100 byte）となるデータをバインドすることにします。
（チャンネルが2だから2をかけてます。これが5.1chだったら6になるとは思いますけど・・・そうなったらもう手に負えないところですな）

##### transform feedback

Transform Feedbackとは、シェーダによる計算結果をVBOに直接格納する処理です。（CPUを経由しません）
今回のケースでは、Transform Feedbackを利用して「GPUで1回波形の計算して結果を取得する」ことになります。
よって、glDrawArrays()で描画命令→シェーダ計算開始→VBOに計算結果を格納という流れになります。（実際に描画して結果が出てくるわけじゃないですよ。あくまでシェーダを動かすために描画命令を起動するというだけです）

ただこの処理はOpenGL3じゃないとできないので注意。
機能を使いたい場合は別途機能を呼び出す必要があります。

※ちなみにShadertoyは、「波形の計算結果をテクスチャに書き込む」という違う方法で実装してるっぽいです。

##### シェーダで波形生成

波形生成の式はVBOをいじる関係上頂点シェーダ上で行う形になります。
計算方法としては、時間における波形の位置をシェーダで計算する形になります。
すなわち、1秒時は波形はこういう計算してるはず、2秒ではこういう計算・・・
という形で作り込んで行くことになります。

shadertoyのほうに割と基本的な情報を書いておきましたが、割とクセがあります。
（とは言っても波形をわざわざ計算で書く人は少ないと思いますが）

##### VBOをクライアント側のメモリに格納

描画命令の処理後、CPU側から計算結果にアクセスするため、glGetBufferSubData等を使ってCPU側からアクセスできるメモリとして格納する必要があります。

##### 計算結果をWAVとして再生する

プラットフォーム依存になりますので、呼び出しはそれほど難しくないです。
Win32APIとかで鳴らすことができます。

#### 使い所

* Shadertoy
* シンセサイザー実装のテストするとき
* その他

今の所利用できる場所は限定されますが、音楽機材も必要なく、他にライブラリも必要なく、さらに計算資源をフル活用できるので、知識を持っているのであれば割と有用な選択肢だと思います。

また、実際のアナログシンセの回路に関する知識も必要ないため、
単に波形を生成する式を書けばさっさと動くのが利点ではありますね。

#### 私見

正直まだ商用でGPU制御の音源・エフェクトとかはあまり出てません（一時期CUDA使ったリバーブとかあった）。
ただ、一部商用のゲームではピッチや音の鳴る方向に対して制御をかけているため、どうしても計算が必要になるケースが存在します。

そういうときの選択肢としてGPGPU含めたサウンドの逃し方みたいなので使えることがわかるだけでも問題が出なくなるんじゃないんかなと考えます。

#### 最後に

結構ニッチな使い方ではありますが、ライブラリが限定されていても使える処理なので、割と応用がきくと思います。
（そもそもこれっていわゆるGPGPU処理に類する形ですし）

ということで、皆様のシェーダ利用ライフに幸あれ。

#### 参考資料

https://wgld.org/d/webgl2/w015.html
みんな大好きwgld.org。

http://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/index.php?OpenGL%20-%20VBO
VBOの使い方とか参考にできそうなところって意外と少ないので。

https://qiita.com/MachiaWorx/items/68f8691d0866e0e0ff68
https://qiita.com/MachiaWorx/items/75c63dff12b4a6189a35
https://qiita.com/MachiaWorx/items/43bc7868e9fc770b52f4
https://qiita.com/MachiaWorx/items/848b529afd4290d8cfda
https://qiita.com/MachiaWorx/items/a146400fd438a411b0b2
https://qiita.com/MachiaWorx/items/70a14b725d8a8b1d2450
1年くらい前に自分が書いた記事。

https://machiaworx.net/?p=332
Shadertoyだと使いにくいという人向けのツールを公開してます。
これならスタンドアロンで使うことが可能です。
（ただ結構GPUに負荷をかける作りになっているので、一部ノートPCだと動かないケースがあります。前回TDFで直前で調整しようとしたらSurface上で動かなくて焦った）