2021年6月23日水曜日

アナログ・エンベロープ・ジェネレーターをプログラムでシミュレートする

アナログ回路とArduinoで作ったエンベロープ・ジェネレーターArduino_EGを利用していますが、今回はそれをソフトウェア的にシミュレートしました。

一番簡単なのはAttack、Release、Sustain、Relaseをパラメータとして線形補間することですが、よりアナログ的にCRの充放電をプログラムで表現しました。

LTSpiceによるArduio_EGのシミュレーション


Arduino_EGの動作をLTspiceでシミュレーションしたものです。

シミュレーション回路図

過渡解析

数式に置き換える


プログラムの出力をグラフ化してコメントを加えたものです。



ADSR波形ですが、曲線はA、D、Rの3つです。SustainはAttackの終わりからGateがOffになるまでコンデンサを放電させる目標の電位です。

コンデンサに充電するステート(Attack)はある電位に達した時終了し、コンデンサから放電するステート(Decay)に切り替わりますが、今回は基準とする電位を0.6としています。

v3は前回の波形の最終的な電位です。初期値は0としています。

C++を使ったほうがスッキリとしますが、組み込み用途を想定してCで書きました。

プログラム
/*
 * ADSR Envelope Test
 *
 */

#include <stdio.h>
#include <stdint.h>
#include <math.h>

#define ATTACK_THRESHOLD  (0.6f)

typedef enum {
	stNone,
	stAttack,
	stDecay,
	stRelease
} EGState;

typedef struct {
	float attackR;			// kΩ
	float decayR;			// kΩ
	float sustainLevel;		// 0.0f ~ ATTACK_THTESHOLD
	float releaseR;			// kΩ
	float capacitance;		// uF

	EGState state;
	float samplingPeriod;	// second
	uint32_t tick;
	float amplitude;
	float v1;
	float v2;
	float v3;
	float t1;
	float t2;
} Envelope;

void Envelope_init(Envelope* p_env, float samplingPeriod);
float Envelope_step(Envelope* p_env);
void Envelope_gateOn(Envelope* p_env);
void Envelope_gateOff(Envelope* p_env);

void Envelope_init(Envelope* p_env, float samplingPeriod)
{
	p_env->samplingPeriod = samplingPeriod;
	p_env->state = stNone;
	p_env->amplitude = 0.0f;
	p_env->v3 = 0.0f;
}

void Envelope_gateOn(Envelope* p_env)
{
	p_env->v3 = p_env->amplitude;
	p_env->tick = 0;
	p_env->state = stAttack;
}

void Envelope_gateOff(Envelope* p_env)
{
	p_env->v2 = p_env->amplitude;
	p_env->t2 = p_env->tick * p_env->samplingPeriod;
	p_env->state = stRelease;
}

static float attack(Envelope* p_env, float t)
{
	float tau = p_env->attackR * p_env->capacitance / 1000.0f;
	return 1.0f - ( 1.0f - p_env->v3 ) * expf( -t / tau );
}

static float decay(Envelope* p_env, float t)
{
	float tau = p_env->decayR * p_env->capacitance / 1000.0f;
	return ( p_env->v1 - p_env->sustainLevel ) * expf( -( t - p_env->t1 ) / tau ) + p_env->sustainLevel;
}

static float release(Envelope* p_env, float t)
{
	float tau = p_env->releaseR * p_env->capacitance / 1000.0f;
	return p_env->v2 * expf( -( t - p_env->t2 ) / tau );
}

float Envelope_step(Envelope* p_env)
{
	float t = p_env->tick * p_env->samplingPeriod;

	switch (p_env->state) {
	case stNone:
		break;
	case stAttack:
		p_env->amplitude = attack(p_env, t);
		if (p_env->amplitude > ATTACK_THRESHOLD) {
			p_env->v1 = p_env->amplitude;
			p_env->t1 = t;
			p_env->state = stDecay;
		}
		break;
	case stDecay:
		p_env->amplitude = decay(p_env, t);
		break;
	case stRelease:
		p_env->amplitude = release(p_env, t);
		break;
	}

	p_env->tick++;

	if (p_env->tick == INT32_MAX) {
		perror("tick overflow\n");
	}

	return p_env->amplitude;
}

#define LOOP_N (3)
#define STEP_LENGTH (1000)
#define GATE_LENGTH (500)
#define SAMPLING_PERIOD (0.001f)

int main()
{
	Envelope env;

	Envelope_init(&env, SAMPLING_PERIOD);
	env.capacitance = 22;
	env.attackR = 3;
	env.decayR = 4;
	env.releaseR = 10;
	env.sustainLevel = 0.3;

	for (int j = 0; j < LOOP_N; j++) {
		Envelope_gateOn(&env);
		for (int i = 0; i < STEP_LENGTH; i++) {
			if (i == GATE_LENGTH) {
				Envelope_gateOff(&env);
			}
			float v = Envelope_step(&env);
			printf("%f\n", v);
		}
	}
}

