desc:Overstratum MIDI Dynamics & Expression [v1.1]
author:Overstratum Audio
version:1.1

// ==========================================
// DSP Core Powered by Airwindows Galactic (Reverb) & Geraint Luff (Vibrato)
// License: MIT
// 
// Copyright (c) Chris Johnson (Airwindows)
// Copyright (c) Geraint Luff
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// ==========================================

// Versteckte Parameter zur internen Statusspeicherung über Projekt-Saves hinweg
slider1:127<0,127,1>-Value_Dyn
slider2:127<0,127,1>-Value_Expr
slider3:1<0,127,1>-CC_Dyn
slider4:11<0,127,1>-CC_Expr
slider5:2<0,3,1>-Toggle_Dyn
slider6:2<0,3,1>-Toggle_Expr
slider7:21<0,127,1>-CC_Vib
slider8:0<0,3,1>-Toggle_Vib
slider9:0<0,127,1>-Value_Vib
slider10:0<0,3,1{Strings,Woodwinds,Brass,Choir}>-Preset

// --- Panning, Reverb & Gain ---
slider11:0<-100,100,1>-Pan
slider12:0<0,17,1{Manual (Knob),Violin,Viola,Cello,Double Bass,Harp,Trumpet,Trombone,French Horn,Tuba,Flute,Oboe,Clarinet,Bassoon,Piccolo,English Horn,Contrabassoon,Percussion}>-Auto Pan Preset
slider13:0<0,100,1>-Reverb Amount (%)
slider14:30<0,100,1>-Reverb Size (%)
slider15:0<-12,12,0.1>-Output Gain (dB)

// --- Auto Movement Toggles ---
slider16:0<0,2,1>-Auto_Dyn
slider17:0<0,2,1>-Auto_Expr
slider18:0<0,2,1>-Auto_Vib

// --- MIDI Out Mode ---
slider19:0<0,1,1{Thru,Sliders}>-MIDI CC Output Mode

@init
// --- Interne Variablen & Initiale Zustände ---
odme_cc_dyn = slider3;
odme_cc_expr = slider4;
odme_cc_vib = slider7;

odme_toggle_dyn = slider5;
odme_toggle_expr = slider6;
odme_toggle_vib = slider8;

odme_auto_dyn = slider16;
odme_auto_expr = slider17;
odme_auto_vib = slider18;

odme_midi_out_mode = slider19;

odme_val_dyn_target = slider1;
odme_val_expr_target = slider2;
odme_val_vib_target = slider9;

odme_val_dyn_current = odme_val_dyn_target;
odme_val_expr_current = odme_val_expr_target;
odme_val_vib_current = odme_val_vib_target;

current_out_gain_db = slider15;

// CC Lock Timestamps & Tracking
last_cc_dyn = 0;
last_cc_expr = 0;
last_cc_vib = 0;

last_sent_cc_dyn = -1;
last_sent_cc_expr = -1;
last_sent_cc_vib = -1;

// Auto Movement Variables
auto_dyn_val = slider1;
auto_expr_val = slider2;
auto_vib_val = slider9;

auto_dyn_timer = 0;
auto_expr_timer = 0;
auto_vib_timer = 0;

PI = 3.141592653589793;

// --- EQ Speicheradressen ---
mem_eq_coefs = 100;
mem_eq_states_L = 200;
mem_eq_states_R = 300;

// --- Custom Curve Functions (Piecewise Linear Interpolation) ---
function get_dyn_db(val) (
  val <= 16 ? -10.0 + (val/16.0)*(1.5) :
  val <= 32 ? -8.5 + ((val-16.0)/16.0)*(2.0) :
  val <= 48 ? -6.5 + ((val-32.0)/16.0)*(2.0) :
  val <= 64 ? -4.5 + ((val-48.0)/16.0)*(1.5) :
  val <= 80 ? -3.0 + ((val-64.0)/16.0)*(1.2) :
  val <= 96 ? -1.8 + ((val-80.0)/16.0)*(0.8) :
  val <= 112 ? -1.0 + ((val-96.0)/16.0)*(0.6) :
  -0.4 + ((val-112.0)/15.0)*(0.4);
);

function get_expr_db(val) (
  val <= 16 ? -60.0 + (val/16.0)*(24.0) :
  val <= 32 ? -36.0 + ((val-16.0)/16.0)*(12.0) :
  val <= 48 ? -24.0 + ((val-32.0)/16.0)*(8.0) :
  val <= 64 ? -16.0 + ((val-48.0)/16.0)*(6.0) :
  val <= 80 ? -10.0 + ((val-64.0)/16.0)*(4.0) :
  val <= 96 ? -6.0 + ((val-80.0)/16.0)*(3.0) :
  val <= 112 ? -3.0 + ((val-96.0)/16.0)*(2.0) :
  -1.0 + ((val-112.0)/15.0)*(1.0);
);

// --- Dynamische Speicherverwaltung für Reverb (Sicher ab Index 100.000) ---
ptr = 100000; function alloc(size) local(p) ( p = ptr; ptr += size; p; );
UINT32_MAX = 4294967295;

// --- Galactic Reverb Memory ---
aIL=alloc(6480);aJL=alloc(3660);aKL=alloc(1720);aLL=alloc(680);aAL=alloc(9700);aBL=alloc(6000);aCL=alloc(2320);aDL=alloc(940);
aEL=alloc(15220);aFL=alloc(8460);aGL=alloc(4540);aHL=alloc(3200);aML=alloc(3111);aMR=alloc(3111);lastRefL=alloc(7);
aIR=alloc(6480);aJR=alloc(3660);aKR=alloc(1720);aLR=alloc(680);aAR=alloc(9700);aBR=alloc(6000);aCR=alloc(2320);aDR=alloc(940);
aER=alloc(15220);aFR=alloc(8460);aGR=alloc(4540);aHR=alloc(3200);lastRefR=alloc(7);
countI=1;countJ=1;countK=1;countL=1;countA=1;countB=1;countC=1;countD=1;countE=1;countF=1;countG=1;countH=1;countM=1;
vibM=3.0;oldfpd=429496.7295; fpdL=1.0;while(fpdL<16386)(fpdL=rand()*UINT32_MAX); fpdR=1.0;while(fpdR<16386)(fpdR=rand()*UINT32_MAX);

function proc_biquad(x, coef_offset, state_offset)
local(b0, b1, b2, a1, a2, x1, x2, y1, y2, y, c_ptr, s_ptr) (
  c_ptr = mem_eq_coefs + coef_offset; s_ptr = state_offset;
  b0=c_ptr[0]; b1=c_ptr[1]; b2=c_ptr[2]; a1=c_ptr[3]; a2=c_ptr[4];
  x1=s_ptr[0]; x2=s_ptr[1]; y1=s_ptr[2]; y2=s_ptr[3];
  y = b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2;
  s_ptr[1] = x1; s_ptr[0] = x; s_ptr[3] = y1; s_ptr[2] = y;
  y;
);

function calc_biquad(type, f, q, gain_db, offset)
local(w0, cos_w0, alpha, A, a0, a1, a2, b0, b1, b2, out_ptr) (
  f = min(f, srate * 0.49); q = max(q, 0.01);
  w0 = 2.0 * PI * f / srate; cos_w0 = cos(w0); alpha = sin(w0) / (2 * q);
  A = 10 ^ (gain_db / 40); out_ptr = mem_eq_coefs + offset;
  
  type == 1 ? ( // Low Shelf
    b0 = A * ((A + 1) - (A - 1) * cos_w0 + 2 * sqrt(A) * alpha);
    b1 = 2 * A * ((A - 1) - (A + 1) * cos_w0);
    b2 = A * ((A + 1) - (A - 1) * cos_w0 - 2 * sqrt(A) * alpha);
    a0 = (A + 1) + (A - 1) * cos_w0 + 2 * sqrt(A) * alpha;
    a1 = -2 * ((A - 1) + (A + 1) * cos_w0);
    a2 = (A + 1) + (A - 1) * cos_w0 - 2 * sqrt(A) * alpha;
  ) : type == 2 ? ( // Peaking (Bell)
    b0 = 1 + alpha * A; b1 = -2 * cos_w0; b2 = 1 - alpha * A;
    a0 = 1 + alpha / A; a1 = -2 * cos_w0; a2 = 1 - alpha / A;
  ) : type == 3 ? ( // High Shelf
    b0 = A * ((A + 1) + (A - 1) * cos_w0 + 2 * sqrt(A) * alpha);
    b1 = -2 * A * ((A - 1) + (A + 1) * cos_w0);
    b2 = A * ((A + 1) + (A - 1) * cos_w0 - 2 * sqrt(A) * alpha);
    a0 = (A + 1) - (A - 1) * cos_w0 + 2 * sqrt(A) * alpha;
    a1 = 2 * ((A - 1) - (A + 1) * cos_w0);
    a2 = (A + 1) - (A - 1) * cos_w0 - 2 * sqrt(A) * alpha;
  );
  a0 = max(a0, 0.0000000001);
  out_ptr[0] = b0/a0; out_ptr[1] = b1/a0; out_ptr[2] = b2/a0;
  out_ptr[3] = a1/a0; out_ptr[4] = a2/a0;
);

current_eq_amount = 0;
function update_biquads() local(amt) (
  amt = current_eq_amount;
  slider10 == 0 ? ( // Strings
    calc_biquad(1, 250, 0.707, 2.0 * amt, 0);   
    calc_biquad(2, 1000, 1.0, 0.0 * amt, 5);    
    calc_biquad(3, 2000, 0.707, -7.0 * amt, 10); 
  ) : slider10 == 1 ? ( // Woodwinds
    calc_biquad(1, 300, 0.707, 1.5 * amt, 0);   
    calc_biquad(2, 5000, 1.0, -3.0 * amt, 5);   
    calc_biquad(3, 4000, 0.707, -6.0 * amt, 10); 
  ) : slider10 == 2 ? ( // Brass 
    calc_biquad(1, 200, 0.707, 3.0 * amt, 0);   
    calc_biquad(2, 1000, 1.0, -2.0 * amt, 5);   
    calc_biquad(3, 1500, 0.707, -15.0 * amt, 10);
  ) : slider10 == 3 ? ( // Choir
    calc_biquad(1, 300, 0.707, 9.0 * amt, 0);   
    calc_biquad(2, 4500, 1.0, -9.0 * amt, 5);   
    calc_biquad(3, 12000, 0.707, 4.0 * amt, 10); 
  );
);

