#メロディーのシーケンス
ドラムのシーケンスは、1小節しか、しなかった。メロディーじゃ足りなさすぎるのを、カバーする為に配列登場です。それと、ノート番号の配列を使うと大きくなりがちなので、スケールの配列を使ってます。
サンプルのshaderを書きました。
#define BPM 120.
#define A (15./BPM)
#define B (240./BPM)
#define C3 60
#define Scale C3+int[](0,2,4,5,7,9,11)
float distortion(float gain, float d)
{
return clamp(gain * d, -1.0, 1.0);
}
float sound(float f, float time)
{
return distortion(sin(6.2831*f*time), 2.5) *exp(-time*3.0);
}
float note2freq(int n)
{
return 440.0*exp2((float(n)-69.0)/12.0);
}
int sequenceOrder(int s,float time)
{
int n =-1;
for(int i=0;i<=int(mod(time,B)/A);i++){
if((s>>i&1)==1)n++;
}
return n;
}
float sequenceTime(int s,float time)
{
float n =mod(time,A);
for(int i=0;i<16;i++){
if((s>>(int(time/A)-i)%16&1)==1)break;
n+=A;
}
return n;
}
vec2 mainSound( float time )
{
int b = int(time/B)%8;
int s = int[](0x1111, 0x0111, 0x0101, 0x5555)[
int[](0,1,0,1,2,2,3,1)[b]
];
int o = sequenceOrder(s,time);
int i = int[](1672,10,2842,156,0,0,7152192,10)[b]>>(o*3)&7;
int n = Scale[i];
float t = sequenceTime(s,time);
return vec2(sound(note2freq(n),t));
}
この音はshader toyで聞けます。
#note2freq()関数の説明
ノート番号を周波数に変える関数です。シーケンスはノート番号で管理して、音源の関数に周波数を送ります。
sequenceOrder()関数について
何回目のノートオンかを返します。配列にメロディーのノート番号を仕込んでおいて、この返り値でノート番号を引き出します。
#今回のメロディーのデータについて
メロディーのデータ部分は
int i = int[](1672,10,2842,156,0,0,7152192,10
)[b]>>(o*3)&7;
これです。
どういう事かというと以下のscriptをintに書き換えたもの。
int i = int[](
(0<<0)+(1<<3)+(2<<6)+(3<<9),
(2<<0)+(1<<3)+(0<<6),
(2<<0)+(3<<3)+(4<<6)+(5<<9),
(4<<0)+(3<<3)+(2<<6),
(0<<0)+(0<<3),
(0<<0)+(0<<3),
(0<<0)+(0<<3)+(1<<6)+(1<<9)+(2<<12)+(2<<15)+(3<<18)+(3<<21),
(2<<0)+(1<<3)+(0<<6)
)[b]>>(o*3)&7;
これはスケールのインデックスを3bitづつ使ってメロディーをデータさせたもの。
>>(o*3)&7
この部分はデータを3bitづつ取り出す為の処理。マスクをかけるとか言うと思った。
使い方のアイディア
オーソドックスに配列に仕込んでおいて使うのも良いけど、乱数と、スケールを使ってのランダムメロディー。まだ試せてないけど、ピンクノイズを使う方法もありそう。
#シーケンスの関数
シーケンスは、sequenceOrder(),sequenceTime()の2つで用は足りそうです。あとは応用かな。
#おまけ
スケールをGipsyScaleにしても面白い。
#define Scale C3+int[](0,1,4,5,7,9,10)
#追記
データをshaderの中で作るようにしました。2019/12/10
_Melody sequence
#define BPM 60.
#define A (15./BPM)
#define B (240./BPM)
#define C3 60
float distortion(float gain, float d)
{
return clamp(gain * d, -1.0, 1.0);
}
float sound(float f, float time)
{
return distortion(sin(6.2831*f*time), 2.5) *exp(-time*3.0);
}
float note2freq(int n)
{
return 440.0*exp2((float(n)-69.0)/12.0);
}
int major(int n)
{
return n/7*12+int[](0,2,4,5,7,9,11)[n%7];
}
int sequenceOrder(int s,float time)
{
int n =-1;
for(int i=0;i<=int(mod(time,B)/A);i++){
if((s>>i&1)==1)n++;
}
return n;
}
float sequenceTime(int s,float time)
{
float n =mod(time,A);
for(int i=0;i<16;i++){
if((s>>(int(time/A)-i)%16&1)==1)break;
n+=A;
}
return n;
}
#define _ -1
#define Melody2Int(r,m,a)r=0;m=0;{int s=0;for(int i=0;i<16;i++){if(-1<a[i]){r+=1<<i;m+=a[i]<<s;s+=4;}}}
vec2 mainSound( float time )
{
// 使える数字は、0~15 で、1小節に8個まで。
int[8] r_score, m_score;
Melody2Int( r_score[0], m_score[0], int[]( 0,_,1,_,2,_,_,_,0,_,1,_,2,_,_,_ ))
Melody2Int( r_score[1], m_score[1], int[]( 0,_,1,_,2,_,3,_,4,_,5,_,6,_,7,_ ))
Melody2Int( r_score[2], m_score[2], int[]( 0,_,1,_,2,_,_,_,0,_,1,_,2,_,_,_ ))
Melody2Int( r_score[3], m_score[3], int[]( 0,_,1,_,2,_,3,_,4,_,5,_,6,_,7,_ ))
Melody2Int( r_score[4], m_score[4], int[]( 0,_,1,_,2,_,_,_,0,_,1,_,2,_,_,_ ))
Melody2Int( r_score[5], m_score[5], int[]( 0,_,1,_,2,_,3,_,4,_,5,_,6,_,7,_ ))
Melody2Int( r_score[6], m_score[6], int[]( 0,_,1,_,2,_,_,_,0,_,1,_,2,_,_,_ ))
Melody2Int( r_score[7], m_score[7], int[]( 0,_,1,_,2,_,3,_,4,_,5,_,6,_,7,_ ))
int b = int(time/B)%2;
int r = r_score[b];
int o = sequenceOrder(r,time);
int i = m_score[b]>>(o*4)&0xF;
int n = C3+major(i);
float t = sequenceTime(r,time);
return vec2(sound(note2freq(n),t));
}
#GLSLで音楽の記事
GLSLで音楽(はじめに)
GLSLで音楽(まずは、ドラムだ)
GLSLで音楽(和音を使ってみる)
GLSLで音楽(今までの応用の一つ)