☰ Dup Goto 📝

MidiCcSender_001

DAW/reaper/jsfx/gemini jsfx 08-15 15:07:53
To Pop
249 lines, 1019 words, 6198 chars Saturday 2026-08-15 15:07:53

This is a simple way of sending mod wheel, expression and breath to plugins that receive them, but don't have visible controls.

desc: Simple 3-Bar MIDI CC Drag Pad (with Send Buttons)
author: Assistant
version: 3.1

in_pin:none
out_pin:none

@init
// ==========================================
// CONFIGURATION: Edit CC Numbers & Channel Here
// ==========================================
MIDI_CHAN = 1;  // MIDI Channel (1 - 16)

CC_1 = 1;       // Modulation Wheel (CC 1)
CC_2 = 11;      // Expression (CC 11)
CC_3 = 2;       // Breath Controller (CC 2)

// Initial Values (0 - 127)
val_1 = 0;
val_2 = 0;
val_3 = 0;

// ==========================================
// THREAD-SAFE RING BUFFER (UI -> Audio Thread)
// ==========================================
BUF_WRITE = 1000;
BUF_READ  = 1001;
BUF_START = 1010;
BUF_SIZE  = 256;

BUF_WRITE[0] = 0;
BUF_READ[0]  = 0;

function queue_cc(num, val) (
  val = max(0, min(127, val));

  w_idx = BUF_WRITE[0];
  slot_ptr = BUF_START + ((w_idx % BUF_SIZE) * 2);

  slot_ptr[0] = num;
  slot_ptr[1] = val;

  BUF_WRITE[0] = w_idx + 1;
);

function queue_send_all() (
  queue_cc(CC_1, val_1);
  queue_cc(CC_2, val_2);
  queue_cc(CC_3, val_3);
);

// Debug Display State
debug_msg_status = "Status: Initialized. Drag a bar or click Send.";
debug_msg_cc = 0;
debug_msg_val = 0;
debug_flash = 0;

@block
// 1. Pass through external incoming MIDI
while (midirecv(offset, msg1, msg23) ? (
  midisend(offset, msg1, msg23);
));

// 2. Drain queued UI events inside the audio block thread
r_idx = BUF_READ[0];
w_idx = BUF_WRITE[0];

while (r_idx < w_idx) (
  slot_ptr = BUF_START + ((r_idx % BUF_SIZE) * 2);
  cc_num = slot_ptr[0];
  cc_val = slot_ptr[1];

  midisend(0, 176 + (MIDI_CHAN - 1), cc_num, cc_val);

  debug_msg_status = "SENT MIDI CC";
  debug_msg_cc = cc_num;
  debug_msg_val = cc_val;
  debug_flash = 1.0;

  r_idx += 1;
);

BUF_READ[0] = r_idx;

@gfx 400 370

// Dark Background
gfx_r = 0.10; gfx_g = 0.10; gfx_b = 0.12; gfx_a = 1.0;
gfx_rect(0, 0, gfx_w, gfx_h);

// Mouse Click Triggering (Single Click Detection)
is_click = (mouse_cap & 1) && !(last_mouse_cap & 1);

// ==========================================
// 1. DEBUG CONSOLE & "SEND ALL" HEADER
// ==========================================
header_h = 34;
send_all_w = 85;

// Debug Box Bounds
dbg_w = gfx_w - send_all_w - 30;

gfx_r = 0.05 + (debug_flash * 0.1); 
gfx_g = 0.08 + (debug_flash * 0.3); 
gfx_b = 0.10 + (debug_flash * 0.2);
gfx_rect(10, 10, dbg_w, header_h, 1);

gfx_r = 0.3; gfx_g = 0.5; gfx_b = 0.6;
gfx_rect(10, 10, dbg_w, header_h, 0);

// Print Debug Output Text
gfx_r = 0.4 + (debug_flash * 0.6); 
gfx_g = 0.9; 
gfx_b = 0.4 + (debug_flash * 0.6);
gfx_x = 18; gfx_y = 21;
gfx_drawstr(debug_msg_status);

debug_msg_cc > 0 ? (
  gfx_drawstr(" -> CC: ");
  gfx_drawnumber(debug_msg_cc, 0);
  gfx_drawstr(" | Val: ");
  gfx_drawnumber(debug_msg_val, 0);
);

debug_flash = max(0, debug_flash - 0.05);

// "SEND ALL" Button
send_all_x = gfx_w - send_all_w - 10;
send_all_y = 10;

hover_send_all = (mouse_x >= send_all_x && mouse_x <= send_all_x + send_all_w && mouse_y >= send_all_y && mouse_y <= send_all_y + header_h);