// Initiale Lautstärken & Intensitäten
dyn_int = odme_toggle_dyn == 1 ? 0.5 : odme_toggle_dyn == 2 ? 1.0 : odme_toggle_dyn == 3 ? 1.5 : 0.0;
expr_int = odme_toggle_expr == 1 ? 0.5 : odme_toggle_expr == 2 ? 1.0 : odme_toggle_expr == 3 ? 1.5 : 0.0;

current_dyn_vol_db = dyn_int * get_dyn_db(odme_val_dyn_current);
current_expr_db = expr_int * get_expr_db(odme_val_expr_current);

// --- Vibrato Variablen ---
v_rate_hz = 5.1;
v_attack_ms = 500.0;
v_release_ms = 1000.0;
v_attack_samples = v_attack_ms * 0.001 * srate;
v_release_samples = v_release_ms * 0.001 * srate;
v_phase_rate = v_rate_hz / srate;

v_currently_active = 0;
v_current_depth_samples = 0;
v_current_phase = 1.0;
v_note_count = 0;
v_samples_since_attack = 0;
v_samples_since_release = 0;

v_buf_size = 4096;
v_buf_mask = 4095;
v_buf_L = 30000;
v_buf_R = 40000;
v_write_pos = 0;

// Konstanten & Speicheradressen für Sample-Akkurate MIDI-Queue
QUEUE_MAX = 512;
q_offset = 20000; 
q_type = 21000; 
q_val = 22000;

// GUI Status
gui_dropdown_open_preset = 0;
gui_dropdown_open_cc_dyn = 0;
gui_dropdown_open_cc_expr = 0;
gui_dropdown_open_cc_vib = 0;
gui_dropdown_open_pan = 0;
gui_mouse_last = 0;
active_fader = 0;

scroll_y_dyn = 0;
scroll_y_expr = 0;
scroll_y_vib = 0;

last_preset = slider10;
last_pan_preset = slider12;
update_biquads();

@slider
odme_val_dyn_target = slider1;
odme_val_expr_target = slider2;
odme_cc_dyn = slider3;
odme_cc_expr = slider4;
odme_toggle_dyn = slider5;
odme_toggle_expr = slider6;
odme_cc_vib = slider7;
odme_toggle_vib = slider8;
odme_val_vib_target = slider9;
odme_out_gain_target = slider15;
odme_auto_dyn = slider16;
odme_auto_expr = slider17;
odme_auto_vib = slider18;
odme_midi_out_mode = slider19;

@block
// --- Panning Preset Logik ---
slider12 != last_pan_preset ? (
  last_pan_preset = slider12;
  slider12 == 1 ? slider11 = -40 : // Violin
  slider12 == 2 ? slider11 = -10 : // Viola
  slider12 == 3 ? slider11 = 30 :  // Cello
  slider12 == 4 ? slider11 = 50 :  // Double Bass
  slider12 == 5 ? slider11 = -50 : // Harp
  slider12 == 6 ? slider11 = 20 :  // Trumpet
  slider12 == 7 ? slider11 = 20 :  // Trombone
  slider12 == 8 ? slider11 = 40 :  // French Horn
  slider12 == 9 ? slider11 = -30 : // Tuba
  slider12 == 10 ? slider11 = -20 : // Flute
  slider12 == 11 ? slider11 = -10 : // Oboe
  slider12 == 12 ? slider11 = 20 : // Clarinet
  slider12 == 13 ? slider11 = 20 : // Bassoon
  slider12 == 14 ? slider11 = -20 : // Piccolo
  slider12 == 15 ? slider11 = -10 : // English Horn
  slider12 == 16 ? slider11 = 20 : // Contrabassoon
  slider12 == 17 ? slider11 = 30 ; // Percussion
  slider_automate(slider11);
);

// --- Auto Movement Interpolation ---
time_step = samplesblock / srate;

// Auto Dynamics
odme_auto_dyn > 0 ? (
    auto_dyn_timer -= time_step;
    auto_dyn_timer <= 0 ? (
        auto_dyn_timer = odme_auto_dyn == 1 ? (4.0 + rand(4.0)) : (2.0 + rand(2.0));
        auto_dyn_next = 60.0 + rand(60.0);
        auto_dyn_rate = (auto_dyn_next - auto_dyn_val) / auto_dyn_timer;
    );
    auto_dyn_val += auto_dyn_rate * time_step;
    auto_dyn_val = max(60.0, min(120.0, auto_dyn_val));
    odme_val_dyn_target = auto_dyn_val;
    slider1 = auto_dyn_val; 
    slider_automate(slider1);
) : (
    auto_dyn_val = odme_val_dyn_target;
    auto_dyn_timer = 0;
);

// Auto Expression
odme_auto_expr > 0 ? (
    auto_expr_timer -= time_step;
    auto_expr_timer <= 0 ? (
        auto_expr_timer = odme_auto_expr == 1 ? (4.0 + rand(4.0)) : (2.0 + rand(2.0));
        auto_expr_next = 60.0 + rand(60.0);
        auto_expr_rate = (auto_expr_next - auto_expr_val) / auto_expr_timer;
    );
    auto_expr_val += auto_expr_rate * time_step;
    auto_expr_val = max(60.0, min(120.0, auto_expr_val));
    odme_val_expr_target = auto_expr_val;
    slider2 = auto_expr_val; 
    slider_automate(slider2);
) : (
    auto_expr_val = odme_val_expr_target;
    auto_expr_timer = 0;
);

// Auto Vibrato
odme_auto_vib > 0 ? (
    auto_vib_timer -= time_step;
    auto_vib_timer <= 0 ? (
        auto_vib_timer = odme_auto_vib == 1 ? (2.0 + rand(2.0)) : (1.0 + rand(1.0));
        auto_vib_next = 0.0 + rand(60.0);
        auto_vib_rate = (auto_vib_next - auto_vib_val) / auto_vib_timer;
    );
    auto_vib_val += auto_vib_rate * time_step;
    auto_vib_val = max(0.0, min(60.0, auto_vib_val));
    odme_val_vib_target = auto_vib_val;
    slider9 = auto_vib_val; 
    slider_automate(slider9);
) : (
    auto_vib_val = odme_val_vib_target;
    auto_vib_timer = 0;
);

// --- Reverb Koeffizienten Update ---
local_srate = max(8000, srate);
rvb_wet_amt = slider13 / 100.0;
rvb_D = slider14 / 100.0;
rvb_wet_amt > 0.0 ? (
    rvb_A = 0.5; rvb_B = 0.5; rvb_C = 0.5; rvb_E = rvb_wet_amt;
    rvb_overallscale = 1.0 / 44100.0 * local_srate; rvb_cycleEnd = floor(rvb_overallscale) | 0;
    (rvb_cycleEnd < 1) ? rvb_cycleEnd = 1; (rvb_cycleEnd > 4) ? rvb_cycleEnd = 4; (rvb_cycle > rvb_cycleEnd-1) ? rvb_cycle = rvb_cycleEnd-1; 
    rvb_regen = 0.0625+((1.0-rvb_A)*0.0625); rvb_attenuate = (1.0 - (rvb_regen / 0.125))*1.333;
    rvb_lowpass = pow(1.00001-(1.0-rvb_B),2.0)/sqrt(rvb_overallscale); rvb_drift = pow(rvb_C,3)*0.001;
    rvb_size = (rvb_D*1.77)+0.1; rvb_wet = 1.0-(pow(1.0-rvb_E,3));
    delayI = 3407.0*rvb_size | 0; delayJ = 1823.0*rvb_size | 0; delayK = 859.0*rvb_size | 0; delayL = 331.0*rvb_size | 0;
    delayA = 4801.0*rvb_size | 0; delayB = 2909.0*rvb_size | 0; delayC = 1153.0*rvb_size | 0; delayD = 461.0*rvb_size | 0;
    delayE = 7607.0*rvb_size | 0; delayF = 4217.0*rvb_size | 0; delayG = 2269.0*rvb_size | 0; delayH = 1597.0*rvb_size | 0; delayM = 256;
);

// --- MIDI Empfang & Sample-Akkurate Queue ---
q_len = 0;
sample_counter = 0;

while (midirecv(offset, m1, m2, m3)) (
  status = m1 & 240;

  // Im SLIDERS Mode werden die spezifisch verknüpften CCs geblockt, alles andere passiert (Note On/Off, Modwheel etc.)
  pass_thru = 1;
  odme_midi_out_mode == 1 && status == 176 ? (
      (m2 == odme_cc_dyn || m2 == odme_cc_expr || m2 == odme_cc_vib) ? pass_thru = 0;
  );
  
  pass_thru ? midisend(offset, m1, m2, m3);
  
  status == 176 ? ( 
    // Unabhängige Abfragen ermöglichen die Belegung mehrerer Regler mit demselben CC
    m2 == odme_cc_dyn ? ( q_len < QUEUE_MAX ? (q_offset[q_len]=offset; q_type[q_len]=1; q_val[q_len]=m3; q_len+=1; last_cc_dyn=time_precise();) );
    m2 == odme_cc_expr ? ( q_len < QUEUE_MAX ? (q_offset[q_len]=offset; q_type[q_len]=2; q_val[q_len]=m3; q_len+=1; last_cc_expr=time_precise();) );
    m2 == odme_cc_vib ? ( q_len < QUEUE_MAX ? (q_offset[q_len]=offset; q_type[q_len]=3; q_val[q_len]=m3; q_len+=1; last_cc_vib=time_precise();) );
    (m2 == 120 || m2 == 123) ? ( q_len < QUEUE_MAX ? (q_offset[q_len]=offset; q_type[q_len]=6; q_len+=1;) );
  ) : status == 144 ? ( 
    m3 > 0 ? ( q_len < QUEUE_MAX ? (q_offset[q_len]=offset; q_type[q_len]=4; q_len+=1;) )
           : ( q_len < QUEUE_MAX ? (q_offset[q_len]=offset; q_type[q_len]=5; q_len+=1;) );
  ) : status == 128 ? ( 
    q_len < QUEUE_MAX ? (q_offset[q_len]=offset; q_type[q_len]=5; q_len+=1;);
  );
);
q_idx = 0;

