User:Eufalesio/How to build edos in DAWs: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Eufalesio (talk | contribs)
Birth
 
Eufalesio (talk | contribs)
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:


This guide explains the workflow I use to build any edo. I could theoretically build any edo well up until the forty-thousands, but for practicality's sake (plus stylistic choice and personal taste) I really only use [[12edo|12]], [[72edo|72]], [[94edo|94]], [[217edo|217]], [[270edo|270]], [[311edo|311]].  
This guide explains the workflow I use to build and compose with any [[EDO|edo]]. It does '''not''' discuss stylistic choices in composing with these edos. It does however have my recommendations based on my own experience, like in the "good edos" section.  


'''This guide is for Logic Pro''', for Apple, the DAW I use to compose my music. I have no guarantee that my workflow will be compatible with other DAWs, but it can help from a theoretical standpoint if you choose to build edos in other DAWs using the methods I expose in this article.
'''The content here applies for Logic Pro''', for Apple, the DAW I use to compose my music.
 
'''I have no guarantee that my workflow will be compatible with other DAWs''', but it can help from a theoretical standpoint if you choose to build edos in other DAWs using the methods I expose in this article.


== Edos 1,2,3,4,6 ==
== Edos 1,2,3,4,6 ==
Just use 12edo.
These are subsets of 12edo. 'Nuff said.


== Edos 5,7,8,9,10,11 ==
== Edos 5,7,8,9,10,11 ==
Line 11: Line 13:


* Advantages: Trivially easy to build and use, accessible xenharmony (not microtonality).
* Advantages: Trivially easy to build and use, accessible xenharmony (not microtonality).
* Disdvantages: Non-transposing friendly, very coarse edos, bad intonation.
* Disdvantages: Non-transposing friendly, very coarse edos.


== Edos 12n ==
== Edos 12n ==
Line 21: Line 23:
* Disadvantages: Only possible with 12n edos. Edos above 72 or 84 may become unwieldy or RAM-intensive, due to the abundance of many instrument instances.  
* Disadvantages: Only possible with 12n edos. Edos above 72 or 84 may become unwieldy or RAM-intensive, due to the abundance of many instrument instances.  


Good edos: [[24edo|24]], [[72edo|72]], [[84edo|84]]
Good edos: [[24edo|24]], [[72edo|72]], [[84edo|84]] - Reasoning: Efficient approximations of JI / 24edo is an entry-level microtonal edo, great [[2.3.11 subgroup|ila]] tuning.


== Edos >12 ==
== Edos >12 ==
Line 33: Line 35:
* Disadvantages: Much harder to compose using this method because the keyboard has the notes way out of place. Programming notes in, is a more viable workflow, albeit less ''in the feels''. Limited octave range, but can be expanded with MIDI channels.
* Disadvantages: Much harder to compose using this method because the keyboard has the notes way out of place. Programming notes in, is a more viable workflow, albeit less ''in the feels''. Limited octave range, but can be expanded with MIDI channels.


Good edos: [[17edo|17]], [[19edo|19]], [[22edo|22]], [[31edo|31]], [[34edo|34]] - Reasoning: Good fifths and/or Good 5-limit approximations
Good edos: [[17edo|17]], [[19edo|19]], [[22edo|22]], [[31edo|31]], [[34edo|34]] - Reasoning: Good fifths and/or Good 5-limit approximations. Of all these ones, I'd say 31 is the best one around here, as it provides a very intuitive framework for 13-limit xenharmony. 19 is also another entry-level edo, the next best edo at 5-limit other than 12edo. Both of these edos are meantone, which aligns quite well with Western music theory.


