0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

はじめに

近年、市場には「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()

結果

image.png

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")

結果

image.png
=== フィルターのカットオフ周波数 / Filter Cutoff Frequencies ===
ローパスフィルタ / Low-pass cutoff: 500 Hz
ハイパスフィルタ / High-pass cutoff: 500 Hz
バンドパスフィルタ / Band-pass cutoff: 300 Hz ~ 1000 Hz

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?