充放電に使うコンデンサの容量は
capacitance: 22uF

パラメータに使うPOTの値は
AttackR: 3kΩ
DecayR: 4kΩ
SustainLevel: 0.3
ReleaseR: 10kΩ

としています。

メモ:

微分方程式を数値積分したほうが計算量が少なくプログラムの動作にも合っていると思いますが、技量がありません。いずれやってみるかもしれません。

2021年6月14日月曜日

トロイダルトランスHDB-30をケースに収めました。

貰い物のトロイダルトランスのHDB-30を、実験用にケースに収めました。

HDB-30のスペック
●出力:0V(灰)-15V(黄)/1.0A、0V(白)-15V(橙)/1.0A
●入力:0V(黒)-100V(緑)-120V(赤) 50/60Hz
●出力ケーブル長さ:約60cm(AWG22)
●入力ケーブル長さ:約60cm(AWG22)
●自動復帰タイプ温度ヒューズ(85℃)付
●固定穴付ベース:70mm角
●固定穴サイズ:M5向けサイズ×4
●本体サイズ:約78φ×H40mm
●重さ:約0.7kg(実測)

外観

ケースはタカチSY-110Gを使用しました。フロントとリアのパネルがアルミで他はプラスチック製(ABS?)です。

内部

トランス本体のみで台座がなかったので、超強力両面テープでケースに固定しました。

内部(前方から)

配線図

AC100VのパイロットランプはAC110VのLEDを使いました。LED、ダイオード、抵抗でも自作できますが既製品のほうが安心です。

パッケージの図によると2本のLEDが封入されているようです。


出力をAnalog Discovery 2のScope機能で観測しました。

無負荷時

16.8Vrms程度。

負荷抵抗RL=220Ω

16.6Vrms程度。

負荷抵抗RLは220Ω/3Wの酸金抵抗を使用。少々熱くなります。

I = Vrms / RL = 16.6 / 220 ≒ 75mA
P = (Vrms^2) / RL = (16.6^2) / 220 ≒ 1.25W

メモ


電源ケーブル用のケーブルブッシュはGM-71を使用しましたが、径がやや小さすぎました。

ミヤマのトグルスイッチは、パネル取り付け用のPOTと同じように、グルグル回らないように小孔を使って固定するようになっています。開け忘れたので爪付きのワッシャは逆向きに使用しました。


2021年6月11日金曜日

Spin Semicondutor FV-1 動作確認

秋月電子で販売されている、扱いやすいDIP化モジュールを購入しました。


取り扱い説明書に記載されている回路の一部をテストしました。

回路図


ブレッドボード配線図

FV-1は3.3VのICで絶対定格が3.5Vなので、3.3Vで動作する本体部分のみです。9V駆動の入出力のミキサー部分は省いてテストしました。

実験のようす

電源電流計の表示は48mAです。DATASHEETによると55mA/typ.なのでOKとします。

入出力

リバーブ固定の設定なので入力を止めて出力の変化を測定しました。入力停止後も残響が残っています。

細かく見ると

矩形波

ノコギリ波

波形は長い時間で見るともっと大きく変化します。

2021年6月3日木曜日

Antilog-MMの入出力を測定

Antilog-MMはPNPとNPNを使ったアンチログ回路で、Mini Moogでも使われているタイプです。NPNトランジスタの差動ペアを使ったAntilog-NPNO  と比較すると部品数が少なく済みます。

外観

回路図

EuroRack仕様で電源電圧は12Vです。

シミュレーション回路図

変換の比率を決めるトリムの値(R3:R4)を.paramで変化させてDC解析します。

DC解析 出力電圧

DC解析 出力電流(対数軸)

縦軸は対数軸です。直線ならば指数変換されていることになります。若干上側に湾曲しています。

テスト用配線

少しややこしいですが負荷抵抗RLにVCC→Outに向かって電流が流れます。RLに流れる電流によってOutの電位(オシロの入力)が電圧降下します。

入力信号は菊水MODEL459から三角波を最大レベルで出力しました。+12V~-8Vの波形になっています。

オシロはAnalog Discovery 2のScope機能です。

入出力を観測

観測データをCSVで出力しExcelでグラフ化しました。


目印として黄色い点線で直線を引いています。実験ではシミュレーションより上側に湾曲しています。

Analog-NPNO比較すると指数変換の精度は劣りますが、VCFのカットオフ周波数を制御するために使うならこの程度でも十分かもしれません。


TLF01と結合して音出ししてみましたが、ほんの少し違うか違わないかなという感じです。こういう微妙なズレはソフトウェア的に再現するのは面倒なのでバリエーションとしてありだと思います。