Script for Multi-Slot MIDI Routing & Scaling Scriptlet (Aftertouch + CC Mapper)

Hello everyone

Lately I have been using this scriptlet made with some gpt help. Maybe it is helpful for you. This scriptlet provides MIDI routing and transformation designed for expressive performance control. I use it because I do the complete MIDI routing in the wiring view.

Multi-Source Routing

  • Converts Channel Aftertouch into up to three independent CC outputs

  • Provides three additional CC-to-CC mapping slots

  • Each slot processes one input source and can generate multiple output target

Multi-Target Output Per Slot

Each slot can transmit to:

  • Up to three independent target CC numbers

  • Each target can use a different MIDI channel (1–16)

Per-Target Scaling

Each target supports individual:

  • Minimum output value

  • Maximum output value

This allows different modulation ranges for each controlled parameter.

Response Curve Processing

Each slot includes an adjustable curve that smoothly blends between:

  • Logarithmic response

  • Linear response

  • Exponential response

This allows precise control over controller sensitivity.

Slot Control Options

Every slot includes:

  • Enable switch

    Activates or bypasses the slot

  • Invert switch

    Reverses controller polarity (0 ↔ 127)

  • Pass-Through switch

    Allows the original MIDI message to pass alongside the mapped output

All targets within a slot share the same input and curve processing, but each output can have unique scaling and channel routing.

If there is any issue let me know.

code:

// ============================================================

// ================= AFTERTOUCH SLOT ===========================

// ============================================================

var AT_Enable : Parameter 0..1 = 1

var AT_Invert : Parameter 0..1 = 0

var AT_PassThrough : Parameter 0..1 = 0

var AT_Target_CC1 : Parameter 0..127 = 1

var AT_Target_CC2 : Parameter 0..127 = 11

var AT_Target_CC3 : Parameter 0..127 = 74

var AT_Ch1 : Parameter 1..16 = 1

var AT_Ch2 : Parameter 1..16 = 1

var AT_Ch3 : Parameter 1..16 = 1

var AT_Curve : Continuous Parameter = 0.5

var AT_Min1 : Parameter 0..127 = 63

var AT_Max1 : Parameter 0..127 = 127

var AT_Min2 : Parameter 0..127 = 63

var AT_Max2 : Parameter 0..127 = 127

var AT_Min3 : Parameter 0..127 = 63

var AT_Max3 : Parameter 0..127 = 127

// ============================================================

// ================= SLOT 2 ====================================

// ============================================================

var CC2_Enable : Parameter 0..1 = 1

var CC2_Invert : Parameter 0..1 = 0

var CC2_PassThrough : Parameter 0..1 = 0

var CC2_Source : Parameter 0..127 = 2

var CC2_Target_CC1 : Parameter 0..127 = 11

var CC2_Target_CC2 : Parameter 0..127 = 74

var CC2_Target_CC3 : Parameter 0..127 = 71

var CC2_Ch1 : Parameter 1..16 = 1

var CC2_Ch2 : Parameter 1..16 = 1

var CC2_Ch3 : Parameter 1..16 = 1

var CC2_Curve : Continuous Parameter = 0.5

var CC2_Min1 : Parameter 0..127 = 0

var CC2_Max1 : Parameter 0..127 = 127

var CC2_Min2 : Parameter 0..127 = 0

var CC2_Max2 : Parameter 0..127 = 127

var CC2_Min3 : Parameter 0..127 = 0

var CC2_Max3 : Parameter 0..127 = 127

// ============================================================

// ================= SLOT 3 ====================================

// ============================================================

var CC3_Enable : Parameter 0..1 = 1

var CC3_Invert : Parameter 0..1 = 0

var CC3_PassThrough : Parameter 0..1 = 0

var CC3_Source : Parameter 0..127 = 12

var CC3_Target_CC1 : Parameter 0..127 = 74

var CC3_Target_CC2 : Parameter 0..127 = 1

var CC3_Target_CC3 : Parameter 0..127 = 91

var CC3_Ch1 : Parameter 1..16 = 1

var CC3_Ch2 : Parameter 1..16 = 1

var CC3_Ch3 : Parameter 1..16 = 1

var CC3_Curve : Continuous Parameter = 0.5

var CC3_Min1 : Parameter 0..127 = 0

var CC3_Max1 : Parameter 0..127 = 127

var CC3_Min2 : Parameter 0..127 = 0

var CC3_Max2 : Parameter 0..127 = 127

var CC3_Min3 : Parameter 0..127 = 0

