第12章:包络与LFO的数学模型

包络(Envelope)和低频振荡器(LFO)是合成器中控制音色动态变化的核心组件。它们决定了声音如何随时间演变,从最初的起音到最终的消失,以及各种周期性调制效果的产生。本章将深入探讨这些控制信号的数学模型,分析不同实现方式的特点,以及它们在现代数字合成器中的优化策略。我们将从基础的ADSR包络扩展到复杂的多段包络系统,探讨LFO的各种同步模式,并介绍包络跟随器这一强大的动态控制工具。

12.1 指数与线性包络曲线

线性包络的数学表达

线性包络是最简单直观的包络形式,其数值随时间呈线性变化。对于从起始值v₀到目标值v₁的线性过渡,在时间段[0, T]内的包络值可表示为:

v(t) = v₀ + (v₁ - v₀) × (t/T)

其中t ∈ [0, T]。这种包络的变化率恒定,即dv/dt = (v₁ - v₀)/T。

在离散时间域中,采样率为fs时,线性包络的递推实现为:

v[n+1] = v[n] + Δv
Δv = (v₁ - v₀)/(T × fs)

指数包络的自然特性

指数包络更符合自然界中的物理过程,如RC电路的充放电、弦的振动衰减等。标准的指数衰减可表示为:

v(t) = v₀ × e^(-t/τ) + v∞ × (1 - e^(-t/τ))

其中τ是时间常数,v∞是渐近值。对于标准ADSR包络的Release阶段,通常v∞ = 0,简化为:

v(t) = v₀ × e^(-t/τ)

时间常数τ与包络到达目标值63.2%所需的时间相关。实际应用中,我们常用包络衰减到目标值特定百分比(如1%)的时间来定义包络长度。

RC电路模拟与数字实现

模拟合成器中的包络生成器基于RC电路原理。电容充电过程可描述为:

V(t) = Vs × (1 - e^(-t/RC))

放电过程为:

V(t) = V₀ × e^(-t/RC)

在数字域实现时,我们使用差分方程近似:

v[n+1] = v[n] + α × (target - v[n])

其中α = 1 - e^(-1/(τ×fs))是包络系数。这种一阶低通滤波器结构提供了平滑的指数响应。

对于更精确的控制,可以使用以下参数化形式:

α = 1 - e^(-2π×fc/fs)

其中fc是等效截止频率,与包络速度成正比。

曲线形状参数控制

为了在线性和指数之间灵活调整,我们可以引入形状参数s:

v(t) = v₀ + (v₁ - v₀) × curve(t/T, s)

curve(x, s) = {
    x^s,           当s > 0时(凸曲线)
    x,             当s = 0时(线性)
    1-(1-x)^(-s),  当s < 0时(凹曲线)
}

另一种常用的参数化方法是使用双指数插值:

v(t) = v₀ + (v₁ - v₀) × [(1-w)×(t/T) + w×(1-e^(-k×t/T))/(1-e^(-k))]

其中w ∈ [0,1]控制线性和指数成分的混合比例,k控制指数的陡度。

感知线性vs物理线性

人耳对响度的感知遵循对数规律(韦伯-费希纳定律)。因此,物理上的线性变化在感知上是非线性的。对于振幅包络,感知线性的实现需要:

A(t) = A₀ × 10^(L(t)/20)

其中L(t)是以dB为单位的线性变化的电平。

实际应用中,我们常用简化的指数映射:

A(t) = A_min × (A_max/A_min)^(v(t))

其中v(t) ∈ [0,1]是归一化的包络值,A_min和A_max定义了动态范围。

对于音高包络(如滤波器截止频率),感知线性要求频率按指数变化:

f(t) = f₀ × 2^(v(t)×octaves)

其中octaves是调制范围(八度数)。

实现优化技巧

  1. 查表法:预计算指数曲线,通过线性插值获取中间值
  2. 分段线性近似:用多段直线近似指数曲线,降低计算复杂度
  3. SIMD并行化:同时处理多个包络通道
  4. 状态机优化:使用高效的状态转换逻辑,避免条件分支

数值稳定性考虑:

  • 使用双精度浮点避免累积误差
  • 在接近目标值时切换到线性模式,避免指数收敛过慢
  • 设置最小增量阈值,防止denormal数值

12.2 多段包络与循环包络

ADSR之外:多段包络设计

