$6 Bezier curve random CV generator - DIY Eurorack Modular Synthesizer
背景
自作モジュラーシンセの58作品目。
ゆっくりと不規則に動くCVモジュールを持ってなかった。
Mutable instruments MarblesのCVセクションのような機能が欲しい。
適当なCVシーケンサと、Slew limiterを組み合わせれば、ゆっくりと不規則に動くCVを作ることは出来るが、Slew limiterはまだ作ってない。(今度作ろう)
ということで、今回のモジュールの作成を企画した。
制作物のスペック
ユーロラック規格 3U 6HPサイズ
電源:50mA ( at 5V )
5V単電源で動作可能。
Frequency POT: 電圧の変化の周波数
Curve POT: 電圧変化のカーブの曲率。
値が0のとき、変化は直線的になる。
値が大きくなると、曲率が大きくなる。曲率はベジェ曲線に準じる。
σ(標準偏差) POT: Frequencyの揺らぎの幅。
0の時は、一定の周波数で電圧が変化する。
値を上げると、周波数の揺らぎが大きくなる。
揺らぎの値は完全なランダムではなく、正規分布に従うので、大きく周波数がずれる頻度は稀になる。
LEVEL POT: CV電圧の範囲。MAX値を0~5Vで設定する。
Frequency CV: 電圧変化のスピード(レンジ0~5V)
Curve CV:電圧変化のカーブの曲率(レンジ0~5V)
OUT:電圧出力(レンジ0~5V)
製作費
総額約600円(PCBではなく、ユニバーサル基板を使う場合)
---------------------------------
Arduino nano 200円
フロントパネル 100円
可変抵抗 28円*4pcs
ジャック 8円*4
他(汎用部品は下記リンク先参照)
PCBを発注する場合、100円/pcs+送料が追加となる。
部品費は私が購入した時点での値段を記載している。2022年10月時点で、Arduino nano互換品はAliexpressで$3ドルとなっている。
この記事を書いている2022現在は、部品価格の変動が大きい。半導体不足による価格変動、ドル高による価格変動(円の価値は1年で35%も低下している)があるので、製作費は参考値として扱ってほしい。
ハードウェア
過去に私が作成したモジュールに登場する定番回路。
基板はArduino nano用の汎用基板「Genaral board」使用している。下記のリンク先を参考してほしい。
CADファイルはpatreonで公開している。オープンソースプロジェクトの開発を継続するために支援いただけると嬉しい。
https://www.patreon.com/posts/dual-envelope-64079059
ソフトウェア
ベジェ曲線
ベジェ曲線は2つの座標が定まれば数式で求めることができる。
wait = 3 * pow((1 - x[i]), 2) * x[i] * curve + 3 * (1 - x[i]) * pow(x[i], 2) * (255 - curve) + pow(x[i], 3) * 255;
bz_val = pow((1 - x[i]), 3) * start_val + 3 * pow((1 - x[i]), 2) * x[i] * start_val + 3 * (1 - x[i]) * pow(x[i], 2) * end_val + pow(x[i], 3) * end_val;
waitがx軸(時間)の座標で、bz_valがy軸(電圧)の座標である。
waitで算出した時間だけ、bz_valで算出した電圧を出力する仕組み。
正規分布
σ(標準偏差)パラメータに用いる正規分布グラフはプログラム内では演算していない。
あらかじめエクセル等の表計算ソフトで求めた計算結果をテーブルに保存している。
int chance[32] = {5, 12, 21, 33, 48, 67, 90, 118, 151, 189, 232, 279, 331, 386, 443, 501, 559, 616, 671, 723, 770, 813, 851, 884, 912, 935, 954, 969, 981, 990, 997, 1000};//normal distribution table
int freq_err[32] = {8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30, 33, 36, 40, 46, 52, 58, 64, 70, 76, 82, 90, 98, 110, 122, 136, 148};//Frequency Variation
chance[32]は正規分布グラフの値を、下位から加算していったテーブルだ。
5=5
12=5+7
21=5+7+9
freq_err[32]は周波数のばらつき量。
ソースコード
粗末だが公開する。悪い点があれば指摘を貰えると嬉しい。
micros()を使っているので、タイマーカウントは70分でオーバーフローする。
#include <avr/io.h>//for fast PWM
int i = 0;
int start_val = 0;//Bezier Curve Starting Point
int end_val = 255;//Bezier Curve end Point
float old_wait = 0;
float wait = 0;//Bezier curve x-axis (time)
float bz_val = 0;//Bezier curve y-axis (voltage)
int dev, level, curve, freq;
long timer = 0;//
long timer1 = 0;//analog read interval
float x[256];//Bezier Curve Calculation Tables
int freq_rnd = 501;
int freq_dev = 40;
int chance[32] = {5, 12, 21, 33, 48, 67, 90, 118, 151, 189, 232, 279, 331, 386, 443, 501, 559, 616, 671, 723, 770, 813, 851, 884, 912, 935, 954, 969, 981, 990, 997, 1000};//normal distribution table
int freq_err[32] = {8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30, 33, 36, 40, 46, 52, 58, 64, 70, 76, 82, 90, 98, 110, 122, 136, 148};//Frequency Variation
void setup()
{
for (int j = 0; j < 255; j++) {//Preparation of Bezier Curve Calculation Tables
x[j] = j * 0.003921; //j/255
}
pinMode(10, OUTPUT) ;//CV output
timer = micros();
timer1 = millis();
//9,10pin pwm setting
TCCR1B &= B11111000;
TCCR1B |= B00000001;
delay(50);
}
void loop()
{
if(timer1 +50 < millis()){
freq = min(511,(analogRead(0) / 2 + analogRead(7) / 2)) * freq_dev;
curve = min(255,(analogRead(1) / 4 + analogRead(6) / 4));
level = analogRead(3) / 4;
timer1 = millis();
}
if (timer + (wait - old_wait) <= micros()) {
old_wait = wait;
i++;
if (i >= 255) {//Recalculation of target voltage values
i = 0;
start_val = end_val;
end_val = random(0, 255);
change_freq_error();
}
//Bezier Curve Calculations
wait = 3 * pow((1 - x[i]), 2) * x[i] * curve + 3 * (1 - x[i]) * pow(x[i], 2) * (255 - curve) + pow(x[i], 3) * 255;
wait = 1+wait * freq*2;
bz_val = pow((1 - x[i]), 3) * start_val + 3 * pow((1 - x[i]), 2) * x[i] * start_val + 3 * (1 - x[i]) * pow(x[i], 2) * end_val + pow(x[i], 3) * end_val;
timer = micros();
PWM_OUT();//PWM output
}
}
void change_freq_error() {//Frequency variation is obtained from the standard deviation table
dev = map(analogRead(2), 0, 1023, 0, 500);
freq_rnd = random(500 - dev, 500 + dev);
for (int k = 0; k < 32; k++) {
if (freq_rnd >= chance[k] && freq_rnd < chance[k + 1]) {
freq_dev = freq_err[k];
}
}
}
void PWM_OUT() {//PWM output
analogWrite(10, bz_val * level / 255);
}