var CC3_Max3 : Parameter 0..127 = 127

// ============================================================

// ================= SLOT 4 ====================================

// ============================================================

var CC4_Enable : Parameter 0..1 = 1

var CC4_Invert : Parameter 0..1 = 0

var CC4_PassThrough : Parameter 0..1 = 0

var CC4_Source : Parameter 0..127 = 13

var CC4_Target_CC1 : Parameter 0..127 = 71

var CC4_Target_CC2 : Parameter 0..127 = 10

var CC4_Target_CC3 : Parameter 0..127 = 93

var CC4_Ch1 : Parameter 1..16 = 1

var CC4_Ch2 : Parameter 1..16 = 1

var CC4_Ch3 : Parameter 1..16 = 1

var CC4_Curve : Continuous Parameter = 0.5

var CC4_Min1 : Parameter 0..127 = 0

var CC4_Max1 : Parameter 0..127 = 127

var CC4_Min2 : Parameter 0..127 = 0

var CC4_Max2 : Parameter 0..127 = 127

var CC4_Min3 : Parameter 0..127 = 0

var CC4_Max3 : Parameter 0..127 = 127

// ============================================================

// ================= VARIABLES =================================

// ============================================================

var ccMsg : ControlChangeMessage

// ============================================================

// ================= CURVE =====================================

// ============================================================

Function ApplyCurve(v : Double, curveAmount : Double) Returns Double

var resultVal : Double

var strength : Double

strength = (curveAmount - 0.5) * 2.0

if strength > 0.0 then

resultVal = v * (1.0 - strength) + (v * v) * strength

elsif strength < 0.0 then

resultVal = v * (1.0 + strength) + (1.0 - (1.0 - v) * (1.0 - v)) * (-strength)

else

resultVal = v

end

result = resultVal

End

// ============================================================

// ================= UNIVERSAL TARGET SENDER ===================

// ============================================================

Function SendTargets3(value : Integer,

invert : Integer,

t1 : Integer, ch1 : Integer, min1 : Integer, max1 : Integer,

t2 : Integer, ch2 : Integer, min2 : Integer, max2 : Integer,

t3 : Integer, ch3 : Integer, min3 : Integer, max3 : Integer,

curve : Double)

var norm : Double

var midiValue : Integer

var scaled : Integer

if invert == 1 then

value = 127 - value

end

norm = value / 127.0

norm = ApplyCurve(norm, curve)

midiValue = Round(norm * 127.0)

scaled = ScaleInt(midiValue, 0, 127, min1, max1)

ccMsg = MakeControlChangeMessageEx(t1, scaled, ch1)

SendNow(ccMsg)

scaled = ScaleInt(midiValue, 0, 127, min2, max2)

ccMsg = MakeControlChangeMessageEx(t2, scaled, ch2)

SendNow(ccMsg)

scaled = ScaleInt(midiValue, 0, 127, min3, max3)

ccMsg = MakeControlChangeMessageEx(t3, scaled, ch3)

SendNow(ccMsg)

End

// ============================================================

// ================= AFTERTOUCH ================================

// ============================================================

On AfterTouchEvent(m : AfterTouchMessage)

var value : Integer

value = GetAfterTouchValue(m)

if AT_Enable == 1 then

SendTargets3(value, AT_Invert,

AT_Target_CC1, AT_Ch1, AT_Min1, AT_Max1,

AT_Target_CC2, AT_Ch2, AT_Min2, AT_Max2,

AT_Target_CC3, AT_Ch3, AT_Min3, AT_Max3,

AT_Curve)

if AT_PassThrough == 1 then

SendNow(m)

end

else

SendNow(m)

end

End

// ============================================================

// ================= CC EVENTS =================================

// ============================================================

On ControlChangeEvent(c : ControlChangeMessage)

var value : Integer

var number : Integer

var handled : Boolean = false

value = GetCCValue(c)

number = GetCCNumber(c)

if number == CC2_Source then

if CC2_Enable == 1 then

SendTargets3(value, CC2_Invert,

CC2_Target_CC1, CC2_Ch1, CC2_Min1, CC2_Max1,

CC2_Target_CC2, CC2_Ch2, CC2_Min2, CC2_Max2,

CC2_Target_CC3, CC2_Ch3, CC2_Min3, CC2_Max3,

CC2_Curve)

handled = true

if CC2_PassThrough == 1 then

SendNow(c)

end

else

SendNow(c)

end

end

if number == CC3_Source then

if CC3_Enable == 1 then