smooth_coeff = 1.0 - exp(-1.0 / (0.020 * srate));

slider10 != last_preset ? (
  last_preset = slider10;
  update_biquads();
);

// --- Generierte CCs ausgeben (falls Modus aktiviert) ---
odme_midi_out_mode == 1 ? (
    curr_val_dyn = floor(slider1);
    curr_val_dyn != last_sent_cc_dyn ? (
        midisend(0, 176, odme_cc_dyn, curr_val_dyn); 
        last_sent_cc_dyn = curr_val_dyn;
    );
    
    curr_val_expr = floor(slider2);
    curr_val_expr != last_sent_cc_expr ? (
        midisend(0, 176, odme_cc_expr, curr_val_expr);
        last_sent_cc_expr = curr_val_expr;
    );
    
    curr_val_vib = floor(slider9);
    curr_val_vib != last_sent_cc_vib ? (
        midisend(0, 176, odme_cc_vib, curr_val_vib);
        last_sent_cc_vib = curr_val_vib;
    );
);

@sample
// --- Intensitäts-Skalierung (Off/Soft/Mid/Hard) ---
dyn_int = odme_toggle_dyn == 1 ? 0.5 : odme_toggle_dyn == 2 ? 1.0 : odme_toggle_dyn == 3 ? 1.5 : 0.0;
expr_int = odme_toggle_expr == 1 ? 0.5 : odme_toggle_expr == 2 ? 1.0 : odme_toggle_expr == 3 ? 1.5 : 0.0;
vib_int = odme_toggle_vib == 1 ? 0.5 : odme_toggle_vib == 2 ? 1.0 : odme_toggle_vib == 3 ? 1.5 : 0.0;

// --- Sample-Akkurate MIDI Verarbeitung ---
while (q_idx < q_len && sample_counter == q_offset[q_idx]) (
  type = q_type[q_idx];
  val = q_val[q_idx];
  
  // Ignoriere CC, falls AUTO aktiviert ist
  type == 1 ? ( odme_auto_dyn == 0 ? ( odme_val_dyn_target = val; slider1 = val; ) ) :
  type == 2 ? ( odme_auto_expr == 0 ? ( odme_val_expr_target = val; slider2 = val; ) ) :
  type == 3 ? ( odme_auto_vib == 0 ? ( odme_val_vib_target = val; slider9 = val; ) ) :
  type == 4 ? ( 
    v_currently_active = 1;
    v_note_count += 1;
    v_samples_since_attack = 0;
  ) :
  type == 5 ? ( 
    v_note_count -= 1;
    v_note_count <= 0 ? (
        v_note_count = 0;
        v_samples_since_release = 0;
    );
  ) :
  type == 6 ? ( 
    v_note_count = 0;
    v_samples_since_release = 0;
  );
  q_idx += 1;
);
sample_counter += 1;

// --- Parameter Glättung & Berechnung ---
odme_val_dyn_current += (odme_val_dyn_target - odme_val_dyn_current) * smooth_coeff;
odme_val_expr_current += (odme_val_expr_target - odme_val_expr_current) * smooth_coeff;
odme_val_vib_current += (odme_val_vib_target - odme_val_vib_current) * smooth_coeff;

abs(slider15 - current_out_gain_db) > 0.001 ? (
  current_out_gain_db += (slider15 - current_out_gain_db) * smooth_coeff;
);

target_eq_amount = dyn_int * (1.0 - odme_val_dyn_current / 127.0);  // Unbeeinflusst vom dB-Curve Update!
target_dyn_vol_db = dyn_int * get_dyn_db(odme_val_dyn_current);     // Nutzt neue Interpolation
target_expr_db = expr_int * get_expr_db(odme_val_expr_current);     // Nutzt neue Interpolation

v_target_depth_cents = vib_int * (odme_val_vib_current * (40.0 / 127.0));

update_eq_needed = 0;
abs(target_eq_amount - current_eq_amount) > 0.001 ? (
  current_eq_amount += (target_eq_amount - current_eq_amount) * smooth_coeff;
  update_eq_needed = 1;
);

abs(target_dyn_vol_db - current_dyn_vol_db) > 0.001 ? (
  current_dyn_vol_db += (target_dyn_vol_db - current_dyn_vol_db) * smooth_coeff;
);

abs(target_expr_db - current_expr_db) > 0.001 ? (
  current_expr_db += (target_expr_db - current_expr_db) * smooth_coeff;
);

total_gain_lin = 10.0 ^ ((current_expr_db + current_dyn_vol_db + current_out_gain_db) / 20.0);

update_eq_needed ? update_biquads();

in_L = spl0;
in_R = spl1;

// --- 1. Multi-Band EQ (Dynamics) ---
current_eq_amount > 0.005 ? (
  out_L = proc_biquad(in_L, 0, mem_eq_states_L);
  out_L = proc_biquad(out_L, 5, mem_eq_states_L + 5);
  out_L = proc_biquad(out_L, 10, mem_eq_states_L + 10);
  
  out_R = proc_biquad(in_R, 0, mem_eq_states_R);
  out_R = proc_biquad(out_R, 5, mem_eq_states_R + 5);
  out_R = proc_biquad(out_R, 10, mem_eq_states_R + 10);
) : (
  out_L = in_L; out_R = in_R;
);

// --- 2. Vibrato (Luff Delay-Line) ---
v_buf_L[v_write_pos] = out_L;
v_buf_R[v_write_pos] = out_R;

(v_currently_active || v_current_depth_samples > 0.0001) ? (
  v_samples_since_attack += 1;
  v_samples_since_release += 1;
  
  v_current_depth_samples > 0.0001 ? (
      phase_value = (0.5 - 0.5*cos(v_current_phase * 2.0 * PI));
      delay_samples = v_current_depth_samples * phase_value;
      
      read_pos = v_write_pos - delay_samples;
      read_pos < 0 ? read_pos += v_buf_size;
      
      idx1 = floor(read_pos);
      frac = read_pos - idx1;
      idx1 = idx1 & v_buf_mask;
      idx2 = (idx1 + 1) & v_buf_mask;
      
      out_L = (v_buf_L[idx1] * (1.0 - frac) + v_buf_L[idx2] * frac);
      out_R = (v_buf_R[idx1] * (1.0 - frac) + v_buf_R[idx2] * frac);
  );
  
  v_current_phase += v_phase_rate;
  v_current_phase >= 1.0 ? (
      v_samples_since_attack < v_attack_samples ? (
          envelope = max(0, v_samples_since_attack) / v_attack_samples;
      ) : (
          envelope = 1.0;
      );
      
      v_note_count <= 0 ? (
          v_samples_since_release < v_release_samples ? (
              envelope *= (1.0 - max(0, v_samples_since_release) / v_release_samples);
          ) : (
              envelope = 0.0;
              v_currently_active = 0;
          );
      );
      v_current_phase -= 1.0; 
      v_current_depth_samples = envelope * srate * (v_target_depth_cents * 0.01 / 12.0 / (2.0*PI) / v_rate_hz);
  );
);
v_write_pos = (v_write_pos + 1) & v_buf_mask;

// --- 3. Combined Volume Gain ---
spl0 = out_L * total_gain_lin;
spl1 = out_R * total_gain_lin;

// --- 4. True Stereo Panning ---
pan_val = slider11 / 100.0;
pan_val != 0.0 ? (
    pan_val < 0.0 ? (
        spl0 = spl0 + spl1 * abs(pan_val);
        spl1 = spl1 * (1.0 - abs(pan_val));
    ) : (
        spl1 = spl1 + spl0 * abs(pan_val);
        spl0 = spl0 * (1.0 - abs(pan_val));
    );
);

