見出し画像

$6 Bezier curve random CV generator - DIY Eurorack Modular Synthesizer

背景

自作モジュラーシンセの58作品目。
ゆっくりと不規則に動くCVモジュールを持ってなかった。
Mutable instruments MarblesのCVセクションのような機能が欲しい。

適当なCVシーケンサと、Slew limiterを組み合わせれば、ゆっくりと不規則に動くCVを作ることは出来るが、Slew limiterはまだ作ってない。(今度作ろう)
ということで、今回のモジュールの作成を企画した。

画像1

制作物のスペック

ユーロラック規格 3U 6HPサイズ
電源:50mA ( at 5V )
5V単電源で動作可能。

Frequency POT: 電圧の変化の周波数

Curve POT: 電圧変化のカーブの曲率。
値が0のとき、変化は直線的になる。
値が大きくなると、曲率が大きくなる。曲率はベジェ曲線に準じる。

スクリーンショット 2022-10-09 143002

σ(標準偏差) POT: Frequencyの揺らぎの幅。
0の時は、一定の周波数で電圧が変化する。
値を上げると、周波数の揺らぎが大きくなる。
揺らぎの値は完全なランダムではなく、正規分布に従うので、大きく周波数がずれる頻度は稀になる。

スクリーンショット 2022-10-09 142820

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%も低下している)があるので、製作費は参考値として扱ってほしい。

ハードウェア

画像5

過去に私が作成したモジュールに登場する定番回路。
基板は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

スクリーンショット 2022-10-09 142820

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);
}

この記事が気に入ったらサポートをしてみませんか?