传统的ADSR包络虽然经典,但在复杂音色设计中常显不足。现代合成器普遍采用多段包络(Multi-Stage Envelope),允许任意数量的段,每段可独立设置:

段结构 = {
    目标电平: float,
    持续时间: float,
    曲线类型: enum {线性, 指数, 对数, S曲线},
    曲线参数: float
}

多段包络的状态机实现:

当前段索引: i
段内位置: p ∈ [0, 1]
输出值: v

v = interpolate(v[i], v[i+1], p, curve_type[i])
p += 1/(duration[i] × fs)

if (p >= 1) {
    i = next_stage(i)
    p = 0
}

常见的扩展包络类型:

  1. DAHDSR(Delay-Attack-Hold-Decay-Sustain-Release)
  2. ADBSSR(Attack-Decay-Breakpoint-Slope-Sustain-Release)
  3. 任意段包络:8段、16段甚至更多

循环包络与节奏生成

循环包络将包络变成了可编程的LFO,特别适合创建复杂的节奏模式。循环模式包括:

  1. 前向循环:到达循环终点后跳回起点
  2. 乒乓循环:正向播放后反向播放
  3. 单次触发后循环:完成初始段后进入循环

循环逻辑的数学描述:

循环起点: loop_start
循环终点: loop_end
当前位置: pos

前向循环:
if (pos >= loop_end) {
    pos = loop_start + (pos - loop_end) % (loop_end - loop_start)
}

乒乓循环:
if (direction == forward && pos >= loop_end) {
    direction = backward
    pos = 2×loop_end - pos
} else if (direction == backward && pos <= loop_start) {
    direction = forward
    pos = 2×loop_start - pos
}

条件触发与分支逻辑

高级包络系统支持条件分支,根据外部输入改变包络路径:

条件类型:

- 速度阈值: if (velocity > threshold) goto segment_x
- 调制轮位置: if (mod_wheel > 0.5) use_alternate_decay
- 随机分支: if (random() > 0.5) skip_to_release

这种设计允许创建响应式、动态的包络,实现:

  • 速度敏感的音色变化
  • 基于演奏力度的包络形状调整
  • 随机化的自然变化

包络段的平滑过渡

段间过渡的平滑处理对避免咔嗒声至关重要。常用技术:

  1. 微淡入淡出:在段边界应用短暂的交叉淡化
过渡时间: t_fade = 5ms
权重: w = smooth_step(t/t_fade)
v = v_old × (1-w) + v_new × w
  1. 曲率连续:确保段间一阶导数连续
使用Hermite插值确保平滑:
h(t) = (2t³ - 3t² + 1)×v₀ + (t³ - 2t² + t)×m₀ + 
       (-2t³ + 3t²)×v₁ + (t³ - t²)×m₁

其中m₀、m₁是端点的切线斜率。

  1. 贝塞尔曲线过渡:使用二次或三次贝塞尔曲线
B(t) = (1-t)²×P₀ + 2(1-t)t×P₁ + t²×P₂

复杂包络的应用场景

  1. 打击乐建模: - 初始瞬态(0-5ms):快速上升模拟击打 - 音头共振(5-50ms):复杂的衰减振荡 - 持续振动(50ms+):指数衰减

  2. 弦乐crescendo: - 缓慢上升段(2-3秒) - 峰值保持 - 自然衰减

  3. 呼吸控制管乐: - 气流建立(Attack) - 稳定吹奏(Sustain随呼吸控制器调制) - 气流中断(快速Release)

  4. 电子音乐门限效果: - 16分音符同步的循环包络 - 方波式的开关切换 - 与节奏严格同步

实时性能考虑

多段包络的优化策略:

  1. 段预计算:预先计算每段的参数,避免实时计算
  2. 查找表加速:复杂曲线使用查找表
  3. SIMD并行处理:同时处理多个语音的包络
  4. 增量更新:只在段切换时更新状态

内存布局优化:

struct EnvelopeState {
    float current_value;
    float target_value;
    float increment;
    uint32_t samples_remaining;
    uint8_t current_stage;
    uint8_t curve_type;
    uint16_t flags;
} __attribute__((packed));

12.3 LFO波形与相位关系

基本LFO波形的数学描述

LFO的核心是相位累加器,以归一化相位φ ∈ [0, 1)生成各种波形:

φ[n+1] = (φ[n] + f_lfo/fs) mod 1