SendTargets3(value, CC3_Invert,

CC3_Target_CC1, CC3_Ch1, CC3_Min1, CC3_Max1,

CC3_Target_CC2, CC3_Ch2, CC3_Min2, CC3_Max2,

CC3_Target_CC3, CC3_Ch3, CC3_Min3, CC3_Max3,

CC3_Curve)

handled = true

if CC3_PassThrough == 1 then

SendNow(c)

end

else

SendNow(c)

end

end

if number == CC4_Source then

if CC4_Enable == 1 then

SendTargets3(value, CC4_Invert,

CC4_Target_CC1, CC4_Ch1, CC4_Min1, CC4_Max1,

CC4_Target_CC2, CC4_Ch2, CC4_Min2, CC4_Max2,

CC4_Target_CC3, CC4_Ch3, CC4_Min3, CC4_Max3,

CC4_Curve)

handled = true

if CC4_PassThrough == 1 then

SendNow(c)

end

else

SendNow(c)

end

end

if handled == false then

SendNow(c)

end

End

This script is a simplified version of a two-input MIDI processor and dynamic expression engine.

It processes:

  • Input A β†’ typically CC11 (Expression)

  • Input B β†’ either CC (e.g. CC1) or Channel Pressure (Aftertouch)

And produces three outputs:

  • OutA β†’ processed version of Input A

  • OutB β†’ processed version of Input B

  • OutC β†’ a dynamic β€œmaster” controller combining A and B

All outputs support:

  • Min / Max scaling

  • Adjustable curve (log ↔ linear ↔ exponential)

  • Send-on-change optimization

  • Proper filtering of original incoming CCs

Code:

Var

// ================= INPUT =================

Input_Filter_Enable   : Parameter 0..1   = 0;
Input_Filter_Channel  : Parameter 1..16  = 1;

CC_A_In               : Parameter 0..127 = 11;
CC_B_In               : Parameter 0..127 = 1;
CC_B_Use_AT           : Parameter 0..1   = 1;

// ================= OUTPUT A =================

OutA_Enable            : Parameter 0..1   = 1;
OutA_CC                : Parameter 0..127 = 11;
OutA_Midi_Channel      : Parameter 1..16  = 1;
OutA_Min               : Parameter 0..127 = 100;
OutA_Max               : Parameter 0..127 = 127;
OutA_Curve             : Parameter 0..127 = 64;

// ================= OUTPUT B =================

OutB_Enable            : Parameter 0..1   = 1;
OutB_CC                : Parameter 0..127 = 21;
OutB_Midi_Channel      : Parameter 1..16  = 1;
OutB_Min               : Parameter 0..127 = 0;
OutB_Max               : Parameter 0..127 = 127;
OutB_Curve             : Parameter 0..127 = 64;

// ================= OUTPUT C =================

OutC_Enable            : Parameter 0..1   = 1;
OutC_Mode_CC_OR_AT     : Parameter 0..1   = 0;

OutC_CC                : Parameter 0..127 = 1;
OutC_CC_Midi_Channel   : Parameter 1..16  = 1;
OutC_AT_Midi_Channel   : Parameter 1..16  = 1;

OutC_Min               : Parameter 0..127 = 64;
OutC_Max               : Parameter 0..127 = 127;
OutC_Curve             : Parameter 0..127 = 64;

// ================= STATE =================

a_in_val  : Integer = 0;
b_in_val  : Integer = 0;

last_outA : Integer = -1;
last_outB : Integer = -1;
last_outC : Integer = -1;

// ================= MAIN =================

On MidiEvent(m : MidiMessage)

var statusByte : Integer
var status     : Integer
var ch0        : Integer
var ch         : Integer
var num        : Integer
var val        : Integer

var dirtyA     : Integer
var dirtyB     : Integer

var x          : Integer
var spanInt    : Integer
var zoneMin    : Integer
var zoneMax    : Integer

var curve      : Integer
var amt        : Integer
var quad       : Integer

var a_new      : Integer
var b_new      : Integer
var c_new      : Integer

dirtyA = 0
dirtyB = 0

statusByte = GetByte(m,0)
status = statusByte and 0xF0
ch0 = statusByte and 0x0F
ch = ch0 + 1

// ================= INPUT =================

if status == 176 then
num = GetByte(m,1)
val = GetByte(m,2)

  if Input_Filter_Enable == 0 or ch == Input_Filter_Channel then

     if num == CC_A_In then
        a_in_val = val
        dirtyA = 1
     end

     if CC_B_Use_AT == 0 then
        if num == CC_B_In then
           b_in_val = val
           dirtyB = 1
        end
     end

  end