// --- 5. Galactic Reverb (Absolutely Last in Chain) ---
rvb_wet_amt > 0.0 ? (
    inputSampleL = spl0; inputSampleR = spl1; 
    drySampleL = spl0; drySampleR = spl1;
    
    vibM += (oldfpd*rvb_drift);
    (vibM > (3.141592653589793238*2.0)) ? ( vibM = 0.0; oldfpd = 0.4294967295+(fpdL*0.0000000000618); );
    aML[countM] = inputSampleL * rvb_attenuate; aMR[countM] = inputSampleR * rvb_attenuate;
    countM+=1; (countM < 0 || countM > delayM) ? countM = 0;
    offsetML = (sin(vibM)+1.0)*127; offsetMR = (sin(vibM+(3.141592653589793238/2.0))+1.0)*127;
    workingML = countM + offsetML | 0; workingMR = countM + offsetMR | 0;
    interpolML = (aML[workingML-((workingML > delayM)?delayM+1:0)] * (1-(offsetML-floor(offsetML))));
    interpolML += (aML[workingML+1-((workingML+1 > delayM)?delayM+1:0)] * ((offsetML-floor(offsetML))) );
    interpolMR = (aMR[workingMR-((workingMR > delayM)?delayM+1:0)] * (1-(offsetMR-floor(offsetMR))));
    interpolMR += (aMR[workingMR+1-((workingMR+1 > delayM)?delayM+1:0)] * ((offsetMR-floor(offsetMR))) );
    inputSampleL = interpolML; inputSampleR = interpolMR;

    (abs(iirAL)<1.18*10^-37) ? iirAL = 0.0; iirAL = (iirAL*(1.0-rvb_lowpass))+(inputSampleL*rvb_lowpass); inputSampleL = iirAL;
    (abs(iirAR)<1.18*10^-37) ? iirAR = 0.0; iirAR = (iirAR*(1.0-rvb_lowpass))+(inputSampleR*rvb_lowpass); inputSampleR = iirAR;

    rvb_cycle+=1;
    (rvb_cycle >= rvb_cycleEnd) ? ( 
        aIL[countI] = inputSampleL + (feedbackAR * rvb_regen); aJL[countJ] = inputSampleL + (feedbackBR * rvb_regen); aKL[countK] = inputSampleL + (feedbackCR * rvb_regen); aLL[countL] = inputSampleL + (feedbackDR * rvb_regen);
        aIR[countI] = inputSampleR + (feedbackAL * rvb_regen); aJR[countJ] = inputSampleR + (feedbackBL * rvb_regen); aKR[countK] = inputSampleR + (feedbackCL * rvb_regen); aLR[countL] = inputSampleR + (feedbackDL * rvb_regen);
        countI+=1; (countI < 0 || countI > delayI) ? countI = 0; countJ+=1; (countJ < 0 || countJ > delayJ) ? countJ = 0; countK+=1; (countK < 0 || countK > delayK) ? countK = 0; countL+=1; (countL < 0 || countL > delayL) ? countL = 0;
        
        outIL = aIL[countI-((countI > delayI)?delayI+1:0)]; outJL = aJL[countJ-((countJ > delayJ)?delayJ+1:0)]; outKL = aKL[countK-((countK > delayK)?delayK+1:0)]; outLL = aLL[countL-((countL > delayL)?delayL+1:0)];
        outIR = aIR[countI-((countI > delayI)?delayI+1:0)]; outJR = aJR[countJ-((countJ > delayJ)?delayJ+1:0)]; outKR = aKR[countK-((countK > delayK)?delayK+1:0)]; outLR = aLR[countL-((countL > delayL)?delayL+1:0)];
        
        aAL[countA] = (outIL - (outJL + outKL + outLL)); aBL[countB] = (outJL - (outIL + outKL + outLL)); aCL[countC] = (outKL - (outIL + outJL + outLL)); aDL[countD] = (outLL - (outIL + outJL + outKL));
        aAR[countA] = (outIR - (outJR + outKR + outLR)); aBR[countB] = (outJR - (outIR + outKR + outLR)); aCR[countC] = (outKR - (outIR + outJR + outLR)); aDR[countD] = (outLR - (outIR + outJR + outKR));
        
        countA+=1; (countA < 0 || countA > delayA) ? countA = 0; countB+=1; (countB < 0 || countB > delayB) ? countB = 0; countC+=1; (countC < 0 || countC > delayC) ? countC = 0; countD+=1; (countD < 0 || countD > delayD) ? countD = 0;
        
        outAL = aAL[countA-((countA > delayA)?delayA+1:0)]; outBL = aBL[countB-((countB > delayB)?delayB+1:0)]; outCL = aCL[countC-((countC > delayC)?delayC+1:0)]; outDL = aDL[countD-((countD > delayD)?delayD+1:0)];
        outAR = aAR[countA-((countA > delayA)?delayA+1:0)]; outBR = aBR[countB-((countB > delayB)?delayB+1:0)]; outCR = aCR[countC-((countC > delayC)?delayC+1:0)]; outDR = aDR[countD-((countD > delayD)?delayD+1:0)];
        
        aEL[countE] = (outAL - (outBL + outCL + outDL)); aFL[countF] = (outBL - (outAL + outCL + outDL)); aGL[countG] = (outCL - (outAL + outBL + outDL)); aHL[countH] = (outDL - (outAL + outBL + outCL));
        aER[countE] = (outAR - (outBR + outCR + outDR)); aFR[countF] = (outBR - (outAR + outCR + outDR)); aGR[countG] = (outCR - (outAR + outBR + outDR)); aHR[countH] = (outDR - (outAR + outBR + outCR));
        
        countE+=1; (countE < 0 || countE > delayE) ? countE = 0; countF+=1; (countF < 0 || countF > delayF) ? countF = 0; countG+=1; (countG < 0 || countG > delayG) ? countG = 0; countH+=1; (countH < 0 || countH > delayH) ? countH = 0;
        
        outEL = aEL[countE-((countE > delayE)?delayE+1:0)]; outFL = aFL[countF-((countF > delayF)?delayF+1:0)]; outGL = aGL[countG-((countG > delayG)?delayG+1:0)]; outHL = aHL[countH-((countH > delayH)?delayH+1:0)];
        outER = aER[countE-((countE > delayE)?delayE+1:0)]; outFR = aFR[countF-((countF > delayF)?delayF+1:0)]; outGR = aGR[countG-((countG > delayG)?delayG+1:0)]; outHR = aHR[countH-((countH > delayH)?delayH+1:0)];
        
        feedbackAL = (outEL - (outFL + outGL + outHL)); feedbackBL = (outFL - (outEL + outGL + outHL)); feedbackCL = (outGL - (outEL + outFL + outHL)); feedbackDL = (outHL - (outEL + outFL + outGL));
        feedbackAR = (outER - (outFR + outGR + outHR)); feedbackBR = (outFR - (outER + outGR + outHR)); feedbackCR = (outGR - (outER + outFR + outHR)); feedbackDR = (outHR - (outER + outFR + outGR));
        
        inputSampleL = (outEL + outFL + outGL + outHL)/8.0; inputSampleR = (outER + outFR + outGR + outHR)/8.0;
        
        (rvb_cycleEnd == 4) ? ( lastRefL[0] = lastRefL[4]; lastRefL[2] = (lastRefL[0] + inputSampleL)/2; lastRefL[1] = (lastRefL[0] + lastRefL[2])/2; lastRefL[3] = (lastRefL[2] + inputSampleL)/2; lastRefL[4] = inputSampleL; lastRefR[0] = lastRefR[4]; lastRefR[2] = (lastRefR[0] + inputSampleR)/2; lastRefR[1] = (lastRefR[0] + lastRefR[2])/2; lastRefR[3] = (lastRefR[2] + inputSampleR)/2; lastRefR[4] = inputSampleR; );
        (rvb_cycleEnd == 3) ? ( lastRefL[0] = lastRefL[3]; lastRefL[2] = (lastRefL[0]+lastRefL[0]+inputSampleL)/3; lastRefL[1] = (lastRefL[0]+inputSampleL+inputSampleL)/3; lastRefL[3] = inputSampleL; lastRefR[0] = lastRefR[3]; lastRefR[2] = (lastRefR[0]+lastRefR[0]+inputSampleR)/3; lastRefR[1] = (lastRefR[0]+inputSampleR+inputSampleR)/3; lastRefR[3] = inputSampleR; );
        (rvb_cycleEnd == 2) ? ( lastRefL[0] = lastRefL[2]; lastRefL[1] = (lastRefL[0] + inputSampleL)/2; lastRefL[2] = inputSampleL; lastRefR[0] = lastRefR[2]; lastRefR[1] = (lastRefR[0] + inputSampleR)/2; lastRefR[2] = inputSampleR; );
        (rvb_cycleEnd == 1) ? ( lastRefL[0] = inputSampleL; lastRefR[0] = inputSampleR; );
        rvb_cycle = 0; inputSampleL = lastRefL[rvb_cycle]; inputSampleR = lastRefR[rvb_cycle];
    ) : (
        inputSampleL = lastRefL[rvb_cycle]; inputSampleR = lastRefR[rvb_cycle];
    );

    (abs(iirBL)<1.18*10^-37) ? iirBL = 0.0; iirBL = (iirBL*(1.0-rvb_lowpass))+(inputSampleL*rvb_lowpass); inputSampleL = iirBL;
    (abs(iirBR)<1.18*10^-37) ? iirBR = 0.0; iirBR = (iirBR*(1.0-rvb_lowpass))+(inputSampleR*rvb_lowpass); inputSampleR = iirBR;

    (rvb_wet < 1.0) ? (
        inputSampleL = (inputSampleL * rvb_wet) + (drySampleL * (1.0-rvb_wet));
        inputSampleR = (inputSampleR * rvb_wet) + (drySampleR * (1.0-rvb_wet));
    );

    fpdL = rand(UINT32_MAX); fpdR = rand(UINT32_MAX); 
    spl0 = inputSampleL; spl1 = inputSampleR;
);

@gfx 760 660
// --- SCALING SETUP ---
base_w = 760;
base_h = 660;

scale_x = gfx_w / base_w;
scale_y = gfx_h / base_h;
scale = min(scale_x, scale_y);

off_x = floor((gfx_w - (base_w * scale)) / 2);
off_y = floor((gfx_h - (base_h * scale)) / 2);

// Virtuelle Maus-Koordinaten für das skalierte Fenster berechnen
m_x = (mouse_x - off_x) / scale;
m_y = (mouse_y - off_y) / scale;

// Offscreen-Buffer 1 initialisieren
gfx_dest = 1;
gfx_setimgdim(1, base_w, base_h);

// Buffer Hintergrund bereinigen (entspricht gfx_clear = 0x1A1A1A)
gfx_set(26/255, 26/255, 26/255, 1.0);
gfx_rect(0, 0, base_w, base_h);

// Klickstatus zu Beginn von GFX sauber auswerten
mouse_click = (mouse_cap & 1) && !gui_mouse_last;
!(mouse_cap & 1) ? active_knob = 0;


// --- Hilfsfunktionen für GUI ---
function draw_arc(x, y, r, a_start, a_end) local(a, steps, st_a, px, py, nx, ny) (
    steps = 25; st_a = (a_end - a_start)/steps; a = 0; while(a < steps) ( px = x + cos(a_start + a*st_a)*r; py = y + sin(a_start + a*st_a)*r; nx = x + cos(a_start + (a+1)*st_a)*r; ny = y + sin(a_start + (a+1)*st_a)*r; gfx_line(px, py, nx, ny); a += 1; );
);

