GPUSound入門(5) 応用-音色まわり

### 応用編はじまり

適当に思いつくままに書いていこうと思います。
時間ないので細切れにしてきます。
あとコードも書けたら書くというレベルにしていきますのでご了承下さい。

だんだん音楽の専門用語が増えてくる感じがありますが、不明点あればググるか聞いてもらえればある程度は回答できるかと。

### 音源形式

#### VA(バーチャルアナログ)音源

これはいわゆる「減算合成」と呼ばれる形式です。
アンプ部分で音量を上下させ、フィルター等で音をカットしていく形式です。
不完全ではありますがコード載せときます。

“` GLSL:SampleCode_simpleSynth1.vs
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) );
}
“`

ただGPUで実装する際は問題が出てきます。
フィルターは基本的に「前に出た音を複数回サンプリングして」実装していくんですが、GPUみたいに並列計算だとそもそもサンプリングができない可能性があるんですね。
前段階で不完全と書いたのはフィルターがないからです。

まだ実装はしていませんが、TransformFeedbackを使って音を作る形式なら、1個前のサンプリングを行ってフィードバックとすることは可能な気はします。
(ただそのために計算量を単純に3倍以上にするのは考えものです)

参考資料:
https://github.com/MachiaWorks/TinySynth/blob/master/synth.h
まさか自分の実装参考にしてなんてリンク貼ることになるとは予想してなかったが、
そんなにヒョイヒョイ実装例があるわけじゃないので引っこ抜くなら自分のソースが早いという考え。

ちなみに実装自体はEQCookbookを参考にしてます。
(EQやフィルタ実装するのに必要なパラメータがまとまってるありがたい資料)
http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt

#### FM音源

FrequencyModulation(周波数変調)という形式です。
つまり音程を入力するときに周波数を入力しますが、これを特定の方法で変更させるという形になります。

実装については第3回参照。
https://qiita.com/MachiaWorx/items/43bc7868e9fc770b52f4

並列のキャリア-オペレータの場合は単純に波形を重ね合わせればいいので、
単純に発音を増やすだけで対応可能です。

#### 加算合成

古くはオルガンのような作りのシンセ。

フーリエ変換によって、サイン波の組み合わせで実質無限に音が作れるみたいだけど、シンセサイザーとして使う場合、まずはサイン波を1個のオペレータとしてどう調整するかという話になってしまう。

自分の場合は3OSCくらいにしてそれぞれにセミトーン入れる形で音色を作ったりしてます。
前回の記事から引っ張ってきたコードを加工します。

“` GLSL:SampleCode_AdditiveSynth.vs
vec2 mainSound( float time )
{
// 加算合成音源のサンプル
vec2 result = vec2(0.0f);

//和音
float pitch_plus;
for( int i=0; i<4; i++){
pitch_plus = float(i)*200.+220.0;

for( int j=0;j<4; j++){
result += vec2( sin(6.2831*pitch_plus*time))/float(i+j+1);
}
}

result *= exp(-0.5*time) * vec2(0.5,0.5);

return result*0.4 ;
}
“`
あまりうまくないですが。

ちなみに32kbでGPU酷使したデモ作ったグループの人が有用なサンプル作ってます。
https://www.shadertoy.com/view/4syXRc

参考資料(デモグループのひとが書いたシンセの資料)
http://www.graffathon.fi/2016/presentations/additive_slides.pdf

### LFO

結局のところ考えはFM音源と同じです。
周波数が上下するという考えですね。

単に周波数だけに影響与えるという考えから、音程にだけサイン波を与えてみましょう。

“` GLSL:SampleCode_LFO
vec2 mainSound( float time )
{
// A 440 Hz wave that attenuates quickly overt time
return vec2( sin(6.2831*(440.0+sin(time*40.))*time))*exp(-0.5*time);
}
“`

なんか時間が経過するごとにLFOの値がすごくなってきてますが、気にしないでください。

### キックの作り方

こっから、結構わかりやすい音色になると思われるキック(バスドラム)の作りについていろいろ意見があったので、作り方でも書いておこうと思います。

音色に困ってもキックが鳴ればだいたい曲の形になる!というなかなかに割り切った考えですw

#### ノイズを短く出す

ファミコン音源やSID音源で使われるっぽい気がする実装ですが、
自分がやってみたところノイズ関数自体いじっても適当な音しか出ませんorz
ランダム数値をいじってみるのもめどがつかないので、
その周波数の細かさゆえに(44100サンプル内でランダム数値になるので結構振動してるあつかい)ハイハットなら利用可能と思いますが、キックでノイズ使うの辛いです。

“` GLSL:SampleCode_kick1
// wave genelators ———-
float noiz(float s){
return floor(sin(s*78.233)*43758.5453)*2.-1.;
}

vec2 mainSound( float time )
{
// A 440 Hz wave that attenuates quickly overt time
return vec2( noiz(time))*exp(-70.*time);
}
“`

#### サイン波を使ったキック

自分はこの方法でキックを作ってます。
やってることは、以下の通り。

* サイン波を出す際、周波数をどんどん下げていく
* リリースの時間を短くする

自分がTRのピッチ少し高くしたドラムが好きなのでこういう音になりますが、低い音が好きならもうちょっとピッチの緩やかさとか基準になる音を高くする等で調整可能です。

“` GLSL:SampleCode_Kick_Sin
vec2 mainSound( float time )
{
return vec2( sin(6.2831*(110.0-90.*time)*time)*exp(-5.0*time) );
}
“`

#### Cos波を使ったキック

TDFのときにGPUSound興味がある人で集まってあーだこーだ話したり人のソース見てたとき、こんなのあるんじゃね?って話になりました。
とりあえず参考資料をベースに書いてみました。

技術的な部分で言うと、サイン波でキックを作る技術に加えて「コサイン波で書くため、値が1から始まることで微妙にノイズが入る」んですね。
きれいな音と一緒に慣らしてみると結構際立つんではないでしょうか。
もちろん波形がブツ切れになるので少しばかりカドが立たないような調整するのが好ましいですが。
(スピーカにも優しい)

あと、周波数の減衰についてもexp関数を使ってみました。この方が記述はコンパクトかもしれませんね。

“` GLSL:SampleCode_Kick_Cos
vec2 mainSound( float time )
{
return vec2( cos(6.281*(140.0*exp(-2.5*time))*time+0.5)*exp(-6.0*time) );
}
“`

参考:
https://github.com/FMS-Cat/20180310-glsl-music

FMS_CatさんはPhase(位相)も気にして加工されてるみたいですね。
突き詰めていくとその方がより音を追い込めるかもしれない。

## さて、次は

エフェクト周りはいくつかストックがあるので作ってみる予定です。