% YM2151-style Envelope Generator with proper gate handling (AR, D1R, D2R, RR) % 2025-07-16 mchris clear; close all; clc; % Sampling and timing Fs = 44100; T_total = 3.0; t = linspace(0, T_total, Fs*T_total); % Envelope parameters AR = 0.02; % Attack time (seconds) D1R = 0.3; % Decay 1 time (seconds) SL = 0.5; % Sustain level (0–1) D2R = 5.0; % Decay 2 time (seconds) — optional decay below SL RR = 0.1; % Release time (seconds) gateTime = 2.0; % When gate goes low (key released) % Sample indices attack_end = round(AR * Fs); decay1_end = attack_end + round(D1R * Fs); gate_idx = round(gateTime * Fs); env = zeros(size(t)); release_start_val = 0; released = false; for i = 1:length(t) if i <= attack_end % Attack env(i) = (i - 1) / attack_end; elseif i <= decay1_end % Decay 1: from 1 → SL env(i) = 1 - ((i - attack_end) / (decay1_end - attack_end)) * (1 - SL); elseif t(i) <= gateTime % Decay 2: from SL → 0 (or holds if D2R = 0) if D2R > 0 decay2_progress = (t(i) - t(decay1_end)) / D2R; env(i) = SL * (1 - decay2_progress); env(i) = max(env(i), 0); % don't go below zero else env(i) = SL; % sustain level is held end release_start_val = env(i); % continuously update release start value else % Release phase begins after gate low if ~released released = true; release_start_idx = i; end release_progress = (i - release_start_idx) / (RR * Fs); env(i) = release_start_val * (1 - release_progress); env(i) = max(env(i), 0); end end % Plot plot(t, env, 'LineWidth', 2); xlabel('Time (s)'); ylabel('Amplitude'); title('YM2151 Envelope Generator: AR → D1R → SL → D2R → RR'); grid on; ylim([-0.1, 1.1]);