function draw_knob(x, y, r, val, minv, maxv, defv, title, unit, idx) local(ang_min, ang_max, fill, a_val, in_bounds, dy, sens, vh, vw, tw, th) (
    ang_min = 0.75 * PI; ang_max = 2.25 * PI; 
    fill = (val - minv) / (maxv - minv);
    fill = min(max(fill, 0), 1); a_val = ang_min + fill * (ang_max - ang_min); 
    in_bounds = (m_x >= x-r && m_x <= x+r && m_y >= y-r && m_y <= y+r);
    
    mouse_click && in_bounds ? ( 
        (time_precise() - last_click_time < 0.25) && (idx == last_click_idx) ? ( 
            idx==11?(slider11=defv;slider_automate(slider11);slider12=0;):
            idx==13?(slider13=defv;slider_automate(slider13);):
            idx==14?(slider14=defv;slider_automate(slider14);):
            idx==15?(slider15=defv;slider_automate(slider15);); 
            val = defv;
        ) : ( active_knob = idx; start_mouse_y = m_y; start_val = val; ); 
        last_click_time = time_precise(); last_click_idx = idx; 
    );
    active_knob == idx && (mouse_cap & 1) ? ( 
        dy = start_mouse_y - m_y; sens = (maxv - minv) * 0.005; 
        val = start_val + dy * sens; 
        val = floor(val * 10 + 0.5) / 10; val = min(max(val, minv), maxv); 
        idx==11?(slider11=floor(val);slider_automate(slider11);slider12=0;):
        idx==13?(slider13=floor(val);slider_automate(slider13);):
        idx==14?(slider14=floor(val);slider_automate(slider14);):
        idx==15?(slider15=val;slider_automate(slider15);); 
    );
    
    gfx_set(0.12, 0.12, 0.12, 1.0); gfx_circle(x, y, r, 1); 
    gfx_set(0.4, 0.4, 0.4, 1.0); gfx_circle(x, y, r, 0); 
    gfx_set(0.2, 0.6, 1.0, 1.0); draw_arc(x, y, r * 0.75, ang_min, a_val); gfx_circle(x + cos(a_val)*(r*0.75), y + sin(a_val)*(r*0.75), 4, 1);
    
    gfx_setfont(4); gfx_set(0.9, 0.9, 0.9, 1.0); gfx_measurestr(title, tw, th); gfx_x = x - tw/2; gfx_y = y + r + 6; gfx_drawstr(title);
    sprintf(#sv, idx==15 ? "%.1f %s" : "%.0f %s", val, unit); 
    gfx_setfont(5); gfx_measurestr(#sv, vw, vh); gfx_x = x - vw/2; gfx_y = y + r + 18; gfx_drawstr(#sv);
);

function draw_fader(x, y, w, h, target_val, current_val, r_c, g_c, b_c, enabled, blocked, fader_id) local(track_w, cap_h, cap_y, i, scale_y, click_zone, val_pct, tw, th, draw_val) (
  !enabled ? ( r_c = 0.3; g_c = 0.3; b_c = 0.3; );

  gfx_set(enabled ? 0.12 : 0.08, enabled ? 0.12 : 0.08, enabled ? 0.12 : 0.08, 1.0);
  gfx_rect(x, y, w, h);
  
  track_w = 6;
  gfx_set(0.05, 0.05, 0.05, 1.0);
  gfx_rect(x + w/2 - track_w/2, y + 20, track_w, h - 40);
  
  cap_h = 30;
  
  // Interaktion per Maus
  click_zone = m_x >= x+20 && m_x <= x+w-20 && m_y >= y+10 && m_y <= y+h-10;
  
  mouse_click && click_zone && enabled && blocked == 0 && active_fader == 0 ? (
      active_fader = fader_id;
  );
  !(mouse_cap & 1) && active_fader == fader_id ? (
      active_fader = 0;
  );
  
  active_fader == fader_id && blocked == 0 ? (
      val_pct = 1.0 - ((m_y - (y+20 + cap_h/2)) / (h - 40 - cap_h));
      val_pct = max(0, min(val_pct, 1.0));
      target_val = val_pct * 127;
      draw_val = target_val;
      cap_y = y + 20 + (h - 40 - cap_h) * (1.0 - target_val/127.0);
  ) : (
      draw_val = current_val;
      cap_y = y + 20 + (h - 40 - cap_h) * (1.0 - current_val/127.0);
  );

  // Gefüllter Balken
  gfx_set(r_c, g_c, b_c, enabled ? 0.6 : 0.15);
  gfx_rect(x + w/2 - track_w/2 - 12, y + 20 + (h-40) * (1.0 - draw_val/127.0), 6, (h-40) * (draw_val/127.0));
  
  i = 0;
  gfx_set(0.3, 0.3, 0.3, 1.0);
  while(i <= 10) (
    scale_y = y + 20 + (h-40) * (1.0 - i/10.0);
    gfx_line(x + w/2 + 15, scale_y, x + w/2 + 25, scale_y);
    i += 1;
  );

  // Fader Cap zeichnen
  gfx_set(0.0, 0.0, 0.0, 0.8);
  gfx_rect(x + 22, cap_y + 2, w - 40, cap_h);
  
  blocked > 0 && enabled ? (
     gfx_set(0.3, 0.15, 0.15, 1.0); // Rötliche Einfärbung bei Sperre
  ) : (
     gfx_set(enabled ? 0.2 : 0.15, enabled ? 0.2 : 0.15, enabled ? 0.2 : 0.15, 1.0);
  );
  gfx_rect(x + 20, cap_y, w - 40, cap_h);
  
  gfx_set(enabled ? 0.9 : 0.4, enabled ? 0.9 : 0.4, enabled ? 0.9 : 0.4, 1.0);
  gfx_rect(x + 20, cap_y + cap_h/2 - 2, w - 40, 4);

  // Feedback falls blockiert
  blocked > 0 && enabled ? (
    gfx_set(0.9, 0.4, 0.4, 0.8);
    gfx_setfont(5);
    blocked == 2 ? strcpy(#blk_str, "AUTO") : strcpy(#blk_str, "MIDI CC");
    gfx_measurestr(#blk_str, tw, th);
    gfx_x = x + w/2 - tw/2; gfx_y = y + h - 18;
    gfx_drawstr(#blk_str);
  );

  target_val;
);

function draw_cc_dropdown(x, y, cc_val, state_var) local(click) (
  gfx_set(0.2, 0.2, 0.2, 1.0);
  gfx_rect(x, y, 45, 20);
  gfx_set(0.4, 0.4, 0.4, 1.0);
  gfx_rect(x, y, 45, 20, 0);
  
  gfx_set(0.9, 0.9, 0.9, 1.0);
  gfx_setfont(4);
  sprintf(#cc_str, "%d", cc_val);
  gfx_x = x + 5; gfx_y = y + 3;
  gfx_drawstr(#cc_str);
  gfx_x = x + 30; gfx_drawstr("v");
  
  click = m_x >= x && m_x <= x + 45 && m_y >= y && m_y <= y + 20 && mouse_click;
  click ? state_var = !state_var;
  state_var;
);

function draw_toggle_button(x, y, w, h, state) local(click, str_w, str_h) (
  gfx_set(0.1, 0.1, 0.1, 1.0);
  gfx_rect(x, y, w, h);
  
  state == 1 ? (
    gfx_set(0.2, 0.3, 0.2, 1.0); gfx_rect(x+1, y+1, w-2, h-2); gfx_set(0.6, 0.8, 0.6, 1.0); strcpy(#st_str, "SOFT");
  ) : state == 2 ? (
    gfx_set(0.2, 0.4, 0.2, 1.0); gfx_rect(x+1, y+1, w-2, h-2); gfx_set(0.8, 1.0, 0.8, 1.0); strcpy(#st_str, "MID");
  ) : state == 3 ? (
    gfx_set(0.3, 0.5, 0.2, 1.0); gfx_rect(x+1, y+1, w-2, h-2); gfx_set(0.9, 1.0, 0.6, 1.0); strcpy(#st_str, "HARD");
  ) : (
    gfx_set(0.15, 0.15, 0.15, 1.0); gfx_rect(x+1, y+1, w-2, h-2); gfx_set(0.5, 0.5, 0.5, 1.0); strcpy(#st_str, "OFF");
  );
  
  gfx_setfont(5);
  gfx_measurestr(#st_str, str_w, str_h);
  gfx_x = x + (w - str_w)/2; gfx_y = y + (h - str_h)/2;
  gfx_drawstr(#st_str);
  
  click = m_x >= x && m_x <= x + w && m_y >= y && m_y <= y + h && mouse_click;
  click ? state = (state + 1) % 4;
  state;
);

function draw_auto_button(x, y, w, h, state) local(click, str_w, str_h) (
  gfx_set(0.1, 0.1, 0.1, 1.0);
  gfx_rect(x, y, w, h);
  
  state == 1 ? (
    gfx_set(0.2, 0.35, 0.4, 1.0); gfx_rect(x+1, y+1, w-2, h-2); gfx_set(0.7, 0.9, 1.0, 1.0); strcpy(#st_str, "SLOW");
  ) : state == 2 ? (
    gfx_set(0.4, 0.2, 0.25, 1.0); gfx_rect(x+1, y+1, w-2, h-2); gfx_set(1.0, 0.7, 0.7, 1.0); strcpy(#st_str, "FAST");
  ) : (
    gfx_set(0.15, 0.15, 0.15, 1.0); gfx_rect(x+1, y+1, w-2, h-2); gfx_set(0.5, 0.5, 0.5, 1.0); strcpy(#st_str, "AUTO");
  );
  
  gfx_setfont(5);
  gfx_measurestr(#st_str, str_w, str_h);
  gfx_x = x + (w - str_w)/2; gfx_y = y + (h - str_h)/2;
  gfx_drawstr(#st_str);
  
  click = m_x >= x && m_x <= x + w && m_y >= y && m_y <= y + h && mouse_click;
  click ? state = (state + 1) % 3;
  state;
);


// --- Block-Status durch eingehende CCs oder AUTO aktualisieren ---
block_dyn = odme_auto_dyn > 0 ? 2 : ((time_precise() - last_cc_dyn < 0.5) ? 1 : 0);
block_expr = odme_auto_expr > 0 ? 2 : ((time_precise() - last_cc_expr < 0.5) ? 1 : 0);
block_vib = odme_auto_vib > 0 ? 2 : ((time_precise() - last_cc_vib < 0.5) ? 1 : 0);


gfx_setfont(1, "Arial", 22, 'b'); 
gfx_setfont(2, "Arial", 14, '');  
gfx_setfont(3, "Arial", 28, 'b'); 
gfx_setfont(4, "Arial", 12, 'b'); 
gfx_setfont(5, "Arial", 11, 'b'); 

// --- Kopfbereich ---
gfx_set(0.9, 0.9, 0.9, 1.0);
gfx_x = 20; gfx_y = 20;
gfx_setfont(1);
gfx_drawstr("MIDI DYNAMICS & EXPRESSION");

gfx_set(0.6, 0.6, 0.6, 1.0);
gfx_x = 20; gfx_y = 48;
gfx_setfont(2);
gfx_drawstr("OVERSTRATUM AUDIO");

// Trennlinie
gfx_set(0.3, 0.3, 0.3, 1.0);
gfx_line(20, 75, base_w - 20, 75);

// --- VSTi Info Text (Unterhalb der Linie, rechtsbündig) ---
gfx_setfont(5);
gfx_set(0.85, 0.65, 0.2, 1.0); 
strcpy(#note_str, "NOTE: This plugin must be placed AFTER the virtual instrument (VSTi).");
gfx_measurestr(#note_str, ntw, nth);
gfx_x = base_w - ntw - 20; gfx_y = 85;
gfx_drawstr(#note_str);

// --- MIDI CC Out Toggle (Oben rechts im Header) ---
btn_w = 120; btn_h = 24;
btn_x = base_w - btn_w - 20; btn_y = 20;

gfx_set(0.15, 0.15, 0.15, 1.0);
gfx_rect(btn_x, btn_y, btn_w, btn_h);

odme_midi_out_mode == 0 ? (
    gfx_set(0.2, 0.35, 0.4, 1.0); gfx_rect(btn_x+1, btn_y+1, btn_w-2, btn_h-2); 
    gfx_set(0.7, 0.9, 1.0, 1.0); strcpy(#mo_str, "CC OUT: THRU");
) : (
    gfx_set(0.4, 0.3, 0.1, 1.0); gfx_rect(btn_x+1, btn_y+1, btn_w-2, btn_h-2); 
    gfx_set(1.0, 0.8, 0.4, 1.0); strcpy(#mo_str, "CC OUT: SLIDERS");
);

gfx_setfont(5);
gfx_measurestr(#mo_str, mo_w, mo_h);
gfx_x = btn_x + (btn_w - mo_w)/2; gfx_y = btn_y + (btn_h - mo_h)/2;
gfx_drawstr(#mo_str);

m_x >= btn_x && m_x <= btn_x + btn_w && m_y >= btn_y && m_y <= btn_y + btn_h && mouse_click ? (
    odme_midi_out_mode = !odme_midi_out_mode;
    slider19 = odme_midi_out_mode;
    sliderchange(19);
);


// --- Dropdown: Preset ---
gfx_set(0.7, 0.7, 0.7, 1.0);
gfx_setfont(4);
gfx_x = 20; gfx_y = 90;
gfx_drawstr("PRESET");

dd_x = 20; dd_y = 105; dd_w = 200; dd_h = 24;
m_x >= dd_x && m_x <= dd_x + dd_w && m_y >= dd_y && m_y <= dd_y + dd_h && mouse_click ? (
  gui_dropdown_open_preset = !gui_dropdown_open_preset;
  gui_dropdown_open_pan = 0; gui_dropdown_open_cc_dyn = 0; gui_dropdown_open_cc_expr = 0; gui_dropdown_open_cc_vib = 0;
) : (
  mouse_click && !(m_x >= dd_x && m_x <= dd_x + dd_w && m_y > dd_y && m_y < dd_y+dd_h*5) ? gui_dropdown_open_preset = 0;
);

gfx_set(0.15, 0.15, 0.15, 1.0);
gfx_rect(dd_x, dd_y, dd_w, dd_h);
gfx_set(0.4, 0.4, 0.4, 1.0);
gfx_rect(dd_x, dd_y, dd_w, dd_h, 0);

slider10 == 0 ? strcpy(#cur_p, "Strings") : slider10 == 1 ? strcpy(#cur_p, "Woodwinds") : slider10 == 2 ? strcpy(#cur_p, "Brass") : strcpy(#cur_p, "Choir");
gfx_set(0.9, 0.9, 0.9, 1.0);
gfx_setfont(2);
gfx_x = dd_x + 10; gfx_y = dd_y + 4;
gfx_drawstr(#cur_p);
gfx_x = dd_x + dd_w - 20; gfx_drawstr("v");


// --- Box Definitionen (Fader-Bereich) ---
box_w = 200;
c1_x = 45;       // Dynamics
c2_x = 280;      // Expression
c3_x = 515;      // Vibrato
fader_y = c1_y = 150;
val_y = fader_y + 290;


// ==========================================
// --- Sektion 1: Dynamics (Links) ---
// ==========================================
gfx_set(0.8, 0.8, 0.8, 1.0); gfx_x = c1_x; gfx_y = c1_y; gfx_setfont(2); gfx_drawstr("CC ");
dd_dyn_x = c1_x + 25; dd_dyn_y = c1_y - 2;
gui_dropdown_open_cc_dyn = draw_cc_dropdown(dd_dyn_x, dd_dyn_y, odme_cc_dyn, gui_dropdown_open_cc_dyn);

new_auto_dyn = draw_auto_button(c1_x + box_w - 85, c1_y - 2, 40, 20, odme_auto_dyn);
new_auto_dyn != odme_auto_dyn ? ( odme_auto_dyn = new_auto_dyn; slider16 = new_auto_dyn; sliderchange(16); auto_dyn_timer = 0; );

new_toggle_dyn = draw_toggle_button(c1_x + box_w - 40, c1_y - 2, 40, 20, odme_toggle_dyn);
new_toggle_dyn != odme_toggle_dyn ? ( odme_toggle_dyn = new_toggle_dyn; slider5 = new_toggle_dyn; sliderchange(5); );

gfx_set(0.8, 0.8, 0.8, 1.0); gfx_setfont(4); gfx_measurestr("DYNAMICS", str_w, str_h);
gfx_x = c1_x + (box_w - str_w)/2; gfx_y = c1_y + 35; gfx_drawstr("DYNAMICS");

new_dyn = draw_fader(c1_x, fader_y + 60, box_w, 220, odme_val_dyn_target, odme_val_dyn_current, 0.4, 0.5, 0.9, odme_toggle_dyn != 0, block_dyn, 1);
new_dyn != odme_val_dyn_target ? ( odme_val_dyn_target = new_dyn; slider1 = new_dyn; slider_automate(slider1); );

gfx_setfont(2);
odme_toggle_dyn != 0 ? gfx_set(0.8, 0.8, 0.8, 1.0) : gfx_set(0.4, 0.4, 0.4, 1.0);
sprintf(#dyn_str, "Val: %d", floor(odme_val_dyn_target));
gfx_measurestr(#dyn_str, str_w, str_h); gfx_x = c1_x + (box_w - str_w)/2; gfx_y = val_y; gfx_drawstr(#dyn_str);


// ==========================================
// --- Sektion 2: Expression (Mitte) ---
// ==========================================
gfx_set(0.8, 0.8, 0.8, 1.0); gfx_x = c2_x; gfx_y = c1_y; gfx_setfont(2); gfx_drawstr("CC ");
dd_expr_x = c2_x + 25; dd_expr_y = c1_y - 2;
gui_dropdown_open_cc_expr = draw_cc_dropdown(dd_expr_x, dd_expr_y, odme_cc_expr, gui_dropdown_open_cc_expr);

new_auto_expr = draw_auto_button(c2_x + box_w - 85, c1_y - 2, 40, 20, odme_auto_expr);
new_auto_expr != odme_auto_expr ? ( odme_auto_expr = new_auto_expr; slider17 = new_auto_expr; sliderchange(17); auto_expr_timer = 0; );

new_toggle_expr = draw_toggle_button(c2_x + box_w - 40, c1_y - 2, 40, 20, odme_toggle_expr);
new_toggle_expr != odme_toggle_expr ? ( odme_toggle_expr = new_toggle_expr; slider6 = new_toggle_expr; sliderchange(6); );

gfx_set(0.8, 0.8, 0.8, 1.0); gfx_setfont(4); gfx_measurestr("EXPRESSION", str_w, str_h);
gfx_x = c2_x + (box_w - str_w)/2; gfx_y = c1_y + 35; gfx_drawstr("EXPRESSION");

new_expr = draw_fader(c2_x, fader_y + 60, box_w, 220, odme_val_expr_target, odme_val_expr_current, 0.9, 0.4, 0.2, odme_toggle_expr != 0, block_expr, 2);
new_expr != odme_val_expr_target ? ( odme_val_expr_target = new_expr; slider2 = new_expr; slider_automate(slider2); );

gfx_setfont(2);
odme_toggle_expr != 0 ? gfx_set(0.8, 0.8, 0.8, 1.0) : gfx_set(0.4, 0.4, 0.4, 1.0);
sprintf(#expr_str, "Val: %d", floor(odme_val_expr_target));
gfx_measurestr(#expr_str, str_w, str_h); gfx_x = c2_x + (box_w - str_w)/2; gfx_y = val_y; gfx_drawstr(#expr_str);

odme_toggle_expr != 0 ? gfx_set(0.9, 0.4, 0.2, 1.0) : gfx_set(0.4, 0.4, 0.4, 1.0);
sprintf(#expr_db_str, "%.1f dB", current_expr_db);
gfx_measurestr(#expr_db_str, str_w, str_h); gfx_x = c2_x + (box_w - str_w)/2; gfx_y = val_y + 20; gfx_drawstr(#expr_db_str);


// ==========================================
// --- Sektion 3: Vibrato (Rechts) ---
// ==========================================
gfx_set(0.8, 0.8, 0.8, 1.0); gfx_x = c3_x; gfx_y = c1_y; gfx_setfont(2); gfx_drawstr("CC ");
dd_vib_x = c3_x + 25; dd_vib_y = c1_y - 2;
gui_dropdown_open_cc_vib = draw_cc_dropdown(dd_vib_x, dd_vib_y, odme_cc_vib, gui_dropdown_open_cc_vib);

new_auto_vib = draw_auto_button(c3_x + box_w - 85, c1_y - 2, 40, 20, odme_auto_vib);
new_auto_vib != odme_auto_vib ? ( odme_auto_vib = new_auto_vib; slider18 = new_auto_vib; sliderchange(18); auto_vib_timer = 0; );

new_toggle_vib = draw_toggle_button(c3_x + box_w - 40, c1_y - 2, 40, 20, odme_toggle_vib);
new_toggle_vib != odme_toggle_vib ? ( odme_toggle_vib = new_toggle_vib; slider8 = new_toggle_vib; sliderchange(8); );

gfx_set(0.8, 0.8, 0.8, 1.0); gfx_setfont(4); gfx_measurestr("VIBRATO", str_w, str_h);
gfx_x = c3_x + (box_w - str_w)/2; gfx_y = c1_y + 35; gfx_drawstr("VIBRATO");

new_vib = draw_fader(c3_x, fader_y + 60, box_w, 220, odme_val_vib_target, odme_val_vib_current, 0.2, 0.8, 0.7, odme_toggle_vib != 0, block_vib, 3);
new_vib != odme_val_vib_target ? ( odme_val_vib_target = new_vib; slider9 = new_vib; slider_automate(slider9); );

gfx_setfont(2);
odme_toggle_vib != 0 ? gfx_set(0.8, 0.8, 0.8, 1.0) : gfx_set(0.4, 0.4, 0.4, 1.0);
sprintf(#vib_str, "Val: %d", floor(odme_val_vib_target));
gfx_measurestr(#vib_str, str_w, str_h); gfx_x = c3_x + (box_w - str_w)/2; gfx_y = val_y; gfx_drawstr(#vib_str);

odme_toggle_vib != 0 ? gfx_set(0.2, 0.8, 0.7, 1.0) : gfx_set(0.4, 0.4, 0.4, 1.0);
sprintf(#vib_ct_str, "%.1f ct", v_target_depth_cents);
gfx_measurestr(#vib_ct_str, str_w, str_h); gfx_x = c3_x + (box_w - str_w)/2; gfx_y = val_y + 20; gfx_drawstr(#vib_ct_str);


// ==========================================
// --- Footer (Panning, Reverb, Output) ---
// ==========================================
footer_y = 510;
footer_h = 115;

// Footer Hintergründe
gfx_set(0.08, 0.08, 0.08, 1.0);
gfx_rect(c1_x, footer_y, box_w, footer_h);
gfx_rect(c2_x, footer_y, box_w, footer_h);
gfx_rect(c3_x, footer_y, box_w, footer_h);

gfx_set(0.2, 0.2, 0.2, 1.0);
gfx_rect(c1_x, footer_y, box_w, footer_h, 0);
gfx_rect(c2_x, footer_y, box_w, footer_h, 0);
gfx_rect(c3_x, footer_y, box_w, footer_h, 0);

// Sektions-Titel
gfx_setfont(4); gfx_set(0.5, 0.5, 0.5, 1.0);
gfx_measurestr("PANNING", tw, th); gfx_x = c1_x + (box_w - tw)/2; gfx_y = footer_y + 15; gfx_drawstr("PANNING");
gfx_measurestr("REVERB", tw, th);  gfx_x = c2_x + (box_w - tw)/2; gfx_y = footer_y + 15; gfx_drawstr("REVERB");
gfx_measurestr("OUTPUT", tw, th);  gfx_x = c3_x + (box_w - tw)/2; gfx_y = footer_y + 15; gfx_drawstr("OUTPUT");

// 1. Panning Block (Links)
draw_knob(c1_x + 40, footer_y + 58, 18, slider11, -100, 100, 0, "PAN", "%", 11);

pan_dd_x = c1_x + 85; pan_dd_y = footer_y + 46; pan_dd_w = 100; pan_dd_h = 24;
m_x >= pan_dd_x && m_x <= pan_dd_x + pan_dd_w && m_y >= pan_dd_y && m_y <= pan_dd_y + pan_dd_h && mouse_click ? (
  gui_dropdown_open_pan = !gui_dropdown_open_pan;
  gui_dropdown_open_preset = 0; gui_dropdown_open_cc_dyn = 0; gui_dropdown_open_cc_expr = 0; gui_dropdown_open_cc_vib = 0;
);
gfx_set(0.15, 0.15, 0.15, 1.0); gfx_rect(pan_dd_x, pan_dd_y, pan_dd_w, pan_dd_h);
gfx_set(0.4, 0.4, 0.4, 1.0); gfx_rect(pan_dd_x, pan_dd_y, pan_dd_w, pan_dd_h, 0);

slider12 == 0 ? strcpy(#p_str, "Manual") : slider12 == 1 ? strcpy(#p_str, "Violin") : slider12 == 2 ? strcpy(#p_str, "Viola") : slider12 == 3 ? strcpy(#p_str, "Cello") : slider12 == 4 ? strcpy(#p_str, "D. Bass") : slider12 == 5 ? strcpy(#p_str, "Harp") : slider12 == 6 ? strcpy(#p_str, "Trumpet") : slider12 == 7 ? strcpy(#p_str, "Trombone") : slider12 == 8 ? strcpy(#p_str, "Horn") : slider12 == 9 ? strcpy(#p_str, "Tuba") : slider12 == 10 ? strcpy(#p_str, "Flute") : slider12 == 11 ? strcpy(#p_str, "Oboe") : slider12 == 12 ? strcpy(#p_str, "Clarinet") : slider12 == 13 ? strcpy(#p_str, "Bassoon") : slider12 == 14 ? strcpy(#p_str, "Piccolo") : slider12 == 15 ? strcpy(#p_str, "Eng. Horn") : slider12 == 16 ? strcpy(#p_str, "C.Bassoon") : strcpy(#p_str, "Perc.");
gfx_set(0.9, 0.9, 0.9, 1.0); gfx_setfont(4); gfx_x = pan_dd_x + 8; gfx_y = pan_dd_y + 5; gfx_drawstr(#p_str); gfx_x = pan_dd_x + pan_dd_w - 15; gfx_drawstr("v");

// 2. Reverb Block (Mitte)
draw_knob(c2_x + 60, footer_y + 58, 18, slider13, 0, 100, 0, "AMT", "%", 13);
draw_knob(c2_x + 140, footer_y + 58, 18, slider14, 0, 100, 30, "SIZE", "%", 14);

// 3. Output Block (Rechts)
draw_knob(c3_x + 100, footer_y + 58, 22, slider15, -12, 12, 0, "GAIN", "dB", 15);


// ==========================================
// --- Tooltips Logic ---
// ==========================================
strcpy(#tt, "");

m_y >= footer_y && m_y <= footer_y + footer_h ? (
  m_x >= c1_x && m_x <= c1_x + box_w ? (
    abs(m_x - (c1_x + 40)) < 25 ? strcpy(#tt, "Controls true stereo panning of the processed audio signal.") :
    m_x >= pan_dd_x && m_x <= pan_dd_x + pan_dd_w ? strcpy(#tt, "Selects an auto-panning preset based on orchestral seating.") : strcpy(#tt, "");
  ) :
  m_x >= c2_x && m_x <= c2_x + box_w ? (
    abs(m_x - (c2_x + 60)) < 25 ? strcpy(#tt, "Sets the amount of reverb applied. Airwindows Galactic Algorithm.") :
    abs(m_x - (c2_x + 140)) < 25 ? strcpy(#tt, "Controls the size and decay time of the reverb tail.") : strcpy(#tt, "");
  ) :
  m_x >= c3_x && m_x <= c3_x + box_w ? (
    abs(m_x - (c3_x + 100)) < 30 ? strcpy(#tt, "Adjusts the final output gain from -12dB to +12dB.") : strcpy(#tt, "");
  ) : strcpy(#tt, "");
);

m_x >= dd_x && m_x <= dd_x + dd_w && m_y >= dd_y && m_y <= dd_y + dd_h ? strcpy(#tt, "Selects the instrument group to optimize dynamic EQ curves.");
m_x >= btn_x && m_x <= btn_x + btn_w && m_y >= btn_y && m_y <= btn_y + btn_h ? strcpy(#tt, "Toggles MIDI CC output. THRU: Passes original CCs. SLIDERS: Outputs CC values generated by the sliders/AUTO movements.");

// Box Hover Detection (Faders)
m_y >= c1_y - 5 && m_y <= fader_y + 300 ? (
  m_x >= c1_x && m_x <= c1_x + box_w ? (
    m_y < c1_y + 25 ? (
      m_x < c1_x + 80 ? strcpy(#tt, "Selects the MIDI CC mapped to control the Dynamics section.") :
      m_x >= c1_x + box_w - 90 && m_x < c1_x + box_w - 45 ? strcpy(#tt, "Toggles Auto-Movement: OFF, SLOW (4-8s), FAST (2-4s). Ignores CC.") :
      m_x >= c1_x + box_w - 40 ? strcpy(#tt, "Toggles Dynamics intensity: Off, Soft (50%), Mid (100%), Hard (150%).") : 
      strcpy(#tt, "");
    ) : strcpy(#tt, "Current Dynamics value. Controls a multiband EQ and subtle volume scaling.");
  ) :
  m_x >= c2_x && m_x <= c2_x + box_w ? (
    m_y < c1_y + 25 ? (
      m_x < c2_x + 80 ? strcpy(#tt, "Selects the MIDI CC mapped to control the Expression section.") :
      m_x >= c2_x + box_w - 90 && m_x < c2_x + box_w - 45 ? strcpy(#tt, "Toggles Auto-Movement: OFF, SLOW (4-8s), FAST (2-4s). Ignores CC.") :
      m_x >= c2_x + box_w - 40 ? strcpy(#tt, "Toggles Expression intensity: Off, Soft (50%), Mid (100%), Hard (150%).") : 
      strcpy(#tt, "");
    ) : strcpy(#tt, "Current Expression value. Controls transparent volume attenuation.");
  ) :
  m_x >= c3_x && m_x <= c3_x + box_w ? (
    m_y < c1_y + 25 ? (
      m_x < c3_x + 80 ? strcpy(#tt, "Selects the MIDI CC mapped to control the Vibrato section.") :
      m_x >= c3_x + box_w - 90 && m_x < c3_x + box_w - 45 ? strcpy(#tt, "Toggles Auto-Movement: OFF, SLOW (2-4s), FAST (1-2s). Ignores CC.") :
      m_x >= c3_x + box_w - 40 ? strcpy(#tt, "Toggles Vibrato intensity: Off, Soft (50%), Mid (100%), Hard (150%).") : 
      strcpy(#tt, "");
    ) : strcpy(#tt, "Current Vibrato value. Modulates pitch using a sample-accurate delay line. Uses Geraint Luff Vibrato Algorithm.");
  );
);

// Draw Tooltip
strlen(#tt) > 0 ? (
  gfx_setfont(5);
  gfx_set(0.6, 0.8, 1.0, 1.0);
  gfx_x = 20;
  gfx_y = base_h - 22;
  gfx_drawstr("Info: ");
  gfx_set(0.8, 0.8, 0.8, 1.0);
  gfx_drawstr(#tt);
);


// ==========================================
// --- Overlays (Dropdown Listen) ---
// ==========================================
gfx_setfont(5, "Arial", 11, ''); 

function draw_scroll_list(list_x, list_y, cc_var, slider_idx, state_var, scroll_var) local(list_h, i, item_y) (
  state_var ? (
    list_h = 150;
    gfx_set(0.15, 0.15, 0.15, 1.0); gfx_rect(list_x, list_y + 20, 45, list_h);
    gfx_set(0.4, 0.4, 0.4, 1.0); gfx_rect(list_x, list_y + 20, 45, list_h, 0);
    
    m_x >= list_x && m_x <= list_x + 45 && m_y >= list_y + 20 && m_y <= list_y + 20 + list_h ? (
      scroll_var -= mouse_wheel / 10;
      mouse_wheel = 0;
    );
    scroll_var = max(0, min(scroll_var, 127*18 - list_h + 10));

    i = 0;
    while(i <= 127) (
      item_y = list_y + 25 + i * 18 - scroll_var;
      item_y >= list_y + 20 && item_y <= list_y + 20 + list_h - 18 ? (
        m_x >= list_x && m_x <= list_x + 45 && m_y >= item_y && m_y < item_y + 18 ? (
          gfx_set(0.3, 0.3, 0.3, 1.0); gfx_rect(list_x+1, item_y, 43, 18);
          mouse_click ? (
            slider_idx == 3 ? (slider3 = i; sliderchange(3); odme_cc_dyn = i; gui_dropdown_open_cc_dyn = 0;);
            slider_idx == 4 ? (slider4 = i; sliderchange(4); odme_cc_expr = i; gui_dropdown_open_cc_expr = 0;);
            slider_idx == 7 ? (slider7 = i; sliderchange(7); odme_cc_vib = i; gui_dropdown_open_cc_vib = 0;);
          );
        );
        gfx_set(0.9, 0.9, 0.9, 1.0);
        sprintf(#opt_str, "%d", i); gfx_x = list_x + 8; gfx_y = item_y + 2; gfx_drawstr(#opt_str);
      );
      i += 1;
    );
  );
  scroll_var; 
);

// Panning Dropdown Overlay (Klappt nach oben auf)
gui_dropdown_open_pan ? (
  list_h_pan = 18 * 18; 
  p_list_y = pan_dd_y - list_h_pan; 
  
  gfx_set(0.15, 0.15, 0.15, 1.0); gfx_rect(pan_dd_x, p_list_y, pan_dd_w, list_h_pan);
  gfx_set(0.4, 0.4, 0.4, 1.0); gfx_rect(pan_dd_x, p_list_y, pan_dd_w, list_h_pan, 0);
  
  m_x >= pan_dd_x && m_x <= pan_dd_x + pan_dd_w && m_y >= p_list_y && m_y <= p_list_y + list_h_pan ? (
    hover_idx_p = floor((m_y - p_list_y) / 18);
    gfx_set(0.3, 0.3, 0.3, 1.0); gfx_rect(pan_dd_x+1, p_list_y + hover_idx_p*18 + 1, pan_dd_w-2, 16);
    mouse_click ? (
        slider12 = hover_idx_p; sliderchange(12);
        gui_dropdown_open_pan = 0; 
    );
  );
  
  gfx_set(0.9, 0.9, 0.9, 1.0); gfx_setfont(4);
  py_base = p_list_y + 3;
  
  gfx_x = pan_dd_x + 8; gfx_y = py_base; gfx_drawstr("Manual");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 18; gfx_drawstr("Violin");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 36; gfx_drawstr("Viola");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 54; gfx_drawstr("Cello");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 72; gfx_drawstr("Double Bass");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 90; gfx_drawstr("Harp");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 108; gfx_drawstr("Trumpet");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 126; gfx_drawstr("Trombone");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 144; gfx_drawstr("French Horn");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 162; gfx_drawstr("Tuba");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 180; gfx_drawstr("Flute");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 198; gfx_drawstr("Oboe");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 216; gfx_drawstr("Clarinet");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 234; gfx_drawstr("Bassoon");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 252; gfx_drawstr("Piccolo");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 270; gfx_drawstr("English Horn");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 288; gfx_drawstr("Contrabassoon");
  gfx_x = pan_dd_x + 8; gfx_y = py_base + 306; gfx_drawstr("Percussion");
);

// Click-Out Logik für Panning Menü
mouse_click && gui_dropdown_open_pan && !(m_x >= pan_dd_x && m_x <= pan_dd_x + pan_dd_w && m_y > pan_dd_y - (18*18) && m_y < pan_dd_y + pan_dd_h) ? gui_dropdown_open_pan = 0;

// Listen Zeichnen (Reihenfolge: Dynamics, Expression, Vibrato)
scroll_y_dyn = draw_scroll_list(dd_dyn_x, dd_dyn_y, odme_cc_dyn, 3, gui_dropdown_open_cc_dyn, scroll_y_dyn);
mouse_click && gui_dropdown_open_cc_dyn && !(m_x >= dd_dyn_x && m_x <= dd_dyn_x+45 && m_y >= dd_dyn_y && m_y <= dd_dyn_y+170) ? gui_dropdown_open_cc_dyn = 0;

scroll_y_expr = draw_scroll_list(dd_expr_x, dd_expr_y, odme_cc_expr, 4, gui_dropdown_open_cc_expr, scroll_y_expr);
mouse_click && gui_dropdown_open_cc_expr && !(m_x >= dd_expr_x && m_x <= dd_expr_x+45 && m_y >= dd_expr_y && m_y <= dd_expr_y+170) ? gui_dropdown_open_cc_expr = 0;

scroll_y_vib = draw_scroll_list(dd_vib_x, dd_vib_y, odme_cc_vib, 7, gui_dropdown_open_cc_vib, scroll_y_vib);
mouse_click && gui_dropdown_open_cc_vib && !(m_x >= dd_vib_x && m_x <= dd_vib_x+45 && m_y >= dd_vib_y && m_y <= dd_vib_y+170) ? gui_dropdown_open_cc_vib = 0;

// Preset Overlay (mit 4 Optionen)
gui_dropdown_open_preset ? (
  list_h = 4 * 24;
  gfx_set(0.15, 0.15, 0.15, 1.0); gfx_rect(dd_x, dd_y + dd_h, dd_w, list_h);
  gfx_set(0.4, 0.4, 0.4, 1.0); gfx_rect(dd_x, dd_y + dd_h, dd_w, list_h, 0);
  
  m_x >= dd_x && m_x <= dd_x + dd_w && m_y >= dd_y + dd_h && m_y <= dd_y + dd_h + list_h ? (
    hover_idx = floor((m_y - (dd_y + dd_h)) / 24);
    gfx_set(0.3, 0.3, 0.3, 1.0); gfx_rect(dd_x+1, dd_y + dd_h + hover_idx*24 + 1, dd_w-2, 22);
    mouse_click ? (
        slider10 = hover_idx; sliderchange(10);
        gui_dropdown_open_preset = 0; 
    );
  );
  
  gfx_set(0.9, 0.9, 0.9, 1.0); gfx_setfont(2);
  gfx_x = dd_x + 10; gfx_y = dd_y + dd_h + 4; gfx_drawstr("Strings");
  gfx_x = dd_x + 10; gfx_y = dd_y + dd_h + 28; gfx_drawstr("Woodwinds");
  gfx_x = dd_x + 10; gfx_y = dd_y + dd_h + 52; gfx_drawstr("Brass");
  gfx_x = dd_x + 10; gfx_y = dd_y + dd_h + 76; gfx_drawstr("Choir");
);

// Gui Mouse Zustand am ganz ans Ende setzen
gui_mouse_last = mouse_cap & 1;


// ==========================================
// --- DRAW SCALED BUFFER TO SCREEN ---
// ==========================================
gfx_dest = -1; // Ziel wieder auf den Hauptbildschirm setzen

// Hintergrund schwarz ausfüllen (für "Letterboxing" Randbereiche)
gfx_set(0.05, 0.05, 0.05, 1.0); 
gfx_rect(0, 0, gfx_w, gfx_h);

// Puffer in voller Deckkraft auf das Fenster stempeln
gfx_set(1, 1, 1, 1);
gfx_x = off_x; 
gfx_y = off_y;
gfx_blit(1, 1, 0, 0, 0, base_w, base_h, off_x, off_y, base_w * scale, base_h * scale);