はじめに
近年、市場には「MOSFET搭載」をうたうエフェクターペダルが数多く登場しています。
実際にデジマートで「エフェクター MOSFET」と検索してみると、Fulltone OCD や ZenDrive のクローンなど、たくさんのモデルがヒットします。
これらのエフェクターがどのようにMOSFETを使って信号を増幅・変化させているのかを、Pythonによるシミュレーションで検証してみましょう。
参考リンクまとめ
Pythonコード
1kHzのサイン波を+32dBで増幅し、±0.3Vでクリップして波形を表示するコード
import numpy as np
import matplotlib.pyplot as plt
# === パラメータ設定 Parameters ===
# サイン波設定 / Sine wave settings
frequency = 1000 # 周波数 [Hz] / Frequency of the sine wave
input_amplitude = 0.01 # 入力振幅 [V] / Amplitude of input signal
duration_cycle = 2 # 表示する周期数 / Number of cycles to display
# サンプリング設定 / Sampling settings
sampling_rate = 100_000 # サンプリング周波数 [Hz] / Sampling rate
duration = duration_cycle / frequency # 表示時間 [s] / Duration = N cycles
t = np.linspace(0, duration, int(sampling_rate * duration), endpoint=False)
# ゲイン設定 / Gain settings
gain_db = 32 # ゲイン [dB] / Gain in decibels
gain_linear = 10 ** (gain_db / 20) # 電圧倍率 / Voltage gain
# クリッピング設定 / Clipping settings
clip_voltage = 0.3 # クリップ電圧 [V] / Clipping level
# === サイン波の生成 Generate input sine wave ===
vin = input_amplitude * np.sin(2 * np.pi * frequency * t) # 入力波形 / Input waveform
# === 増幅処理 Apply gain ===
vout_amplified = vin * gain_linear # 増幅後の波形(未クリップ) / Amplified signal (raw)
# === クリッピング処理 Apply clipping ===
vout_clipped = np.clip(vout_amplified, -clip_voltage, clip_voltage) # 出力(クリップあり) / Clipped output
# === プロット Plot ===
plt.figure(figsize=(10, 5))
plt.plot(t * 1000, vin, label=f"Input ({input_amplitude} V)", linestyle='--')
plt.plot(t * 1000, vout_amplified, label=f"Amplified (+{gain_db} dB)", alpha=0.5)
plt.plot(t * 1000, vout_clipped, label=f"Clipped (±{clip_voltage} V)", linewidth=2)
# === グラフ装飾 Styling ===
plt.title(f"{frequency} Hz Sine Wave Amplified by +{gain_db} dB with Clipping")
plt.xlabel("Time [ms]")
plt.ylabel("Voltage [V]")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
結果
Pythonコード
伝達関数でLPF/HPF/BPFを定義し、ゲインと位相の周波数特性をプロット
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import freqs, TransferFunction
# === フィルターパラメータ Filter parameters ===
cutoff_lp = 500 # ローパスカットオフ周波数 [Hz]
cutoff_hp = 500 # ハイパスカットオフ周波数 [Hz]
low_cut = 300 # バンドパス下限 [Hz]
high_cut = 1000 # バンドパス上限 [Hz]
# === s = jω のための周波数範囲 Frequency range ===
w = np.logspace(1, 5, 1000) # 10 Hz ~ 100 kHz
s = 1j * 2 * np.pi * w # s = jω
# === ローパスフィルタ LPF: H(s) = 1 / (RCs + 1) ===
RC_lp = 1 / (2 * np.pi * cutoff_lp)
num_lp = [1]
den_lp = [RC_lp, 1]
H_lp = TransferFunction(num_lp, den_lp)
# === ハイパスフィルタ HPF: H(s) = RCs / (RCs + 1) ===
RC_hp = 1 / (2 * np.pi * cutoff_hp)
num_hp = [RC_hp, 0]
den_hp = [RC_hp, 1]
H_hp = TransferFunction(num_hp, den_hp)
# === バンドパスフィルタ BPF: 2次フィルタで構成 ===
w0 = 2 * np.pi * np.sqrt(low_cut * high_cut) # 中心角周波数
bw = 2 * np.pi * (high_cut - low_cut) # 帯域幅
num_bp = [bw, 0]
den_bp = [1, bw, w0**2]
H_bp = TransferFunction(num_bp, den_bp)
# === 周波数応答計算 Compute frequency responses ===
_, h_lp = freqs(num_lp, den_lp, worN=2*np.pi*w)
_, h_hp = freqs(num_hp, den_hp, worN=2*np.pi*w)
_, h_bp = freqs(num_bp, den_bp, worN=2*np.pi*w)
# === プロット Plot ===
fig, axs = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
# ゲイン特性 Magnitude
axs[0].semilogx(w, 20 * np.log10(np.abs(h_lp)), label=f'LPF ({cutoff_lp} Hz)')
axs[0].semilogx(w, 20 * np.log10(np.abs(h_hp)), label=f'HPF ({cutoff_hp} Hz)')
axs[0].semilogx(w, 20 * np.log10(np.abs(h_bp)), label=f'BPF ({low_cut}-{high_cut} Hz)')
axs[0].set_title("Frequency Response (Magnitude)")
axs[0].set_ylabel("Gain [dB]")
axs[0].axhline(-3, color='gray', linestyle=':', label='-3 dB line')
axs[0].grid(True, which='both', linestyle='--')
axs[0].legend()
# 位相特性 Phase
axs[1].semilogx(w, np.unwrap(np.angle(h_lp)) * 180 / np.pi, label='LPF')
axs[1].semilogx(w, np.unwrap(np.angle(h_hp)) * 180 / np.pi, label='HPF')
axs[1].semilogx(w, np.unwrap(np.angle(h_bp)) * 180 / np.pi, label='BPF')
axs[1].set_title("Frequency Response (Phase)")
axs[1].set_ylabel("Phase [degrees]")
axs[1].set_xlabel("Frequency [Hz]")
axs[1].grid(True, which='both', linestyle='--')
axs[1].legend()
plt.tight_layout()
plt.show()
# === カットオフ周波数表示 Print cutoff info ===
print("=== フィルターのカットオフ周波数 / Filter Cutoff Frequencies ===")
print(f"ローパスフィルタ / Low-pass cutoff: {cutoff_lp} Hz")
print(f"ハイパスフィルタ / High-pass cutoff: {cutoff_hp} Hz")
print(f"バンドパスフィルタ / Band-pass cutoff: {low_cut} Hz ~ {high_cut} Hz")
結果
=== フィルターのカットオフ周波数 / Filter Cutoff Frequencies ===
ローパスフィルタ / Low-pass cutoff: 500 Hz
ハイパスフィルタ / High-pass cutoff: 500 Hz
バンドパスフィルタ / Band-pass cutoff: 300 Hz ~ 1000 Hz