基本波形的数学表达:

  1. 正弦波:
y = sin(2πφ)
  1. 三角波:
y = 1 - 4|φ - 0.5|
  1. 锯齿波:
y = 2φ - 1
  1. 方波:
y = sign(φ - pw)

其中pw是脉冲宽度(通常0.5)。

  1. 随机采样保持(S&H):
if (φ[n] < φ[n-1]) {  // 相位回绕检测
    y = random(-1, 1)
}

相位偏移与立体声效果

多通道LFO通过相位偏移创建立体声宽度:

左通道: y_L = waveform(φ)
右通道: y_R = waveform((φ + Δφ) mod 1)

常用的相位关系:

  • 单声道:Δφ = 0
  • 反相:Δφ = 0.5(180°)
  • 正交:Δφ = 0.25(90°)
  • 轻微偏移:Δφ = 0.1(36°)用于细腻的立体声效果

对于N个通道的均匀分布:

φ_i = (φ + i/N) mod 1, i ∈ [0, N-1]

波形整形与自定义LFO

波形整形技术扩展基本波形的表现力:

  1. 波形折叠(Wavefold):
y = fold(x, threshold) = {
    x,                      |x| < threshold
    2×threshold - x,        x > threshold
    -2×threshold - x,       x < -threshold
}
  1. 斜率调制:
三角波变形:
y = {
    x/skew,              x < skew
    (1-x)/(1-skew),      x >= skew
}
  1. 平滑过渡:
方波到正弦的变形:
y = tanh(k × square(φ))

k控制过渡的锐度。

  1. 波表混合:
y = Σ(w_i × waveform_i(φ))

其中Σw_i = 1。

频率调制与振动效果

LFO频率本身可被调制,产生复杂的调制效果:

  1. 颤音(Vibrato)频率调制:
f_lfo(t) = f_base × (1 + depth × sin(2π×f_mod×t))
  1. 加速/减速效果:
f_lfo(t) = f_start × (f_end/f_start)^(t/T)
  1. 量化频率(节奏同步):
f_lfo = BPM/60 × subdivision
subdivision ∈ {1/4, 1/8, 1/16, ...}

相位增量的精确计算:

Δφ = f_lfo × (1/fs) × 2^phase_bits

使用定点运算提高精度。

LFO的采样率考虑

LFO通常以较低采样率运行以节省计算资源:

  1. 降采样运行:
LFO采样率: fs_lfo = fs/M
每M个音频采样更新一次LFO
  1. 插值上采样:
线性插值:
y[n] = y_lfo[k] + (y_lfo[k+1] - y_lfo[k]) × (n - kM)/M

立方插值(更平滑):
使用Catmull-Rom样条插值
  1. 抗锯齿考虑: 对于快速LFO(>20Hz),需要带限处理:
使用polyBLEP或BLIT技术生成带限波形
或应用低通滤波: H(z) = (1 + z^-1)/2

高级LFO技术

  1. 多段LFO:
段定义: {波形类型, 持续时间, 目标值}
实现类似多段包络的LFO
  1. 混沌LFO:
Lorenz系统:
dx/dt = σ(y - x)
dy/dt = x(ρ - z) - y
dz/dt = xy - βz
  1. 分形噪声LFO:
y = Σ(amplitude_i × noise(frequency_i × t))
amplitude_i = 1/i
frequency_i = 2^i
  1. 概率LFO:
每个周期随机选择波形:
waveform = random_choice([sine, triangle, square], weights)

12.4 键同步与自由运行

键同步模式的实现

键同步(Key Sync)模式在每次按键时重置LFO相位,确保调制从相同的起点开始:

on_note_on(note, velocity) {
    if (key_sync_enabled) {
        φ = initial_phase  // 通常为0
    }
}

相位重置的时机选择:

  1. 硬同步:立即重置
φ[n] = 0
  1. 软同步:过零点重置
if (crossing_zero && sync_pending) {
    φ = 0
    sync_pending = false
}
  1. 相位锁定:保持相对相位
φ = (master_phase + phase_offset) mod 1

自由运行LFO的相位管理

自由运行(Free Running)模式下,LFO相位持续累加,不受按键影响:

全局LFO状态:
struct GlobalLFO {
    double phase;
    double frequency;
    uint64_t sample_counter;
}

获取当前值:
value = waveform(global_lfo.phase)

