■ 結論
真空管アンプは、非線形歪が滑らかかつ耳に心地よい形で現れ、高調波の構成が自然に感じられるため、「音がよい」とされる。
■ 理由
① 非線形特性の違い(真空管 vs トランジスタ)
| 特性 | 真空管(バルブ) | トランジスタ(BJT, FET) |
|---|---|---|
| 入出力関係 | 曲線的(なめらかなカーブ) | 急峻なスイッチ的カーブ |
| 増幅特性 | 3/2乗則(Child's law) | 指数関数(BJT)またはリニア(FET) |
| 飽和領域 | ソフトに入る | シャープにクリップする |
→ 真空管はクリッピング時にも滑らかに信号が歪む(“ソフトクリップ”)、トランジスタは急激に波形を切る(“ハードクリップ”)
② 高調波スペクトルの構成(フーリエ級数的観点)
波形の歪みはフーリエ展開すると**高調波成分(harmonics)**として現れる。
- 真空管:**偶数次高調波(2次, 4次…)**が多く、耳に心地よい
- トランジスタ:**奇数次高調波(3次, 5次…)**が多く、耳に鋭く感じる
例:
原音:正弦波
ソフトクリップ:高調波が滑らかに減衰
ハードクリップ:矩形波に近く、強い奇数次高調波を含む
③ フーリエ級数での説明
矩形波:
$$
f(t) = \frac{4A}{\pi} \sum_{n=1,3,5,\dots}^{\infty} \frac{1}{n} \sin(n \omega t)
$$
- 奇数次成分のみ → トランジスタのハードクリップに近い → “ざらついた音”
ソフトな歪み:
- 波形のなめらかさが残る
- 偶数次成分を含む、波形もなだらか
- → 人間の耳は“暖かみ・太さ”と感じる
④ 過渡応答・サチュレーションの違い
- 真空管は過渡応答がゆるやかで、ピークでも破綻しにくい
- トランジスタは一定以上で急にリニア性を失い、バチッと切れる
⑤ 心理音響効果(人間の感覚)
- 偶数次高調波は基本波と整合性があり「倍音」として自然に感じられる
- 奇数次高調波は不協和に近く「汚れた音」に感じる場合がある
■ まとめ(再主張)
真空管アンプの「音のよさ」は、
- 非線形性が穏やかで自然
- 偶数次高調波が豊かで心地よい
- クリッピングがなめらか
- 心理的に“温かく滑らか”と認識される
というフーリエ級数・物理特性・人間の感性すべてにまたがる総合的な要因によるものです。
# Program Name: tube_vs_transistor_waveform_with_audio.py
# Creation Date: 20250801
# Overview: Simulate and compare tube-like (soft clip) and transistor-like (hard clip) distortion, analyze spectrum, and export audio
# Usage: Run to generate waveforms, perform FFT, plot, and download sound of each waveform
# 必要なライブラリのインストール / Install required libraries
!pip install -q numpy matplotlib scipy
# ライブラリのインポート / Import libraries
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, fftfreq
from scipy.io.wavfile import write
from IPython.display import Audio, display
# -----------------------------
# パラメータ設定 / Parameters
# -----------------------------
fs = 44100 # サンプリング周波数 / Sampling rate [Hz]
T = 1.0 # 信号時間 / Duration [sec]
f0 = 440 # 入力周波数 / Input frequency [Hz]
A = 1.0 # 振幅 / Amplitude
N = int(fs * T) # サンプル数 / Number of samples
t = np.linspace(0, T, N, endpoint=False) # 時間軸 / Time axis
# -----------------------------
# 入力波形(正弦波) / Input sine wave
# -----------------------------
x = A * np.sin(2 * np.pi * f0 * t)
# -----------------------------
# ソフトクリップ(真空管風)/ Soft clip: tube-like
# -----------------------------
def soft_clip(x):
return np.tanh(2 * x) # なだらかな非線形 / smooth nonlinearity
# -----------------------------
# ハードクリップ(トランジスタ風)/ Hard clip: transistor-like
# -----------------------------
def hard_clip(x, threshold=0.6):
return np.clip(x, -threshold, threshold) # 急峻なクリッピング / hard clipping
# 日本語:波形生成 / 英語:Generate clipped waveforms
y_soft = soft_clip(x)
y_hard = hard_clip(x)
# -----------------------------
# FFT解析関数 / FFT function
# -----------------------------
def compute_fft(signal):
spectrum = fft(signal) # FFTの実行 / Perform FFT
freq = fftfreq(len(signal), 1/fs) # 周波数軸の生成 / Generate frequency axis
amp = 2.0 / len(signal) * np.abs(spectrum) # 振幅スペクトルに変換 / Convert to amplitude spectrum
return freq[:N//2], amp[:N//2] # 正の周波数成分のみを返す / Return positive frequencies only
# 日本語:各波形のスペクトル取得 / 英語:Get FFT for each waveform
f_soft, a_soft = compute_fft(y_soft)
f_hard, a_hard = compute_fft(y_hard)
# -----------------------------
# 波形とスペクトルの可視化 / Visualization
# -----------------------------
plt.figure(figsize=(14, 8))
plt.subplot(2, 2, 1)
plt.plot(t[:1000], y_soft[:1000])
plt.title("Soft Clipping (Tube-like)")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.grid(True)
plt.subplot(2, 2, 2)
plt.plot(t[:1000], y_hard[:1000])
plt.title("Hard Clipping (Transistor-like)")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.grid(True)
plt.subplot(2, 2, 3)
plt.stem(f_soft, a_soft, basefmt=" ")
plt.title("FFT Spectrum: Soft Clipping")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Amplitude")
plt.xlim(0, 5000)
plt.grid(True)
plt.subplot(2, 2, 4)
plt.stem(f_hard, a_hard, basefmt=" ")
plt.title("FFT Spectrum: Hard Clipping")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Amplitude")
plt.xlim(0, 5000)
plt.grid(True)
plt.tight_layout()
plt.show()
# -----------------------------
# 音の保存と再生 / Save and play audio
# -----------------------------
y_soft_int16 = np.int16(y_soft / np.max(np.abs(y_soft)) * 32767)
y_hard_int16 = np.int16(y_hard / np.max(np.abs(y_hard)) * 32767)
write("soft_clip.wav", fs, y_soft_int16)
write("hard_clip.wav", fs, y_hard_int16)
print("Playing soft clipping sound (tube-like)...")
display(Audio("soft_clip.wav"))
print("Playing hard clipping sound (transistor-like)...")
display(Audio("hard_clip.wav"))