end

if status == 208 then
val = GetByte(m,1)


  if CC_B_Use_AT == 1 then
     if Input_Filter_Enable == 0 or ch == Input_Filter_Channel then
        b_in_val = val
        dirtyB = 1
     end
  end


end

// ================= OUTPUT A =================

if dirtyA == 1 then


  x = a_in_val

  curve = OutA_Curve

  if curve > 64 then
     amt = (curve - 64) * 2
     if amt > 127 then amt = 127 end
     quad = x * x / 127
     x = (x * (127 - amt) + quad * amt) / 127
  elsif curve < 64 then
     amt = (64 - curve) * 2
     if amt > 127 then amt = 127 end
     quad = (127 - x)
     quad = quad * quad / 127
     quad = 127 - quad
     x = (x * (127 - amt) + quad * amt) / 127
  end

  spanInt = Round(OutA_Max) - Round(OutA_Min)
  if spanInt < 0 then spanInt = 0 end

  a_new = x * spanInt / 127 + Round(OutA_Min)

  if a_new > 127 then a_new = 127 end
  if a_new < 0 then a_new = 0 end

  if OutA_Enable == 1 then
     if a_new <> last_outA then
        last_outA = a_new
        SendNow(MakeControlChangeMessageEx(OutA_CC, a_new, OutA_Midi_Channel))
     end
  end


end

// ================= OUTPUT B =================

if dirtyB == 1 then


  x = b_in_val

  curve = OutB_Curve

  if curve > 64 then
     amt = (curve - 64) * 2
     if amt > 127 then amt = 127 end
     quad = x * x / 127
     x = (x * (127 - amt) + quad * amt) / 127
  elsif curve < 64 then
     amt = (64 - curve) * 2
     if amt > 127 then amt = 127 end
     quad = (127 - x)
     quad = quad * quad / 127
     quad = 127 - quad
     x = (x * (127 - amt) + quad * amt) / 127
  end

  spanInt = Round(OutB_Max) - Round(OutB_Min)
  if spanInt < 0 then spanInt = 0 end

  b_new = x * spanInt / 127 + Round(OutB_Min)

  if b_new > 127 then b_new = 127 end
  if b_new < 0 then b_new = 0 end

  if OutB_Enable == 1 then
     if b_new <> last_outB then
        last_outB = b_new
        SendNow(MakeControlChangeMessageEx(OutB_CC, b_new, OutB_Midi_Channel))
     end
  end


end

// ================= MASTER C =================

if dirtyA == 1 or dirtyB == 1 then


  zoneMin = a_in_val * 96 / 127
  zoneMax = 64 + (a_in_val * 63 / 127)

  if zoneMin < 0 then zoneMin = 0 end
  if zoneMax > 127 then zoneMax = 127 end
  if zoneMax <= zoneMin then zoneMax = zoneMin + 1 end

  x = zoneMin + (b_in_val * (zoneMax - zoneMin) / 127)

  curve = OutC_Curve

  if curve > 64 then
     amt = (curve - 64) * 2
     if amt > 127 then amt = 127 end
     quad = x * x / 127
     x = (x * (127 - amt) + quad * amt) / 127
  elsif curve < 64 then
     amt = (64 - curve) * 2
     if amt > 127 then amt = 127 end
     quad = (127 - x)
     quad = quad * quad / 127
     quad = 127 - quad
     x = (x * (127 - amt) + quad * amt) / 127
  end

  spanInt = Round(OutC_Max) - Round(OutC_Min)
  if spanInt < 0 then spanInt = 0 end

  c_new = x * spanInt / 127 + Round(OutC_Min)

  if c_new > 127 then c_new = 127 end
  if c_new < 0 then c_new = 0 end

  if OutC_Enable == 1 then
     if c_new <> last_outC then
        last_outC = c_new

        if OutC_Mode_CC_OR_AT == 0 then
           SendNow(MakeControlChangeMessageEx(OutC_CC, c_new, OutC_CC_Midi_Channel))
        else
           SendNow(MakeMidiMessage3(0xD0 + (OutC_AT_Midi_Channel-1), c_new, 0))
        end
     end
  end
end
if status == 176 then
   if num <> CC_A_In and num <> CC_B_In then
      SendNow(m)
   end
else
   SendNow(m)
end

End

(moderation notice: re-formatted for better readability / Mod: Schamass)

2 Likes