自由运行的优势:

  • 创建有机的、非同步的调制
  • 多个音符间产生相位差异
  • 模拟模拟合成器的自然行为

相位漂移补偿:

// 防止长时间运行的精度损失
if (phase > 1000.0) {
    phase = fmod(phase, 1.0);
}

复音合成中的LFO策略

复音合成器需要考虑LFO的分配策略:

  1. 全局LFO(所有语音共享):
struct Voice {
    float* global_lfo_ptr;  // 指向共享LFO
}
  1. 每语音LFO(独立控制):
struct Voice {
    LFO lfo;  // 每个语音独立的LFO实例
    float phase_offset;  // 可选的相位偏移
}
  1. 混合模式:
struct Voice {
    float blend;  // 0=全局, 1=局部
    LFO local_lfo;
}
output = (1-blend)×global_value + blend×local_value

复音LFO的相位分散策略:

// 为每个新语音分配不同相位
voice[i].phase = (i × phase_spread) mod 1

节奏同步与音乐应用

将LFO与音乐节奏同步,创建节奏性的调制效果:

  1. BPM同步:
f_lfo = (BPM/60) × rate_multiplier
rate_multiplier ∈ {1/32, 1/16, 1/8, 1/4, 1/2, 1, 2, 4, ...}
  1. 小节同步:
每小节开始时:
if (bar_sync_enabled) {
    φ = 0
}
  1. 摇摆节奏(Swing):
// 调整偶数拍的时间位置
if (beat % 2 == 0) {
    phase_adjustment = swing_amount × 0.1
    φ = (φ + phase_adjustment) mod 1
}
  1. 节奏量化:
量化到最近的节奏细分:
quantized_phase = round(φ × subdivisions) / subdivisions

相位重置的音色影响

相位重置方式对音色有显著影响:

  1. 瞬时重置产生咔嗒声:
解决方案:应用短暂淡入
reset_envelope = 1 - exp(-t/τ_reset)
output = old_value×(1-reset_envelope) + new_value×reset_envelope
  1. 相位对齐创建梳状滤波效果:
多个LFO同相位会产生增强
反相位会产生抵消
解决:添加随机相位偏移
φ_offset = random(0, spread_amount)
  1. 触发时机的音乐性:
// 量化到音乐节拍
next_reset_time = ceil(current_time / beat_duration) × beat_duration

高级同步技术

  1. 主从同步:
slave_lfo.phase = master_lfo.phase × ratio + offset
  1. 相位调制同步:
φ_modulated = φ + modulation_depth × sin(2π × φ_master)
  1. 触发模式:
enum TriggerMode {
    SINGLE,     // 单次触发
    MULTI,      // 每次按键触发
    LEGATO,     // 仅第一个音符触发
    RANDOM      // 随机触发
}
  1. 相位群组:
// 将多个LFO组织成群组
group_phase = master_phase
for each lfo in group:
    lfo.phase = group_phase × lfo.ratio + lfo.offset

12.5 包络跟随器设计

包络检测算法

包络跟随器(Envelope Follower)从音频信号中提取振幅包络,用于动态控制:

基本流程:

输入信号 → 整流 → 平滑滤波 → 包络输出

数学表达:

1. 整流: |x[n]|
2. 平滑: y[n] = α×|x[n]| + (1-α)×y[n-1]

整流与平滑滤波

整流方式的选择:

  1. 全波整流:
y = |x|
  1. 半波整流:
y = max(0, x)
  1. RMS检测(更准确的能量测量):
rms[n] = √(α×x²[n] + (1-α)×rms²[n-1])
  1. 峰值检测:
peak[n] = max(|x[n]|, peak[n-1]×decay)

平滑滤波器设计:

  1. 单极点低通:
H(z) = α/(1 - (1-α)z^-1)
α = 1 - exp(-2π×fc/fs)
  1. 双极点巴特沃斯(更平滑):
H(s) = ωc²/(s² + √2×ωc×s + ωc²)
  1. 移动平均:
y[n] = (1/N) × Σ(|x[n-k]|), k=0..N-1

攻击和释放时间控制

非对称时间常数实现快速响应和平滑释放:

if (|x[n]| > y[n-1]) {
    α = α_attack    // 快速上升
} else {
    α = α_release   // 缓慢下降
}
y[n] = α×|x[n]| + (1-α)×y[n-1]

时间常数计算:

α_attack = 1 - exp(-1/(τ_attack×fs))
α_release = 1 - exp(-1/(τ_release×fs))

典型参数范围:

  • Attack: 0.1-10ms(快速捕捉瞬态)
  • Release: 10-1000ms(平滑衰减)

侧链调制应用

包络跟随器在侧链(Sidechain)应用中的实现:

  1. 侧链压缩(Ducking):
gain = 1 - sensitivity × envelope_follower(sidechain_input)
output = main_input × gain
  1. 包络转移:
// 从一个声音提取包络,应用到另一个
envelope = follower(source_signal)
output = target_signal × envelope
  1. 门限触发:
if (envelope > threshold) {
    trigger_event()
}
  1. 频段跟随:
// 特定频段的包络跟随
filtered = bandpass(input, f_center, Q)
envelope = follower(filtered)

实时性能优化

优化策略提高实时性能:

  1. 查表近似:
// 预计算指数衰减表
decay_table[i] = exp(-i/τ)
  1. 定点运算:
// 使用定点数避免浮点运算
y_fixed = (α_fixed × x_fixed + 
          (ONE_FIXED - α_fixed) × y_prev_fixed) >> FRAC_BITS
  1. SIMD并行处理:
// 同时处理多个通道
__m128 abs_mask = _mm_set1_ps(-0.0f);
__m128 x_abs = _mm_andnot_ps(abs_mask, x);
  1. 降采样处理:
// 以较低采样率运行包络检测
if (sample_count % downsample_factor == 0) {
    update_envelope()
}

高级包络跟随技术

  1. 多段包络跟随:
struct MultiEnvFollower {
    float bands[N];      // 频段中心频率
    float envelopes[N];  // 各频段包络
    float weights[N];    // 混合权重
}
  1. 自适应时间常数:
// 根据信号特性调整时间常数
if (is_transient) {
    τ = τ_fast
} else {
    τ = τ_slow
}
  1. 预测性包络:
// 使用线性预测提前响应
prediction = 2×y[n] - y[n-1]
if (prediction > threshold) {
    prepare_for_peak()
}
  1. 谱包络提取:
// FFT分析提取频谱包络
fft(input) → magnitude → smoothing → spectral_envelope

常见应用实例

  1. 自动哇音(Auto-Wah):
envelope = follower(input)
filter_freq = f_min + (f_max - f_min) × envelope
output = resonant_filter(input, filter_freq, Q)
  1. 包络同步延迟:
delay_time = base_delay × (1 + depth × envelope)
  1. 动态均衡:
gain_reduction = compute_from_envelope(envelope)
eq_gain = 1 - gain_reduction × (1 - eq_curve(f))

本章小结

本章深入探讨了包络和LFO的数学模型及其实现细节:

  1. 包络曲线:线性和指数包络各有特点,指数包络更符合自然物理过程。通过形状参数可以在两者间灵活调整。

  2. 多段包络:突破传统ADSR限制,支持任意段数、循环和条件分支,极大扩展了音色设计的可能性。

  3. LFO波形生成:基于相位累加器的各种波形生成,相位关系对立体声效果的影响,以及波形整形技术。

  4. 同步模式:键同步vs自由运行的实现差异,复音合成中的LFO策略,以及与音乐节奏的同步方法。

  5. 包络跟随器:从音频信号提取动态控制信息,通过非对称时间常数实现自然的跟随特性。

关键公式回顾:

  • 指数包络:v[n+1] = v[n] + α×(target - v[n]),其中α = 1 - e^(-1/(τ×fs))
  • LFO相位累加:φ[n+1] = (φ[n] + f_lfo/fs) mod 1
  • 包络跟随:y[n] = α×|x[n]| + (1-α)×y[n-1]

练习题

基础题

练习12.1:推导RC电路充电过程的电压公式,并说明时间常数τ的物理意义。

提示:从基本的RC电路微分方程开始:C×dV/dt + V/R = Vs/R

参考答案

微分方程:C×dV/dt = (Vs - V)/R

重新排列:dV/(Vs - V) = dt/(RC)

积分得:-ln(Vs - V) = t/(RC) + C

边界条件V(0) = 0,得C = -ln(Vs)

最终解:V(t) = Vs×(1 - e^(-t/τ)),其中τ = RC

时间常数τ表示电压达到最终值63.2%(1-1/e)所需的时间。