=== Preset-scale deviations - MIDI channels only ===
=== Preset-scale deviations - MIDI channels only ===
Tune your 12 notes in your octave to a fixed scale, I recommend 12edo or [[5L 7s]] 6|5 (or [[7L 5s]] 6|5 if you're using meantone), as it is very easy to extend to any edo, specially as they get large. Then, make separate instances of each track that differ by edosteps, with a whitelisted midi channel. Of course, there are only 16 MIDI channels, so this method cannot be extended indefinitely. This method is robust, as it can allow you to reach any edo up to sharp-16 (A1≥16). This includes every edo up to 199 except for 148, 160, 162, 165, 167, 169, 172, 174, 176, 177, 179, 181, 183, 184, 186, 188, 189, 190, 191, 193, 195, 196, 197, 198.
Tune your 12 notes in your octave to a fixed scale supported by your edo. I recommend 12edo or [[5L 7s]] 6|5 (or [[7L 5s]] 6|5 if you're using meantone), as it is very easy to extend to any edo, specially as they get large. Then, make separate instances of each track that differ by edosteps, with a whitelisted midi channel. Of course, there are only 16 MIDI channels, so this method cannot be extended indefinitely. This method is robust, as it can allow you to reach any edo up to sharp-16 (augmented unison ≤ 16 edosteps).  
 
This includes every edo up to 199 '''except''' for 148, 160, 162, 165, 167, 169, 172, 174, 176, 177, 179, 181, 183, 184, 186, 188, 189, 190, 191, 193, 195, 196, 197, 198. It also allows you to build 12n edos up to 192edo, by using 12edo as the fixed scale.
 
* Advantages: Allows use of very fine-grained edos, 10.<u>3</u> octave range, practical to compose in.
* Disadvantages: Harder to set up, less transposing-friendly because of wolf intervals. Not usable live.


* Advantages: Able to build very fine-grained edos, able to compose using 12 notes, then able to retune them.
Good edos: [[41edo|41]], [[53edo|53]], [[94edo|94]], [[130edo|130]], [[159edo|159]] - Reasoning: Astounding fifths and/or Great 13-limit approximations.  
* Disadvantages: Harder to set up, less transposing-friendly because of wolf intervals.


Good edos: [[41edo|41]], [[53edo|53]], [[94edo|94]], [[130edo|130]], [[159edo|159]] - Reasoning: Astounding fifths and/or Great 13-limit approximations
94edo is my favorite here, because it combines two top-tier edos into one great jack-of-all trades 23-odd-limit tuning. 159edo, [[Dawson Berry|Aura]]'s favorite, is also a top-tier edo, making an ''astonishingly good'' ila tuning, and/or an overall a very good jack-of-all trades tuning, both of them with a graspable gamut.


=== Preset-scale deviations - MIDI and articulations ===
=== Preset-scale deviations - MIDI and articulations ===
Line 49: Line 55:


* Advantages: Near total freedom of pitch and tuning options, allows use of extremely fine edos, 10.<u>3</u> octave range, practical to compose in.
* Advantages: Near total freedom of pitch and tuning options, allows use of extremely fine edos, 10.<u>3</u> octave range, practical to compose in.
* Disadvantages: (Probably) Non-transposing friendly. Requires MPE or else instrument must be monophonic. Hard to setup, very technical, prone to error.
* Disadvantages: (Probably) Non-transposing friendly. Requires MPE or else instrument must be monophonic. Hard to setup, very technical, prone to error, cannot use articulations for their intended purpose.
 
Good edos: [[217edo|217]], [[270edo|270]], [[311edo|311]] - Reasoning: High-prime-limit support / Unbeatably accurate yazalathana. 
 
Excessive edos: Anything beyond 311. Highly recommend you use superfine 12n edos for these ones. I don't go here. 
 
==== About edo usage and JI approximations (my experience) ====
[[User:Eufalesio/My temperament|Ultimate]] is my temperament of temperaments, which supports {{Edos|12e, 41, 53, 94, 217, 270, 311}}, and of those my best choices are undoubtedly 94edo and 270edo. They have respectively, very good and extremely good 13-limit, and are even, so I can split the [[Pythagorean comma|poma]] in halves for easier navigation. 
 
I aim for 13-limit JI, and if you also aim for that, then these tunings are the best for you. Check the Ultimate article for more info. Note, I don't care about essential tempering, VAOs, MOSes beyond the theoretical, or structural exploiting. 
 
If you still want to go even finer than that... then try 612edo and 2460; S-tier 12n edos, so you get transposing-friendly hyperfine microtonality. But, since their step sizes are smaller than the melodic JND, you can't tell notes edosteps apart. You are likely wasting your time here, however.
 
If you STILL want to go finer... you are surely wasting your time. Just use [[JI]] scales and forget about edos and temperaments altogether. Remember when I said you could ''theoretically'' compose in these insane edos? That's because PB resolution is going to be your main bottleneck. The other one is your paranoia. 
 
===== Vibe-coded Javascript script (V7) =====
<syntaxhighlight lang="javascript" line="1">// Generalized EDO Retuner with TRUE POLYPHONIC Micropitch Deviation (CC27 per-note)
// FIXED: simultaneous chords with master-channel CC27 now work correctly


Good edos: [[217edo|217]], [[270edo|270]], [[311edo|311]], [[612edo|612]], [[1600edo|1600]], [[2460edo|2460]] - Reasoning: Very high [[consistency limit]] / ''Astonishing'' 2.3.5.7.11.13.19 approximations (270edo). When you begin to use edos beyond 159, the absolute error starts to be come negligible with good edos, and consistency is what makes a fine-grained edo great. 270 is special because it approximates the 2.3.5.7.11.13.19 astonishingly well for its size. 1600edo has absolute error comparable to 270, and almost 43-odd-limit consistency. 612edo and 2460 are S tier 12n edos, so you get transposing-friendly hyperfine microtonality.
var NeedsTimingInfo = true;


===== Vibe-coded Javascript script =====
// Output pool
<syntaxhighlight lang="javascript" line="1">// Generalized EDO via MIDI Channel base steps + CC micro offsets + per-note channel allocator (MPE-style)
var memberStart = 1, memberEnd = 16;
// ─────────────────────────────────────────────────────────────────────────────────────────────────────
 
// Base step: (incomingChannel - (n+1)) * MidiChDeviationSplit
// Scale-bend
// Micro step (only if Split > 1):
var scaleBendEnabled = true;
//   Split=2: CC values 63,64,65 → -1,0,+1
var mpeMasterCCFeedsMembers = true;   // set false if your controller sends CC27 on member channels
//  Split=3: CC values 62..66    → -2,-1,0,+1,+2
// Final step = base + micro; cents = step * (1200/EDO)
//
// Each NoteOn gets a dedicated member channel (2..16 by default). We send PB there, then delay NoteOn
// slightly so the bend lands first. NoteOff releases the channel and re-centers PB when idle.


// ===== Defaults / State =====
// ===== Defaults / State =====
var n = 8;                       // zero step at channel (n+1); handled IN channels 2..(2n-2)
var EDO = 94;
var maxDeviation = 200;           // synth PB ±range in cents (±100=±1 st, ±200=±2 st)
var rootKeyPC = 0;
var ccNumber = 27;               // articulation CC number
var refMidiNote = 69;
var EDO = 311;                   // generalized EDO
var refHz = 440.0;
var MidiChDeviationSplit = 2;     // 1=+1/chan, 2=+2/chan, 3=+3/chan (others = base-only)
var ch1Mode = 0;
var n = 8;
var maxDeviation = 4800;
var ccNumber = 27;
var MidiChDeviationSplit = 1;
var noteDelayMs = 2;
var offsetScopeGlobal = false;  // Per-Channel recommended for independent per-note micro
var swallowCC = true;
var debug = false;
 
// Sustain and tail protection
var sustainDown = false;
var releaseHoldMs = 300;


var noteDelayMs = 2;              // ensure PB lands before NoteOn
// Panic
var memberStart = 2, memberEnd = 16; // allocator pool for per-note channels
var panicCCNumber = 83;
var offsetScopeGlobal = true;     // Global-Last vs Per-Channel CC latching
var panicThreshold = 63;
var swallowCC = true;            // keep articulation CC from reaching the synth
var debug = false;


// Derived
// Derived
Line 83: Line 111:
// Micro offset memory
// Micro offset memory
var globalMicro = 0;
var globalMicro = 0;
var microByInCh = Array(17).fill(0);   // 1..16
var masterMicro = 0;                    // last CC27 received on MPE master (ch1)
var currentMicroPerInCh = Array(17).fill(0);
var microTouchedByInCh = Array(17).fill(false);
 
// Output-channel state
var channelBusy = Array(17).fill(0);
var centered = Array(17).fill(true);
var releaseUntil = Array(17).fill(0);
var lastPB = Array(17).fill(999999);


// Allocator state
// Note tracking
var channelBusy = Array(17).fill(0);  // active note count per member channel
var noteMap = {};
var centered = Array(17).fill(true);  // whether PB is centered on that channel
var noteMap = {};                     // key(inCh,pitch,id) -> { ch, id }
var uniq = 1;
var uniq = 1;
// Allocator
var pitchKeyToCh = {};
var chToPitchKey = Array(17).fill("");
var pitchKeyActive = {};
var rrNext = 1;
// Scale-bend state
var gSteps = 0;
var scaleBend = Array(12).fill(0.0);
var masterOffsetCents = 0.0;
var FIFTH_OFFSETS = [0, -5, +2, -3, +4, -1, +6, +1, -4, +3, -2, +5];
// Safe Trace
function T(s){
  if (!debug) return;
  s = String(s);
  if (s.length > 120) s = s.slice(0, 120);
  Trace(s);
}


// ===== UI =====
// ===== UI =====
var PluginParameters = [
var PluginParameters = [
  // EDO numeric entry, now capped at 65535
   { name:"EDO", type:"lin", minValue:1, maxValue:65535, numberOfSteps:65534, defaultValue:311 },
   { name:"EDO", type:"lin", minValue:1, maxValue:65535, numberOfSteps:65534, defaultValue:311 },
 
  { name:"Root key (PC)", type:"menu", valueStrings:["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"], defaultValue:0 },
   { name:"Channel at which step=0 (n)", type:"lin", minValue:3, maxValue:9, numberOfSteps:6, defaultValue:8 },
  { name:"Reference MIDI note", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:69 },
  { name:"Reference Hz", type:"float", minValue:1, maxValue:20000, numberOfSteps:19999, defaultValue:440 },
  { name:"Ch 1 treatment", type:"menu", valueStrings:["Normal","ScaleBendOnly","Bypass (12edo)","Nullify"], defaultValue:0 },
   { name:"Channel at which step=0 (n)", type:"lin", minValue:1, maxValue:16, numberOfSteps:15, defaultValue:8 },
   { name:"PB range (± cents)", type:"float", minValue:1, maxValue:4800, numberOfSteps:4799, defaultValue:200 },
   { name:"PB range (± cents)", type:"float", minValue:1, maxValue:4800, numberOfSteps:4799, defaultValue:200 },
   { name:"Articulation CC#", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:27 },
   { name:"Articulation CC#", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:27 },
  // MidiChDeviationSplit slider 0..63 (1/2/3 keep special CC semantics; others = base-only)
   { name:"MidiChDeviationSplit", type:"lin", minValue:0, maxValue:63, numberOfSteps:63, defaultValue:2 },
   { name:"MidiChDeviationSplit", type:"lin", minValue:0, maxValue:63, numberOfSteps:63, defaultValue:2 },
 
  { name:"Release Hold (ms)", type:"lin", minValue:0, maxValue:20000, numberOfSteps:20000, defaultValue:300 },
   { name:"Offset scope", type:"menu", valueStrings:["Per-Channel","Global-Last"], defaultValue:1 },
  { name:"Panic CC#", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:83 },
  { name:"Panic thresh (>)", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:63 },
  { name:"PANIC (kill all)", type:"menu", valueStrings:["Off","TRIGGER"], defaultValue:0 },
   { name:"Offset scope", type:"menu", valueStrings:["Per-Channel","Global-Last"], defaultValue:0 },
   { name:"Swallow CC to synth", type:"menu", valueStrings:["No","Yes"], defaultValue:1 },
   { name:"Swallow CC to synth", type:"menu", valueStrings:["No","Yes"], defaultValue:1 },
  { name:"Member Ch Start", type:"lin", minValue:2, maxValue:16, numberOfSteps:14, defaultValue:2 },
  { name:"Member Ch End", type:"lin", minValue:2, maxValue:16, numberOfSteps:14, defaultValue:16 },
   { name:"NoteOn Delay (ms)", type:"lin", minValue:0, maxValue:10, numberOfSteps:10, defaultValue:2 },
   { name:"NoteOn Delay (ms)", type:"lin", minValue:0, maxValue:10, numberOfSteps:10, defaultValue:2 },
   { name:"Debug Trace", type:"menu", valueStrings:["Off","On"], defaultValue:0 }
   { name:"Debug Trace", type:"menu", valueStrings:["Off","On"], defaultValue:0 },
  { name:"Output ch start", type:"lin", minValue:1, maxValue:16, numberOfSteps:15, defaultValue:1 },
  { name:"Output ch end", type:"lin", minValue:1, maxValue:16, numberOfSteps:15, defaultValue:16 },
  { name:"Scale-bend or 12EDO", type:"menu", valueStrings:["On","Off"], defaultValue:0 }
];
];


function ParameterChanged(p,v){
function ParameterChanged(p,v){
   if (p===0){
   if (p===0){ EDO = Math.max(1, Math.min(65535, Math.floor(v))); STEP_CENTS = 1200.0 / EDO; rebuildScaleAndRetuneAssignedChannels(); }
    // EDO clamp 1..65535; keep as integer
  else if (p===1){ rootKeyPC = (v|0) % 12; rebuildScaleAndRetuneAssignedChannels(); }
    var EDO_MAX = 65535;
  else if (p===2){ refMidiNote = Math.max(0, Math.min(127, v|0)); rebuildScaleAndRetuneAssignedChannels(); }
    var iv = Math.floor(v);
  else if (p===3){ refHz = Math.max(1.0, v); rebuildScaleAndRetuneAssignedChannels(); }
    if (iv < 1) iv = 1;
   else if (p===4){ ch1Mode = (v|0) & 3; }
    if (iv > EDO_MAX) iv = EDO_MAX;
   else if (p===5){ n = Math.max(1, Math.min(16, v|0)); }
    EDO = iv;
   else if (p===6){ maxDeviation = Math.max(1, v); rebuildScaleAndRetuneAssignedChannels(); }
    STEP_CENTS = 1200.0 / EDO;
   else if (p===7){ ccNumber = Math.floor(v); }
   }
   else if (p===8){ MidiChDeviationSplit = Math.max(0, Math.min(63, Math.floor(v))); }
   else if (p===1){ n = Math.floor(v); }
  else if (p===9){ releaseHoldMs = Math.max(0, Math.min(20000, Math.floor(v))); }
   else if (p===2){ maxDeviation = Math.max(1, v); }
  else if (p===10){ panicCCNumber = Math.max(0, Math.min(127, v|0)); }
   else if (p===3){ ccNumber = Math.floor(v); }
  else if (p===11){ panicThreshold = Math.max(0, Math.min(127, v|0)); }
   else if (p===4){
  else if (p===12){ if ((v|0)===1) panicAll(); }
    // Split 0..63 integer; only 1/2/3 have micro-CC semantics; others = base-only (micro=0)
   else if (p===13){ offsetScopeGlobal = (v|0)===1; }
    var sv = Math.floor(v);
   else if (p===14){ swallowCC = (v|0)===1; }
    if (sv < 0) sv = 0;
   else if (p===15){ noteDelayMs = Math.max(0, Math.min(10, v|0)); }
    if (sv > 63) sv = 63;
   else if (p===16){ debug = (v|0)===1; }
    MidiChDeviationSplit = sv;
   else if (p===17){ setOutputPool(v|0, memberEnd); allocatorReset(); rebuildScaleAndRetuneAssignedChannels(); }
  }
   else if (p===18){ setOutputPool(memberStart, v|0); allocatorReset(); rebuildScaleAndRetuneAssignedChannels(); }
   else if (p===5){ offsetScopeGlobal = (v|0)===1; }
   else if (p===19){ scaleBendEnabled = ((v|0) === 0); rebuildScaleAndRetuneAssignedChannels(); }
   else if (p===6){ swallowCC = (v|0)===1; }
   else if (p===7){ memberStart = Math.max(2, Math.min(16, v|0)); }
   else if (p===8){ memberEnd   = Math.max(2, Math.min(16, v|0)); }
   else if (p===9){ noteDelayMs = v|0; }
   else if (p===10){ debug = (v|0)===1; }
  if (memberEnd < memberStart) memberEnd = memberStart;
}
}


// ===== Helpers =====
// ===== Helpers =====
function safeCh(ch){ ch=(ch|0)||1; if (ch<1) ch=1; if (ch>16) ch=16; return ch; }
function safeCh(ch){ ch=(ch|0)||1; if (ch<1) ch=1; if (ch>16) ch=16; return ch; }
 
function mod(a,m){ var r=a%m; if (r<0) r+=m; return r; }
function baseStepFromInChannel(ch){
function log2(x){ return Math.log(x) / Math.log(2.0); }
   // Zero at (n+1); channels 2..(2n-2) are the intended control lanes
function setOutputPool(a, b){
   return (ch - (n + 1)) * MidiChDeviationSplit;
  a = safeCh(a); b = safeCh(b); if (a > b){ var t=a; a=b; b=t; }
   memberStart = a; memberEnd = b;
  if (rrNext < memberStart || rrNext > memberEnd) rrNext = memberStart;
  T("Pool " + memberStart + ".." + memberEnd);
}
function makePitchKey(inCh, pitch, step){ return String(inCh|0) + ":" + String(pitch|0) + ":" + String(step|0); }
function GetNowMs(){
   if (typeof GetTimingInfo !== "function") return 0;
  var t = GetTimingInfo();
  if (!t) return 0;
  var tempo = (t.tempo && t.tempo > 0) ? t.tempo : 120.0;
  var beat = t.blockStartBeat !== undefined ? t.blockStartBeat : (t.playhead !== undefined ? t.playhead : 0);
  return (beat * 60000.0) / tempo;
}
}
 
function baseStepFromInChannel(ch){ return (ch - n) * MidiChDeviationSplit; }
// CC micro decoding per split setting (unchanged semantics)
function microFromCC(val){
function microFromCC(val){
  // Split==1: CC ignored (base-only), as designed
   if (MidiChDeviationSplit === 1) return 0;
   if (MidiChDeviationSplit === 1) return 0;
  // General rule for all other Split values:
  // interpret CC around 64 as a signed offset in EDO steps.
  // CC 0..127 → micro −64..+63 (clamped).
   var m = (val|0) - 64;
   var m = (val|0) - 64;
   if (m < -64) m = -64;
   if (m < -64) m = -64; if (m > 63) m = 63;
  if (m > 63) m = 63;
   return m;
   return m;
}
}
function centsToPB(c){
function centsToPB(c){
   var v = (c / maxDeviation) * 8191.0;
   var v = (c / maxDeviation) * 8191.0;
   if (v > 8191) v = 8191;
   if (v > 8191) v = 8191; if (v < -8192) v = -8192;
  if (v < -8192) v = -8192;
   return Math.round(v);
   return Math.round(v);
}
function sendPB(ch, value){
  ch = safeCh(ch);
  var pb = new PitchBend(); pb.channel = ch; pb.value = value; pb.send();
  centered[ch] = (value === 0); lastPB[ch] = value;
}
function sendCC(ch, num, val){
  var cc = new ControlChange(); cc.channel = safeCh(ch); cc.number = num|0; cc.value = val|0; cc.send();
}
}


function sendPB(ch, value){
// ===== Scale bend =====
   var pb = new PitchBend;
function rebuildScale(){
   pb.channel = safeCh(ch);
  STEP_CENTS = 1200.0 / EDO;
   pb.value = value;
   var gReal = EDO * (log2(3.0) - 1.0);
   pb.send();
   gSteps = Math.round(gReal);
   centered[ch] = (value === 0);
  var rawBend = Array(12).fill(0.0);
   for (var i=0; i<12; i++){
    var st = mod(FIFTH_OFFSETS[i] * gSteps, EDO);
    rawBend[i] = st * STEP_CENTS - (i * 100.0);
  }
  var stdHz = 440.0 * Math.pow(2.0, (refMidiNote - 69) / 12.0);
   masterOffsetCents = 1200.0 * log2(refHz / stdHz);
  var refDeg = mod((refMidiNote|0) - rootKeyPC, 12);
  var anchor = rawBend[refDeg];
  for (var k=0; k<12; k++) scaleBend[k] = rawBend[k] - anchor;
  T("Scale g="+gSteps+" EDO="+EDO);
}
function rebuildScaleAndRetuneAssignedChannels(){
  rebuildScale();
   for (var ch=memberStart; ch<=memberEnd; ch++){
    var pk = chToPitchKey[ch];
    if (pk && pk.length){
      var p = parsePitchKey(pk);  // note: parsePitchKey is defined below
      var deg = degreeForPitch(p.pitch);
      var sb = scaleBendEnabled ? (scaleBend[deg] || 0.0) : 0.0;
      var cents = masterOffsetCents + sb + p.step * STEP_CENTS;
      var pb = centsToPB(cents);
      if (lastPB[ch] !== pb) sendPB(ch, pb);
    }
  }
}
function parsePitchKey(pk){
  var parts = String(pk).split(":");
  return { inCh: (parts[0]|0), pitch: (parts[1]|0), step: (parts[2]|0) };
}
}
function degreeForPitch(pitch){ return mod((pitch|0) - rootKeyPC, 12); }


// ===== Note tracking =====
function key(inCh, pitch, id){ return ((safeCh(inCh)&0xFF)<<16) | ((pitch&0x7F)<<8) | (id&0xFF); }
function key(inCh, pitch, id){ return ((safeCh(inCh)&0xFF)<<16) | ((pitch&0x7F)<<8) | (id&0xFF); }
function findMostRecent(inCh, pitch){
  var foundKey = null, info = null, bestId = -1;
  for (var k in noteMap){
    var packed = k|0;
    var kCh = (packed>>16)&0xFF;
    var kPitch = (packed>>8)&0x7F;
    var val = noteMap[k];
    if (kCh===inCh && kPitch===(pitch|0) && val.id>bestId){
      bestId = val.id; foundKey = k; info = val;
    }
  }
  return { foundKey: foundKey, info: info };
}


function allocChannel(){
// ===== Tail protection & allocator =====
function maybeCenterIdleChannels(now){
   for (var ch=memberStart; ch<=memberEnd; ch++){
   for (var ch=memberStart; ch<=memberEnd; ch++){
     if (channelBusy[ch]===0) return ch;
     if (channelBusy[ch]===0 && now >= releaseUntil[ch] && !centered[ch]) sendPB(ch, 0);
   }
   }
   var best = memberStart, min = channelBusy[memberStart];
}
   for (var c=memberStart+1; c<=memberEnd; c++){
function nextChInPool(ch){
     if (channelBusy[c] < min){ min = channelBusy[c]; best = c; }
   ch = (ch|0); if (ch < memberStart || ch > memberEnd) return memberStart;
  ch++; if (ch > memberEnd) ch = memberStart; return ch;
}
function freePitchKeyIfInactive(pk){
   var c = pitchKeyActive[pk] || 0;
  if (c <= 0){
    var ch = pitchKeyToCh[pk];
     if (ch){ if (chToPitchKey[ch] === pk) chToPitchKey[ch] = ""; delete pitchKeyToCh[pk]; }
    delete pitchKeyActive[pk];
   }
   }
   return best;
}
function hardKillChannel(ch){
   ch = safeCh(ch);
  sendCC(ch, 120, 0); sendCC(ch, 123, 0); sendPB(ch, 0);
  for (var k in noteMap){
    var info = noteMap[k];
    if (info && info.ch === ch){
      var pk = info.pitchKey;
      if (pitchKeyActive[pk]) pitchKeyActive[pk] = Math.max(0, (pitchKeyActive[pk]|0) - 1);
      delete noteMap[k];
    }
  }
  channelBusy[ch] = 0; centered[ch] = true; releaseUntil[ch] = GetNowMs(); lastPB[ch] = 0;
  var oldPk = chToPitchKey[ch];
  if (oldPk && oldPk.length){ chToPitchKey[ch] = ""; if (pitchKeyToCh[oldPk] === ch) delete pitchKeyToCh[oldPk]; freePitchKeyIfInactive(oldPk); }
}
function allocChannelForPitchKey(pk, now){
  var existing = pitchKeyToCh[pk];
  if (existing) return existing|0;
  var ch = rrNext; var span = (memberEnd - memberStart + 1);
  for (var i=0; i<span; i++){
    if (!chToPitchKey[ch] && channelBusy[ch]===0 && now >= releaseUntil[ch]){
      pitchKeyToCh[pk] = ch; chToPitchKey[ch] = pk; rrNext = nextChInPool(ch); return ch;
    }
    ch = nextChInPool(ch);
  }
  var steal = rrNext; rrNext = nextChInPool(steal);
  hardKillChannel(steal);
  pitchKeyToCh[pk] = steal; chToPitchKey[steal] = pk;
  return steal;
}
 
// ===== Panic & Reset =====
function allocatorReset(){
  var now = GetNowMs();
  pitchKeyToCh = {}; chToPitchKey = Array(17).fill(""); pitchKeyActive = {}; rrNext = memberStart;
  for (var ch=1; ch<=16; ch++){
    sendCC(ch, 123, 0); sendPB(ch, 0);
    channelBusy[ch] = 0; centered[ch] = true; releaseUntil[ch] = now; lastPB[ch] = 0;
  }
  noteMap = {}; uniq = 1;
  T("ALLOC RESET");
}
function panicAll(){
  var now = GetNowMs();
  for (var ch=1; ch<=16; ch++){
    sendCC(ch, 120, 0); sendCC(ch, 123, 0); sendPB(ch, 0);
    channelBusy[ch] = 0; centered[ch] = true; releaseUntil[ch] = now; lastPB[ch] = 0;
  }
  noteMap = {}; uniq = 1;
  globalMicro = 0; masterMicro = 0;
  for (var i=1; i<=16; i++){ currentMicroPerInCh[i]=0; microTouchedByInCh[i]=false; }
  pitchKeyToCh = {}; chToPitchKey = Array(17).fill(""); pitchKeyActive = {}; rrNext = memberStart;
  sustainDown = false;
  T("PANIC ALL");
}
function flushSustained(now){
  var any = false;
  for (var k in noteMap){
    var info = noteMap[k];
    if (info && info.sustained){
      any = true;
      var ch = info.ch; var pk = info.pitchKey;
      channelBusy[ch] = Math.max(0, channelBusy[ch]-1);
      if (pitchKeyActive[pk]) pitchKeyActive[pk] = Math.max(0, (pitchKeyActive[pk]|0) - 1);
      delete noteMap[k];
      if (channelBusy[ch]===0) releaseUntil[ch] = now + releaseHoldMs;
      freePitchKeyIfInactive(pk);
    }
  }
  if (any) T("SustainUp: flushed");
}
}


// ===== Main =====
// ===== Main =====
function HandleMIDI(e){
function HandleMIDI(e){
  var now = GetNowMs();
  maybeCenterIdleChannels(now);


  if (e instanceof ControlChange && e.number === (panicCCNumber|0)){
    if ((e.value|0) > (panicThreshold|0)){ allocatorReset(); return; }
    e.send(); return;
  }
  if (e instanceof ControlChange && e.number === 64){
    var v = e.value|0; var was = sustainDown;
    sustainDown = (v >= 64); e.send();
    if (was && !sustainDown) flushSustained(now);
    return;
  }
  // CC27 capture (no live retune)
   if (e instanceof ControlChange && e.number === ccNumber){
   if (e instanceof ControlChange && e.number === ccNumber){
     var ch = safeCh(e.channel);
     var chCC = safeCh(e.channel);
     var micro = microFromCC(e.value|0);
     var micro = microFromCC(e.value|0);
     microByInCh[ch] = micro;
 
     if (offsetScopeGlobal) globalMicro = micro;
     currentMicroPerInCh[chCC] = micro;
     if (debug) Trace("CC"+ccNumber+" ch "+ch+" val="+e.value+" → micro "+micro+(offsetScopeGlobal?" (GLOBAL)":""));
    microTouchedByInCh[chCC] = true;
 
     if (chCC === 1){
      masterMicro = micro;   // applies to all future member notes
    }
 
     if (offsetScopeGlobal){
      globalMicro = micro;
      T("CC"+ccNumber+" ch"+chCC+" → GLOBAL m="+micro);
    } else {
      T("CC"+ccNumber+" ch"+chCC+" → captured m="+micro);
    }
     if (swallowCC) return;
     if (swallowCC) return;
     e.send();
     e.send();
Line 205: Line 415:
   }
   }


  // NoteOn — capture micro at exact moment of arrival
   if (e instanceof NoteOn){
   if (e instanceof NoteOn){
    if ((e.velocity|0) === 0){ HandleMIDI(new NoteOff(e)); return; }
     var inCh = safeCh(e.channel);
     var inCh = safeCh(e.channel);
    if (inCh === 1){
      if (ch1Mode === 3) return;
      if (ch1Mode === 2){ e.send(); return; }
    }
    var deg = degreeForPitch(e.pitch|0);
     var base = baseStepFromInChannel(inCh);
     var base = baseStepFromInChannel(inCh);
     var micro = offsetScopeGlobal ? globalMicro : (microByInCh[inCh] || 0);
 
     var step = base + micro;
     var microN = 0;
     var cents = step * STEP_CENTS;
    if (offsetScopeGlobal){
      microN = globalMicro;
    } else {
      microN = currentMicroPerInCh[inCh] || 0;
      // MPE master fallback — now applies to ALL simultaneous notes
      if (inCh !== 1 && mpeMasterCCFeedsMembers && !microTouchedByInCh[inCh]){
        microN = masterMicro;
      }
    }
 
    if (inCh === 1 && ch1Mode === 1){ base = 0; microN = 0; }
 
     var stepOffset = (base + microN) | 0;
    var pk = makePitchKey(inCh, (e.pitch|0), stepOffset);
 
    var tgt = allocChannelForPitchKey(pk, now);
 
    var sb = scaleBendEnabled ? (scaleBend[deg] || 0.0) : 0.0;
     var cents = masterOffsetCents + sb + stepOffset * STEP_CENTS;
     var pb = centsToPB(cents);
     var pb = centsToPB(cents);


     var tgt = allocChannel();
     T("NOTE in="+inCh+" out="+tgt+" pitch="+(e.pitch|0)+" micro="+microN+" cents="+cents.toFixed(3)+" pb="+pb);


     if (debug){
     if (lastPB[tgt] !== pb) sendPB(tgt, pb);
      Trace("Note "+e.pitch+" inCh "+inCh+" base="+base+" micro="+micro+" step="+step+
            " → "+cents.toExponential(5)+"¢  allocCh="+tgt+"  PB="+pb);
    }


     sendPB(tgt, pb);
     var on = new NoteOn(e); on.channel = tgt;
    if (noteDelayMs > 0) on.sendAfterMilliseconds(noteDelayMs); else on.send();


     var on = new NoteOn(e);
     channelBusy[tgt]++; pitchKeyActive[pk] = ((pitchKeyActive[pk]|0) + 1) | 0;
    on.channel = tgt;
    if (noteDelayMs>0) on.sendAfterMilliseconds(noteDelayMs);
    else on.send();
 
    channelBusy[tgt]++;
     var id = (uniq = (uniq+1)&0xFF) || 1;
     var id = (uniq = (uniq+1)&0xFF) || 1;
     noteMap[key(inCh, e.pitch, id)] = { ch:tgt, id:id };
     noteMap[key(inCh, e.pitch, id)] = { ch:tgt, id:id, pitchKey:pk, sustained:false };
     return;
     return;
   }
   }


   if (e instanceof NoteOff){
   if (e instanceof NoteOff){
     var inCh = safeCh(e.channel);
     var inChOff = safeCh(e.channel);
     var foundKey = null, info = null, bestId = -1;
     var r = findMostRecent(inChOff, e.pitch|0);
     for (var k in noteMap){
     if (r.info){
       var packed = k|0, kCh = (packed>>16)&0xFF, kPitch = (packed>>8)&0x7F, val = noteMap[k];
       var info = r.info; var chOut = info.ch; var pk2 = info.pitchKey;
       if (kCh===inCh && kPitch===(e.pitch|0) && val.id>bestId){ bestId = val.id; foundKey=k; info=val; }
      var off = new NoteOff(e); off.channel = chOut; off.send();
      if (sustainDown){
        info.sustained = true; noteMap[r.foundKey] = info;
       } else {
        delete noteMap[r.foundKey];
        channelBusy[chOut] = Math.max(0, channelBusy[chOut]-1);
        if (pitchKeyActive[pk2]) pitchKeyActive[pk2] = Math.max(0, (pitchKeyActive[pk2]|0) - 1);
        if (channelBusy[chOut]===0) releaseUntil[chOut] = now + releaseHoldMs;
        freePitchKeyIfInactive(pk2);
      }
      return;
     }
     }
 
     if (inChOff === 1){
    var off = new NoteOff(e);
       if (ch1Mode === 3) return;
     if (info){
       if (ch1Mode === 2){ e.send(); return; }
       off.channel = info.ch;
      off.send();
      channelBusy[info.ch] = Math.max(0, channelBusy[info.ch]-1);
       if (channelBusy[info.ch]===0 && !centered[info.ch]) sendPB(info.ch, 0);
      delete noteMap[foundKey];
    } else {
      off.channel = inCh;
      off.send();
     }
     }
     return;
     e.send(); return;
   }
   }
   e.send();
   e.send();
}
}


function Reset(){
function Reset(){
   for (var ch=1; ch<=16; ch++){
  sustainDown = false; globalMicro = 0; masterMicro = 0;
    channelBusy[ch]=0; centered[ch]=false; sendPB(ch,0);
   for (var i=1; i<=16; i++){ currentMicroPerInCh[i]=0; microTouchedByInCh[i]=false; }
    microByInCh[ch]=0;
   panicAll();
  }
   rebuildScaleAndRetuneAssignedChannels();
   globalMicro=0; noteMap={}; uniq=1;
}
   Trace("Reset: allocator "+memberStart+".."+memberEnd+" cleared; PB centered.");
}</syntaxhighlight>'''NOTE''': You need to tune your 12 notes to a scale supported by your edo. I recommend 12edo or Pyth-like 5L 7s 6|5 if you're not using a 12n edo. I use MTS-ESP to do this globally, but you could do it inside the plugin itself.


Parameters:
// init
rebuildScale();
allocatorReset();</syntaxhighlight>Parameters:


* EDO: Obviously, the edo you'll be working with.
* EDO: Obviously, the edo you'll be working with.
* Root key: modes of 5L 7s 6|5 (built from the best fifth), transposing from C
* Reference MIDI note, reference Hz: Reference pitch at NOTE = FREQUENCY. Default 69 = 440, but you can make it anything.
* Channel 1 treatment:
** Normal: CH1 acts normally as one midiCH deviation step.
** ScaleBendOnly: CH1 Matches the 5L 7s of the tuning, but ignores midiCH and microdeviations.
** Bypass: CH1 notes ignore the script completely.
** Nullify: CH1 notes are killed.
* Channel at which step=0 (n): from 3 to 9. The midpoint of your edostep deviations. Channel 1 is not used here. If you choose 5, then your edosteps will be 2:-3, 3:-2, 4:-1, 5:0, 6:+1, 7:+2, 8:+3, so the amount of deviations you have at your disposal is 2n+1. Note that no matter the input channel (the one in your piano roll), it will be sent as Channel 1, or allocated to another channel for MPE.
* Channel at which step=0 (n): from 3 to 9. The midpoint of your edostep deviations. Channel 1 is not used here. If you choose 5, then your edosteps will be 2:-3, 3:-2, 4:-1, 5:0, 6:+1, 7:+2, 8:+3, so the amount of deviations you have at your disposal is 2n+1. Note that no matter the input channel (the one in your piano roll), it will be sent as Channel 1, or allocated to another channel for MPE.
* PB range (± cents): The range of your plugin's PB. Recommend values like 200 or 1200. For MPE, it is often 4800, but if you're using very fine grained edos, you might want to bring this down to get more resolution.
* PB range (± cents): The range of your plugin's PB. Recommend values like 200 or 1200. For MPE, it is often 4800, but if you're using very fine grained edos, you might want to bring this down to get more resolution.
* Articulation CC#: The CC at which articulations will be sent, you can change it if it causes conflicts with your plugin, but then you need to change all the articulation set's CC. Not recommended.
* Articulation CC#: The CC at which articulations will be sent, you can change it if it causes conflicts with your plugin, but then you need to change all the articulation set's CC. Default 27, Not recommended to change it.
* MidiChDeviationSplit: Multiplies the edosteps deviations of the MIDI channels by this number. at n=9, and this=16, then 10:+16, 8:-16, 11:+32, 7:-32... etc. If you are using HUGE edos (or edos with sharpness higher than 15), you will need to set this to a value bigger than 1. If you set this to 1, you can use it for smaller edos without duplicating instances '''(Preset-scale deviations - MIDI channels only, 12n)'''
* MidiChDeviationSplit: Multiplies the edosteps deviations of the MIDI channels by this number. at n=9, and this=16, then 10:+16, 8:-16, 11:+32, 7:-32... etc. If you are using HUGE edos (or edos with sharpness higher than 15), you will need to set this to a value bigger than 1. If you set this to 1, you can use it for smaller edos without duplicating instances '''(Preset-scale deviations - MIDI channels only, 12n)'''
* Offset scope: I don't know what this is. I set it to Global-Last, and I assume it has something to do with the MPE. But it works.
* Release Hold (ms): The duration that the MPE allocator holds each output channel's pitch after NoteOff messages. Recommended it be long for sounds with a long tail.
* Panic CC#: Default 83. Exceeding {Panic Threshold} in this will kill all MIDI events, you can use this if the script behaves erratically.
* Offset scope: I don't know exactly what this does.
* Swallow CC to synth: I suppose this makes the synth recieve CC27...?  
* Swallow CC to synth: I suppose this makes the synth recieve CC27...?  
* Member Ch Start: The lowest working channel for MPE.
* Member Ch Start: The lowest working channel for MPE. If you hear muted notes when you play, put it at 2.
* Member Ch End: The highest working channel for MPE.
* Member Ch End: The highest working channel for MPE. No reason to put it lower than 16.
* NoteOn Delay (ms): Set higher than 0 if the notes aren't being detuned correctly. Obviously, don't set it too high. Default 2.
* NoteOn Delay (ms): Set higher than 0 if the notes aren't being detuned correctly. Obviously, don't set it too high. Default 2.
* Debug Trace: If something is not working, check this box to see debugging output in the Scripter.
* Debug Trace: If something is not working, check this box to see debugging output in the Scripter.
* Scale-bend or 12edo: This makes only sense to deactivate with hyperfine 12n edos, to use 12edo instead of the near-pyth 5L 7s.


===== .plist Articulation set =====
===== .plist Articulation set (alternate) =====
<syntaxhighlight lang="xml" line="1">
<syntaxhighlight lang="xml" line="1">
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
Line 289: Line 530:
<key>Articulations</key>
<key>Articulations</key>
<array>
<array>
<dict><key>ArticulationID</key><integer>1</integer><key>ID</key><integer>1001</integer><key>Name</key><string>-64</string><key>Output</key><array><dict><key>MB1</key><integer>27</integer><key>Status</key><string>Controller</string><key>ValueLow</key><integer>0</integer></dict></array></dict>
<dict>
 
<key>ArticulationID</key>
<integer>1</integer>
<key>ID</key>
<integer>1001</integer>
<key>Name</key>
<string>0</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>64</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>2</integer>
<key>ID</key>
<integer>1002</integer>
<key>Name</key>
<string>+1</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>65</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>3</integer>
<key>ID</key>
<integer>1003</integer>
<key>Name</key>
<string>-1</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>63</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>4</integer>
<key>ID</key>
<integer>1004</integer>
<key>Name</key>
<string>+2</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>66</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>5</integer>
<key>ID</key>
<integer>1005</integer>
<key>Name</key>
<string>-2</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>62</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>6</integer>
<key>ID</key>
<integer>1006</integer>
<key>Name</key>
<string>+3</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>67</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>7</integer>
<key>ID</key>
<integer>1007</integer>
<key>Name</key>
<string>-3</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>61</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>8</integer>
<key>ID</key>
<integer>1008</integer>
<key>Name</key>
<string>+4</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>68</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>9</integer>
<key>ID</key>
<integer>1009</integer>
<key>Name</key>
<string>-4</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>60</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>10</integer>
<key>ID</key>
<integer>1010</integer>
<key>Name</key>
<string>+5</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>69</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>11</integer>
<key>ID</key>
<integer>1011</integer>
<key>Name</key>
<string>-5</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>59</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>12</integer>
<key>ID</key>
<integer>1012</integer>
<key>Name</key>
<string>+6</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>70</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>13</integer>
<key>ID</key>
<integer>1013</integer>
<key>Name</key>
<string>-6</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>58</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>14</integer>
<key>ID</key>
<integer>1014</integer>
<key>Name</key>
<string>+7</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
<integer>71</integer>
</dict>
</array>
</dict>
<dict>
<key>ArticulationID</key>
<integer>15</integer>
<key>ID</key>
<integer>1015</integer>
<key>Name</key>
<string>-7</string>
<key>Output</key>
<array>
<dict>
<key>MB1</key>
<integer>27</integer>
<key>Status</key>
<string>Controller</string>
<key>ValueLow</key>
</array>
</array>
<key>Name</key>
<key>Name</key>
Line 424: Line 2,969:
</dict>
</dict>
</plist>
</plist>
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 18:32, 17 May 2026

This guide explains the workflow I use to build and compose with any edo. It does not discuss stylistic choices in composing with these edos. It does however have my recommendations based on my own experience, like in the "good edos" section.

The content here applies for Logic Pro, for Apple, the DAW I use to compose my music.

I have no guarantee that my workflow will be compatible with other DAWs, but it can help from a theoretical standpoint if you choose to build edos in other DAWs using the methods I expose in this article.

Edos 1,2,3,4,6

These are subsets of 12edo. 'Nuff said.

Edos 5,7,8,9,10,11

You need to retune your plugin. Since they have less than 12 notes, you can fit everything within an octave.

  • Advantages: Trivially easy to build and use, accessible xenharmony (not microtonality).
  • Disdvantages: Non-transposing friendly, very coarse edos.

Edos 12n

This technically also includes subset edos like 8, 9, 10, 16, 18, 27 with a fine enough 12n edo. Just create copies of the same track, detuned by 100/n cents from each other, and make each instance to a whitelist a unique MIDI channel, controlled by a master track.

To compose in it, play your thing in the master track as if it were 12edo, and detune the notes to your heart's content.

  • Advantages: Trivially easy to build and use, transposing friendly, extremely intuitive, accessible microtonality!
  • Disadvantages: Only possible with 12n edos. Edos above 72 or 84 may become unwieldy or RAM-intensive, due to the abundance of many instrument instances.

Good edos: 24, 72, 84 - Reasoning: Efficient approximations of JI / 24edo is an entry-level microtonal edo, great ila tuning.

Edos >12

At this point, you have two ways of building edos, each of them benefits and drawbacks.

Keyboard retuning

Fit the whole gamut of your edo within as many semitones as you need. Viable for edos for up to ~36. Absolute limit is 128edo, which maps every single midi note to an edostep, but you only have an octave span. Thankfully, if you need more octaves, you can make more instances at different octaves. This is how Lumatone mappings often work.

  • Advantages: Naïve approach to retune an edo. Extremely easy to conceptualize and build, if your plugin supports scale export.
  • Disadvantages: Much harder to compose using this method because the keyboard has the notes way out of place. Programming notes in, is a more viable workflow, albeit less in the feels. Limited octave range, but can be expanded with MIDI channels.

Good edos: 17, 19, 22, 31, 34 - Reasoning: Good fifths and/or Good 5-limit approximations. Of all these ones, I'd say 31 is the best one around here, as it provides a very intuitive framework for 13-limit xenharmony. 19 is also another entry-level edo, the next best edo at 5-limit other than 12edo. Both of these edos are meantone, which aligns quite well with Western music theory.

Preset-scale deviations - MIDI channels only

Tune your 12 notes in your octave to a fixed scale supported by your edo. I recommend 12edo or 5L 7s 6|5 (or 7L 5s 6|5 if you're using meantone), as it is very easy to extend to any edo, specially as they get large. Then, make separate instances of each track that differ by edosteps, with a whitelisted midi channel. Of course, there are only 16 MIDI channels, so this method cannot be extended indefinitely. This method is robust, as it can allow you to reach any edo up to sharp-16 (augmented unison ≤ 16 edosteps).

This includes every edo up to 199 except for 148, 160, 162, 165, 167, 169, 172, 174, 176, 177, 179, 181, 183, 184, 186, 188, 189, 190, 191, 193, 195, 196, 197, 198. It also allows you to build 12n edos up to 192edo, by using 12edo as the fixed scale.

  • Advantages: Allows use of very fine-grained edos, 10.3 octave range, practical to compose in.
  • Disadvantages: Harder to set up, less transposing-friendly because of wolf intervals. Not usable live.

Good edos: 41, 53, 94, 130, 159 - Reasoning: Astounding fifths and/or Great 13-limit approximations.

94edo is my favorite here, because it combines two top-tier edos into one great jack-of-all trades 23-odd-limit tuning. 159edo, Aura's favorite, is also a top-tier edo, making an astonishingly good ila tuning, and/or an overall a very good jack-of-all trades tuning, both of them with a graspable gamut.

Preset-scale deviations - MIDI and articulations

Have your fixed scale, and your instances, but now, not only are we detuning with MIDI channels, but with articulations too. These are part of MIDI 2, mind you. In Logic Pro, you can have up to 255 articulations, and with some clever scripting and combining that with MIDI channels, you can theoretically compose in any edo with sharpness up to 4080! That's around 43000edo!

But there's a caveat. To do this method effectively, you must use a plugin with MPE (MIDI Polyphonic Expression), and make sure you have enough pitch bend resolution to represent the amount of pitchbend per edostep accurately (MPE PB spans +- 8192). I vibe coded a Javascript script that manages to do so, with the following .plist for all the articulations. I don't know if it works in other DAWs, but it works in Logic, and that's enough for me.

  • Advantages: Near total freedom of pitch and tuning options, allows use of extremely fine edos, 10.3 octave range, practical to compose in.
  • Disadvantages: (Probably) Non-transposing friendly. Requires MPE or else instrument must be monophonic. Hard to setup, very technical, prone to error, cannot use articulations for their intended purpose.

Good edos: 217, 270, 311 - Reasoning: High-prime-limit support / Unbeatably accurate yazalathana.

Excessive edos: Anything beyond 311. Highly recommend you use superfine 12n edos for these ones. I don't go here.

About edo usage and JI approximations (my experience)

Ultimate is my temperament of temperaments, which supports 12e, 41, 53, 94, 217, 270, 311, and of those my best choices are undoubtedly 94edo and 270edo. They have respectively, very good and extremely good 13-limit, and are even, so I can split the poma in halves for easier navigation.

I aim for 13-limit JI, and if you also aim for that, then these tunings are the best for you. Check the Ultimate article for more info. Note, I don't care about essential tempering, VAOs, MOSes beyond the theoretical, or structural exploiting.

If you still want to go even finer than that... then try 612edo and 2460; S-tier 12n edos, so you get transposing-friendly hyperfine microtonality. But, since their step sizes are smaller than the melodic JND, you can't tell notes edosteps apart. You are likely wasting your time here, however.

If you STILL want to go finer... you are surely wasting your time. Just use JI scales and forget about edos and temperaments altogether. Remember when I said you could theoretically compose in these insane edos? That's because PB resolution is going to be your main bottleneck. The other one is your paranoia.

Vibe-coded Javascript script (V7)
// Generalized EDO Retuner with TRUE POLYPHONIC Micropitch Deviation (CC27 per-note)
// FIXED: simultaneous chords with master-channel CC27 now work correctly

var NeedsTimingInfo = true;

// Output pool
var memberStart = 1, memberEnd = 16;

// Scale-bend
var scaleBendEnabled = true;
var mpeMasterCCFeedsMembers = true;   // set false if your controller sends CC27 on member channels

// ===== Defaults / State =====
var EDO = 94;
var rootKeyPC = 0;
var refMidiNote = 69;
var refHz = 440.0;
var ch1Mode = 0;
var n = 8;
var maxDeviation = 4800;
var ccNumber = 27;
var MidiChDeviationSplit = 1;
var noteDelayMs = 2;
var offsetScopeGlobal = false;   // Per-Channel recommended for independent per-note micro
var swallowCC = true;
var debug = false;

// Sustain and tail protection
var sustainDown = false;
var releaseHoldMs = 300;

// Panic
var panicCCNumber = 83;
var panicThreshold = 63;

// Derived
var STEP_CENTS = 1200.0 / EDO;

// Micro offset memory
var globalMicro = 0;
var masterMicro = 0;                    // last CC27 received on MPE master (ch1)
var currentMicroPerInCh = Array(17).fill(0);
var microTouchedByInCh = Array(17).fill(false);

// Output-channel state
var channelBusy = Array(17).fill(0);
var centered = Array(17).fill(true);
var releaseUntil = Array(17).fill(0);
var lastPB = Array(17).fill(999999);

// Note tracking
var noteMap = {};
var uniq = 1;

// Allocator
var pitchKeyToCh = {};
var chToPitchKey = Array(17).fill("");
var pitchKeyActive = {};
var rrNext = 1;

// Scale-bend state
var gSteps = 0;
var scaleBend = Array(12).fill(0.0);
var masterOffsetCents = 0.0;

var FIFTH_OFFSETS = [0, -5, +2, -3, +4, -1, +6, +1, -4, +3, -2, +5];

// Safe Trace
function T(s){
  if (!debug) return;
  s = String(s);
  if (s.length > 120) s = s.slice(0, 120);
  Trace(s);
}

// ===== UI =====
var PluginParameters = [
  { name:"EDO", type:"lin", minValue:1, maxValue:65535, numberOfSteps:65534, defaultValue:311 },
  { name:"Root key (PC)", type:"menu", valueStrings:["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"], defaultValue:0 },
  { name:"Reference MIDI note", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:69 },
  { name:"Reference Hz", type:"float", minValue:1, maxValue:20000, numberOfSteps:19999, defaultValue:440 },
  { name:"Ch 1 treatment", type:"menu", valueStrings:["Normal","ScaleBendOnly","Bypass (12edo)","Nullify"], defaultValue:0 },
  { name:"Channel at which step=0 (n)", type:"lin", minValue:1, maxValue:16, numberOfSteps:15, defaultValue:8 },
  { name:"PB range (± cents)", type:"float", minValue:1, maxValue:4800, numberOfSteps:4799, defaultValue:200 },
  { name:"Articulation CC#", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:27 },
  { name:"MidiChDeviationSplit", type:"lin", minValue:0, maxValue:63, numberOfSteps:63, defaultValue:2 },
  { name:"Release Hold (ms)", type:"lin", minValue:0, maxValue:20000, numberOfSteps:20000, defaultValue:300 },
  { name:"Panic CC#", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:83 },
  { name:"Panic thresh (>)", type:"lin", minValue:0, maxValue:127, numberOfSteps:127, defaultValue:63 },
  { name:"PANIC (kill all)", type:"menu", valueStrings:["Off","TRIGGER"], defaultValue:0 },
  { name:"Offset scope", type:"menu", valueStrings:["Per-Channel","Global-Last"], defaultValue:0 },
  { name:"Swallow CC to synth", type:"menu", valueStrings:["No","Yes"], defaultValue:1 },
  { name:"NoteOn Delay (ms)", type:"lin", minValue:0, maxValue:10, numberOfSteps:10, defaultValue:2 },
  { name:"Debug Trace", type:"menu", valueStrings:["Off","On"], defaultValue:0 },
  { name:"Output ch start", type:"lin", minValue:1, maxValue:16, numberOfSteps:15, defaultValue:1 },
  { name:"Output ch end", type:"lin", minValue:1, maxValue:16, numberOfSteps:15, defaultValue:16 },
  { name:"Scale-bend or 12EDO", type:"menu", valueStrings:["On","Off"], defaultValue:0 }
];

function ParameterChanged(p,v){
  if (p===0){ EDO = Math.max(1, Math.min(65535, Math.floor(v))); STEP_CENTS = 1200.0 / EDO; rebuildScaleAndRetuneAssignedChannels(); }
  else if (p===1){ rootKeyPC = (v|0) % 12; rebuildScaleAndRetuneAssignedChannels(); }
  else if (p===2){ refMidiNote = Math.max(0, Math.min(127, v|0)); rebuildScaleAndRetuneAssignedChannels(); }
  else if (p===3){ refHz = Math.max(1.0, v); rebuildScaleAndRetuneAssignedChannels(); }
  else if (p===4){ ch1Mode = (v|0) & 3; }
  else if (p===5){ n = Math.max(1, Math.min(16, v|0)); }
  else if (p===6){ maxDeviation = Math.max(1, v); rebuildScaleAndRetuneAssignedChannels(); }
  else if (p===7){ ccNumber = Math.floor(v); }
  else if (p===8){ MidiChDeviationSplit = Math.max(0, Math.min(63, Math.floor(v))); }
  else if (p===9){ releaseHoldMs = Math.max(0, Math.min(20000, Math.floor(v))); }
  else if (p===10){ panicCCNumber = Math.max(0, Math.min(127, v|0)); }
  else if (p===11){ panicThreshold = Math.max(0, Math.min(127, v|0)); }
  else if (p===12){ if ((v|0)===1) panicAll(); }
  else if (p===13){ offsetScopeGlobal = (v|0)===1; }
  else if (p===14){ swallowCC = (v|0)===1; }
  else if (p===15){ noteDelayMs = Math.max(0, Math.min(10, v|0)); }
  else if (p===16){ debug = (v|0)===1; }
  else if (p===17){ setOutputPool(v|0, memberEnd); allocatorReset(); rebuildScaleAndRetuneAssignedChannels(); }
  else if (p===18){ setOutputPool(memberStart, v|0); allocatorReset(); rebuildScaleAndRetuneAssignedChannels(); }
  else if (p===19){ scaleBendEnabled = ((v|0) === 0); rebuildScaleAndRetuneAssignedChannels(); }
}

// ===== Helpers =====
function safeCh(ch){ ch=(ch|0)||1; if (ch<1) ch=1; if (ch>16) ch=16; return ch; }
function mod(a,m){ var r=a%m; if (r<0) r+=m; return r; }
function log2(x){ return Math.log(x) / Math.log(2.0); }
function setOutputPool(a, b){
  a = safeCh(a); b = safeCh(b); if (a > b){ var t=a; a=b; b=t; }
  memberStart = a; memberEnd = b;
  if (rrNext < memberStart || rrNext > memberEnd) rrNext = memberStart;
  T("Pool " + memberStart + ".." + memberEnd);
}
function makePitchKey(inCh, pitch, step){ return String(inCh|0) + ":" + String(pitch|0) + ":" + String(step|0); }
function GetNowMs(){
  if (typeof GetTimingInfo !== "function") return 0;
  var t = GetTimingInfo();
  if (!t) return 0;
  var tempo = (t.tempo && t.tempo > 0) ? t.tempo : 120.0;
  var beat = t.blockStartBeat !== undefined ? t.blockStartBeat : (t.playhead !== undefined ? t.playhead : 0);
  return (beat * 60000.0) / tempo;
}
function baseStepFromInChannel(ch){ return (ch - n) * MidiChDeviationSplit; }
function microFromCC(val){
  if (MidiChDeviationSplit === 1) return 0;
  var m = (val|0) - 64;
  if (m < -64) m = -64; if (m > 63) m = 63;
  return m;
}
function centsToPB(c){
  var v = (c / maxDeviation) * 8191.0;
  if (v > 8191) v = 8191; if (v < -8192) v = -8192;
  return Math.round(v);
}
function sendPB(ch, value){
  ch = safeCh(ch);
  var pb = new PitchBend(); pb.channel = ch; pb.value = value; pb.send();
  centered[ch] = (value === 0); lastPB[ch] = value;
}
function sendCC(ch, num, val){
  var cc = new ControlChange(); cc.channel = safeCh(ch); cc.number = num|0; cc.value = val|0; cc.send();
}

// ===== Scale bend =====
function rebuildScale(){
  STEP_CENTS = 1200.0 / EDO;
  var gReal = EDO * (log2(3.0) - 1.0);
  gSteps = Math.round(gReal);
  var rawBend = Array(12).fill(0.0);
  for (var i=0; i<12; i++){
    var st = mod(FIFTH_OFFSETS[i] * gSteps, EDO);
    rawBend[i] = st * STEP_CENTS - (i * 100.0);
  }
  var stdHz = 440.0 * Math.pow(2.0, (refMidiNote - 69) / 12.0);
  masterOffsetCents = 1200.0 * log2(refHz / stdHz);
  var refDeg = mod((refMidiNote|0) - rootKeyPC, 12);
  var anchor = rawBend[refDeg];
  for (var k=0; k<12; k++) scaleBend[k] = rawBend[k] - anchor;
  T("Scale g="+gSteps+" EDO="+EDO);
}
function rebuildScaleAndRetuneAssignedChannels(){
  rebuildScale();
  for (var ch=memberStart; ch<=memberEnd; ch++){
    var pk = chToPitchKey[ch];
    if (pk && pk.length){
      var p = parsePitchKey(pk);   // note: parsePitchKey is defined below
      var deg = degreeForPitch(p.pitch);
      var sb = scaleBendEnabled ? (scaleBend[deg] || 0.0) : 0.0;
      var cents = masterOffsetCents + sb + p.step * STEP_CENTS;
      var pb = centsToPB(cents);
      if (lastPB[ch] !== pb) sendPB(ch, pb);
    }
  }
}
function parsePitchKey(pk){
  var parts = String(pk).split(":");
  return { inCh: (parts[0]|0), pitch: (parts[1]|0), step: (parts[2]|0) };
}
function degreeForPitch(pitch){ return mod((pitch|0) - rootKeyPC, 12); }

// ===== Note tracking =====
function key(inCh, pitch, id){ return ((safeCh(inCh)&0xFF)<<16) | ((pitch&0x7F)<<8) | (id&0xFF); }
function findMostRecent(inCh, pitch){
  var foundKey = null, info = null, bestId = -1;
  for (var k in noteMap){
    var packed = k|0;
    var kCh = (packed>>16)&0xFF;
    var kPitch = (packed>>8)&0x7F;
    var val = noteMap[k];
    if (kCh===inCh && kPitch===(pitch|0) && val.id>bestId){
      bestId = val.id; foundKey = k; info = val;
    }
  }
  return { foundKey: foundKey, info: info };
}

// ===== Tail protection & allocator =====
function maybeCenterIdleChannels(now){
  for (var ch=memberStart; ch<=memberEnd; ch++){
    if (channelBusy[ch]===0 && now >= releaseUntil[ch] && !centered[ch]) sendPB(ch, 0);
  }
}
function nextChInPool(ch){
  ch = (ch|0); if (ch < memberStart || ch > memberEnd) return memberStart;
  ch++; if (ch > memberEnd) ch = memberStart; return ch;
}
function freePitchKeyIfInactive(pk){
  var c = pitchKeyActive[pk] || 0;
  if (c <= 0){
    var ch = pitchKeyToCh[pk];
    if (ch){ if (chToPitchKey[ch] === pk) chToPitchKey[ch] = ""; delete pitchKeyToCh[pk]; }
    delete pitchKeyActive[pk];
  }
}
function hardKillChannel(ch){
  ch = safeCh(ch);
  sendCC(ch, 120, 0); sendCC(ch, 123, 0); sendPB(ch, 0);
  for (var k in noteMap){
    var info = noteMap[k];
    if (info && info.ch === ch){
      var pk = info.pitchKey;
      if (pitchKeyActive[pk]) pitchKeyActive[pk] = Math.max(0, (pitchKeyActive[pk]|0) - 1);
      delete noteMap[k];
    }
  }
  channelBusy[ch] = 0; centered[ch] = true; releaseUntil[ch] = GetNowMs(); lastPB[ch] = 0;
  var oldPk = chToPitchKey[ch];
  if (oldPk && oldPk.length){ chToPitchKey[ch] = ""; if (pitchKeyToCh[oldPk] === ch) delete pitchKeyToCh[oldPk]; freePitchKeyIfInactive(oldPk); }
}
function allocChannelForPitchKey(pk, now){
  var existing = pitchKeyToCh[pk];
  if (existing) return existing|0;
  var ch = rrNext; var span = (memberEnd - memberStart + 1);
  for (var i=0; i<span; i++){
    if (!chToPitchKey[ch] && channelBusy[ch]===0 && now >= releaseUntil[ch]){
      pitchKeyToCh[pk] = ch; chToPitchKey[ch] = pk; rrNext = nextChInPool(ch); return ch;
    }
    ch = nextChInPool(ch);
  }
  var steal = rrNext; rrNext = nextChInPool(steal);
  hardKillChannel(steal);
  pitchKeyToCh[pk] = steal; chToPitchKey[steal] = pk;
  return steal;
}

// ===== Panic & Reset =====
function allocatorReset(){
  var now = GetNowMs();
  pitchKeyToCh = {}; chToPitchKey = Array(17).fill(""); pitchKeyActive = {}; rrNext = memberStart;
  for (var ch=1; ch<=16; ch++){
    sendCC(ch, 123, 0); sendPB(ch, 0);
    channelBusy[ch] = 0; centered[ch] = true; releaseUntil[ch] = now; lastPB[ch] = 0;
  }
  noteMap = {}; uniq = 1;
  T("ALLOC RESET");
}
function panicAll(){
  var now = GetNowMs();
  for (var ch=1; ch<=16; ch++){
    sendCC(ch, 120, 0); sendCC(ch, 123, 0); sendPB(ch, 0);
    channelBusy[ch] = 0; centered[ch] = true; releaseUntil[ch] = now; lastPB[ch] = 0;
  }
  noteMap = {}; uniq = 1;
  globalMicro = 0; masterMicro = 0;
  for (var i=1; i<=16; i++){ currentMicroPerInCh[i]=0; microTouchedByInCh[i]=false; }
  pitchKeyToCh = {}; chToPitchKey = Array(17).fill(""); pitchKeyActive = {}; rrNext = memberStart;
  sustainDown = false;
  T("PANIC ALL");
}
function flushSustained(now){
  var any = false;
  for (var k in noteMap){
    var info = noteMap[k];
    if (info && info.sustained){
      any = true;
      var ch = info.ch; var pk = info.pitchKey;
      channelBusy[ch] = Math.max(0, channelBusy[ch]-1);
      if (pitchKeyActive[pk]) pitchKeyActive[pk] = Math.max(0, (pitchKeyActive[pk]|0) - 1);
      delete noteMap[k];
      if (channelBusy[ch]===0) releaseUntil[ch] = now + releaseHoldMs;
      freePitchKeyIfInactive(pk);
    }
  }
  if (any) T("SustainUp: flushed");
}

// ===== Main =====
function HandleMIDI(e){
  var now = GetNowMs();
  maybeCenterIdleChannels(now);

  if (e instanceof ControlChange && e.number === (panicCCNumber|0)){
    if ((e.value|0) > (panicThreshold|0)){ allocatorReset(); return; }
    e.send(); return;
  }
  if (e instanceof ControlChange && e.number === 64){
    var v = e.value|0; var was = sustainDown;
    sustainDown = (v >= 64); e.send();
    if (was && !sustainDown) flushSustained(now);
    return;
  }

  // CC27 capture (no live retune)
  if (e instanceof ControlChange && e.number === ccNumber){
    var chCC = safeCh(e.channel);
    var micro = microFromCC(e.value|0);

    currentMicroPerInCh[chCC] = micro;
    microTouchedByInCh[chCC] = true;

    if (chCC === 1){
      masterMicro = micro;   // applies to all future member notes
    }

    if (offsetScopeGlobal){
      globalMicro = micro;
      T("CC"+ccNumber+" ch"+chCC+" → GLOBAL m="+micro);
    } else {
      T("CC"+ccNumber+" ch"+chCC+" → captured m="+micro);
    }
    if (swallowCC) return;
    e.send();
    return;
  }

  // NoteOn — capture micro at exact moment of arrival
  if (e instanceof NoteOn){
    if ((e.velocity|0) === 0){ HandleMIDI(new NoteOff(e)); return; }

    var inCh = safeCh(e.channel);
    if (inCh === 1){
      if (ch1Mode === 3) return;
      if (ch1Mode === 2){ e.send(); return; }
    }

    var deg = degreeForPitch(e.pitch|0);
    var base = baseStepFromInChannel(inCh);

    var microN = 0;
    if (offsetScopeGlobal){
      microN = globalMicro;
    } else {
      microN = currentMicroPerInCh[inCh] || 0;
      // MPE master fallback — now applies to ALL simultaneous notes
      if (inCh !== 1 && mpeMasterCCFeedsMembers && !microTouchedByInCh[inCh]){
        microN = masterMicro;
      }
    }

    if (inCh === 1 && ch1Mode === 1){ base = 0; microN = 0; }

    var stepOffset = (base + microN) | 0;
    var pk = makePitchKey(inCh, (e.pitch|0), stepOffset);

    var tgt = allocChannelForPitchKey(pk, now);

    var sb = scaleBendEnabled ? (scaleBend[deg] || 0.0) : 0.0;
    var cents = masterOffsetCents + sb + stepOffset * STEP_CENTS;
    var pb = centsToPB(cents);

    T("NOTE in="+inCh+" out="+tgt+" pitch="+(e.pitch|0)+" micro="+microN+" cents="+cents.toFixed(3)+" pb="+pb);

    if (lastPB[tgt] !== pb) sendPB(tgt, pb);

    var on = new NoteOn(e); on.channel = tgt;
    if (noteDelayMs > 0) on.sendAfterMilliseconds(noteDelayMs); else on.send();

    channelBusy[tgt]++; pitchKeyActive[pk] = ((pitchKeyActive[pk]|0) + 1) | 0;
    var id = (uniq = (uniq+1)&0xFF) || 1;
    noteMap[key(inCh, e.pitch, id)] = { ch:tgt, id:id, pitchKey:pk, sustained:false };
    return;
  }

  if (e instanceof NoteOff){
    var inChOff = safeCh(e.channel);
    var r = findMostRecent(inChOff, e.pitch|0);
    if (r.info){
      var info = r.info; var chOut = info.ch; var pk2 = info.pitchKey;
      var off = new NoteOff(e); off.channel = chOut; off.send();
      if (sustainDown){
        info.sustained = true; noteMap[r.foundKey] = info;
      } else {
        delete noteMap[r.foundKey];
        channelBusy[chOut] = Math.max(0, channelBusy[chOut]-1);
        if (pitchKeyActive[pk2]) pitchKeyActive[pk2] = Math.max(0, (pitchKeyActive[pk2]|0) - 1);
        if (channelBusy[chOut]===0) releaseUntil[chOut] = now + releaseHoldMs;
        freePitchKeyIfInactive(pk2);
      }
      return;
    }
    if (inChOff === 1){
      if (ch1Mode === 3) return;
      if (ch1Mode === 2){ e.send(); return; }
    }
    e.send(); return;
  }
  e.send();
}

function Reset(){
  sustainDown = false; globalMicro = 0; masterMicro = 0;
  for (var i=1; i<=16; i++){ currentMicroPerInCh[i]=0; microTouchedByInCh[i]=false; }
  panicAll();
  rebuildScaleAndRetuneAssignedChannels();
}

// init
rebuildScale();
allocatorReset();

Parameters:

  • EDO: Obviously, the edo you'll be working with.
  • Root key: modes of 5L 7s 6|5 (built from the best fifth), transposing from C
  • Reference MIDI note, reference Hz: Reference pitch at NOTE = FREQUENCY. Default 69 = 440, but you can make it anything.
  • Channel 1 treatment:
    • Normal: CH1 acts normally as one midiCH deviation step.
    • ScaleBendOnly: CH1 Matches the 5L 7s of the tuning, but ignores midiCH and microdeviations.
    • Bypass: CH1 notes ignore the script completely.
    • Nullify: CH1 notes are killed.
  • Channel at which step=0 (n): from 3 to 9. The midpoint of your edostep deviations. Channel 1 is not used here. If you choose 5, then your edosteps will be 2:-3, 3:-2, 4:-1, 5:0, 6:+1, 7:+2, 8:+3, so the amount of deviations you have at your disposal is 2n+1. Note that no matter the input channel (the one in your piano roll), it will be sent as Channel 1, or allocated to another channel for MPE.
  • PB range (± cents): The range of your plugin's PB. Recommend values like 200 or 1200. For MPE, it is often 4800, but if you're using very fine grained edos, you might want to bring this down to get more resolution.
  • Articulation CC#: The CC at which articulations will be sent, you can change it if it causes conflicts with your plugin, but then you need to change all the articulation set's CC. Default 27, Not recommended to change it.
  • MidiChDeviationSplit: Multiplies the edosteps deviations of the MIDI channels by this number. at n=9, and this=16, then 10:+16, 8:-16, 11:+32, 7:-32... etc. If you are using HUGE edos (or edos with sharpness higher than 15), you will need to set this to a value bigger than 1. If you set this to 1, you can use it for smaller edos without duplicating instances (Preset-scale deviations - MIDI channels only, 12n)
  • Release Hold (ms): The duration that the MPE allocator holds each output channel's pitch after NoteOff messages. Recommended it be long for sounds with a long tail.
  • Panic CC#: Default 83. Exceeding {Panic Threshold} in this will kill all MIDI events, you can use this if the script behaves erratically.
  • Offset scope: I don't know exactly what this does.
  • Swallow CC to synth: I suppose this makes the synth recieve CC27...?
  • Member Ch Start: The lowest working channel for MPE. If you hear muted notes when you play, put it at 2.
  • Member Ch End: The highest working channel for MPE. No reason to put it lower than 16.
  • NoteOn Delay (ms): Set higher than 0 if the notes aren't being detuned correctly. Obviously, don't set it too high. Default 2.
  • Debug Trace: If something is not working, check this box to see debugging output in the Scripter.
  • Scale-bend or 12edo: This makes only sense to deactivate with hyperfine 12n edos, to use 12edo instead of the near-pyth 5L 7s.
.plist Articulation set (alternate)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Articulations</key>
	<array>
		<dict>
			<key>ArticulationID</key>
			<integer>1</integer>
			<key>ID</key>
			<integer>1001</integer>
			<key>Name</key>
			<string>0</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>64</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>2</integer>
			<key>ID</key>
			<integer>1002</integer>
			<key>Name</key>
			<string>+1</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>65</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>3</integer>
			<key>ID</key>
			<integer>1003</integer>
			<key>Name</key>
			<string>-1</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>63</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>4</integer>
			<key>ID</key>
			<integer>1004</integer>
			<key>Name</key>
			<string>+2</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>66</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>5</integer>
			<key>ID</key>
			<integer>1005</integer>
			<key>Name</key>
			<string>-2</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>62</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>6</integer>
			<key>ID</key>
			<integer>1006</integer>
			<key>Name</key>
			<string>+3</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>67</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>7</integer>
			<key>ID</key>
			<integer>1007</integer>
			<key>Name</key>
			<string>-3</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>61</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>8</integer>
			<key>ID</key>
			<integer>1008</integer>
			<key>Name</key>
			<string>+4</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>68</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>9</integer>
			<key>ID</key>
			<integer>1009</integer>
			<key>Name</key>
			<string>-4</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>60</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>10</integer>
			<key>ID</key>
			<integer>1010</integer>
			<key>Name</key>
			<string>+5</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>69</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>11</integer>
			<key>ID</key>
			<integer>1011</integer>
			<key>Name</key>
			<string>-5</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>59</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>12</integer>
			<key>ID</key>
			<integer>1012</integer>
			<key>Name</key>
			<string>+6</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>70</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>13</integer>
			<key>ID</key>
			<integer>1013</integer>
			<key>Name</key>
			<string>-6</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>58</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>14</integer>
			<key>ID</key>
			<integer>1014</integer>
			<key>Name</key>
			<string>+7</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>71</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>15</integer>
			<key>ID</key>
			<integer>1015</integer>
			<key>Name</key>
			<string>-7</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>57</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>16</integer>
			<key>ID</key>
			<integer>1016</integer>
			<key>Name</key>
			<string>+8</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>72</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>17</integer>
			<key>ID</key>
			<integer>1017</integer>
			<key>Name</key>
			<string>-8</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>56</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>18</integer>
			<key>ID</key>
			<integer>1018</integer>
			<key>Name</key>
			<string>+9</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>73</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>19</integer>
			<key>ID</key>
			<integer>1019</integer>
			<key>Name</key>
			<string>-9</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>55</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>20</integer>
			<key>ID</key>
			<integer>1020</integer>
			<key>Name</key>
			<string>+10</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>74</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>21</integer>
			<key>ID</key>
			<integer>1021</integer>
			<key>Name</key>
			<string>-10</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>54</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>22</integer>
			<key>ID</key>
			<integer>1022</integer>
			<key>Name</key>
			<string>+11</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>75</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>23</integer>
			<key>ID</key>
			<integer>1023</integer>
			<key>Name</key>
			<string>-11</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>53</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>24</integer>
			<key>ID</key>
			<integer>1024</integer>
			<key>Name</key>
			<string>+12</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>76</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>25</integer>
			<key>ID</key>
			<integer>1025</integer>
			<key>Name</key>
			<string>-12</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>52</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>26</integer>
			<key>ID</key>
			<integer>1026</integer>
			<key>Name</key>
			<string>+13</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>77</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>27</integer>
			<key>ID</key>
			<integer>1027</integer>
			<key>Name</key>
			<string>-13</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>51</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>28</integer>
			<key>ID</key>
			<integer>1028</integer>
			<key>Name</key>
			<string>+14</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>78</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>29</integer>
			<key>ID</key>
			<integer>1029</integer>
			<key>Name</key>
			<string>-14</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>50</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>30</integer>
			<key>ID</key>
			<integer>1030</integer>
			<key>Name</key>
			<string>+15</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>79</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>31</integer>
			<key>ID</key>
			<integer>1031</integer>
			<key>Name</key>
			<string>-15</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>49</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>32</integer>
			<key>ID</key>
			<integer>1032</integer>
			<key>Name</key>
			<string>+16</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>80</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>33</integer>
			<key>ID</key>
			<integer>1033</integer>
			<key>Name</key>
			<string>-16</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>48</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>34</integer>
			<key>ID</key>
			<integer>1034</integer>
			<key>Name</key>
			<string>+17</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>81</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>35</integer>
			<key>ID</key>
			<integer>1035</integer>
			<key>Name</key>
			<string>-17</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>47</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>36</integer>
			<key>ID</key>
			<integer>1036</integer>
			<key>Name</key>
			<string>+18</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>82</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>37</integer>
			<key>ID</key>
			<integer>1037</integer>
			<key>Name</key>
			<string>-18</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>46</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>38</integer>
			<key>ID</key>
			<integer>1038</integer>
			<key>Name</key>
			<string>+19</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>83</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>39</integer>
			<key>ID</key>
			<integer>1039</integer>
			<key>Name</key>
			<string>-19</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>45</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>40</integer>
			<key>ID</key>
			<integer>1040</integer>
			<key>Name</key>
			<string>+20</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>84</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>41</integer>
			<key>ID</key>
			<integer>1041</integer>
			<key>Name</key>
			<string>-20</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>44</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>42</integer>
			<key>ID</key>
			<integer>1042</integer>
			<key>Name</key>
			<string>+21</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>85</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>43</integer>
			<key>ID</key>
			<integer>1043</integer>
			<key>Name</key>
			<string>-21</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>43</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>44</integer>
			<key>ID</key>
			<integer>1044</integer>
			<key>Name</key>
			<string>+22</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>86</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>45</integer>
			<key>ID</key>
			<integer>1045</integer>
			<key>Name</key>
			<string>-22</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>42</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>46</integer>
			<key>ID</key>
			<integer>1046</integer>
			<key>Name</key>
			<string>+23</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>87</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>47</integer>
			<key>ID</key>
			<integer>1047</integer>
			<key>Name</key>
			<string>-23</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>41</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>48</integer>
			<key>ID</key>
			<integer>1048</integer>
			<key>Name</key>
			<string>+24</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>88</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>49</integer>
			<key>ID</key>
			<integer>1049</integer>
			<key>Name</key>
			<string>-24</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>40</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>50</integer>
			<key>ID</key>
			<integer>1050</integer>
			<key>Name</key>
			<string>+25</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>89</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>51</integer>
			<key>ID</key>
			<integer>1051</integer>
			<key>Name</key>
			<string>-25</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>39</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>52</integer>
			<key>ID</key>
			<integer>1052</integer>
			<key>Name</key>
			<string>+26</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>90</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>53</integer>
			<key>ID</key>
			<integer>1053</integer>
			<key>Name</key>
			<string>-26</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>38</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>54</integer>
			<key>ID</key>
			<integer>1054</integer>
			<key>Name</key>
			<string>+27</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>91</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>55</integer>
			<key>ID</key>
			<integer>1055</integer>
			<key>Name</key>
			<string>-27</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>37</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>56</integer>
			<key>ID</key>
			<integer>1056</integer>
			<key>Name</key>
			<string>+28</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>92</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>57</integer>
			<key>ID</key>
			<integer>1057</integer>
			<key>Name</key>
			<string>-28</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>36</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>58</integer>
			<key>ID</key>
			<integer>1058</integer>
			<key>Name</key>
			<string>+29</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>93</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>59</integer>
			<key>ID</key>
			<integer>1059</integer>
			<key>Name</key>
			<string>-29</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>35</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>60</integer>
			<key>ID</key>
			<integer>1060</integer>
			<key>Name</key>
			<string>+30</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>94</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>61</integer>
			<key>ID</key>
			<integer>1061</integer>
			<key>Name</key>
			<string>-30</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>34</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>62</integer>
			<key>ID</key>
			<integer>1062</integer>
			<key>Name</key>
			<string>+31</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>95</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>63</integer>
			<key>ID</key>
			<integer>1063</integer>
			<key>Name</key>
			<string>-31</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>33</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>64</integer>
			<key>ID</key>
			<integer>1064</integer>
			<key>Name</key>
			<string>+32</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>96</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>65</integer>
			<key>ID</key>
			<integer>1065</integer>
			<key>Name</key>
			<string>-32</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>32</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>66</integer>
			<key>ID</key>
			<integer>1066</integer>
			<key>Name</key>
			<string>+33</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>97</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>67</integer>
			<key>ID</key>
			<integer>1067</integer>
			<key>Name</key>
			<string>-33</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>31</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>68</integer>
			<key>ID</key>
			<integer>1068</integer>
			<key>Name</key>
			<string>+34</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>98</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>69</integer>
			<key>ID</key>
			<integer>1069</integer>
			<key>Name</key>
			<string>-34</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>30</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>70</integer>
			<key>ID</key>
			<integer>1070</integer>
			<key>Name</key>
			<string>+35</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>99</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>71</integer>
			<key>ID</key>
			<integer>1071</integer>
			<key>Name</key>
			<string>-35</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>29</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>72</integer>
			<key>ID</key>
			<integer>1072</integer>
			<key>Name</key>
			<string>+36</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>100</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>73</integer>
			<key>ID</key>
			<integer>1073</integer>
			<key>Name</key>
			<string>-36</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>28</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>74</integer>
			<key>ID</key>
			<integer>1074</integer>
			<key>Name</key>
			<string>+37</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>101</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>75</integer>
			<key>ID</key>
			<integer>1075</integer>
			<key>Name</key>
			<string>-37</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>27</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>76</integer>
			<key>ID</key>
			<integer>1076</integer>
			<key>Name</key>
			<string>+38</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>102</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>77</integer>
			<key>ID</key>
			<integer>1077</integer>
			<key>Name</key>
			<string>-38</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>26</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>78</integer>
			<key>ID</key>
			<integer>1078</integer>
			<key>Name</key>
			<string>+39</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>103</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>79</integer>
			<key>ID</key>
			<integer>1079</integer>
			<key>Name</key>
			<string>-39</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>25</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>80</integer>
			<key>ID</key>
			<integer>1080</integer>
			<key>Name</key>
			<string>+40</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>104</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>81</integer>
			<key>ID</key>
			<integer>1081</integer>
			<key>Name</key>
			<string>-40</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>24</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>82</integer>
			<key>ID</key>
			<integer>1082</integer>
			<key>Name</key>
			<string>+41</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>105</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>83</integer>
			<key>ID</key>
			<integer>1083</integer>
			<key>Name</key>
			<string>-41</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>23</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>84</integer>
			<key>ID</key>
			<integer>1084</integer>
			<key>Name</key>
			<string>+42</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>106</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>85</integer>
			<key>ID</key>
			<integer>1085</integer>
			<key>Name</key>
			<string>-42</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>22</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>86</integer>
			<key>ID</key>
			<integer>1086</integer>
			<key>Name</key>
			<string>+43</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>107</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>87</integer>
			<key>ID</key>
			<integer>1087</integer>
			<key>Name</key>
			<string>-43</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>21</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>88</integer>
			<key>ID</key>
			<integer>1088</integer>
			<key>Name</key>
			<string>+44</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>108</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>89</integer>
			<key>ID</key>
			<integer>1089</integer>
			<key>Name</key>
			<string>-44</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>20</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>90</integer>
			<key>ID</key>
			<integer>1090</integer>
			<key>Name</key>
			<string>+45</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>109</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>91</integer>
			<key>ID</key>
			<integer>1091</integer>
			<key>Name</key>
			<string>-45</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>19</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>92</integer>
			<key>ID</key>
			<integer>1092</integer>
			<key>Name</key>
			<string>+46</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>110</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>93</integer>
			<key>ID</key>
			<integer>1093</integer>
			<key>Name</key>
			<string>-46</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>18</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>94</integer>
			<key>ID</key>
			<integer>1094</integer>
			<key>Name</key>
			<string>+47</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>111</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>95</integer>
			<key>ID</key>
			<integer>1095</integer>
			<key>Name</key>
			<string>-47</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>17</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>96</integer>
			<key>ID</key>
			<integer>1096</integer>
			<key>Name</key>
			<string>+48</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>112</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>97</integer>
			<key>ID</key>
			<integer>1097</integer>
			<key>Name</key>
			<string>-48</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>16</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>98</integer>
			<key>ID</key>
			<integer>1098</integer>
			<key>Name</key>
			<string>+49</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>113</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>99</integer>
			<key>ID</key>
			<integer>1099</integer>
			<key>Name</key>
			<string>-49</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>15</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>100</integer>
			<key>ID</key>
			<integer>1100</integer>
			<key>Name</key>
			<string>+50</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>114</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>101</integer>
			<key>ID</key>
			<integer>1101</integer>
			<key>Name</key>
			<string>-50</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>14</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>102</integer>
			<key>ID</key>
			<integer>1102</integer>
			<key>Name</key>
			<string>+51</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>115</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>103</integer>
			<key>ID</key>
			<integer>1103</integer>
			<key>Name</key>
			<string>-51</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>13</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>104</integer>
			<key>ID</key>
			<integer>1104</integer>
			<key>Name</key>
			<string>+52</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>116</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>105</integer>
			<key>ID</key>
			<integer>1105</integer>
			<key>Name</key>
			<string>-52</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>12</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>106</integer>
			<key>ID</key>
			<integer>1106</integer>
			<key>Name</key>
			<string>+53</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>117</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>107</integer>
			<key>ID</key>
			<integer>1107</integer>
			<key>Name</key>
			<string>-53</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>11</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>108</integer>
			<key>ID</key>
			<integer>1108</integer>
			<key>Name</key>
			<string>+54</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>118</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>109</integer>
			<key>ID</key>
			<integer>1109</integer>
			<key>Name</key>
			<string>-54</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>10</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>110</integer>
			<key>ID</key>
			<integer>1110</integer>
			<key>Name</key>
			<string>+55</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>119</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>111</integer>
			<key>ID</key>
			<integer>1111</integer>
			<key>Name</key>
			<string>-55</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>9</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>112</integer>
			<key>ID</key>
			<integer>1112</integer>
			<key>Name</key>
			<string>+56</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>120</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>113</integer>
			<key>ID</key>
			<integer>1113</integer>
			<key>Name</key>
			<string>-56</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>8</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>114</integer>
			<key>ID</key>
			<integer>1114</integer>
			<key>Name</key>
			<string>+57</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>121</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>115</integer>
			<key>ID</key>
			<integer>1115</integer>
			<key>Name</key>
			<string>-57</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>7</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>116</integer>
			<key>ID</key>
			<integer>1116</integer>
			<key>Name</key>
			<string>+58</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>122</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>117</integer>
			<key>ID</key>
			<integer>1117</integer>
			<key>Name</key>
			<string>-58</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>6</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>118</integer>
			<key>ID</key>
			<integer>1118</integer>
			<key>Name</key>
			<string>+59</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>123</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>119</integer>
			<key>ID</key>
			<integer>1119</integer>
			<key>Name</key>
			<string>-59</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>5</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>120</integer>
			<key>ID</key>
			<integer>1120</integer>
			<key>Name</key>
			<string>+60</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>124</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>121</integer>
			<key>ID</key>
			<integer>1121</integer>
			<key>Name</key>
			<string>-60</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>4</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>122</integer>
			<key>ID</key>
			<integer>1122</integer>
			<key>Name</key>
			<string>+61</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>125</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>123</integer>
			<key>ID</key>
			<integer>1123</integer>
			<key>Name</key>
			<string>-61</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>3</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>124</integer>
			<key>ID</key>
			<integer>1124</integer>
			<key>Name</key>
			<string>+62</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>126</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>125</integer>
			<key>ID</key>
			<integer>1125</integer>
			<key>Name</key>
			<string>-62</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>2</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>126</integer>
			<key>ID</key>
			<integer>1126</integer>
			<key>Name</key>
			<string>+63</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>127</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>127</integer>
			<key>ID</key>
			<integer>1127</integer>
			<key>Name</key>
			<string>-63</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>1</integer>
				</dict>
			</array>
		</dict>
		<dict>
			<key>ArticulationID</key>
			<integer>128</integer>
			<key>ID</key>
			<integer>1128</integer>
			<key>Name</key>
			<string>-64</string>
			<key>Output</key>
			<array>
				<dict>
					<key>MB1</key>
					<integer>27</integer>
					<key>Status</key>
					<string>Controller</string>
					<key>ValueLow</key>
					<integer>0</integer>
				</dict>
			</array>
		</dict>
	</array>
	<key>Name</key>
	<string>ScriptEDOmicrodeviation_CC27_full_range</string>
	<key>Switches</key>
	<array/>
</dict>
</plist>