Pythonで学ぶ音信号処理(エンベロープ)


---

##  はじめ

音響信号処理において、**エンベロープ（envelope）**は音の振幅の時間的変化を制御する重要な要素です。音そのものはサイン波などの高周波成分で構成されますが、その**強弱や形（アタックや減衰）**はエンベロープによって表現されます。

たとえば、ピアノの音は打鍵直後に強く鳴り、すぐに音量が減衰していきます。一方、バイオリンの音はゆるやかに立ち上がり、弓を引く間は持続します。このような**「音のかたち」**を再現するために、リニア、指数関数、ADSR、ガウス型などのさまざまなエンベロープが使われます。

本記事では、音響用に使われる**代表的な10種類のエンベロープ関数とその数式**を一覧で紹介し、用途や特徴もあわせて解説します。

--- 



##  主なエンベロープの種類と数式一覧（音響用）

| 種類 | 説明 | 数式（時間 \( t \)、最大時間 \( T \)、初期振幅 \( A_0 \)、減衰係数 αなど） |
|------|------|--------------------------------------------------------------|
| **1. リニア**（直線） | 単純な立ち上がり・減衰 | \( A(t) = A_0 \cdot (1 - t/T) \)（減衰）<br>または \( A(t) = A_0 \cdot (t/T) \)（立ち上がり） |
| **2. 指数減衰** | ピアノなど自然な余韻 | \( A(t) = A_0 \cdot e^{-\alpha t} \) |
| **3. 指数立ち上がり** | シンセのアタック表現 | \( A(t) = A_0 \cdot (1 - e^{-\alpha t}) \) |
| **4. ADSR（各フェーズで切替）** | 楽器音制御の定番モデル | Attack: \( A(t) = \frac{t}{T_a} \)<br>Decay: \( A(t) = 1 - (1 - S)\cdot \frac{t}{T_d} \)<br>Sustain: \( A(t) = S \)<br>Release: \( A(t) = S \cdot e^{-\alpha t} \) |
| **5. ガウス型エンベロープ** | 短くて滑らかな音（例：打楽器） | \( A(t) = A_0 \cdot e^{-\left( \frac{t - \mu}{\sigma} \right)^2} \) |
| **6. サイン型エンベロープ** | ゆるやかな立ち上がりと減衰 | \( A(t) = \sin\left( \frac{\pi t}{T} \right) \) |
| **7. 三角エンベロープ** | 対称な立ち上がりと下り | \( A(t) = \frac{2A_0}{T} \cdot \min(t, T - t) \) |
| **8. 半周期コサイン** | より柔らかなアタック・減衰 | \( A(t) = \frac{1 - \cos\left( \frac{\pi t}{T} \right)}{2} \) |
| **9. Hanning / Hann** | ウィンドウ関数にも使用される | \( A(t) = 0.5 \cdot \left(1 - \cos\left( \frac{2\pi t}{T} \right) \right) \) |
| **10. Blackman** | 滑らかに減衰 | \( A(t) = 0.42 - 0.5\cos\left( \frac{2\pi t}{T} \right) + 0.08\cos\left( \frac{4\pi t}{T} \right) \) |

---

##  使い分け例

| 用途 | 適したエンベロープ |
|------|-------------------|
| ピアノ・打楽器 | 指数減衰, ガウス, 三角 |
| シンセサイザー | ADSR, 指数立ち上がり, 半周期コサイン |
| 音のブレンド・フェードイン | サイン型, Hanning |
| 残響シミュレーション | 指数減衰, Blackman |

---

##  Pythonでこれらのエンベロープを実装・比較


```Python
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import Audio, display

# --- パラメータ設定 / Parameter settings ---
duration = 2.0          # 音の長さ / Duration (seconds)
sample_rate = 44100     # サンプリングレート / Sample rate (Hz)
frequency = 261.63      # 周波数 C4（ド）/ Frequency of C4
amplitude = 1.0         # 振幅 / Amplitude
time = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)

# --- 各エンベロープ関数の定義 / Define envelope functions ---

def linear_envelope(t, T):
    return amplitude * (1 - t / T)

def exponential_decay(t, alpha=3.0):
    return amplitude * np.exp(-alpha * t)

def exponential_rise(t, alpha=5.0):
    return amplitude * (1 - np.exp(-alpha * t))

def adsr_envelope(t, attack=0.1, decay=0.2, sustain_level=0.6, sustain=1.0, release=0.7):
    total = attack + decay + sustain + release
    env = np.zeros_like(t)
    for i, ti in enumerate(t):
        if ti < attack:
            env[i] = ti / attack
        elif ti < attack + decay:
            env[i] = 1 - (1 - sustain_level) * ((ti - attack) / decay)
        elif ti < attack + decay + sustain:
            env[i] = sustain_level
        elif ti < total:
            env[i] = sustain_level * np.exp(-5 * (ti - (attack + decay + sustain)))
        else:
            env[i] = 0
    return env

def gaussian_envelope(t, mu, sigma):
    return amplitude * np.exp(-((t - mu) / sigma)**2)

def sine_envelope(t, T):
    return np.sin(np.pi * t / T)

def triangle_envelope(t, T):
    return (2 * amplitude / T) * np.minimum(t, T - t)

def half_cosine_envelope(t, T):
    return (1 - np.cos(np.pi * t / T)) / 2

def hanning_envelope(t, T):
    return 0.5 * (1 - np.cos(2 * np.pi * t / T))

def blackman_envelope(t, T):
    return 0.42 - 0.5 * np.cos(2 * np.pi * t / T) + 0.08 * np.cos(4 * np.pi * t / T)

# --- 各エンベロープの波形生成 / Generate waveform with envelopes ---
envelopes = {
    "Linear": linear_envelope(time, duration),
    "Exponential Decay": exponential_decay(time),
    "Exponential Rise": exponential_rise(time),
    "ADSR": adsr_envelope(time),
    "Gaussian": gaussian_envelope(time, duration/2, 0.2),
    "Sine": sine_envelope(time, duration),
    "Triangle": triangle_envelope(time, duration),
    "Half Cosine": half_cosine_envelope(time, duration),
    "Hanning": hanning_envelope(time, duration),
    "Blackman": blackman_envelope(time, duration),
}

# --- プロット / Plot all envelopes ---
plt.figure(figsize=(14, 10))
for i, (name, env) in enumerate(envelopes.items()):
    plt.subplot(5, 2, i + 1)
    plt.plot(time, env)
    plt.title(name)
    plt.xlabel("Time (s)")
    plt.ylabel("Amplitude")
    plt.grid(True)
plt.tight_layout()
plt.show()

# --- サイン波に任意のエンベロープを適用して音を生成 / Apply one envelope to sine wave ---
selected_envelope = envelopes["Exponential Decay"]  # 例: 指数減衰 / Example: exponential decay
sine_wave = selected_envelope * np.sin(2 * np.pi * frequency * time)

from IPython.display import Audio, display

# --- すべてのエンベロープでサイン波を生成して再生 / Play sine wave for all envelopes ---
audios = {}
for name, env in envelopes.items():
    wave = env * np.sin(2 * np.pi * frequency * time)
    audios[name] = Audio(wave, rate=sample_rate)

# --- 再生オブジェクトを表示 / Display all audio objects ---
for name, audio in audios.items():
    print(f"🎵 {name}")
    display(audio)
```
結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/54b46a66-5c74-45db-ab82-0f75ea3dc7b0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/fa6aa9a2-c517-4602-8075-ce1f2e91c456.png)