练习12.2:设计一个包络,使得在感知上产生线性的音量渐强效果(crescendo)。假设动态范围为60dB。

提示:考虑分贝与线性振幅的关系:dB = 20×log₁₀(A)

参考答案

感知线性要求dB值线性增长: L(t) = L_min + (L_max - L_min)×(t/T)

其中L_min = -60dB,L_max = 0dB

转换为振幅: A(t) = 10^(L(t)/20) = 10^((-60 + 60t/T)/20) = 10^(-3 + 3t/T)

简化:A(t) = 0.001×1000^(t/T) = 0.001×(1000)^(t/T)

这是一个指数包络,底数为1000^(1/T)。

练习12.3:计算LFO产生1Hz正弦波调制时,在44100Hz采样率下的相位增量。如果使用32位相位累加器,精确的增量值是多少?

提示:相位增量 = 频率/采样率×2^位数

参考答案

归一化相位增量:Δφ = f/fs = 1/44100 ≈ 0.0000227

32位相位累加器的增量: Δφ_int = (1/44100)×2^32 = 97391.548...

取整为97392(实际频率约1.0000103Hz)

误差:(97392/2^32)×44100 - 1 ≈ 0.00001Hz,可忽略不计。

挑战题

练习12.4:分析并推导乒乓循环包络在循环边界处保持一阶导数连续的条件。

提示:考虑前向和反向播放在转折点的斜率

参考答案

设包络函数为e(t),循环区间[t₁, t₂]

前向到达t₂时的斜率:de/dt|t₂⁻ 反向开始时的斜率:-de/dt|t₂⁺

连续条件:de/dt|t₂⁻ = -de/dt|t₂⁺

这要求在循环终点处斜率为0,或使用对称的包络形状。

实现方案:

  1. 在循环边界使用平滑过渡(如余弦窗)
  2. 确保循环段本身是对称的
  3. 在边界处应用短暂的交叉淡化

练习12.5:设计一个自适应包络跟随器,能够区分打击乐瞬态和持续音,并相应调整时间常数。

提示:可以使用双包络跟随器系统,一个快速一个慢速

参考答案

算法设计:

  1. 双跟随器: - fast_env:τ = 1ms - slow_env:τ = 100ms

  2. 瞬态检测: ratio = fast_env/slow_env is_transient = (ratio > threshold)

  3. 自适应时间常数: if is_transient: τ_adaptive = τ_fast×(1 - transient_amount) + τ_transient×transient_amount else: τ_adaptive = τ_slow

  4. 平滑过渡: τ_final = smooth(τ_adaptive, smoothing_time)

其中transient_amount = min(1, (ratio - 1)/sensitivity)

练习12.6:证明使用N个相位均匀分布的LFO求和时,如果N足够大,结果趋近于恒定值。这对多语音合成有什么启示?

提示:考虑复数表示和相位求和

参考答案

设N个LFO的相位为φₖ = φ₀ + 2πk/N,k = 0,1,...,N-1

正弦求和:S = Σsin(φₖ) = Σsin(φ₀ + 2πk/N)

使用复数:Σe^(i×φₖ) = e^(iφ₀)×Σe^(i2πk/N)

几何级数求和:Σe^(i2πk/N) = (1 - e^(i2πN/N))/(1 - e^(i2π/N)) = 0(N>1)

因此S = 0(所有相位的正弦和为0)

启示:

  1. 多语音使用相同全局LFO会产生相位相消
  2. 需要为每个语音添加随机相位偏移
  3. 或使用每语音独立的LFO避免相消

练习12.7:设计一个"呼吸"包络,模拟自然的呼吸节奏,包括吸气、屏息、呼气和停顿四个阶段。给出数学模型和参数建议。

提示:观察真实呼吸曲线,注意不同阶段的曲线特征

参考答案

呼吸包络模型:

  1. 吸气(Inhale):上凸曲线 v(t) = (1 - e^(-3t/T_in)),t ∈ [0, T_in]

  2. 屏息(Hold):轻微下降 v(t) = 1 - 0.05×(t-T_in)/T_hold,t ∈ [T_in, T_in+T_hold]

  3. 呼气(Exhale):S型曲线 v(t) = 0.95×(1 + cos(π×(t-T_in-T_hold)/T_ex))/2

  4. 停顿(Pause):零值 v(t) = 0,t ∈ [T_in+T_hold+T_ex, T_total]

