[VRChat]AudioLinkシェーダー定義関数の日本語解説

:::note info
本解説は、2023年12月(AudioLink Ver1.2.0)に書かれた解説になります。
最新版とは違う場合もありますので、適時に調べてみましょう！
::::

# 初めに
この記事は **AudioLink** を使ったシェーダーの制作する時に使う関数を解説していきます。
導入方法などは解説しませんので、ご了承下さい。
また、もしも間違えている個所がありましたら、教えていただけたら幸いです。

https://github.com/llealloo/vrc-udon-audio-link

<details><summary>AudioLinkで解析するAudioSourceについて</summary>


現在VRChat上の様々なワールドで使われているAudioLink。
(最新のバージョンでは通常のunityアプリでも動作するようになっているようです)
ワールドにAudioLinkを置いて、解析する __AudioSource__ を入れれば動いてくれる簡単設計です。

その __AudioSource__ の設定については、__AudioLink.prefab__ に入っている[AudioLinkInput]が使用している設定を参考にするのが良いです。

+ エフェクトの影響を受け無くする （Bypass *** のチェックボックスをONにする）
+  _Volume_ を0.01に（0にしてはいけない）

そしてVRChatのワールドとしてアップロードするために必要な __VRC_SpatialAudioSource__ を追加して、_Enable Spatialization_ のチェックを外して2D音源にし、NearとFarを調節して音の解析させたい範囲全体に響き渡るようにしましょう。


AudioLinkの設計上（Unityの仕様上）音の解析は、__VRC_SpatialAudioSource__ で設定されている音源からの位置減衰を適応して、聞こえている音量にて解析をしています。なので、3D音響や減衰を強めにしていると音源の近くに居る人と遠くに居る人で解析の結果に違いが出てしまいます。~~ですが、 __AudioSource__ の _Volume_ 設定は解析に関係なかったりします。
これによって、実際には聞こえてないぐらい遠い場所からでも音の解析が出来る事になります。~~
AudioLinkは小さな音でも解析した音のデータに補正を掛かけています。しかし、大きい音の方がしっかりと解析できると思われます。
(補正計算: _1 / Volume_ を解析したデータに積算している)

例えば、少し離れた場所に野外フェス会場が見えていて、音がよく聞こえない状況であっても、フェス会場にあるAudioLinkに対応した照明はビカビカ光っているなどという事も可能です。
</details>

# AudioLink Shader関数


__VRChat Creator Companion__ よりAudioLinkをインストールする事でAudioLinkを利用する事が出来ます。

`#include "Packages/com.llealloo.audiolink/Runtime/Shaders/AudioLink.cginc"`
をShaderに追加する事によってAudioLinkの関数を使用する事が出来ます。