hover_send_all ? (
  gfx_r = 0.25; gfx_g = 0.55; gfx_b = 0.85;
  is_click ? queue_send_all();
) : (
  gfx_r = 0.18; gfx_g = 0.28; gfx_b = 0.38;
);

gfx_rect(send_all_x, send_all_y, send_all_w, header_h, 1);
gfx_r = 0.3; gfx_g = 0.5; gfx_b = 0.6;
gfx_rect(send_all_x, send_all_y, send_all_w, header_h, 0);

gfx_r = 0.95; gfx_g = 0.95; gfx_b = 0.95;
gfx_x = send_all_x + 12; gfx_y = send_all_y + 11;
gfx_drawstr("SEND ALL");


// ==========================================
// 2. VERTICAL BARS LAYOUT WITH SEND BUTTONS
// ==========================================
btn_bar_h = 24;
margin_top = 90;
margin_bottom = 35;
pad = 15;
num_bars = 3;
bar_w = (gfx_w - (pad * (num_bars + 1))) / num_bars;
bar_h = gfx_h - margin_top - margin_bottom;

i = 0;
loop(num_bars,

  cc_num = (i == 0 ? CC_1  : i == 1 ? CC_2  : CC_3);
  cc_val = (i == 0 ? val_1 : i == 1 ? val_2 : val_3);

  bx = pad + i * (bar_w + pad);
  by = margin_top;

  // --- Draw Per-Bar "SEND" Button ---
  btn_y = margin_top - btn_bar_h - 10;

  hover_bar_btn = (mouse_x >= bx && mouse_x <= bx + bar_w && mouse_y >= btn_y && mouse_y <= btn_y + btn_bar_h);

  hover_bar_btn ? (
    gfx_r = 0.35; gfx_g = 0.55; gfx_b = 0.45;
    is_click ? queue_cc(cc_num, cc_val);
  ) : (
    gfx_r = 0.18; gfx_g = 0.26; gfx_b = 0.22;
  );

  gfx_rect(bx, btn_y, bar_w, btn_bar_h, 1);
  gfx_r = 0.3; gfx_g = 0.45; gfx_b = 0.35;
  gfx_rect(bx, btn_y, bar_w, btn_bar_h, 0);

  gfx_r = 0.9; gfx_g = 0.9; gfx_b = 0.9;
  gfx_x = bx + (bar_w / 2) - 16;
  gfx_y = btn_y + 6;
  gfx_drawstr("SEND");


  // --- Mouse Drag Logic for Bar Tracks ---
  (mouse_cap & 1) && (mouse_x >= bx && mouse_x <= bx + bar_w && mouse_y >= by && mouse_y <= by + bar_h) ? (
    norm_val = 1 - ((mouse_y - by) / bar_h);
    new_val = max(0, min(127, floor(norm_val * 127 + 0.5)));

    // Push event to queue when value changes during drag
    new_val != cc_val ? (
      i == 0 ? val_1 = new_val :
      i == 1 ? val_2 = new_val :
               val_3 = new_val;

      queue_cc(cc_num, new_val);
    );
  );

  // Read current value
  cc_val = (i == 0 ? val_1 : i == 1 ? val_2 : val_3);

  // Bar Background Track
  gfx_r = 0.04; gfx_g = 0.04; gfx_b = 0.05;
  gfx_rect(bx, by, bar_w, bar_h, 1);

  // Filled Value Bar
  fill_h = (cc_val / 127) * bar_h;
  fill_y = by + (bar_h - fill_h);

  i == 0 ? ( gfx_r = 0.2; gfx_g = 0.6; gfx_b = 0.9; ) :
           ( gfx_r = 0.25; gfx_g = 0.75; gfx_b = 0.45; );

  gfx_rect(bx, fill_y, bar_w, fill_h, 1);

  // Box Border
  gfx_r = 0.25; gfx_g = 0.25; gfx_b = 0.3;
  gfx_rect(bx, by, bar_w, bar_h, 0);

  // Value Number Inside Bar
  gfx_r = 1.0; gfx_g = 1.0; gfx_b = 1.0;
  gfx_x = bx + (bar_w / 2) - 8;
  gfx_y = by + bar_h - 22;
  gfx_drawnumber(cc_val, 0);

  // Label Below Bar
  gfx_r = 0.75; gfx_g = 0.75; gfx_b = 0.75;
  gfx_x = bx + (bar_w / 2) - 24;
  gfx_y = by + bar_h + 8;

  i == 0 ? gfx_drawstr("Mod (1)") :
  i == 1 ? gfx_drawstr("Exp (11)") :
           gfx_drawstr("Breath (2)");

  i += 1;
);

last_mouse_cap = mouse_cap;