参数建议(正常呼吸):

  • T_in = 1.5s(吸气)
  • T_hold = 0.5s(屏息)
  • T_ex = 2s(呼气)
  • T_pause = 0.5s(停顿)
  • 总周期:4.5s(约13次/分钟)

变化因素:

  • 运动时缩短所有时间
  • 冥想时延长屏息和停顿
  • 添加±10%随机变化模拟自然不规则性

练习12.8:分析当LFO频率接近奈奎斯特频率一半时会出现什么问题,如何解决?

提示:考虑混叠和带限信号生成

参考答案

问题分析:

  1. 混叠:非正弦波形(方波、锯齿波)含有高次谐波,当基频>fs/4时,谐波会混叠

  2. 阶梯效应:低采样点数导致波形失真

  3. 调制失真:快速LFO调制会产生边带,可能超出奈奎斯特频率

解决方案:

  1. 带限波形生成: - 使用polyBLEP消除不连续点 - 限制谐波数量:n_max = floor(fs/(2×f_lfo)) - 加法合成:只合成不混叠的谐波

  2. 过采样: - 以2×或4×采样率生成LFO - 低通滤波后降采样

  3. 波形切换: - f_lfo > fs/20时自动切换到正弦波 - 渐进式谐波衰减

  4. 预积分方法: - 直接计算波形的积分形式 - 适用于FM等应用

实现示例(带限方波):

square_bandlimited(φ, f) {
    sum = 0
    n_harmonics = min(10, fs/(2f))
    for k = 1, 3, 5, ... up to n_harmonics:
        sum += sin(2π×k×φ)/k
    return 4/π × sum
}

常见陷阱与错误

1. 数值精度问题

问题:长时间运行的包络和LFO产生累积误差

症状:

  • 包络无法准确到达目标值
  • LFO相位漂移
  • 循环包络的周期逐渐改变

解决方案:

  • 使用双精度浮点数进行关键计算
  • 定期重新校准相位(phase = fmod(phase, 1.0))
  • 在接近目标值时切换到直接赋值

2. 咔嗒声和不连续

问题:包络段切换或LFO重置时产生咔嗒声

症状:

  • 按键时听到"啪"声
  • 包络循环点有周期性噪声
  • LFO同步时的瞬态干扰

解决方案:

  • 实现平滑过渡机制(5-10采样点的淡入淡出)
  • 使用软同步代替硬同步
  • 确保包络段之间的连续性

3. 时间常数计算错误

问题:指数包络的时间参数与预期不符

常见错误:

// 错误:混淆时间常数和持续时间
α = attack_time  // 错!

// 正确:
α = 1 - exp(-1/(attack_time × sample_rate))

调试技巧:

  • 验证包络达到90%目标值的时间是否约为2.3×τ
  • 使用可视化工具检查包络形状
  • 记录并验证关键时间点

4. 复音LFO相位相消

问题:多个语音使用相同相位的LFO导致调制效果消失

症状:

  • 和弦的调制效果比单音弱
  • 立体声场变窄
  • 某些音符的调制不明显

解决方案:

  • 为每个语音添加随机相位偏移
  • 使用语音索引计算确定性偏移
  • 实现可选的"vintage"模式(模拟老式合成器的自由运行LFO)

5. 采样率依赖性

问题:包络和LFO行为随采样率改变

症状:

  • 48kHz和44.1kHz下音色不同
  • 高采样率时包络过快或过慢
  • LFO频率不准确

预防措施:

  • 始终使用采样率归一化的参数
  • 测试多种采样率下的行为
  • 实现采样率无关的参数接口

6. 包络跟随器延迟

问题:包络跟随器响应滞后于输入信号

症状:

  • 瞬态响应迟钝
  • 侧链压缩时机不准
  • 自动哇音效果不自然

优化方法:

  • 使用前瞻(look-ahead)缓冲
  • 实现零延迟反馈架构
  • 针对不同信号类型调整时间常数

7. 性能瓶颈

问题:复杂包络消耗过多CPU

常见原因:

  • 每采样点都进行条件判断
  • 使用三角函数等昂贵操作
  • 未优化的内存访问模式

优化策略:

  • 批处理多个采样点
  • 使用查找表替代复杂计算
  • SIMD指令并行处理
  • 缓存友好的数据结构

记住:包络和LFO看似简单,但它们的质量直接影响合成器的音乐表现力。细节决定成败!