# AudioLink
AudioLinkのテクスチャはこのような感じになってます。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1071164/9a255fa5-5091-279d-d7e8-0ed30e3a692e.png)
>画像引用元: [AudioLink Doc](https://github.com/llealloo/vrc-udon-audio-link/tree/master/Docs)

128 x 64サイズで、解析されたデータが順番に並んでいる形です。
AudioLinkの関数を使用するには、データ取得の関数にテクスチャのUV位置を指定する形となります。
` AudioLinkData( ALPASS_AUDIOLINK + uint(0,x) ).r `
例えばこのような形になり、この場合だと " __AudioLinkData()__ "がピクセルの数値そのままを取得する関数になっており、
" __ALPASS_AUDIOLINK__ "がテクスチャ上のUV位置が指定されていて、どの解析データを取るかの指定になってます。
その後の" + unit(0,x) "は、xには0～3の整数が入りBASSからTrebleまでの、どの音域のデータを取るかの指定です。
".r"については、赤(Red)の数値を取る形です。解析データによってはRGBAそれぞれに数値が入っています。


## 解析データの種類
AudioLinkが解析をするデータは多数あります。
```AudioLink.cginc
// Map of where features in AudioLink are.
#define ALPASS_DFT                      uint2(0,4)   //Size: 128, 2
#define ALPASS_WAVEFORM                 uint2(0,6)   //Size: 128, 16
#define ALPASS_AUDIOLINK                uint2(0,0)   //Size: 128, 4
#define ALPASS_AUDIOBASS                uint2(0,0)   //Size: 128, 1
#define ALPASS_AUDIOLOWMIDS             uint2(0,1)   //Size: 128, 1
#define ALPASS_AUDIOHIGHMIDS            uint2(0,2)   //Size: 128, 1
#define ALPASS_AUDIOTREBLE              uint2(0,3)   //Size: 128, 1
#define ALPASS_AUDIOLINKHISTORY         uint2(1,0)   //Size: 127, 4
#define ALPASS_GENERALVU                uint2(0,22)  //Size: 12, 1
#define ALPASS_CCINTERNAL               uint2(12,22) //Size: 12, 2
#define ALPASS_CCCOLORS                 uint2(25,22) //Size: 11, 1
#define ALPASS_CCSTRIP                  uint2(0,24)  //Size: 128, 1
#define ALPASS_CCLIGHTS                 uint2(0,25)  //Size: 128, 2
#define ALPASS_AUTOCORRELATOR           uint2(0,27)  //Size: 128, 1
#define ALPASS_GENERALVU_INSTANCE_TIME  uint2(2,22)
#define ALPASS_GENERALVU_LOCAL_TIME     uint2(3,22)
#define ALPASS_GENERALVU_NETWORK_TIME   uint2(4,22)
#define ALPASS_GENERALVU_PLAYERINFO     uint2(6,22)
// Added in version 2.5
#define ALPASS_FILTEREDAUDIOLINK        uint2(0,28)  //Size: 16, 4
// Added in version 2.6
#define ALPASS_CHRONOTENSITY            uint2(16,28) //Size: 8, 4
#define ALPASS_THEME_COLOR0             uint2(0,23)
#define ALPASS_THEME_COLOR1             uint2(1,23)
#define ALPASS_THEME_COLOR2             uint2(2,23)
#define ALPASS_THEME_COLOR3             uint2(3,23)
#define ALPASS_FILTEREDVU               uint2(24,28) //Size: 4, 4
#define ALPASS_FILTEREDVU_INTENSITY     uint2(24,28) //Size: 4, 1
#define ALPASS_FILTEREDVU_MARKER        uint2(24,29) //Size: 4, 1
// Added in version 0.3.0
#define ALPASS_GLOBAL_STRINGS           uint2(40,28) //Size: 8, 4
#define ALPASS_GENERALVU_SOURCE_POS     uint2(7,23)
// Added in version 1.0.0
#define ALPASS_MEDIASTATE               uint(5,22)
```
一つ一つ解説していきましょう。

--------------------

#### ALPASS_DFT
波形データを離散フーリエ変換したもの。__128 x 2__ ブロック使用。
A-1 (13.75Hz)を起点として、10オクターブ分の範囲を解析をしている。
1オクターブは24分割して解析しており、合計240ピクセルのデータになっている。
13.75Hzから約14kHzまで配置されており、低音域から高音域までサポートされています。

RGBAでの数値の違いは
+ RED: 解析されたデータそのまま
+ GRN: AudioLinkコントローラーの[Basic EQ]の設定が適応されたデータ
+ BLU: ColorChordで使用する用の強めなフィルターをかけたデータ
+ ALP: (予約)未使用

`AudioLinkLerpMultiline( ALPASS_DFT + uint2( i.uv.x * AUDIOLINK_ETOTALBINS, 0 ) ).rrrr;`

--------------------

#### ALPASS_WAVEFORM
Waveformは __128 x 16__ ブロックを使用し2048サンプルの波形データ（もしくは2046サンプル）
ちなみに、AudioLinkはこの波形データをシェーダーで受け取って、シェーダーであれやこれやして様々なデータに変換をしている（っぽい）。
中身は解析データままの-1～1の範囲を取る。

+ RED: 24kHz audio, 振幅. 2046サンプル
+ GRN: 48kHz audio, 振幅. 2048サンプル
+ BLU: 12kHz audio, 振幅. 1023サンプル
+ ALP: 24kHz audio の ステレオ差分データ. 2046サンプル 
 左チャンネルのデータは足して　`.r + .a`
 右チャンネルのデータは引く　 `.r - .a`　(ver2.5で追加)[^1] 

使用例 (24kHzのWaveFormデータを取得する場合)
`AudioLinkLerpMultiline( ALPASS_WAVEFORM + uint2( i.uv.x * 2046, 0 ) ).r;`

<details><summary>注釈</summary>
Udon#から4092のサンプリングデータをシェーダーで受け取って、そこからダウンサンプリングをしている。
GRNの48kHzのみ、加工無しで2048サンプリングデータを出している。、
</details>

--------------------
#### ALPASS_AUDIOLINK
AudioLinkは __1 x 4__ ブロックのデータ。
Bass , low-Mid , high-Mid , Treble　の音域別に解析データを取得する。
音域の幅やどのぐらいの音量でデータを取得するかは、AudioLinkのコンポーネントや、VRC内のAudioLinkコントローラーで設定をする。

+ RED: 音の強さ・振動データ
+ GRN / BLU: REDと同じデータ。今後のアップデートで解析の計算を変えたのを収納する可能性があります。
+ ALP: (予約)未使用

使用例
`AudioLinkData( ALPASS_AUDIOLINK + uint2( 0, i.uv.y * 4. ) ).rrrr;`

--------------------

 __ALPASS_AUDIOBASS__
 __ALPASS_AUDIOLOWMIDS__ 
 __ALPASS_AUDIOHIGHMIDS__ 
 __ALPASS_AUDIOTREBLE__

ALPASS_AUDIOLINKを使わないで、直接音域のテクスチャアドレスを指定する時に使用。

--------------------

#### ALPASS_AUDIOLINKHISTORY
ALPASS_AUDIOLINKの時間でのデータ変移。127 x 4 ブロック。
テクスチャ上で右に行くほど古くなる。

使用例
`AudioLinkLerp( ALPASS_AUDIOLINK + float2( i.uv.x * AUDIOLINK_WIDTH, i.uv.y * 4. ) ).rrrr;`
（このデータ変移をスムーズに使いたい場合には、_ALPASS_AUDIOLINKHISTORY_ を使用するのではなく  _ALPASS_AUDIOLINK_ を使用してください

--------------------

#### ALPASS_GENERALVU
テクスチャの22～23行目に配置されているデータ群。
様々なデータ が格納されている。

###### VU
ALPASS_GENERALVUの[x:8~10][y:0]の部分には音の強度を示す __VUデータ__ が格納されている
VUと言っているが計算式はRMSだと思われる。
取る数値は0～1になっている。

音の強度(RMS)
`AudioLinkData( ALPASS_GENERALVU + uint2( 8, 0 ));`

音のピークデータ
`AudioLinkData( ALPASS_GENERALVU + uint2( 9, 0 ));`

ピーク時間
`AudioLinkData( ALPASS_GENERALVU + uint2( 10, 0 ));`

それぞれ
RED: 左チャンネルの音の強度
GRN: 左チャンネルのピーク
BLU: 右チャンネルの音の強度
ALP: 右チャンネルのピーク
となっている。

基本はRedとBlueを使用するのが良いです。

<details><summary>注釈</summary>
GRNとALPにピーク音量が入っているのに、unit2(9,0)にもピーク音量があるじゃないかと思うかもしれませんが、
GRNとALPはAudioLink内で計算処理の方に使われています。ピーク時間も同様に計算処理に使用されています。

ピークの更新は、1フレーム処理前の音から大きい音になった場合 と 更新後１秒経ったら必ず更新されます。
</details>


<details><summary>注釈２</summary>
RMS値とPeak値については下記のサイトを参照してください。

https://yugo-music.jp/article-5731.html

</details>

###### Time

時間を観測する系のデータは、デコードが必要になっている。

 __ALPASS_GENERALVU_INSTANCE_TIME__ 
 __ALPASS_GENERALVU_LOCAL_TIME__ 
 __ALPASS_GENERALVU_NETWORK_TIME__ 
それぞれ時間を取る事が出来ます。時間を取る関数は ` AudioLinkDecodeDataAsUInt `と`AudioLinkDecodeDataAsSeconds` 。
" _INSTANCE_TIME_ "が、インスタンスが建ってからの時間(オーナーの退出等により同期がズレる事もあり、非推奨)。
" _LOCAL_TIME_ "が、ローカルでの0時からの経過時間。
" _NETWORK_TIME_ "が、サーバーに居る全員に同期される時間。アニメーションを同期させたい場合はこれを使用する事を推奨。
" _INSTANCE_TIME_ "と" _NETWORK_TIME_ "については1.5日時間で一巡する。


 __ALPASS_GENERALVU_UNIX_DAYS__ 
 __ALPASS_GENERALVU_UNIX_SECONDS__  
UTCタイムゾーンでの日付や時間を取得できる。


 __ALPASS_GENERALVU_PLAYERINFO__ 
VRChatでのプレイヤーデータが取れる。
RED:インスタンスでのプレイヤー人数
GRN:1ならインスタンスマスター
BLU:1ならインスタンスオーナー
ALF:未使用

###### Other

:::note info

他にもあるので詳しくは [公式ドキュメント](https://github.com/llealloo/vrc-udon-audio-link/tree/master/Docs#alpass_generalvu) を参照してください
:::

--------------------

#### ALPASS_FILTEREDVU
VUにスムージングフィルターが掛かったデータ。フィルターの強さは４パターン。
xはフィルター強度の選択。x:0が一番強くフィルターが掛かっていて、x:4が一番弱い。
` float4 vu = AudioLinkData(ALPASS_FILTEREDVU + uint2(i.uv.x*4, 0) ); `
RED : RMS Left
GLN : Peak Left
BLU : RMS Right
ALF : Peak Right


 __ALPASS_FILTEREDVU_INTENSITY__ 
 __ALPASS_FILTEREDVU_MARKER__ 
直接フィルタリングVUのデーターにアクセスする時に使用する。
INTENSITYが音の強度データ。
MARKERがピークのデータ。

REDかBLUEのデータを使用しておけば良いと思います。

FILTEREDVUの機能例については、Shaderフォルダーに入っている"FilteredVUDebug"を確認してみて下さい。

--------------------

#### ALPASS_CCINTERNAL

内部カラーコードノートの表示（？）
今のところ変更される可能性がありますとの事。

--------------------

#### ALPASS_CCCOLORS

ColorChordインデックスカラー。Unity上のAudioLinkで設定したテーマカラーの色を元に、時間経過で色が変化していく。また、音の強さで色も変化する。
UVをマッピングするのに適しています。

これらは本当に強度の異なる色なので、単に色として使用することができます。ColorChord から色をサンプリングする他の方法とは異なり、これらの色は時間が経過しても比較的安定しているので、ライトなどの世界の小道具の色付けに適しています。

AudioLinkコントローラーの下部に表示されている、"Theme Colors"で表示されている色を取得できます。
`AudioLinkData( ALPASS_CCCOLORS + uint2( "colornumber: 0 ~ 3", 0 ) );`

--------------------

#### ALPASS_CCSTRIP

ColorChordの一本の直線的な帯で、直線的な円グラフのようなものと考えてください。ここの色を直接サーフェスに適用することができます。

`AudioLinkLerp( ALPASS_CCSTRIP + float2( i.uv.x * AUDIOLINK_WIDTH, 0 ) ).rgba;`

https://twitter.com/skknyuki/status/1540979275708239872?s=20&t=bfuJjV0Z7tWSyK0vn4fUtg

--------------------

#### ALPASS_CCLIGHTS

2つの行があり、下の行は生のcolorchordライトの値を含んでいます。音に関連した色を必要とする個々のオブジェクトや照明が、バラバラに存在する場合に便利です。例：紙吹雪の破片、ランプ、スピーカー、ブロックなど。

2行目(0,1)は、ColorChordの内部状態を追跡するために使用されます。変更される可能性があります。使用しないでください。

`float4 col = AudioLinkData( ALPASS_CCLIGHTS + uint2( uint( i.uv.x * 128) , 0 ) ).rgba;`

https://twitter.com/skknyuki/status/1540979396961374208?s=20&t=bfuJjV0Z7tWSyK0vn4fUtg

--------------------

#### ALPASS_AUTOCORRELATOR

この行の赤いチャンネルは、波形の偽自己相関を提供します。これはDFTから波形を再合成するものです。これは左右対称なので、右半分だけがオーディオリンクで表示されます。これを使用するには、左軸にミラーリングすることをお勧めします。この列の緑色のチャンネルは、無相関の自己相関を提供します。これは赤のチャンネルに似ていますが、始まりも終わりもありません。

` return AudioLinkLerp( ALPASS_AUTOCORRELATOR + float2( ( abs( 1. - i.uv.x * 2. ) ) * AUDIOLINK_WIDTH, 0 ) ).rrrr; `

https://twitter.com/skknyuki/status/1540979457086963717?s=20&t=bfuJjV0Z7tWSyK0vn4fUtg

--------------------

#### ALPASS_FILTEREDAUDIOLINK

ALPASS_AUDIOLINKのデータと似ていますが、スムージングフィルターがかかっているので、非常に滑らかに動きます。
スムージングの強さは16段階に分かれています。
yで、ALPASS_AUDIOLINKと同様に４つの周波数別のデータを設定して
xで、スムージングの強さを設定します。x:0が一番スムージングが強く、x:15がスムージングが一番弱めです。

` AudioLinkDataMultiline( ALPASS_FILTEREDAUDIOLINK + uint2(i.uv.x * 16, i.uv.y*4 ) ).rgba; `

--------------------

#### ALPASS_CHRONOTENSITY

4バンドのデータをもとに、数値が累積的に増減する区間です。これにより、時間の中でアニメーションがスムーズに動くようになる。


| offset.x | Description |
|:-:|:-:|
| 0  | 音の強さで動きが大きくなる。数値は後戻りしない。 |
| 1  | *１   |
| 2  | 音の強弱で、数値が上昇下降する(補正有り)。  |
| 3  | *１　 |
| 4  | 音が弱いと定速上昇。強いと止まる。音が無いと動かない。  |
| 5  | *１　  |
| 6  | フィルターなし、音の強さが0.05を上回ると上がっていく。 |
| 7  | フィルターなし、音の強さが0.05を下回ると上がっていく。 |



 *１:すぐ上の処理と同じですが、_ALPASS_AUDIOLINK_ の代わりに _ALPASS_FILTERAUDIOLINK_ を使用してます。スムージングレベルはx:0を使用しており、かなり強いフィルターが掛かっています。
 


```
// [0 ～ 1]を繰り返す
float chrono = (AudioLinkDecodeDataAsUInt( ALPASS_CHRONOTENSITY  + uint2( 1, _AudioLinkBand ) ) % 1000000) / 1000000.0;

// [0 ～ 1]を繰り返す。上記のより早く繰り返す。
float chrono = (AudioLinkDecodeDataAsUInt( ALPASS_CHRONOTENSITY  + uint2( 1, _AudioLinkBand ) ) % 100000) / 100000.0;

// [0 ～ 6.28](つまり２π)を繰り返す
float chrono = (AudioLinkDecodeDataAsUInt( ALPASS_CHRONOTENSITY  + uint2( 1, _AudioLinkBand ) ) % 628319) / 100000.0;

// [0 ～ 6.28](つまり２π)を繰り返す。上記と結果はほぼ同じだが計算は早い
float chrono = (AudioLinkDecodeDataAsUInt( ALPASS_CHRONOTENSITY  + uint2( 1, _AudioLinkBand ) ) % 100000) / 100000.0 * 6.28;
```

解析には _AudioLinkDecodeDataAsUInt_ を使用。これはRGBAの色情報を32ビット整数にエンコードします。

--------------------

#### ALPASS_THEME_COLORx

AudioLinkでは、アバターに適用できる4つのテーマカラーを用意しています。デフォルトではColorChordの色が使用されますが、ワールドクリエイターはAudioLinkコントローラーを弄る事によって、マップのテーマカラーを選択し、動的に変更することができます。

--------------------

#### ALPASS_MEDIASTATE
Udon経由で設定された、または接続されたオーディオソースから自動的にクエリされた、接続されたビデオプレーヤーの状態を提供します。
詳しくは公式ドキュメントを読んでください。

--------------------

#### ALPASS_GLOBAL_STRINGS
文字列をShaderで書くやつ。
こちらも詳しくは、公式ドキュメントをお読みください。



# その他の定義済値
```AudioLink.cginc
// Some basic constants to use (Note, these should be compatible with
// future version of AudioLink, but may change.
#define AUDIOLINK_SAMPHIST              3069 // Internal use for algos, do not change.
#define AUDIOLINK_SAMPLEDATA24          2046
#define AUDIOLINK_EXPBINS               24
#define AUDIOLINK_EXPOCT                10
#define AUDIOLINK_ETOTALBINS            (AUDIOLINK_EXPBINS * AUDIOLINK_EXPOCT)
#define AUDIOLINK_WIDTH                 128
#define AUDIOLINK_SPS                   48000 // Samples per second
#define AUDIOLINK_ROOTNOTE              0
#define AUDIOLINK_4BAND_FREQFLOOR       0.123
#define AUDIOLINK_4BAND_FREQCEILING     1
#define AUDIOLINK_BOTTOM_FREQUENCY      13.75
#define AUDIOLINK_BASE_AMPLITUDE        2.5
#define AUDIOLINK_DELAY_COEFFICIENT_MIN 0.3
#define AUDIOLINK_DELAY_COEFFICIENT_MAX 0.9
#define AUDIOLINK_DFT_Q                 4.0
#define AUDIOLINK_TREBLE_CORRECTION     5.0

// ColorChord constants
#define COLORCHORD_EMAXBIN              192
#define COLORCHORD_IIR_DECAY_1          0.90
#define COLORCHORD_IIR_DECAY_2          0.85
#define COLORCHORD_CONSTANT_DECAY_1     0.01
#define COLORCHORD_CONSTANT_DECAY_2     0.0
#define COLORCHORD_NOTE_CLOSEST         3.0
#define COLORCHORD_NEW_NOTE_GAIN        8.0
#define COLORCHORD_MAX_NOTES            10

// Text constants
#define AUDIOLINK_STRING_MAX_CHARS      32
#define AUDIOLINK_STRING_LOCALPLAYER    0
#define AUDIOLINK_STRING_MASTER         1
#define AUDIOLINK_STRING_CUSTOM1        2
#define AUDIOLINK_STRING_CUSTOM2        3
```
これらを使用してシェーダーを制作する事で、もしも仕様変更があった場合でも問題無くシェーダーを使用できることでしょう。

# データ取得関数

` float4 AudioLinkData( int2 coord ) `
_AudioTextureにある1ピクセルからデータを取るための関数

`float4 AudioLinkDataMultiline( int2 coord )`
AudioLinkData()と同じだが、Waveformなど複数の行にまたがっているデータを取るときに使用する関数。

`float4 AudioLinkLerp( float2 fcoord )`
1ピクセルからだけでなく、隣近接ピクセルとの線形補間されたデータ値を取るための関数。

`float4 AudioLinkLerpMultiline( float2 fcoord )`
 複数行のデータで線形補間されたデータ値を取るための関数。

## AudioLink.cgin内で定義済み関数

`glsl_mod( x, y )`
GLSLのmodと同等の計算式。負数を与えられた時の計算結果がfmod()と違う。

`float3 AudioLinkHSVtoRGB( float3 HSV )`
HSVをRGBに変換。

`float3 AudioLinkCCtoRGB( float bin, float intensity, int RootNote )`
カラーコードの標準カラー生成機能関数(?)

`bool AudioLinkIsAvailable()`
_AudioTextureが利用できるかどうか？

`float AudioLinkGetVersion()`
AudioLinkのバージョンをfloatで取得。もしも"0"だったらAudioLinkは存在しない。

`float AudioLinkRemap(float t, float a, float b, float u, float v)`
Remaps value t from [a; b] to [u; v]



# オマケ
UnityがPlayModeになった時に、Youtubeの動画を再生してくれるスクリプトが追加されてます
AudioLink/scriptsフォルダに、[ytdlpPlayer]というのがそのスクリプトです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1071164/4945476d-12f1-7ea5-f017-93a855a2ec01.png)
上記のような設定にする事で、Unity上でYoutubeを再生し好きな動画の音で解析を走らせることが出来ます。
ただのScriptなのでVRC上にアップロードされることは無いので、便利です。

# この記事は以下のURLを元に制作をしました

https://github.com/llealloo/vrc-udon-audio-link/tree/master/Docs

https://docs.google.com/spreadsheets/d/1PkA98uI_zslpTr6ARBVGOBSq5Yna0rKPe_RWbdtbERM/edit#gid=0


[^1]:現在はAVPROに存在するバグの為に、ステレオでの解析は出来ない様だ