Try & Error for Sound Program

FoxDotを始めました。色々と試してみます。

和音をプログラムで扱うには その3

GLSLで和音の配列を扱うのは厄介なので、配列をpackする方法をとっています。
packって何だって思うと思います。GLSLにはpackUnorm4x8()という関数が有ります。これはvec4をビット演算を使いuintにして、packしたuintをunpackUnorm4x8()で又vec4にするといったものです。これの応用になります。和音のノート番号は127未満なので7bitで済みます。4和音を使う事を前提にしても、4x7=28で28bitで済みます。GLSLは32bit使えるから8bitで良いだろうと思われますが、uintを使うと数値に 60uとかuを付けないとならないのです。そこを嫌ってintで31bit以内としました。ここはセコイって言われても記述が1文字増えるのに抵抗があるのです。気にならないならuintの方がスマートだと思います。
ここは改造というか、仕様変更も有るかもなので、具体例は出さないで、そういう例もあるよって紹介だけにしておきます。

和音をプログラムで扱うには その2

3音和音(トライアド)をアルゴリズムで扱うと、馬鹿みたいに単純です。だけど音楽理論が頭に有ると邪魔をします。 4音和音(セブンス)も単純です。さて、説明にあぐねています。
言葉での説明を考えたけどFoxDotの方が良いかなで。

p1>>pluck(P[0,1,2,3,4,5,6]+(0,2,4))

これで、I ,IIm, IIIm, IV, V, VIm, VIIm(♭5)の和音が順次なります。
ここでメジャースケールの配列を見てみます。

>>> print(Scale.major)
P[0, 2, 4, 5, 7, 9, 11]

では、
Iの和音の構成を見ます。0,4,7 これは長3度でメジャーになります。
IIの和音の構成を見ます。2,5,9 これは短3度でマイナーになります。
IIIの和音の構成を見ます。4,7,11 これは短3度でマイナーになります。
スケールを使ってインデックスで0,2,4に組み合わせると、勝手に和音になり、メジャー、マイナーを意識しないでよくなります。
4音和音(セブンス)でも同じ事。

p1>>pluck(P[0,1,2,3,4,5,6]+(0,2,4,6))

なので扱いはディグリーネームIVのトライアドとか、ディグリーネームVIのセブンスという感じになる訳です。

以前の図より、sus4なら

p1>>pluck(P[0,1,2,3,4,5,6]+(0,3,4))

解れば、単純なんですね。ただ覚えた音楽理論が有ると頭の整理に時間が要ります。 慣れなのですかね スケールが変わってもルールは変わりません。これが絶妙です。音楽理論の本質って、むしろこれだろうって思っています。

和音には転回と言うのが有ります。トライアドの場合、3音の内低い音をオクターブ上げて和音にするか、3音の内高い音をオクターブ下げて和音にするかのようですが、この辺りもコントロールしないとならないのですかね。

和音をプログラムで扱うには

ある時にスケールを使って和音を構成すれば簡単である事に気付いた。それ以来、その方法を使っている。FoxDotを始めて眺めた時に、スケールを使って和音を作っていた。これで確信に変わった気がした。それもあるのでFoxDotを選択したのもある。更にpythonがベースなんて最高だね。
今まで3音和音(トライアド)には使っていた方法ではあるが、他の4音和音(セブンス)とかsus4とか考えあぐねていたが良いモノを見つけた。

これだ。ちょっと小躍りしました。まだ、これをどう使うか固めてません。とりあえず、紹介まで。これから、説明を書きながら考えを纏めながら仕組みを考えるつもりです。

FoxDotのインスタンス変数の一覧が見たい時

FoxDotはpythonで書かれているので、便利に使えたりします。
インスタンス変数の一覧を見るには

p1 >> pluck()
print(vars(p1))

これを実行すれば、マニュアルに載ってないのが、いっぱい出てきます。まあ、ほどんど使う事は無いでしょうね。

FoxDotでのスケール

FoxDotのスケール周りの事を書いていきます。
FoxDot | Documentation | Roots And Scales

手始めにメジャースケールの配列を見てみます。

>>> print(Scale.major)
P[0, 2, 4, 5, 7, 9, 11]

FoxDotのデフォルトも見てみます。

>>> print(Scale.default)
P[0, 2, 4, 5, 7, 9, 11]

デフォルトでメジャースケールが設定されてます。 キーも見てみます。

>>> print(Root.default)
0

0が返ってきています。これは'C'です。1なら'C#'、2なら'D'となっていきます。
オクターブを取得方法は分りませんが、おそらく4だと思います。
FoxDotでドレミファソラシドを鳴らしてみます。

p1 >> pluck(P[:8])

これは[0,1,2,3,4,5,6,7,8]という配列でドレミファソラシドを鳴らします。ピアノの白鍵を順番に鳴らした状態です。 スケールの配列に順番に0,1,2.....というわけです。8はサイズ外ですが勝手にオクターブを上げてインデックスを0に戻してくれるようです。オクターブ下は、-1,-2...と書いて行き、やはり勝手にオクターブを合わせてくれます。スケールから外れた#とかは、0.5,1.5....と柔軟に対応してくれます。
これをGLSLにしてみます。

#define C  60
#define Major_scale  int[](0,2,4,5,7,9,11)
#define Pitch(n) C + Major_scale[n%7] + n/7*12

GLSLだとスケールを外れた音には対応できません。スケールを外れた音は、やって出来ない事もないけど、特殊ケースなので素直に諦めましょう。和音の中にはスケールを外れた音が有りますが、それは和音の方で対応出来ます。

shadertoyに載せてあるsound shader

shadertoyに載せてあるsound shaderの近い所から4つ貼っておきます。
一々、探してまでの手間を省きます。
イマイチなんだけど、これがもう少し良い感じになるように、このブログを作りました。
これらは、考え方がブレブレなので、微妙に違っています。何が正解なのですかね。

www.shadertoy.com

www.shadertoy.com

www.shadertoy.com

www.shadertoy.com

スケール

FoxDotを始めから思ったんだけど、live coding勢は、和音を重視してない気がする。リズムがメインで構成してる気がする。
なので、倣ってリズムから始めていけば良いんだろうけど、あえて、情報が薄い和音から纏めていく。これは試行錯誤している部分なので、先に纏めておいてFoxDotのスキルでブラッシュアップする作戦。その前にスケールから。

GLSLではピッチをノート番号で管理してる。

float note2freq(int n){
    return 440.0*exp2((float(n)-69.0)/12.0);
}

これは、ノート番号から周波数を出力する関数。
ノート番号から周波数に変換する関数があるので、これが絶対で、これから全てが構成される。

プログラムで音階を扱うならば、スケールを使って配列で処理をするのが一番簡単だ。 だけど、例外とかあったりして、それをどうするかで頭を悩ます。

話をメジャースケールで進めていきます。
GLSLでは配列で処理しています。

int[](0,2,4,5,7,9,11)

スケールを使うのでインデックスはデグリーになります。ここで問題が入ってきます。デグリーは、I,II,III,IV,V,VI,VIIなので1から始まります。配列は0から始まります。なので

int[](0,2,4,5,7,9,11)[n-1]

と扱っていました。FoxDotではオフセットを取らないで単純に0から始めています。ちょっと悩んだのですが、今度からGLSLもFoxDotに合わせて0からにしようと決めました。音楽をやっている人には抵抗があるとは思うけど、プログラムだから0ですよね。
FoxDotの仕様をGLSLに持ち込みます。