Pythonで学ぶエフェクター制作メモ(増幅素子としてのmosFET)

##  はじめに

近年、市場には「MOSFET搭載」をうたうエフェクターペダルが数多く登場しています。
実際にデジマートで「エフェクター MOSFET」と検索してみると、Fulltone OCD や ZenDrive のクローンなど、たくさんのモデルがヒットします。
これらのエフェクターがどのようにMOSFETを使って信号を増幅・変化させているのかを、Pythonによるシミュレーションで検証してみましょう。

##  参考リンクまとめ





  https://www.asahi-net.or.jp/~ab6s-med/NORTH/effector/sho_muamp/index.html?date=20241125000000



##  Pythonコード

1kHzのサイン波を+32dBで増幅し、±0.3Vでクリップして波形を表示するコード

```python
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()
```

# 結果 


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/34b38ae9-12be-46f2-9f1f-331934c7b56c.png)




##  Pythonコード

伝達関数でLPF/HPF/BPFを定義し、ゲインと位相の周波数特性をプロット
```python
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")

```

# 結果 


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/b3406713-d12c-473c-90c2-8982a8cd3dc3.png)
=== フィルターのカットオフ周波数 / Filter Cutoff Frequencies ===
ローパスフィルタ / Low-pass cutoff: 500 Hz
ハイパスフィルタ / High-pass cutoff: 500 Hz
バンドパスフィルタ / Band-pass cutoff: 300 Hz ～ 1000 Hz