Divisi & Microtuning Engine — Flexible Voice Allocation with Chord and Bass Analysis

This MIDI engine combines flexible voice allocation with chord-based microtuning.
It is designed for ensembles whose instruments receive notes on separate MIDI channels.
Two allocation modes are available: Sorted and Stable.
The Voice Allocation control selects between them.

Sorted mode distributes notes according to their pitch.
The Note Priority control determines whether lower or higher notes take precedence when the voice limit is exceeded.
Selected notes are assigned to output channels in descending pitch order.
Changes in the played notes can therefore change existing channel assignments.

Stable mode assigns each newly played note to an available output channel.
The note retains that channel throughout its lifetime.
Adding or releasing other notes does not retrigger voices that remain active.
This makes Stable mode particularly useful for ensembles of identical instruments.
Note attacks and releases are processed immediately in this mode.
When all available channels are occupied, a new attack replaces the oldest voice.
A replaced note does not return automatically when a channel becomes available.
It must be played again to sound again.
The sustain pedal preserves both the sounding voice and its channel assignment.
Releasing the pedal frees channels belonging to notes whose keys have already been released.
Allocation mode changes take effect only after all Divisi keys and the sustain pedal are released.
A status message indicates the active mode and any pending change.

The Divisi Input Channel control selects the channel used for ensemble notes.
Divisi Voice Count provides between one and ten output voices.
Divisi output channels start at channel 1.
Chord analysis includes every held Divisi input note, even when fewer voices are available.
A separate bass input can contribute additional harmonic information.
Only bass notes below the lowest held Divisi note enter the chord analysis.
Setting Bass Input Channel to zero disables the external bass function.
Selecting the same input channel for bass and Divisi also disables external bass processing.
Bass events are routed to a dedicated output channel between 11 and 16.
That output channel is reserved for the bass.

Input mode enables microtuning only when the complete analyzed pitch-class set matches an allowed chord.
Supported forms are major, dominant seventh, major seventh, major add9, minor, minor add9, minor seventh, minor ninth, sus2, sus4, and power chords.
Octave duplicates do not change the recognized chord.
Additional pitch classes prevent recognition unless the resulting set matches another permitted form.
For major and minor families, the lowest analyzed note must generally be the root, third, or perfect fifth.
Minor seventh chords are accepted only with their root in the bass.
Minor ninth chords retain the general bass rule.
Suspended chords and power chords require the root or perfect fifth in the bass.
When suspended-chord interpretations are ambiguous, the engine prefers an interpretation whose root matches the bass.
Unsupported combinations disable microtuning across all output channels.
Releasing every Divisi key also disables microtuning immediately in Input mode.
Fixed mode instead applies tuning relative to a manually selected root.

Twelve tuning controls define offsets for the chromatic intervals relative to the active root.
Values are expressed in tenths of a cent, so a value of 153 represents 15.3 cents.
Each control ranges from −50 to +50 cents.
Microtuning is applied through channel-specific pitch bend.
The Pitch Bend Range setting must match the receiving instruments.
On a polyphonic bass or melody channel, the highest held note determines the shared tuning offset.
Manual pitch bend remains available and is combined with the microtuning offsets.
Chord Analysis Delay allows incoming note changes to settle before the harmonic tuning is updated.

```
// V3.1: English parameter labels; sorted Divisi or stable channels with oldest-voice stealing.
var
  // 0 = sorted Divisi; 1 = stable channels, immediate notes, oldest-voice stealing.
  // Change modes only after releasing all Divisi keys and the sustain pedal.
  VoiceMode_0Sorted_1Stable("Voice Allocation (0 Sorted / 1 Stable)") : Parameter 0..1 = 0
  // --- MAIN CONTROLS ---
  Mode_0Fixed_1Input("Root Mode (0 Fixed / 1 Input)") : Parameter 0..1 = 1
  FixedChordRoot("Fixed Root (1 C / 12 B)") : Parameter 1..12 = 1
  InputMidiChannel("Divisi Input Channel") : Parameter 1..16 = 2
  DivisiVoiceCount("Divisi Voice Count") : Parameter 1..10 = 4
  Priority_0Low_1High("Note Priority (0 Low / 1 High)") : Parameter 0..1 = 1
  PitchBendRange_Semi("Pitch Bend Range (Semitones)") : Parameter 1..24 = 2
  ChordDelay_ms("Chord Analysis Delay (ms)") : Parameter 0..100 = 70
 
  BassInputMidiChannel("Bass Input Channel (0 Off)") : Parameter 0..16 = 3
  BassOutputMidiChannel("Bass Output Channel") : Parameter 11..16 = 16
  // 0 = external bass disabled. Sharing the Divisi input channel disables external bass.
  // 0 = sorted allocation; 1 = stable channels, immediate notes, oldest-voice stealing.
  // Change modes only after releasing all Divisi keys and the sustain pedal.
   // Tuning values in 0.1 cent: 153 = 15.3 cents.
  Tune_1root("Root Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_2minorSecond("Minor Second Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_3MajorSecond("Major Second Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_4minorThird("Minor Third Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_5MajorThird("Major Third Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_6PerfectFourth("Perfect Fourth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_7diminishedFifth("Diminished Fifth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_8PerfectFifth("Perfect Fifth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_9minorSixth("Minor Sixth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_10MajorSixth("Major Sixth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_11minorSeventh("Minor Seventh Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_12MajorSeventh("Major Seventh Tuning (0.1 Cent)") : Parameter -500..500 = 0

  AccompHeldVelocities : Integer[128]
  AccompPlayingVoices : Integer[128]
  TargetVoices : Integer[128]
  SelectedNotes : Integer[128]
  BassHeldVelocities : Integer[128]
  BassPlayingVelocities : Integer[128]
  MelodyHeldCh1 : Integer[128]; MelodyHeldCh2 : Integer[128]; MelodyHeldCh3 : Integer[128]; MelodyHeldCh4 : Integer[128]
  MelodyHeldCh5 : Integer[128]; MelodyHeldCh6 : Integer[128]; MelodyHeldCh7 : Integer[128]; MelodyHeldCh8 : Integer[128]
  MelodyHeldCh9 : Integer[128]; MelodyHeldCh10 : Integer[128]; MelodyHeldCh11 : Integer[128]; MelodyHeldCh12 : Integer[128]
  MelodyHeldCh13 : Integer[128]; MelodyHeldCh14 : Integer[128]; MelodyHeldCh15 : Integer[128]; MelodyHeldCh16 : Integer[128]
  ActiveInputMidiChannel : Integer = 2
  ActiveBassInputMidiChannel : Integer = 3
  ActiveBassOutputMidiChannel : Integer = 16
  ActiveVoiceMode : Integer = 0
  ChannelPlayingNotes : Integer[11]
  VoiceStartOrder : Integer[11]
  VoiceOrderCounter : Integer = 0
  DivisiSustainDown : Boolean = false
  DivisiSustainValue : Integer = 0
  DisplayedVoiceMode : Integer = -1
  DisplayedRequestedMode : Integer = -1
  GlobalActiveRoot : Integer = 0
  MicroTuningActive : Boolean = false
  ChannelPBOffset : Integer[17]
  LastSentPB : Integer[17]
  GlobalPBValue : Integer = 8192
  PitchClasses : Integer[12]
  TargetTime : Double = 0.0
  EvaluationScheduled : Boolean = false
  ParameterUpdatePending : Boolean = true
Initialization
  var i : Integer
  for i = 0; i < 128; i = i + 1 do
    AccompHeldVelocities[i] = 0; AccompPlayingVoices[i] = 0
    TargetVoices[i] = 0; SelectedNotes[i] = 0
    BassHeldVelocities[i] = 0; BassPlayingVelocities[i] = 0
    MelodyHeldCh1[i] = 0; MelodyHeldCh2[i] = 0; MelodyHeldCh3[i] = 0; MelodyHeldCh4[i] = 0
    MelodyHeldCh5[i] = 0; MelodyHeldCh6[i] = 0; MelodyHeldCh7[i] = 0; MelodyHeldCh8[i] = 0
    MelodyHeldCh9[i] = 0; MelodyHeldCh10[i] = 0; MelodyHeldCh11[i] = 0; MelodyHeldCh12[i] = 0
    MelodyHeldCh13[i] = 0; MelodyHeldCh14[i] = 0; MelodyHeldCh15[i] = 0; MelodyHeldCh16[i] = 0
  end
  for i = 0; i <= 16; i = i + 1 do ChannelPBOffset[i] = 0; LastSentPB[i] = -1 end
  for i = 0; i < 12; i = i + 1 do PitchClasses[i] = 0 end
  for i = 0; i <= 10; i = i + 1 do ChannelPlayingNotes[i] = -1; VoiceStartOrder[i] = 0 end
  ActiveVoiceMode = VoiceMode_0Sorted_1Stable
  VoiceOrderCounter = 0; DivisiSustainDown = false; DivisiSustainValue = 0
  DisplayedVoiceMode = -1; DisplayedRequestedMode = -1
  ActiveInputMidiChannel = InputMidiChannel
  ActiveBassInputMidiChannel = BassInputMidiChannel
  ActiveBassOutputMidiChannel = BassOutputMidiChannel
  if ActiveBassInputMidiChannel == ActiveInputMidiChannel then ActiveBassInputMidiChannel = 0 end
  GlobalActiveRoot = 0; MicroTuningActive = false; GlobalPBValue = 8192
  TargetTime = 0.0; EvaluationScheduled = false; ParameterUpdatePending = true
  SetTimersRunning(true)
End
On Activate
  var i : Integer
  for i = 0; i < 128; i = i + 1 do
    AccompHeldVelocities[i] = 0; AccompPlayingVoices[i] = 0
    TargetVoices[i] = 0; SelectedNotes[i] = 0
    BassHeldVelocities[i] = 0; BassPlayingVelocities[i] = 0
    MelodyHeldCh1[i] = 0; MelodyHeldCh2[i] = 0; MelodyHeldCh3[i] = 0; MelodyHeldCh4[i] = 0
    MelodyHeldCh5[i] = 0; MelodyHeldCh6[i] = 0; MelodyHeldCh7[i] = 0; MelodyHeldCh8[i] = 0
    MelodyHeldCh9[i] = 0; MelodyHeldCh10[i] = 0; MelodyHeldCh11[i] = 0; MelodyHeldCh12[i] = 0
    MelodyHeldCh13[i] = 0; MelodyHeldCh14[i] = 0; MelodyHeldCh15[i] = 0; MelodyHeldCh16[i] = 0
  end
  for i = 0; i <= 16; i = i + 1 do ChannelPBOffset[i] = 0; LastSentPB[i] = -1 end
  for i = 0; i < 12; i = i + 1 do PitchClasses[i] = 0 end
  for i = 0; i <= 10; i = i + 1 do ChannelPlayingNotes[i] = -1; VoiceStartOrder[i] = 0 end
  ActiveVoiceMode = VoiceMode_0Sorted_1Stable
  VoiceOrderCounter = 0; DivisiSustainDown = false; DivisiSustainValue = 0
  DisplayedVoiceMode = -1; DisplayedRequestedMode = -1
  ActiveInputMidiChannel = InputMidiChannel
  ActiveBassInputMidiChannel = BassInputMidiChannel
  ActiveBassOutputMidiChannel = BassOutputMidiChannel
  if ActiveBassInputMidiChannel == ActiveInputMidiChannel then ActiveBassInputMidiChannel = 0 end
  GlobalActiveRoot = 0; MicroTuningActive = false; GlobalPBValue = 8192
  TargetTime = 0.0; EvaluationScheduled = false; ParameterUpdatePending = true
  SetTimersRunning(true)
End
Function GetMelodyVelocity(ch : Integer, noteNum : Integer) Returns Integer
  result = 0
  if ch == 1 then result = MelodyHeldCh1[noteNum]
  elsif ch == 2 then result = MelodyHeldCh2[noteNum]
  elsif ch == 3 then result = MelodyHeldCh3[noteNum]
  elsif ch == 4 then result = MelodyHeldCh4[noteNum]
  elsif ch == 5 then result = MelodyHeldCh5[noteNum]
  elsif ch == 6 then result = MelodyHeldCh6[noteNum]
  elsif ch == 7 then result = MelodyHeldCh7[noteNum]
  elsif ch == 8 then result = MelodyHeldCh8[noteNum]
  elsif ch == 9 then result = MelodyHeldCh9[noteNum]
  elsif ch == 10 then result = MelodyHeldCh10[noteNum]
  elsif ch == 11 then result = MelodyHeldCh11[noteNum]
  elsif ch == 12 then result = MelodyHeldCh12[noteNum]
  elsif ch == 13 then result = MelodyHeldCh13[noteNum]
  elsif ch == 14 then result = MelodyHeldCh14[noteNum]
  elsif ch == 15 then result = MelodyHeldCh15[noteNum]
  elsif ch == 16 then result = MelodyHeldCh16[noteNum]
  end
End
Function SetMelodyVelocity(ch : Integer, noteNum : Integer, vel : Integer)
  if ch == 1 then MelodyHeldCh1[noteNum] = vel
  elsif ch == 2 then MelodyHeldCh2[noteNum] = vel
  elsif ch == 3 then MelodyHeldCh3[noteNum] = vel
  elsif ch == 4 then MelodyHeldCh4[noteNum] = vel
  elsif ch == 5 then MelodyHeldCh5[noteNum] = vel
  elsif ch == 6 then MelodyHeldCh6[noteNum] = vel
  elsif ch == 7 then MelodyHeldCh7[noteNum] = vel
  elsif ch == 8 then MelodyHeldCh8[noteNum] = vel
  elsif ch == 9 then MelodyHeldCh9[noteNum] = vel
  elsif ch == 10 then MelodyHeldCh10[noteNum] = vel
  elsif ch == 11 then MelodyHeldCh11[noteNum] = vel
  elsif ch == 12 then MelodyHeldCh12[noteNum] = vel
  elsif ch == 13 then MelodyHeldCh13[noteNum] = vel
  elsif ch == 14 then MelodyHeldCh14[noteNum] = vel
  elsif ch == 15 then MelodyHeldCh15[noteNum] = vel
  elsif ch == 16 then MelodyHeldCh16[noteNum] = vel
  end
End
Function GetTuningOffset(interval : Integer) Returns Integer
  var centsTenths : Integer = 0
  var pbRangeSemi : Integer = PitchBendRange_Semi
  if MicroTuningActive then
    if interval == 0 then centsTenths = Tune_1root
    elsif interval == 1 then centsTenths = Tune_2minorSecond
    elsif interval == 2 then centsTenths = Tune_3MajorSecond
    elsif interval == 3 then centsTenths = Tune_4minorThird
    elsif interval == 4 then centsTenths = Tune_5MajorThird
    elsif interval == 5 then centsTenths = Tune_6PerfectFourth
    elsif interval == 6 then centsTenths = Tune_7diminishedFifth
    elsif interval == 7 then centsTenths = Tune_8PerfectFifth
    elsif interval == 8 then centsTenths = Tune_9minorSixth
    elsif interval == 9 then centsTenths = Tune_10MajorSixth
    elsif interval == 10 then centsTenths = Tune_11minorSeventh
    elsif interval == 11 then centsTenths = Tune_12MajorSeventh
    end
    if pbRangeSemi < 1 then pbRangeSemi = 1 end
    result = (centsTenths * 8192) / (pbRangeSemi * 1000)
  else
    result = 0
  end
End
// Analyze all Divisi notes and ONLY lower notes from the selected bass input.
// Check each permitted chord shape against the actual lowest analyzed note.
Function FindRoot() Returns Integer
  var i : Integer
  var lowestDivisi : Integer = -1
  var lowestNote : Integer = -1
  var pitchClassCount : Integer = 0
  var foundRoot : Integer = -1
  var bassInterval : Integer
  var candidateAllowed : Boolean
  var fixedRootValue : Integer = FixedChordRoot
  if Mode_0Fixed_1Input == 0 then
    result = fixedRootValue - 1
  else
    for i = 0; i < 12; i = i + 1 do PitchClasses[i] = 0 end
    for i = 0; i < 128; i = i + 1 do
      if AccompHeldVelocities[i] > 0 then
        PitchClasses[i % 12] = 1
        if lowestDivisi == -1 then lowestDivisi = i end
      end
    end
    if lowestDivisi >= 0 then
      lowestNote = lowestDivisi
      if ActiveBassInputMidiChannel > 0 then
        for i = 0; i < lowestDivisi; i = i + 1 do
          if BassHeldVelocities[i] > 0 then
            PitchClasses[i % 12] = 1
            if i < lowestNote then lowestNote = i end
          end
        end
      end
      for i = 0; i < 12; i = i + 1 do
        if PitchClasses[i] == 1 then pitchClassCount = pitchClassCount + 1 end
      end
      for i = 0; i < 12; i = i + 1 do
        candidateAllowed = false
        bassInterval = (lowestNote % 12 - i + 12) % 12
        if PitchClasses[i] == 1 then
          // Major, dominant 7, major 7, major add9: bass must be root, third or fifth.
          if PitchClasses[(i + 4) % 12] == 1 and PitchClasses[(i + 7) % 12] == 1 then
            if bassInterval == 0 or bassInterval == 4 or bassInterval == 7 then
              if pitchClassCount == 3 then candidateAllowed = true
              elsif pitchClassCount == 4 and (PitchClasses[(i + 2) % 12] == 1 or PitchClasses[(i + 10) % 12] == 1 or PitchClasses[(i + 11) % 12] == 1) then
                candidateAllowed = true
              end
            end
          end
          // Minor and minor add9: normal bass rule. Minor 7: root bass ONLY.
          // Minor 9 permits the root, minor third or perfect fifth in the bass.
          if PitchClasses[(i + 3) % 12] == 1 and PitchClasses[(i + 7) % 12] == 1 then
            if bassInterval == 0 or bassInterval == 3 or bassInterval == 7 then
              if pitchClassCount == 3 then candidateAllowed = true
              elsif pitchClassCount == 4 then
                if PitchClasses[(i + 2) % 12] == 1 then candidateAllowed = true
                elsif PitchClasses[(i + 10) % 12] == 1 and bassInterval == 0 then candidateAllowed = true
                end
              elsif pitchClassCount == 5 and PitchClasses[(i + 2) % 12] == 1 and PitchClasses[(i + 10) % 12] == 1 then
                candidateAllowed = true
              end
            end
          end
          // Power chords, sus2 and sus4: bass must be root or perfect fifth.
          if PitchClasses[(i + 7) % 12] == 1 and (bassInterval == 0 or bassInterval == 7) then
            if pitchClassCount == 2 then candidateAllowed = true
            elsif pitchClassCount == 3 and (PitchClasses[(i + 2) % 12] == 1 or PitchClasses[(i + 5) % 12] == 1) then
              candidateAllowed = true
            end
          end
          // For ambiguous suspended chords, prefer the bass as root.
          if candidateAllowed then
            if foundRoot == -1 or bassInterval == 0 then foundRoot = i end
          end
        end
      end
    end
    result = foundRoot
  end
End
Function SendPitchBendToChannels()
  var i : Integer
  var finalPB : Integer
  for i = 1; i <= 16; i = i + 1 do
    finalPB = GlobalPBValue + ChannelPBOffset[i]
    if finalPB < 0 then finalPB = 0 end
    if finalPB > 16383 then finalPB = 16383 end
    if finalPB != LastSentPB[i] then
      SendNow(MakePitchBendMessageEx(finalPB, i))
      LastSentPB[i] = finalPB
    end
  end
End
Function ResetChannelPitchBend(ch : Integer)
  ChannelPBOffset[ch] = 0
  SendNow(MakePitchBendMessageEx(GlobalPBValue, ch))
  LastSentPB[ch] = GlobalPBValue
End
// Bass and melody each use the highest held note on their channel.
Function UpdateMelodyPB()
  var maxEngineCh : Integer = DivisiVoiceCount
  var ch : Integer
  var i : Integer
  var highestNote : Integer
  var interval : Integer
  if MicroTuningActive then
    for ch = 1; ch <= 16; ch = ch + 1 do
      if ch > maxEngineCh then
        ChannelPBOffset[ch] = 0
        highestNote = -1
        for i = 0; i < 128; i = i + 1 do
          if ch == ActiveBassOutputMidiChannel then
            if BassHeldVelocities[i] > 0 then highestNote = i end
          elsif GetMelodyVelocity(ch, i) > 0 then highestNote = i
          end
        end
        if highestNote >= 0 then
          interval = (highestNote % 12 - GlobalActiveRoot + 12) % 12
          ChannelPBOffset[ch] = GetTuningOffset(interval)
        end
      end
    end
  else
    for ch = 1; ch <= 16; ch = ch + 1 do ChannelPBOffset[ch] = 0 end
  end
  SendPitchBendToChannels()
End
// Update pitch bend only: chord changes never retrigger notes in stable mode.
Function RefreshVoicePitchBend()
  var i : Integer
  var chTarget : Integer
  var interval : Integer
  for i = 1; i <= 16; i = i + 1 do ChannelPBOffset[i] = 0 end
  for i = 0; i < 128; i = i + 1 do
    chTarget = TargetVoices[i]
    if chTarget > 0 then
      interval = (i % 12 - GlobalActiveRoot + 12) % 12
      ChannelPBOffset[chTarget] = GetTuningOffset(interval)
    end
  end
  UpdateMelodyPB()
End
Function ClearStableVoice(noteNum : Integer)
  var ch : Integer = AccompPlayingVoices[noteNum]
  if ch > 0 then
    ChannelPlayingNotes[ch] = -1
    VoiceStartOrder[ch] = 0
  end
  AccompPlayingVoices[noteNum] = 0
  TargetVoices[noteNum] = 0
  SelectedNotes[noteNum] = 0
End
// A stolen note still participates in input analysis but never returns automatically.
Function ForceEndStableVoice(noteNum : Integer)
  var ch : Integer = AccompPlayingVoices[noteNum]
  if ch > 0 then
    if DivisiSustainDown then SendNow(MakeControlChangeMessageEx(64, 0, ch)) end
    SendNow(MakeNoteMessageEx(noteNum, 0, ch))
    if DivisiSustainDown then SendNow(MakeControlChangeMessageEx(120, 0, ch)) end
    ClearStableVoice(noteNum)
  end
End
Function StartStableVoice(noteNum : Integer, vel : Integer)
  var voiceLimit : Integer = DivisiVoiceCount
  var ch : Integer = AccompPlayingVoices[noteNum]
  var i : Integer
  var oldestOrder : Integer = 0
  // A genuine new attack on the same note uses its previous channel.
  if ch > 0 then
    ForceEndStableVoice(noteNum)
  else
    ch = 0
    for i = 1; i <= voiceLimit; i = i + 1 do
      if ch == 0 and ChannelPlayingNotes[i] < 0 then ch = i end
    end
    if ch == 0 then
      for i = 1; i <= voiceLimit; i = i + 1 do
        if ch == 0 or VoiceStartOrder[i] < oldestOrder then
          ch = i
          oldestOrder = VoiceStartOrder[i]
        end
      end
      if ch > 0 then ForceEndStableVoice(ChannelPlayingNotes[ch]) end
    end
  end
  if ch > 0 then
    VoiceOrderCounter = VoiceOrderCounter + 1
    VoiceStartOrder[ch] = VoiceOrderCounter
    ChannelPlayingNotes[ch] = noteNum
    AccompPlayingVoices[noteNum] = ch
    TargetVoices[noteNum] = ch
    SelectedNotes[noteNum] = 1
    // Newly used channels also receive the current sustain pedal value.
    if DivisiSustainDown then SendNow(MakeControlChangeMessageEx(64, DivisiSustainValue, ch)) end
    RefreshVoicePitchBend()
    SendNow(MakeNoteMessageEx(noteNum, vel, ch))
  end
End
Function EnforceStableVoiceLimit()
  var voiceLimit : Integer = DivisiVoiceCount
  var ch : Integer
  var changed : Boolean = false
  if ActiveVoiceMode == 1 then
    for ch = voiceLimit + 1; ch <= 10; ch = ch + 1 do
      if ChannelPlayingNotes[ch] >= 0 then
        ForceEndStableVoice(ChannelPlayingNotes[ch])
        changed = true
      end
    end
    if changed then RefreshVoicePitchBend() end
  end
End
Function ApplyVoiceMode()
  var requestedMode : Integer = VoiceMode_0Sorted_1Stable
  var i : Integer
  var busy : Boolean = DivisiSustainDown
  for i = 0; i < 128; i = i + 1 do
    if AccompHeldVelocities[i] > 0 or AccompPlayingVoices[i] > 0 then busy = true end
  end
  if requestedMode != ActiveVoiceMode and not busy then
    ActiveVoiceMode = requestedMode
    for i = 0; i <= 10; i = i + 1 do ChannelPlayingNotes[i] = -1; VoiceStartOrder[i] = 0 end
    VoiceOrderCounter = 0
    ParameterUpdatePending = true
  end
  if DisplayedVoiceMode != ActiveVoiceMode or DisplayedRequestedMode != requestedMode then
    if ActiveVoiceMode == 0 then
      if requestedMode == 0 then SetDisplayMessage("Sorted: channel assignment follows pitch.")
      else
        SetDisplayMessage("Sorted. Stable mode pending: release all Divisi keys and the sustain pedal.")
      end
    else
      if requestedMode == 1 then SetDisplayMessage("Stable: when all voices are busy, a new attack replaces the oldest voice.")
      else
        SetDisplayMessage("Stable. Sorted mode pending: release all Divisi keys and the sustain pedal.")
      end
    end
    DisplayedVoiceMode = ActiveVoiceMode
    DisplayedRequestedMode = requestedMode
  end
End
Function EvaluateAccompaniment()
  var voiceLimit : Integer = DivisiVoiceCount
  var i : Integer
  var voiceCount : Integer = 0
  var currentCh : Integer = 1
  var analyzedRoot : Integer
  analyzedRoot = FindRoot()
  MicroTuningActive = false
  if analyzedRoot >= 0 then
    GlobalActiveRoot = analyzedRoot
    MicroTuningActive = true
  end
  if ActiveVoiceMode == 1 then
    // Keep channel assignments for sustained notes; stolen notes have no output.
    for i = 0; i < 128; i = i + 1 do
      TargetVoices[i] = AccompPlayingVoices[i]
      SelectedNotes[i] = 0
      if TargetVoices[i] > 0 then SelectedNotes[i] = 1 end
    end
    RefreshVoicePitchBend()
  else
    // Original allocation: select low/high notes, then assign in descending pitch order.
    for i = 0; i < 128; i = i + 1 do TargetVoices[i] = 0; SelectedNotes[i] = 0 end
    if Priority_0Low_1High == 0 then
      for i = 0; i < 128; i = i + 1 do
        if AccompHeldVelocities[i] > 0 and voiceCount < voiceLimit then
          SelectedNotes[i] = 1; voiceCount = voiceCount + 1
        end
      end
    else
      for i = 127; i >= 0; i = i - 1 do
        if AccompHeldVelocities[i] > 0 and voiceCount < voiceLimit then
          SelectedNotes[i] = 1; voiceCount = voiceCount + 1
        end
      end
    end
    for i = 127; i >= 0; i = i - 1 do
      if SelectedNotes[i] == 1 and currentCh <= voiceLimit and currentCh <= 10 then
        TargetVoices[i] = currentCh
        currentCh = currentCh + 1
      end
    end
    RefreshVoicePitchBend()
    for i = 0; i < 128; i = i + 1 do
      if AccompPlayingVoices[i] > 0 and TargetVoices[i] != AccompPlayingVoices[i] then
        SendNow(MakeNoteMessageEx(i, 0, AccompPlayingVoices[i]))
      end
    end
    for i = 0; i < 128; i = i + 1 do
      if TargetVoices[i] > 0 and TargetVoices[i] != AccompPlayingVoices[i] then
        SendNow(MakeNoteMessageEx(i, AccompHeldVelocities[i], TargetVoices[i]))
      end
      AccompPlayingVoices[i] = TargetVoices[i]
    end
  end
End
Function ScheduleChordEvaluation()
  var i : Integer
  var keysHeldCount : Integer = 0
  var delayMs : Integer = ChordDelay_ms
  for i = 0; i < 128; i = i + 1 do
    if AccompHeldVelocities[i] > 0 then keysHeldCount = keysHeldCount + 1 end
  end
  if keysHeldCount == 0 then
    EvaluationScheduled = false
    EvaluateAccompaniment()
  else
    TargetTime = TimeSinceStartup() + IntToFloat(delayMs)
    EvaluationScheduled = true
  end
End
Function StopMelodyChannel(ch : Integer)
  var i : Integer
  var maxEngineCh : Integer = DivisiVoiceCount
  // Also end notes held by the pedal after their keys have been released.
  // Do not send channel-wide All Notes Off on occupied Divisi outputs.
  if ch > maxEngineCh then SendNow(MakeControlChangeMessageEx(64, 0, ch)) end
  for i = 0; i < 128; i = i + 1 do
    if GetMelodyVelocity(ch, i) > 0 then SendNow(MakeNoteMessageEx(i, 0, ch)) end
    SetMelodyVelocity(ch, i, 0)
  end
  if ch > maxEngineCh then
    SendNow(MakeControlChangeMessageEx(123, 0, ch))
    ResetChannelPitchBend(ch)
  end
End
Function StopBassOutput()
  var i : Integer
  SendNow(MakeControlChangeMessageEx(64, 0, ActiveBassOutputMidiChannel))
  for i = 0; i < 128; i = i + 1 do
    if BassPlayingVelocities[i] > 0 then
      SendNow(MakeNoteMessageEx(i, 0, ActiveBassOutputMidiChannel))
    end
    BassPlayingVelocities[i] = 0
  end
  SendNow(MakeControlChangeMessageEx(123, 0, ActiveBassOutputMidiChannel))
  ResetChannelPitchBend(ActiveBassOutputMidiChannel)
End
// Check before each event so routing changes cannot leave stale trackers.
Function ApplyRoutingChanges()
  var newInput : Integer = InputMidiChannel
  var newBassInput : Integer = BassInputMidiChannel
  var newBassOutput : Integer = BassOutputMidiChannel
  var i : Integer
  if newBassInput == newInput then newBassInput = 0 end
  if newInput != ActiveInputMidiChannel then
    for i = 0; i < 128; i = i + 1 do
      if AccompPlayingVoices[i] > 0 then
        SendNow(MakeNoteMessageEx(i, 0, AccompPlayingVoices[i]))
      end
      AccompHeldVelocities[i] = 0; AccompPlayingVoices[i] = 0
      TargetVoices[i] = 0; SelectedNotes[i] = 0
    end
    for i = 1; i <= 10; i = i + 1 do
      if ChannelPlayingNotes[i] >= 0 then ChannelPlayingNotes[i] = -1 end
      VoiceStartOrder[i] = 0
    end
    if DivisiSustainDown then
      for i = 1; i <= DivisiVoiceCount; i = i + 1 do
        SendNow(MakeControlChangeMessageEx(64, 0, i))
        SendNow(MakeControlChangeMessageEx(120, 0, i))
      end
    end
    DivisiSustainDown = false; DivisiSustainValue = 0
    if newInput != ActiveBassOutputMidiChannel then StopMelodyChannel(newInput) end
    ActiveInputMidiChannel = newInput
    EvaluationScheduled = false
    ParameterUpdatePending = true
  end
  if newBassInput != ActiveBassInputMidiChannel or newBassOutput != ActiveBassOutputMidiChannel then
    StopBassOutput()
    if newBassInput != ActiveBassInputMidiChannel then
      for i = 0; i < 128; i = i + 1 do BassHeldVelocities[i] = 0 end
      if newBassInput > 0 then StopMelodyChannel(newBassInput) end
    end
    if newBassOutput != ActiveBassOutputMidiChannel then StopMelodyChannel(newBassOutput) end
    ActiveBassInputMidiChannel = newBassInput
    ActiveBassOutputMidiChannel = newBassOutput
    UpdateMelodyPB()
    for i = 0; i < 128; i = i + 1 do
      if BassHeldVelocities[i] > 0 then
        SendNow(MakeNoteMessageEx(i, BassHeldVelocities[i], ActiveBassOutputMidiChannel))
        BassPlayingVelocities[i] = BassHeldVelocities[i]
      end
    end
    ParameterUpdatePending = true
  end
  ApplyVoiceMode()
  EnforceStableVoiceLimit()
End
On ParameterValueChanged
  Matching Mode_0Fixed_1Input, FixedChordRoot, InputMidiChannel, DivisiVoiceCount,
           Priority_0Low_1High, PitchBendRange_Semi, ChordDelay_ms,
           Tune_1root, Tune_2minorSecond, Tune_3MajorSecond,
           Tune_4minorThird, Tune_5MajorThird, Tune_6PerfectFourth,
           Tune_7diminishedFifth, Tune_8PerfectFifth, Tune_9minorSixth,
           Tune_10MajorSixth, Tune_11minorSeventh, Tune_12MajorSeventh,
           BassInputMidiChannel, BassOutputMidiChannel, VoiceMode_0Sorted_1Stable
  ParameterUpdatePending = true
End
On TimerTick(ms : Double)
  ApplyRoutingChanges()
  if ParameterUpdatePending then
    ParameterUpdatePending = false
    EvaluateAccompaniment()
  end
  if EvaluationScheduled and TimeSinceStartup() >= TargetTime then
    EvaluationScheduled = false
    EvaluateAccompaniment()
  end
End
On NoteEvent(m : NoteMessage)
  var noteNum : Integer = GetNoteNumber(m)
  var vel : Integer = GetVelocity(m)
  var ch : Integer = GetChannel(m)
  ApplyRoutingChanges()
  if ch == ActiveInputMidiChannel then
    if ActiveVoiceMode == 1 then
      if IsNoteOn(m) and vel > 0 then
        AccompHeldVelocities[noteNum] = vel
        StartStableVoice(noteNum, vel)
      else
        AccompHeldVelocities[noteNum] = 0
        if AccompPlayingVoices[noteNum] > 0 then
          SendNow(MakeNoteMessageEx(noteNum, 0, AccompPlayingVoices[noteNum]))
          if not DivisiSustainDown then ClearStableVoice(noteNum) end
          RefreshVoicePitchBend()
        end
      end
    else
      if IsNoteOn(m) and vel > 0 then
        AccompHeldVelocities[noteNum] = vel
        if AccompPlayingVoices[noteNum] > 0 then
          SendNow(MakeNoteMessageEx(noteNum, 0, AccompPlayingVoices[noteNum]))
          AccompPlayingVoices[noteNum] = 0
        end
      else
        AccompHeldVelocities[noteNum] = 0
      end
    end
    ScheduleChordEvaluation()
    ApplyVoiceMode()
  elsif ActiveBassInputMidiChannel > 0 and ch == ActiveBassInputMidiChannel then
    if IsNoteOn(m) and vel > 0 then
      BassHeldVelocities[noteNum] = vel
      if BassPlayingVelocities[noteNum] > 0 then
        SendNow(MakeNoteMessageEx(noteNum, 0, ActiveBassOutputMidiChannel))
      end
      UpdateMelodyPB()
      SendNow(MakeNoteMessageEx(noteNum, vel, ActiveBassOutputMidiChannel))
      BassPlayingVelocities[noteNum] = vel
    else
      BassHeldVelocities[noteNum] = 0; BassPlayingVelocities[noteNum] = 0
      SendNow(MakeNoteMessageEx(noteNum, 0, ActiveBassOutputMidiChannel))
      UpdateMelodyPB()
    end
    ScheduleChordEvaluation()
  elsif ch != ActiveBassOutputMidiChannel then
    if IsNoteOn(m) and vel > 0 then
      SetMelodyVelocity(ch, noteNum, vel)
      UpdateMelodyPB()
      SendNow(m)
    else
      SetMelodyVelocity(ch, noteNum, 0)
      SendNow(m)
      UpdateMelodyPB()
    end
  end
End
On ControlChangeEvent(m : ControlChangeMessage)
  var i : Integer
  var ccNum : Integer = GetCCNumber(m)
  var ccVal : Integer = GetCCValue(m)
  var ch : Integer = GetChannel(m)
  ApplyRoutingChanges()
  if ch == ActiveInputMidiChannel then
    if ccNum == 64 then
      DivisiSustainValue = ccVal
      DivisiSustainDown = ccVal >= 64
    end
    for i = 1; i <= DivisiVoiceCount; i = i + 1 do
      if i <= 10 then SendNow(MakeControlChangeMessageEx(ccNum, ccVal, i)) end
    end
    if ccNum == 64 and not DivisiSustainDown and ActiveVoiceMode == 1 then
      for i = 0; i < 128; i = i + 1 do
        if AccompHeldVelocities[i] == 0 and AccompPlayingVoices[i] > 0 then ClearStableVoice(i) end
      end
      RefreshVoicePitchBend()
    end
    if ccNum == 120 or ccNum == 123 then
      for i = 1; i <= DivisiVoiceCount; i = i + 1 do SendNow(MakeControlChangeMessageEx(64, 0, i)) end
      for i = 0; i < 128; i = i + 1 do
        if AccompPlayingVoices[i] > 0 then SendNow(MakeNoteMessageEx(i, 0, AccompPlayingVoices[i])) end
        AccompHeldVelocities[i] = 0; AccompPlayingVoices[i] = 0
        TargetVoices[i] = 0; SelectedNotes[i] = 0
      end
      for i = 0; i <= 10; i = i + 1 do ChannelPlayingNotes[i] = -1; VoiceStartOrder[i] = 0 end
      DivisiSustainDown = false; DivisiSustainValue = 0
      ScheduleChordEvaluation()
    end
    ApplyVoiceMode()
  elsif ActiveBassInputMidiChannel > 0 and ch == ActiveBassInputMidiChannel then
    SendNow(MakeControlChangeMessageEx(ccNum, ccVal, ActiveBassOutputMidiChannel))
  elsif ch != ActiveBassOutputMidiChannel then
    SendNow(m)
  end
End
On PitchBendEvent(m : PitchBendMessage)
  ApplyRoutingChanges()
  GlobalPBValue = GetPitchBendValue(m)
  SendPitchBendToChannels()
End
On MidiEvent(m : MidiMessage)
  var i : Integer
  var ch : Integer = GetChannel(m)
  ApplyRoutingChanges()
  if ch == ActiveInputMidiChannel then
    for i = 1; i <= DivisiVoiceCount; i = i + 1 do
      if i <= 10 then SendNow(WithChannel(m, i)) end
    end
  elsif ActiveBassInputMidiChannel > 0 and ch == ActiveBassInputMidiChannel then
    SendNow(WithChannel(m, ActiveBassOutputMidiChannel))
  elsif ch != ActiveBassOutputMidiChannel then
    SendNow(m)
  end
End
```

Intelligent Voice Allocation, Chord Recognition and Live MIDI Control

Update to Version 4.0: Added CC controls, voice leading mode and separate natural seventh parameter

Divisi & Microtuning Engine V4.0 distributes incoming MIDI notes across individual instrument channels, identifies supported chords and applies adjustable microtuning relative to the detected root. It combines three voice allocation modes with dedicated bass routing, configurable chord acceptance and assignable MIDI CC control.

The engine supports up to ten Divisi voices on output channels 1–10. A separate bass output can be selected from channels 11–16. Chord recognition always considers every held Divisi input note, even when fewer voices are available for playback.

Three Voice Allocation Modes

0 — Sorted

Sorted mode selects notes according to the voice count and Low/High Priority setting, then assigns the selected notes in descending pitch order to consecutive output channels.

The highest selected note goes to channel 1, the next highest to channel 2, and so on. When the held note set changes, the engine recalculates this distribution. A note may therefore move to another channel and be retriggered.

Use Sorted when each output channel represents a defined position within the current voicing, such as the highest, second-highest or lowest selected part.

1 — Stable

Stable mode assigns each new note to an available channel and keeps that assignment until the voice is released or replaced. Adding another note does not redistribute existing voices.

When all available channels are occupied, a new attack replaces the oldest assigned voice. A displaced note remains part of chord recognition while its input key is held, but it does not automatically return when a channel becomes available.

A genuine repeated attack on an already assigned pitch uses that pitch’s existing channel. Chord recognition and tuning changes update pitch bend without retriggering the assigned voices.

Use Stable for immediate response and consistent channel ownership, particularly with ensembles of identical instruments.

2 — Voice Leading

Voice Leading preserves held and sustained voices on their existing channels while assigning new notes according to each available channel’s previous pitch.

Each channel remembers its last actually played MIDI note. This memory remains available after release and across pauses. The engine uses it to reduce the total pitch movement between successive groups of notes.

Voice Leading preserves the exact input pitches. It does not transpose notes, change octaves or generate a different chord voicing.

New attacks are collected during a configurable window. At the end of that window, the engine assigns them together:

  1. Held, sustained and otherwise reserved channels remain protected.
  2. Available channels with pitch memory are preferred.
  3. The total absolute pitch movement is minimized.
  4. Equal solutions are resolved deterministically by choosing the lowest channel sequence for the new notes sorted in descending pitch order.

Without pitch memory, the first group follows the Sorted ordering: descending pitches are assigned to the lowest available channels.

For example, if channels 1–3 previously played G–E–B, a completely new D–F–A group is assigned as F–D–A. This produces smaller total movement than assigning the new pitches solely by their current order.

If E and G remain held while C major changes to E minor, E and G retain their channels. Only the newly played B needs an assignment.

When more new notes arrive than there are available channels, Low/High Priority selects among those new notes. Existing voices are never displaced. Unselected notes remain silent until played again, although held input notes still participate in chord recognition.

Voice Leading Timing and Memory

Voice Leading Collection Time sets the collection window from 0 to 100 ms, with a default of 20 ms.

The first new attack establishes a fixed deadline. Subsequent attacks join the same window without extending it. Changing the collection time affects the next window; an existing window keeps its original deadline.

At 0 ms, allocation is immediate and may depend on the order in which notes arrive.

Note-offs for already sounding voices are processed immediately. With sustain engaged, a released voice continues to occupy its channel until the pedal releases it.

Short notes released before their collection window ends are preserved. They are played after collection and then released according to their original duration, with a minimum duration of 1 ms. Buffered repeated attacks remain separate and retain their relative timing on the same assigned channel.

The event buffer is bounded. If it becomes full, additional attacks are discarded while existing releases remain protected. An English status message reports the overload.

Reset Voice Leading Memory is a momentary control. Setting it to 1 triggers a reset and returns the control to 0. Free channels forget their previous pitches; occupied channels retain their current notes as reference points. The reset does not stop voices or change their tuning.

Memory is also cleared on activation, a completed allocation-mode change, a Divisi input-channel change or Divisi panic.

Reducing the voice count safely ends voices outside the new channel range and clears their memory. Increasing the count does not automatically restore previously silent held notes.

Allocation-mode changes are deferred until Divisi keys, sustain, sounding voices and pending Voice Leading events have been released or completed.

Chord Recognition

In Input mode, the engine analyzes the complete set of held Divisi input notes. Recognition is independent of the number of voices being played.

Octave doublings do not change the recognized pitch-class set. Additional foreign pitch classes prevent a match. A supported triad contained inside a larger, unsupported chord is insufficient to activate microtuning.

The selected bass input can contribute held notes below the lowest held Divisi note. Higher bass notes do not enter the analysis. Notes from other input channels do not influence chord recognition.

The lowest analyzed note determines the actual bass position used to validate the chord.

Chord Analysis Delay controls the harmonic evaluation delay separately from Voice Leading collection. When harmonic evaluation and voice allocation become due together, tuning is updated first.

In Input mode, releasing all Divisi keys immediately disables microtuning, including for any remaining sustained ensemble voices, bass notes or held melody notes.

Chord Acceptance Levels

Chord Acceptance Level provides nine cumulative levels. Each level includes the forms enabled by the preceding levels.

Level Additional accepted forms
0 Major triads with the root in the bass
1 Major triads with the third or fifth in the bass
2 Minor triads and permitted inversions
3 Power chords, sus2 and sus4
4 Major add9 and minor add9
5 Major seventh and minor seventh
6 Dominant seventh
7 Minor ninth
8 Major ninth, dominant ninth, dominant seventh sus4 and dominant ninth sus4

All recognized forms require the root to be present and the complete pitch-class set to match.

Major and minor families generally require the root, third or perfect fifth in the bass. Minor seventh chords have a stricter rule: only root-position bass is accepted. Minor ninth chords retain the general minor-family bass rule.

Power and suspended forms require the root or perfect fifth in the bass. When a suspended set supports multiple interpretations, the interpretation with the bass as root takes precedence.

Consequently, Am7/A is accepted at the appropriate level, while C6/C, which contains the same pitch classes as Am7/C, does not activate microtuning. Chords with an unsupported seventh or foreign tone in the bass are rejected.

Level 7 preserves the previous chord acceptance scope. Level 8 introduces the additional ninth and suspended-seventh forms.

Microtuning and Root Modes

The engine provides twelve interval-tuning controls, from the root through the major seventh. Each has a range of −500 to +500 in units of 0.1 cent. For example, a value of 153 raises the interval by 15.3 cents.

In Fixed mode, tuning is calculated relative to the selected Fixed Root. Automatic chord acceptance and the optional natural-seventh rule are bypassed.

In Input mode, tuning is active only when the complete analyzed note set matches an accepted chord with a permitted bass position.

Microtuning Enabled is the master switch for both root modes. Turning it off immediately clears the microtuning offsets on all sixteen output channels, including held notes. Normal pitch bend remains available.

Turning it on recalculates the current tuning. Stable and Voice Leading assignments remain intact, and tuning changes do not generate additional attacks.

Each Divisi channel receives the offset for its assigned note. On a polyphonic bass or melody channel, the highest held note determines the shared channel offset.

Pitch Bend Range must match the receiving instrument’s configured range for the tuning amounts to be correct.

Optional Natural Seventh

Natural Seventh for Dominant 7 optionally replaces the normal minor-seventh offset for accepted dominant seventh chords in Input mode.

When enabled, the seventh uses an offset approximately 31.2 cents below equal temperament, relative to the root’s tuning adjustment. The rule applies to accepted dominant seventh chords with the root, third or fifth in the bass.

Minor seventh, ninth and suspended-seventh chords continue to use the regular Minor Seventh Tuning control. Fixed mode also retains the normal interval-tuning settings.

Dedicated Bass Routing

Bass Input Channel selects the external bass source. A value of 0 disables the external bass function. Selecting the same input channel as Divisi also disables the separate bass function.

Bass Output Channel selects a reserved output from channels 11–16. Bass notes and associated channel messages are redirected to this output.

Bass Chord Analysis Enabled controls only whether eligible bass notes contribute to recognition. Turning it off preserves bass playback and routing. The bass continues to receive tuning relative to any accepted chord determined from the remaining Divisi input.

Changing the bass input ends the old bass notes and clears their tracking. Changing the bass output transfers held bass notes to the new output and resets the old output’s pitch bend.

Live MIDI CC Control

Eleven assignable CC controls provide remote access to allocation, root mode, fixed root, voice count, priority, chord acceptance, bass analysis, microtuning, natural seventh, Voice Leading collection time and memory reset.

CC Control Input Channel selects the channel used for remote control. A value of 0 disables CC control. Each assignment defaults to −1, meaning unassigned; CC0 is a valid assignment.

Function CC interpretation
Voice Allocation 0–42 Sorted; 43–85 Stable; 86–127 Voice Leading
Root Mode 0–63 Fixed; 64–127 Input
Fixed Root Twelve ranges from C through B
Voice Count Ten ranges from 1 through 10
Note Priority 0–63 Low; 64–127 High
Chord Acceptance Level Nine ranges from 0 through 8
Bass Chord Analysis 0–63 Off; 64–127 On
Microtuning 0–63 Off; 64–127 On
Natural Seventh 0–63 Off; 64–127 On
Voice Leading Collection Time Values 0–127 mapped to 0–100 ms
Voice Leading Memory Reset One reset on a transition from low to high

The reset CC triggers on a transition from 0–63 to 64–127. The first high value after activation or reset-control configuration also triggers. Further high values do not trigger again until a low value is received.

CC64 and CC120–127 are reserved. Assigning them to remote functions leaves those assignments inactive. Duplicate assignments are also inactive, and the help display identifies the conflict.

Valid assigned control messages are consumed before instrument routing. Other CC messages retain their normal processing.

Remote changes update the visible parameters and use the same engine update path as manual changes.

English Controls and Status Display

All forty parameters have English display names and contextual descriptions. Changing a parameter manually or through CC displays its corresponding help text.

Descriptions cover the selected state, routing behavior, chord acceptance levels and CC value mappings. They also report disabled inputs, reserved assignments and duplicate CC conflicts.

Deferred allocation-mode changes remain visible as an additional status message, even when another parameter’s description is displayed. The last selected description remains visible until another control changes.

Upgrade and Verification

V4.0 retains the original thirty-six parameter positions and appends four new controls. The first allocation parameter keeps its existing internal name but expands its range from 0–1 to 0–2.

Existing widgets and variations should be checked during migration. Stable remains the actual parameter value 1. A Voice Allocation CC value of 127 now selects Voice Leading.

// V4.0: Sorted, Stable and Voice Leading; channel memory, timed events and English help.
var
  // 0 = Sorted; 1 = Stable; 2 = Voice Leading with channel memory and grouped attacks.
  // Change modes only after releasing all Divisi keys and the sustain pedal.
  VoiceMode_0Sorted_1Stable("Voice Allocation (0 Sorted / 1 Stable / 2 Voice Leading)") : Parameter 0..2 = 0
  // --- MAIN CONTROLS ---
  Mode_0Fixed_1Input("Root Mode (0 Fixed / 1 Input)") : Parameter 0..1 = 1
  FixedChordRoot("Fixed Root (1 C / 12 B)") : Parameter 1..12 = 1
  InputMidiChannel("Divisi Input Channel") : Parameter 1..16 = 2
  DivisiVoiceCount("Divisi Voice Count") : Parameter 1..10 = 4
  Priority_0Low_1High("Note Priority (0 Low / 1 High)") : Parameter 0..1 = 1
  PitchBendRange_Semi("Pitch Bend Range (Semitones)") : Parameter 1..24 = 2
  ChordDelay_ms("Chord Analysis Delay (ms)") : Parameter 0..100 = 70
  // 0 major root position; 1 major inversions; 2 minor; 3 power/sus; 4 add9;
  // 5 major7/minor7; 6 dominant7; 7 minor9 (V3.1); 8 major9/dominant9/7sus4/9sus4.
  ChordAcceptanceLevel("Chord Acceptance Level") : Parameter 0..8 = 7
 
  BassInputMidiChannel("Bass Input Channel (0 Off)") : Parameter 0..16 = 3
  BassOutputMidiChannel("Bass Output Channel") : Parameter 11..16 = 16
  // 0 = external bass disabled. Sharing the Divisi input channel disables external bass.
  // Tuning values in 0.1 cent: 153 = 15.3 cents.
  Tune_1root("Root Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_2minorSecond("Minor Second Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_3MajorSecond("Major Second Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_4minorThird("Minor Third Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_5MajorThird("Major Third Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_6PerfectFourth("Perfect Fourth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_7diminishedFifth("Diminished Fifth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_8PerfectFifth("Perfect Fifth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_9minorSixth("Minor Sixth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_10MajorSixth("Major Sixth Tuning (0.1 Cent)") : Parameter -500..500 = 0
  Tune_11minorSeventh("Minor Seventh Tuning (0.1 Cent)") : Parameter -500..500 = 0
  // Only complete, accepted dominant 7 chords; includes permitted inversions.
  NaturalSeventh_0Off_1On("Natural Seventh for Dominant 7") : Parameter 0..1 = 0
  Tune_12MajorSeventh("Major Seventh Tuning (0.1 Cent)") : Parameter -500..500 = 0

  // Live control: existing parameter indices remain unchanged.
  MicroTuning_0Off_1On("Microtuning Enabled") : Parameter 0..1 = 1
  BassAnalysis_0Off_1On("Bass Chord Analysis Enabled") : Parameter 0..1 = 1
  ControlMidiChannel("CC Control Input Channel (0 Off)") : Parameter 0..16 = 0
  // -1 = unassigned. CC64 and CC120-127 are reserved; duplicate assignments are inactive.
  CC_VoiceMode("CC Assignment: Voice Allocation") : Parameter -1..127 = -1
  CC_RootMode("CC Assignment: Root Mode") : Parameter -1..127 = -1
  CC_FixedRoot("CC Assignment: Fixed Root") : Parameter -1..127 = -1
  CC_VoiceCount("CC Assignment: Voice Count") : Parameter -1..127 = -1
  CC_Priority("CC Assignment: Note Priority") : Parameter -1..127 = -1
  CC_ChordAcceptanceLevel("CC Assignment: Chord Acceptance Level") : Parameter -1..127 = -1
  CC_BassAnalysis("CC Assignment: Bass Chord Analysis") : Parameter -1..127 = -1
  CC_MicroTuning("CC Assignment: Microtuning") : Parameter -1..127 = -1
  CC_NaturalSeventh("CC Assignment: Natural Seventh") : Parameter -1..127 = -1

  VoiceLeadingDelay_ms("Voice Leading Collection Time (ms)") : Parameter 0..100 = 20
  ResetVoiceLeadingMemory("Reset Voice Leading Memory") : Parameter 0..1 = 0
  CC_VoiceLeadingDelay("CC Assignment: Voice Leading Collection Time") : Parameter -1..127 = -1
  CC_VoiceLeadingReset("CC Assignment: Voice Leading Memory Reset") : Parameter -1..127 = -1

  AccompHeldVelocities : Integer[128]
  AccompPlayingVoices : Integer[128]
  TargetVoices : Integer[128]
  SelectedNotes : Integer[128]
  BassHeldVelocities : Integer[128]
  BassPlayingVelocities : Integer[128]
  MelodyHeldCh1 : Integer[128]; MelodyHeldCh2 : Integer[128]; MelodyHeldCh3 : Integer[128]; MelodyHeldCh4 : Integer[128]
  MelodyHeldCh5 : Integer[128]; MelodyHeldCh6 : Integer[128]; MelodyHeldCh7 : Integer[128]; MelodyHeldCh8 : Integer[128]
  MelodyHeldCh9 : Integer[128]; MelodyHeldCh10 : Integer[128]; MelodyHeldCh11 : Integer[128]; MelodyHeldCh12 : Integer[128]
  MelodyHeldCh13 : Integer[128]; MelodyHeldCh14 : Integer[128]; MelodyHeldCh15 : Integer[128]; MelodyHeldCh16 : Integer[128]
  ActiveInputMidiChannel : Integer = 2
  ActiveBassInputMidiChannel : Integer = 3
  ActiveBassOutputMidiChannel : Integer = 16
  ActiveVoiceMode : Integer = 0
  ChannelPlayingNotes : Integer[11]
  VoiceStartOrder : Integer[11]
  VoiceOrderCounter : Integer = 0
  DivisiSustainDown : Boolean = false
  DivisiSustainValue : Integer = 0
  DisplayedVoiceMode : Integer = -1
  DisplayedRequestedMode : Integer = -1
  GlobalActiveRoot : Integer = 0
  MicroTuningActive : Boolean = false
  // 0 none/fixed; 1 major; 2 minor; 3 power; 4 sus2; 5 sus4; 6 add9;
  // 7 minor add9; 8 major7; 9 minor7; 10 dominant7; 11 minor9;
  // 12 major9; 13 dominant9; 14 dominant7sus4; 15 dominant9sus4.
  AnalyzedChordType : Integer = 0
  ChannelPBOffset : Integer[17]
  LastSentPB : Integer[17]
  GlobalPBValue : Integer = 8192
  PitchClasses : Integer[12]
  TargetTime : Double = 0.0
  EvaluationScheduled : Boolean = false
  ParameterUpdatePending : Boolean = true
  // Help follows the last changed control; show the acceptance level at startup.
  HelpParameterIndex : Integer = 8
  HelpUpdatePending : Boolean = true

  // 0 free; 1 collecting; 2 scheduled attack; 3 scheduled release.
  VLState : Integer[128]; VLNote : Integer[128]; VLVelocity : Integer[128]
  VLToken : Integer[128]; VLChannel : Integer[128]; VLInputToken : Integer[128]
  VLAttackTime : Double[128]; VLReleaseTime : Double[128]
  VLOnDue : Double[128]; VLOffDue : Double[128]
  VLMemory : Integer[11]; VLReserved : Integer[11]; VLChannelToken : Integer[11]
  VLReleased : Boolean[11]
  VLPowers : Integer[11]
  VLCost0 : Integer[256]; VLCost1 : Integer[256]; VLCost2 : Integer[256]; VLCost3 : Integer[256]
  VLFreeChannels : Integer[10]; VLNewNotes : Integer[10]
  VLAssignments : Integer[128]; VLPendingPitch : Integer[128]
  VLTokenCounter : Integer = 0
  VLBatchActive : Boolean = false
  VLBatchDue : Double = 0.0
  VLOverflow : Boolean = false
  VLResetCCHigh : Boolean = false
Initialization
  var i : Integer
  for i = 0; i < 128; i = i + 1 do
    AccompHeldVelocities[i] = 0; AccompPlayingVoices[i] = 0
    TargetVoices[i] = 0; SelectedNotes[i] = 0
    BassHeldVelocities[i] = 0; BassPlayingVelocities[i] = 0
    MelodyHeldCh1[i] = 0; MelodyHeldCh2[i] = 0; MelodyHeldCh3[i] = 0; MelodyHeldCh4[i] = 0
    MelodyHeldCh5[i] = 0; MelodyHeldCh6[i] = 0; MelodyHeldCh7[i] = 0; MelodyHeldCh8[i] = 0
    MelodyHeldCh9[i] = 0; MelodyHeldCh10[i] = 0; MelodyHeldCh11[i] = 0; MelodyHeldCh12[i] = 0
    MelodyHeldCh13[i] = 0; MelodyHeldCh14[i] = 0; MelodyHeldCh15[i] = 0; MelodyHeldCh16[i] = 0
  end
  for i = 0; i <= 16; i = i + 1 do ChannelPBOffset[i] = 0; LastSentPB[i] = -1 end
  for i = 0; i < 12; i = i + 1 do PitchClasses[i] = 0 end
  for i = 0; i <= 10; i = i + 1 do ChannelPlayingNotes[i] = -1; VoiceStartOrder[i] = 0 end
  for i = 0; i < 128; i = i + 1 do VLState[i] = 0; VLInputToken[i] = 0 end
  for i = 0; i <= 10; i = i + 1 do
    VLMemory[i] = -1; VLReserved[i] = -1; VLChannelToken[i] = 0; VLReleased[i] = false
  end
  VLPowers[0] = 1
  for i = 1; i <= 10; i = i + 1 do VLPowers[i] = VLPowers[i - 1] * 2 end
  VLTokenCounter = 0; VLBatchActive = false; VLOverflow = false; VLResetCCHigh = false
  ActiveVoiceMode = VoiceMode_0Sorted_1Stable
  VoiceOrderCounter = 0; DivisiSustainDown = false; DivisiSustainValue = 0
  DisplayedVoiceMode = -1; DisplayedRequestedMode = -1
  ActiveInputMidiChannel = InputMidiChannel
  ActiveBassInputMidiChannel = BassInputMidiChannel
  ActiveBassOutputMidiChannel = BassOutputMidiChannel
  if ActiveBassInputMidiChannel == ActiveInputMidiChannel then ActiveBassInputMidiChannel = 0 end
  GlobalActiveRoot = 0; MicroTuningActive = false; AnalyzedChordType = 0; GlobalPBValue = 8192
  TargetTime = 0.0; EvaluationScheduled = false; ParameterUpdatePending = true
  HelpParameterIndex = 8; HelpUpdatePending = true
  SetTimersRunning(true)
End
On Activate
  var i : Integer
  if ActiveVoiceMode == 2 then
    for i = 1; i <= 10; i = i + 1 do
      if ChannelPlayingNotes[i] >= 0 then
        SendNow(MakeControlChangeMessageEx(64, 0, i))
        SendNow(MakeNoteMessageEx(ChannelPlayingNotes[i], 0, i))
        SendNow(MakeControlChangeMessageEx(120, 0, i))
      end
    end
  end
  for i = 0; i < 128; i = i + 1 do
    AccompHeldVelocities[i] = 0; AccompPlayingVoices[i] = 0
    TargetVoices[i] = 0; SelectedNotes[i] = 0
    BassHeldVelocities[i] = 0; BassPlayingVelocities[i] = 0
    MelodyHeldCh1[i] = 0; MelodyHeldCh2[i] = 0; MelodyHeldCh3[i] = 0; MelodyHeldCh4[i] = 0
    MelodyHeldCh5[i] = 0; MelodyHeldCh6[i] = 0; MelodyHeldCh7[i] = 0; MelodyHeldCh8[i] = 0
    MelodyHeldCh9[i] = 0; MelodyHeldCh10[i] = 0; MelodyHeldCh11[i] = 0; MelodyHeldCh12[i] = 0
    MelodyHeldCh13[i] = 0; MelodyHeldCh14[i] = 0; MelodyHeldCh15[i] = 0; MelodyHeldCh16[i] = 0
  end
  for i = 0; i <= 16; i = i + 1 do ChannelPBOffset[i] = 0; LastSentPB[i] = -1 end
  for i = 0; i < 12; i = i + 1 do PitchClasses[i] = 0 end
  for i = 0; i <= 10; i = i + 1 do ChannelPlayingNotes[i] = -1; VoiceStartOrder[i] = 0 end
  for i = 0; i < 128; i = i + 1 do VLState[i] = 0; VLInputToken[i] = 0 end
  for i = 0; i <= 10; i = i + 1 do
    VLMemory[i] = -1; VLReserved[i] = -1; VLChannelToken[i] = 0; VLReleased[i] = false
  end
  VLPowers[0] = 1
  for i = 1; i <= 10; i = i + 1 do VLPowers[i] = VLPowers[i - 1] * 2 end
  VLTokenCounter = 0; VLBatchActive = false; VLOverflow = false; VLResetCCHigh = false
  ActiveVoiceMode = VoiceMode_0Sorted_1Stable
  VoiceOrderCounter = 0; DivisiSustainDown = false; DivisiSustainValue = 0
  DisplayedVoiceMode = -1; DisplayedRequestedMode = -1
  ActiveInputMidiChannel = InputMidiChannel
  ActiveBassInputMidiChannel = BassInputMidiChannel
  ActiveBassOutputMidiChannel = BassOutputMidiChannel
  if ActiveBassInputMidiChannel == ActiveInputMidiChannel then ActiveBassInputMidiChannel = 0 end
  GlobalActiveRoot = 0; MicroTuningActive = false; AnalyzedChordType = 0; GlobalPBValue = 8192
  TargetTime = 0.0; EvaluationScheduled = false; ParameterUpdatePending = true
  HelpParameterIndex = 8; HelpUpdatePending = true
  SetTimersRunning(true)
End
Function GetMelodyVelocity(ch : Integer, noteNum : Integer) Returns Integer
  result = 0
  if ch == 1 then result = MelodyHeldCh1[noteNum]
  elsif ch == 2 then result = MelodyHeldCh2[noteNum]
  elsif ch == 3 then result = MelodyHeldCh3[noteNum]
  elsif ch == 4 then result = MelodyHeldCh4[noteNum]
  elsif ch == 5 then result = MelodyHeldCh5[noteNum]
  elsif ch == 6 then result = MelodyHeldCh6[noteNum]
  elsif ch == 7 then result = MelodyHeldCh7[noteNum]
  elsif ch == 8 then result = MelodyHeldCh8[noteNum]
  elsif ch == 9 then result = MelodyHeldCh9[noteNum]
  elsif ch == 10 then result = MelodyHeldCh10[noteNum]
  elsif ch == 11 then result = MelodyHeldCh11[noteNum]
  elsif ch == 12 then result = MelodyHeldCh12[noteNum]
  elsif ch == 13 then result = MelodyHeldCh13[noteNum]
  elsif ch == 14 then result = MelodyHeldCh14[noteNum]
  elsif ch == 15 then result = MelodyHeldCh15[noteNum]
  elsif ch == 16 then result = MelodyHeldCh16[noteNum]
  end
End
Function SetMelodyVelocity(ch : Integer, noteNum : Integer, vel : Integer)
  if ch == 1 then MelodyHeldCh1[noteNum] = vel
  elsif ch == 2 then MelodyHeldCh2[noteNum] = vel
  elsif ch == 3 then MelodyHeldCh3[noteNum] = vel
  elsif ch == 4 then MelodyHeldCh4[noteNum] = vel
  elsif ch == 5 then MelodyHeldCh5[noteNum] = vel
  elsif ch == 6 then MelodyHeldCh6[noteNum] = vel
  elsif ch == 7 then MelodyHeldCh7[noteNum] = vel
  elsif ch == 8 then MelodyHeldCh8[noteNum] = vel
  elsif ch == 9 then MelodyHeldCh9[noteNum] = vel
  elsif ch == 10 then MelodyHeldCh10[noteNum] = vel
  elsif ch == 11 then MelodyHeldCh11[noteNum] = vel
  elsif ch == 12 then MelodyHeldCh12[noteNum] = vel
  elsif ch == 13 then MelodyHeldCh13[noteNum] = vel
  elsif ch == 14 then MelodyHeldCh14[noteNum] = vel
  elsif ch == 15 then MelodyHeldCh15[noteNum] = vel
  elsif ch == 16 then MelodyHeldCh16[noteNum] = vel
  end
End
Function GetTuningOffset(interval : Integer) Returns Integer
  var centsTenths : Integer = 0
  var pbRangeSemi : Integer = PitchBendRange_Semi
  var rootCentsTenths : Integer = Tune_1root
  if MicroTuningActive and MicroTuning_0Off_1On == 1 then
    if interval == 0 then centsTenths = Tune_1root
    elsif interval == 1 then centsTenths = Tune_2minorSecond
    elsif interval == 2 then centsTenths = Tune_3MajorSecond
    elsif interval == 3 then centsTenths = Tune_4minorThird
    elsif interval == 4 then centsTenths = Tune_5MajorThird
    elsif interval == 5 then centsTenths = Tune_6PerfectFourth
    elsif interval == 6 then centsTenths = Tune_7diminishedFifth
    elsif interval == 7 then centsTenths = Tune_8PerfectFifth
    elsif interval == 8 then centsTenths = Tune_9minorSixth
    elsif interval == 9 then centsTenths = Tune_10MajorSixth
    elsif interval == 10 then
      if NaturalSeventh_0Off_1On == 1 and Mode_0Fixed_1Input == 1 and AnalyzedChordType == 10 then
        // Approximately 7:4 relative to the tuned root, in tenths of a cent.
        centsTenths = rootCentsTenths - 312
      else
        centsTenths = Tune_11minorSeventh
      end
    elsif interval == 11 then centsTenths = Tune_12MajorSeventh
    end
    if pbRangeSemi < 1 then pbRangeSemi = 1 end
    result = (centsTenths * 8192) / (pbRangeSemi * 1000)
  else
    result = 0
  end
End
// Analyze all Divisi notes and ONLY lower notes from the selected bass input.
// Check each permitted chord shape against the actual lowest analyzed note.
Function FindRoot() Returns Integer
  var i : Integer
  var lowestDivisi : Integer = -1
  var lowestNote : Integer = -1
  var pitchClassCount : Integer = 0
  var foundRoot : Integer = -1
  var foundType : Integer = 0
  var bassInterval : Integer
  var candidateType : Integer
  var acceptanceLevel : Integer = ChordAcceptanceLevel
  var fixedRootValue : Integer = FixedChordRoot
  AnalyzedChordType = 0
  if Mode_0Fixed_1Input == 0 then
    result = fixedRootValue - 1
  else
    for i = 0; i < 12; i = i + 1 do PitchClasses[i] = 0 end
    for i = 0; i < 128; i = i + 1 do
      if AccompHeldVelocities[i] > 0 then
        PitchClasses[i % 12] = 1
        if lowestDivisi == -1 then lowestDivisi = i end
      end
    end
    if lowestDivisi >= 0 then
      lowestNote = lowestDivisi
      if ActiveBassInputMidiChannel > 0 and BassAnalysis_0Off_1On == 1 then
        for i = 0; i < lowestDivisi; i = i + 1 do
          if BassHeldVelocities[i] > 0 then
            PitchClasses[i % 12] = 1
            if i < lowestNote then lowestNote = i end
          end
        end
      end
      for i = 0; i < 12; i = i + 1 do
        if PitchClasses[i] == 1 then pitchClassCount = pitchClassCount + 1 end
      end
      for i = 0; i < 12; i = i + 1 do
        candidateType = 0
        bassInterval = (lowestNote % 12 - i + 12) % 12
        if PitchClasses[i] == 1 then
          // Major family: root, major third or perfect fifth in the bass.
          if PitchClasses[(i + 4) % 12] == 1 and PitchClasses[(i + 7) % 12] == 1 then
            if bassInterval == 0 or bassInterval == 4 or bassInterval == 7 then
              if pitchClassCount == 3 then
                if bassInterval == 0 or acceptanceLevel >= 1 then candidateType = 1 end
              elsif pitchClassCount == 4 then
                if PitchClasses[(i + 2) % 12] == 1 and acceptanceLevel >= 4 then candidateType = 6
                elsif PitchClasses[(i + 11) % 12] == 1 and acceptanceLevel >= 5 then candidateType = 8
                elsif PitchClasses[(i + 10) % 12] == 1 and acceptanceLevel >= 6 then candidateType = 10
                end
              elsif pitchClassCount == 5 and acceptanceLevel >= 8 then
                if PitchClasses[(i + 2) % 12] == 1 then
                  if PitchClasses[(i + 11) % 12] == 1 then candidateType = 12
                  elsif PitchClasses[(i + 10) % 12] == 1 then candidateType = 13
                  end
                end
              end
            end
          end
          // Minor family: minor 7 permits ONLY root bass; minor 9 keeps normal inversions.
          if PitchClasses[(i + 3) % 12] == 1 and PitchClasses[(i + 7) % 12] == 1 then
            if bassInterval == 0 or bassInterval == 3 or bassInterval == 7 then
              if pitchClassCount == 3 and acceptanceLevel >= 2 then candidateType = 2
              elsif pitchClassCount == 4 then
                if PitchClasses[(i + 2) % 12] == 1 and acceptanceLevel >= 4 then candidateType = 7
                elsif PitchClasses[(i + 10) % 12] == 1 and bassInterval == 0 and acceptanceLevel >= 5 then candidateType = 9
                end
              elsif pitchClassCount == 5 and acceptanceLevel >= 7 then
                if PitchClasses[(i + 2) % 12] == 1 and PitchClasses[(i + 10) % 12] == 1 then candidateType = 11 end
              end
            end
          end
          // Power and suspended families: root or perfect fifth in the bass.
          if PitchClasses[(i + 7) % 12] == 1 and (bassInterval == 0 or bassInterval == 7) then
            if acceptanceLevel >= 3 then
              if pitchClassCount == 2 then candidateType = 3
              elsif pitchClassCount == 3 then
                if PitchClasses[(i + 2) % 12] == 1 then candidateType = 4
                elsif PitchClasses[(i + 5) % 12] == 1 then candidateType = 5
                end
              elsif pitchClassCount == 4 and acceptanceLevel >= 8 then
                if PitchClasses[(i + 5) % 12] == 1 and PitchClasses[(i + 10) % 12] == 1 then candidateType = 14 end
              elsif pitchClassCount == 5 and acceptanceLevel >= 8 then
                if PitchClasses[(i + 2) % 12] == 1 and PitchClasses[(i + 5) % 12] == 1 and PitchClasses[(i + 10) % 12] == 1 then candidateType = 15 end
              end
            end
          end
          // The exact pitch-class count rejects every additional foreign pitch class.
          // For ambiguous suspended chords, prefer the bass as root.
          if candidateType > 0 then
            if foundRoot == -1 or bassInterval == 0 then
              foundRoot = i
              foundType = candidateType
            end
          end
        end
      end
    end
    AnalyzedChordType = foundType
    result = foundRoot
  end
End
Function SendPitchBendToChannels()
  var i : Integer
  var finalPB : Integer
  for i = 1; i <= 16; i = i + 1 do
    finalPB = GlobalPBValue + ChannelPBOffset[i]
    if finalPB < 0 then finalPB = 0 end
    if finalPB > 16383 then finalPB = 16383 end
    if finalPB != LastSentPB[i] then
      SendNow(MakePitchBendMessageEx(finalPB, i))
      LastSentPB[i] = finalPB
    end
  end
End
Function ResetChannelPitchBend(ch : Integer)
  ChannelPBOffset[ch] = 0
  SendNow(MakePitchBendMessageEx(GlobalPBValue, ch))
  LastSentPB[ch] = GlobalPBValue
End
// Bass and melody each use the highest held note on their channel.
Function UpdateMelodyPB()
  var maxEngineCh : Integer = DivisiVoiceCount
  var ch : Integer
  var i : Integer
  var highestNote : Integer
  var interval : Integer
  if MicroTuningActive then
    for ch = 1; ch <= 16; ch = ch + 1 do
      if ch > maxEngineCh then
        ChannelPBOffset[ch] = 0
        highestNote = -1
        for i = 0; i < 128; i = i + 1 do
          if ch == ActiveBassOutputMidiChannel then
            if BassHeldVelocities[i] > 0 then highestNote = i end
          elsif GetMelodyVelocity(ch, i) > 0 then highestNote = i
          end
        end
        if highestNote >= 0 then
          interval = (highestNote % 12 - GlobalActiveRoot + 12) % 12
          ChannelPBOffset[ch] = GetTuningOffset(interval)
        end
      end
    end
  else
    for ch = 1; ch <= 16; ch = ch + 1 do ChannelPBOffset[ch] = 0 end
  end
  SendPitchBendToChannels()
End
// Update pitch bend only: chord changes never retrigger notes in stable mode.
Function RefreshVoicePitchBend()
  var i : Integer
  var chTarget : Integer
  var interval : Integer
  for i = 1; i <= 16; i = i + 1 do ChannelPBOffset[i] = 0 end
  for i = 0; i < 128; i = i + 1 do
    chTarget = TargetVoices[i]
    if chTarget > 0 then
      interval = (i % 12 - GlobalActiveRoot + 12) % 12
      ChannelPBOffset[chTarget] = GetTuningOffset(interval)
    end
  end
  UpdateMelodyPB()
End
Function ClearStableVoice(noteNum : Integer)
  var ch : Integer = AccompPlayingVoices[noteNum]
  if ch > 0 then
    ChannelPlayingNotes[ch] = -1
    VoiceStartOrder[ch] = 0
  end
  AccompPlayingVoices[noteNum] = 0
  TargetVoices[noteNum] = 0
  SelectedNotes[noteNum] = 0
End
// A stolen note still participates in input analysis but never returns automatically.
Function ForceEndStableVoice(noteNum : Integer)
  var ch : Integer = AccompPlayingVoices[noteNum]
  if ch > 0 then
    if DivisiSustainDown then SendNow(MakeControlChangeMessageEx(64, 0, ch)) end
    SendNow(MakeNoteMessageEx(noteNum, 0, ch))
    if DivisiSustainDown then SendNow(MakeControlChangeMessageEx(120, 0, ch)) end
    ClearStableVoice(noteNum)
  end
End
Function StartStableVoice(noteNum : Integer, vel : Integer)
  var voiceLimit : Integer = DivisiVoiceCount
  var ch : Integer = AccompPlayingVoices[noteNum]
  var i : Integer
  var oldestOrder : Integer = 0
  // A genuine new attack on the same note uses its previous channel.
  if ch > 0 then
    ForceEndStableVoice(noteNum)
  else
    ch = 0
    for i = 1; i <= voiceLimit; i = i + 1 do
      if ch == 0 and ChannelPlayingNotes[i] < 0 then ch = i end
    end
    if ch == 0 then
      for i = 1; i <= voiceLimit; i = i + 1 do
        if ch == 0 or VoiceStartOrder[i] < oldestOrder then
          ch = i
          oldestOrder = VoiceStartOrder[i]
        end
      end
      if ch > 0 then ForceEndStableVoice(ChannelPlayingNotes[ch]) end
    end
  end
  if ch > 0 then
    VoiceOrderCounter = VoiceOrderCounter + 1
    VoiceStartOrder[ch] = VoiceOrderCounter
    ChannelPlayingNotes[ch] = noteNum
    AccompPlayingVoices[noteNum] = ch
    TargetVoices[noteNum] = ch
    SelectedNotes[noteNum] = 1
    // Newly used channels also receive the current sustain pedal value.
    if DivisiSustainDown then SendNow(MakeControlChangeMessageEx(64, DivisiSustainValue, ch)) end
    RefreshVoicePitchBend()
    SendNow(MakeNoteMessageEx(noteNum, vel, ch))
  end
End
// Voice Leading keeps 128 paired attack/release records: at most 256 pending events.
Function ResetVoiceLeadingMemoryNow()
  var ch : Integer
  for ch = 1; ch <= 10; ch = ch + 1 do VLMemory[ch] = ChannelPlayingNotes[ch] end
End
Function ClearVoiceLeadingState()
  var i : Integer
  for i = 0; i < 128; i = i + 1 do VLState[i] = 0; VLInputToken[i] = 0 end
  for i = 0; i <= 10; i = i + 1 do
    VLMemory[i] = -1; VLReserved[i] = -1; VLChannelToken[i] = 0; VLReleased[i] = false
  end
  VLBatchActive = false; VLOverflow = false
End
Function VoiceLeadingBusy() Returns Boolean
  var i : Integer
  result = VLBatchActive
  for i = 0; i < 128; i = i + 1 do
    if VLState[i] > 0 then result = true end
  end
End
Function RefreshVLReservations()
  var i : Integer
  var ch : Integer
  for ch = 1; ch <= 10; ch = ch + 1 do VLReserved[ch] = -1 end
  for i = 0; i < 128; i = i + 1 do
    ch = VLChannel[i]
    if VLState[i] > 0 and ch > 0 then VLReserved[ch] = VLNote[i] end
  end
End
Function ExistingVLChannel(noteNum : Integer) Returns Integer
  var ch : Integer
  result = AccompPlayingVoices[noteNum]
  for ch = 1; ch <= 10; ch = ch + 1 do
    if VLReserved[ch] == noteNum then result = ch end
  end
End
Function ReleaseVLVoice(noteNum : Integer, token : Integer)
  var ch : Integer = AccompPlayingVoices[noteNum]
  if ch > 0 then
    if VLChannelToken[ch] == token then
      SendNow(MakeNoteMessageEx(noteNum, 0, ch))
      VLReleased[ch] = true
      if not DivisiSustainDown then
        ClearStableVoice(noteNum); VLChannelToken[ch] = 0
      end
    end
  end
End
Function StartVLVoice(noteNum : Integer, vel : Integer, ch : Integer, token : Integer)
  var oldNote : Integer = ChannelPlayingNotes[ch]
  if oldNote >= 0 then ForceEndStableVoice(oldNote) end
  ChannelPlayingNotes[ch] = noteNum; AccompPlayingVoices[noteNum] = ch
  TargetVoices[noteNum] = ch; SelectedNotes[noteNum] = 1
  VLChannelToken[ch] = token; VLReleased[ch] = false; VLMemory[ch] = noteNum
  if DivisiSustainDown then SendNow(MakeControlChangeMessageEx(64, DivisiSustainValue, ch)) end
  RefreshVoicePitchBend()
  SendNow(MakeNoteMessageEx(noteNum, vel, ch))
End
Function MarkVLInputRelease(noteNum : Integer)
  var i : Integer
  var token : Integer = VLInputToken[noteNum]
  var duration : Double
  var currentTime : Double = TimeSinceStartup()
  if token > 0 then
    for i = 0; i < 128; i = i + 1 do
      if VLState[i] == 1 or VLState[i] == 2 then
        if VLToken[i] == token then
          VLReleaseTime[i] = currentTime
          duration = currentTime - VLAttackTime[i]
          if duration < 1.0 then duration = 1.0 end
          VLOffDue[i] = VLOnDue[i] + duration
        end
      end
    end
    ReleaseVLVoice(noteNum, token)
  end
  VLInputToken[noteNum] = 0
End
Function QueueVLAttack(noteNum : Integer, vel : Integer)
  var i : Integer
  var slot : Integer = -1
  var ch : Integer
  var pending : Boolean = false
  var delayMs : Integer = VoiceLeadingDelay_ms
  // A second attack implicitly releases the previous input generation of this pitch.
  ch = ExistingVLChannel(noteNum)
  MarkVLInputRelease(noteNum)
  RefreshVLReservations()
  if ch == 0 then ch = ExistingVLChannel(noteNum) end
  for i = 0; i < 128; i = i + 1 do
    if VLState[i] > 0 and VLNote[i] == noteNum then pending = true end
    if VLState[i] == 0 and slot < 0 then slot = i end
  end
  if ch > 0 and not pending then
    VLTokenCounter = VLTokenCounter + 1; VLInputToken[noteNum] = VLTokenCounter
    StartVLVoice(noteNum, vel, ch, VLTokenCounter)
  elsif slot >= 0 then
    VLTokenCounter = VLTokenCounter + 1; VLInputToken[noteNum] = VLTokenCounter
    VLState[slot] = 1; VLNote[slot] = noteNum; VLVelocity[slot] = vel
    VLToken[slot] = VLTokenCounter; VLChannel[slot] = 0
    VLAttackTime[slot] = TimeSinceStartup(); VLReleaseTime[slot] = -1.0
    VLOnDue[slot] = 0.0; VLOffDue[slot] = 0.0
    if not VLBatchActive then
      VLBatchDue = TimeSinceStartup() + IntToFloat(delayMs); VLBatchActive = true
      if VLOverflow then HelpUpdatePending = true end
      VLOverflow = false
    end
  else
    VLOverflow = true; HelpUpdatePending = true
  end
End
// Suffix dynamic programming: unknown-channel penalty dominates every possible pitch distance.
// Reconstruction scans channels ascending, giving the exact lexicographic tie-break.
Function VLMaskSize(mask : Integer) Returns Integer
  var remainder : Integer = mask
  var i : Integer
  result = 0
  for i = 0; i < 10; i = i + 1 do result = result + remainder % 2; remainder = remainder / 2 end
End
Function VLGetCost(mask : Integer) Returns Integer
  var offset : Integer = mask % 256
  if mask < 256 then result = VLCost0[offset]
  elsif mask < 512 then result = VLCost1[offset]
  elsif mask < 768 then result = VLCost2[offset]
  else
    result = VLCost3[offset]
  end
End
Function VLSetCost(mask : Integer, value : Integer)
  var offset : Integer = mask % 256
  if mask < 256 then VLCost0[offset] = value
  elsif mask < 512 then VLCost1[offset] = value
  elsif mask < 768 then VLCost2[offset] = value
  else
    VLCost3[offset] = value
  end
End
Function SolveVLAssignment(freeCount : Integer, noteCount : Integer)
  var mask : Integer
  var slot : Integer
  var used : Integer
  var nextMask : Integer
  var cost : Integer
  var distance : Integer
  var ch : Integer
  var chosen : Integer
  var stateCount : Integer = VLPowers[freeCount]
  for mask = stateCount - 1; mask >= 0; mask = mask - 1 do
    used = VLMaskSize(mask); VLSetCost(mask, 1000000)
    if used == noteCount then VLSetCost(mask, 0)
    elsif used < noteCount then
      for slot = 0; slot < freeCount; slot = slot + 1 do
        if (mask / VLPowers[slot]) % 2 == 0 then
          ch = VLFreeChannels[slot]; distance = 1280
          if VLMemory[ch] >= 0 then
            distance = VLNewNotes[used] - VLMemory[ch]
            if distance < 0 then distance = 0 - distance end
          end
          nextMask = mask + VLPowers[slot]; cost = distance + VLGetCost(nextMask)
          if cost < VLGetCost(mask) then VLSetCost(mask, cost) end
        end
      end
    end
  end
  mask = 0
  for used = 0; used < noteCount; used = used + 1 do
    chosen = -1
    for slot = 0; slot < freeCount; slot = slot + 1 do
      if (mask / VLPowers[slot]) % 2 == 0 and chosen < 0 then
        ch = VLFreeChannels[slot]; distance = 1280
        if VLMemory[ch] >= 0 then
          distance = VLNewNotes[used] - VLMemory[ch]
          if distance < 0 then distance = 0 - distance end
        end
        nextMask = mask + VLPowers[slot]
        if distance + VLGetCost(nextMask) == VLGetCost(mask) then chosen = slot end
      end
    end
    if chosen >= 0 then
      VLAssignments[VLNewNotes[used]] = VLFreeChannels[chosen]
      mask = mask + VLPowers[chosen]
    end
  end
End
Function FlushVLBatch()
  var i : Integer
  var n : Integer
  var ch : Integer
  var j : Integer
  var freeCount : Integer = 0
  var selectedCount : Integer = 0
  var noteCount : Integer = 0
  var limit : Integer = DivisiVoiceCount
  var priority : Integer = Priority_0Low_1High
  var earliest : Double
  var firstDue : Double
  var duration : Double
  var currentTime : Double = TimeSinceStartup()
  if VLBatchActive and currentTime >= VLBatchDue then
    RefreshVLReservations()
    for n = 0; n < 128; n = n + 1 do VLAssignments[n] = 0; VLPendingPitch[n] = 0 end
    for i = 0; i < 128; i = i + 1 do
      if VLState[i] == 1 then VLPendingPitch[VLNote[i]] = 1 end
    end
    for ch = 1; ch <= limit; ch = ch + 1 do
      if ChannelPlayingNotes[ch] < 0 and VLReserved[ch] < 0 then
        VLFreeChannels[freeCount] = ch; freeCount = freeCount + 1
      end
    end
    for n = 0; n < 128; n = n + 1 do
      if VLPendingPitch[n] > 0 then
        ch = ExistingVLChannel(n)
        if ch > 0 then VLAssignments[n] = ch end
      end
    end
    // Priority selects only new pitches; occupied or reserved channels are protected.
    if priority == 0 then
      for n = 0; n < 128; n = n + 1 do
        if VLPendingPitch[n] > 0 and VLAssignments[n] == 0 and selectedCount < freeCount then
          VLPendingPitch[n] = 2; selectedCount = selectedCount + 1
        end
      end
    else
      for n = 127; n >= 0; n = n - 1 do
        if VLPendingPitch[n] > 0 and VLAssignments[n] == 0 and selectedCount < freeCount then
          VLPendingPitch[n] = 2; selectedCount = selectedCount + 1
        end
      end
    end
    for n = 127; n >= 0; n = n - 1 do
      if VLPendingPitch[n] == 2 then VLNewNotes[noteCount] = n; noteCount = noteCount + 1 end
    end
    if noteCount > 0 then SolveVLAssignment(freeCount, noteCount) end
    for n = 0; n < 128; n = n + 1 do
      if VLPendingPitch[n] > 0 then
        ch = VLAssignments[n]; earliest = currentTime
        for i = 0; i < 128; i = i + 1 do
          if VLState[i] == 1 and VLNote[i] == n then
            if VLAttackTime[i] < earliest then earliest = VLAttackTime[i] end
          end
        end
        firstDue = currentTime
        // Preserve order even if collection time changes while earlier repetitions are scheduled.
        for j = 0; j < 128; j = j + 1 do
          if VLState[j] == 2 and VLNote[j] == n then
            duration = VLOnDue[j] + earliest - VLAttackTime[j]
            if duration > firstDue then firstDue = duration end
          end
        end
        for i = 0; i < 128; i = i + 1 do
          if VLState[i] == 1 and VLNote[i] == n then
            if ch > 0 then
              VLState[i] = 2; VLChannel[i] = ch
              VLOnDue[i] = firstDue + VLAttackTime[i] - earliest
              if VLReleaseTime[i] >= 0.0 then
                duration = VLReleaseTime[i] - VLAttackTime[i]
                if duration < 1.0 then duration = 1.0 end
                VLOffDue[i] = VLOnDue[i] + duration
              end
            else
              VLState[i] = 0
              if VLInputToken[n] == VLToken[i] then VLInputToken[n] = 0 end
            end
          end
        end
      end
    end
    VLBatchActive = false; RefreshVLReservations()
  end
End
Function RunVLEvents()
  var j : Integer
  var lateness : Double
  var iteration : Integer
  var i : Integer
  var nextEvent : Integer
  var due : Double
  var nextDue : Double
  var currentTime : Double = TimeSinceStartup()
  var token : Integer
  FlushVLBatch()
  currentTime = TimeSinceStartup()
  // At most two output events per paired record; no uncancellable SendLater messages.
  for iteration = 0; iteration < 256; iteration = iteration + 1 do
    nextEvent = -1; nextDue = currentTime + 1.0
    for i = 0; i < 128; i = i + 1 do
      due = currentTime + 1.0
      if VLState[i] == 2 then due = VLOnDue[i]
      elsif VLState[i] == 3 then due = VLOffDue[i]
      end
      if due <= currentTime then
        if nextEvent < 0 then nextEvent = i; nextDue = due
        elsif due < nextDue then nextEvent = i; nextDue = due
        elsif due == nextDue then
          if VLToken[i] < VLToken[nextEvent] then nextEvent = i end
        end
      end
    end
    if nextEvent < 0 then iteration = 256
    else
      i = nextEvent; token = VLToken[i]
      if VLState[i] == 2 then
        // Late timer ticks shift this pitch's future attacks/releases together.
        lateness = TimeSinceStartup() - VLOnDue[i]
        if lateness > 0.0 then
          for j = 0; j < 128; j = j + 1 do
            if VLState[j] == 2 and VLNote[j] == VLNote[i] and VLToken[j] >= token then
              VLOnDue[j] = VLOnDue[j] + lateness
              VLOffDue[j] = VLOffDue[j] + lateness
            end
          end
        end
        StartVLVoice(VLNote[i], VLVelocity[i], VLChannel[i], token)
        if VLReleaseTime[i] >= 0.0 then VLState[i] = 3
        else
          VLState[i] = 0
        end
      else
        ReleaseVLVoice(VLNote[i], token); VLState[i] = 0
      end
    end
  end
  RefreshVLReservations(); RefreshVoicePitchBend()
End
Function EnforceStableVoiceLimit()
  var i : Integer
  var voiceLimit : Integer = DivisiVoiceCount
  var ch : Integer
  var changed : Boolean = false
  if ActiveVoiceMode == 1 or ActiveVoiceMode == 2 then
    for ch = voiceLimit + 1; ch <= 10; ch = ch + 1 do
      if ChannelPlayingNotes[ch] >= 0 then
        ForceEndStableVoice(ChannelPlayingNotes[ch])
        changed = true
      end
      if ActiveVoiceMode == 2 then
        VLMemory[ch] = -1; VLReserved[ch] = -1; VLChannelToken[ch] = 0
        for i = 0; i < 128; i = i + 1 do
          if VLState[i] > 0 and VLChannel[i] == ch then
            if VLInputToken[VLNote[i]] == VLToken[i] then VLInputToken[VLNote[i]] = 0 end
            VLState[i] = 0
          end
        end
      end
    end
    if changed then RefreshVoicePitchBend() end
  end
End
Function ApplyVoiceMode()
  var requestedMode : Integer = VoiceMode_0Sorted_1Stable
  var i : Integer
  var busy : Boolean = DivisiSustainDown
  for i = 0; i < 128; i = i + 1 do
    if AccompHeldVelocities[i] > 0 or AccompPlayingVoices[i] > 0 then busy = true end
  end
  if VoiceLeadingBusy() then busy = true end
  if requestedMode != ActiveVoiceMode and not busy then
    ClearVoiceLeadingState()
    ActiveVoiceMode = requestedMode
    for i = 0; i <= 10; i = i + 1 do ChannelPlayingNotes[i] = -1; VoiceStartOrder[i] = 0 end
    VoiceOrderCounter = 0
    ParameterUpdatePending = true
  end
  if DisplayedVoiceMode != ActiveVoiceMode or DisplayedRequestedMode != requestedMode then
    HelpUpdatePending = true
    DisplayedVoiceMode = ActiveVoiceMode
    DisplayedRequestedMode = requestedMode
  end
End
// Shared update path for manual changes and remote CC changes.
Function RequestParameterUpdate(helpIndex : Integer, musicalChange : Boolean)
  if musicalChange then ParameterUpdatePending = true end
  // A manual or remote master-off change clears bends without waiting for the timer.
  if helpIndex == 24 and MicroTuning_0Off_1On == 0 then
    MicroTuningActive = false
    RefreshVoicePitchBend()
  end
  if helpIndex == 26 or helpIndex == 39 then VLResetCCHigh = false end
  if helpIndex == 37 and ResetVoiceLeadingMemory == 1 then
    ResetVoiceLeadingMemoryNow(); ResetVoiceLeadingMemory = 0
    VLOverflow = false
  end
  HelpParameterIndex = helpIndex
  HelpUpdatePending = true
End
Function GetAssignedCC(index : Integer) Returns Integer
  result = -1
  if index == 0 then result = CC_VoiceMode
  elsif index == 1 then result = CC_RootMode
  elsif index == 2 then result = CC_FixedRoot
  elsif index == 3 then result = CC_VoiceCount
  elsif index == 4 then result = CC_Priority
  elsif index == 5 then result = CC_ChordAcceptanceLevel
  elsif index == 6 then result = CC_BassAnalysis
  elsif index == 7 then result = CC_MicroTuning
  elsif index == 8 then result = CC_NaturalSeventh
  elsif index == 9 then result = CC_VoiceLeadingDelay
  elsif index == 10 then result = CC_VoiceLeadingReset
  end
End
Function GetCCTargetName(index : Integer) Returns String
  result = "Unknown"
  if index == 0 then result = "Voice Allocation"
  elsif index == 1 then result = "Root Mode"
  elsif index == 2 then result = "Fixed Root"
  elsif index == 3 then result = "Voice Count"
  elsif index == 4 then result = "Note Priority"
  elsif index == 5 then result = "Chord Acceptance Level"
  elsif index == 6 then result = "Bass Chord Analysis"
  elsif index == 7 then result = "Microtuning"
  elsif index == 8 then result = "Natural Seventh"
  elsif index == 9 then result = "Voice Leading Collection Time"
  elsif index == 10 then result = "Voice Leading Memory Reset"
  end
End
Function GetCCUseCount(ccNum : Integer) Returns Integer
  var i : Integer
  result = 0
  if ccNum >= 0 then
    for i = 0; i < 11; i = i + 1 do
      if GetAssignedCC(i) == ccNum then result = result + 1 end
    end
  end
End
Function IsReservedControlCC(ccNum : Integer) Returns Boolean
  result = ccNum == 64 or ccNum >= 120
End
Function GetCCAssignmentStatus(index : Integer) Returns String
  var ccNum : Integer = GetAssignedCC(index)
  var controlChannel : Integer = ControlMidiChannel
  var i : Integer
  result = GetCCTargetName(index) + ": "
  if ccNum < 0 then
    result = result + "unassigned (-1)."
  else
    result = result + "CC " + ccNum + "."
    if IsReservedControlCC(ccNum) then
      result = result + " Inactive: CC64 and CC120-127 are reserved."
    elsif GetCCUseCount(ccNum) > 1 then
      result = result + " Inactive duplicate; also assigned to "
      for i = 0; i < 11; i = i + 1 do
        if i != index and GetAssignedCC(i) == ccNum then
          result = result + GetCCTargetName(i) + "; "
        end
      end
    end
  end
  if controlChannel == 0 then
    result = result + " CC remote control disabled (channel 0)."
  else
    result = result + " Control input channel " + controlChannel + "."
  end
End
Function GetCCConfigurationWarning() Returns String
  var i : Integer
  var ccNum : Integer
  result = ""
  for i = 0; i < 11; i = i + 1 do
    ccNum = GetAssignedCC(i)
    if ccNum >= 0 then
      if IsReservedControlCC(ccNum) then
        result = result + " " + GetCCTargetName(i) + " CC " + ccNum + " inactive (reserved)."
      elsif GetCCUseCount(ccNum) > 1 then
        result = result + " " + GetCCTargetName(i) + " CC " + ccNum + " inactive (duplicate)."
      end
    end
  end
End
Function GetCCAssignmentHelp(index : Integer) Returns String
  result = GetCCAssignmentStatus(index)
  if index == 0 then result = result + " Values 0-42 select Sorted; 43-85 Stable; 86-127 Voice Leading. CC127 now selects Voice Leading. Switching waits for keys, voices, sustain and queued events."
  elsif index == 1 then result = result + " Values 0-63 select Fixed Root; 64-127 select Input chord analysis. Microtuning Enabled remains the master switch."
  elsif index == 2 then result = result + " Values 0-127 select 12 equal-width ranges: C, C#, D, D#, E, F, F#, G, G#, A, A#, B. Used only in Fixed mode."
  elsif index == 3 then result = result + " Values 0-127 select 10 equal-width ranges for 1-10 Divisi output voices. Every held Divisi input note still enters analysis."
  elsif index == 4 then result = result + " Values 0-63 select Low; 64-127 select High. Sorted selects held notes; Voice Leading selects only new overflow notes and protects occupied voices. Stable ignores priority."
  elsif index == 5 then result = result + " Values 0-127 select 9 equal-width ranges for cumulative levels 0-8: root-position major, major inversions, minor, power/sus, add9, major7/minor7, dominant7, minor9, then extended ninth/sus chords."
  elsif index == 6 then result = result + " Values 0-63 exclude external bass from chord analysis; 64-127 include eligible lower bass notes. Bass routing and playback continue."
  elsif index == 7 then result = result + " Values 0-63 disable all microtuning immediately; 64-127 enable the current fixed-root or accepted-chord tuning. Normal pitch bend continues."
  elsif index == 8 then result = result + " Values 0-63 use Minor Seventh Tuning; 64-127 enable the natural seventh for accepted dominant7 chords in Input mode only."
  elsif index == 9 then result = result + " Values 0-127 select collection time 0-100 ms. Only Voice Leading new attacks wait; running windows keep their original deadline. At 0 ms input order can affect allocation."
  elsif index == 10 then result = result + " A low-to-high transition (0-63 to 64-127) resets free-channel memory once. The first high value after activation also triggers; repeated high values do not. Sounding voices stay assigned."
  end
  result = result + " -1 leaves this function unassigned; CC0 is valid. Active control CCs are consumed."
End
// Consume only unique, non-reserved assignments on the selected control channel.
Function HandleControlCC(ch : Integer, ccNum : Integer, ccVal : Integer) Returns Boolean
  var controlChannel : Integer = ControlMidiChannel
  var index : Integer = -1
  var i : Integer
  var newValue : Integer
  var oldValue : Integer
  result = false
  if controlChannel > 0 and ch == controlChannel and not IsReservedControlCC(ccNum) then
    if GetCCUseCount(ccNum) == 1 then
      for i = 0; i < 11; i = i + 1 do
        if GetAssignedCC(i) == ccNum then index = i end
      end
      result = true
      newValue = 0
      if ccVal >= 64 then newValue = 1 end
      if index == 0 then newValue = (ccVal * 3) / 128
      elsif index == 9 then newValue = (ccVal * 101) / 128
      elsif index == 2 then newValue = (ccVal * 12) / 128 + 1
      elsif index == 3 then newValue = (ccVal * 10) / 128 + 1
      elsif index == 5 then newValue = (ccVal * 9) / 128
      end
      if index == 0 then
        oldValue = VoiceMode_0Sorted_1Stable
        if oldValue != newValue then
          VoiceMode_0Sorted_1Stable = newValue
          RequestParameterUpdate(0, true)
        end
      elsif index == 1 then
        oldValue = Mode_0Fixed_1Input
        if oldValue != newValue then
          Mode_0Fixed_1Input = newValue
          RequestParameterUpdate(1, true)
        end
      elsif index == 2 then
        oldValue = FixedChordRoot
        if oldValue != newValue then
          FixedChordRoot = newValue
          RequestParameterUpdate(2, true)
        end
      elsif index == 3 then
        oldValue = DivisiVoiceCount
        if oldValue != newValue then
          DivisiVoiceCount = newValue
          RequestParameterUpdate(4, true)
        end
      elsif index == 4 then
        oldValue = Priority_0Low_1High
        if oldValue != newValue then
          Priority_0Low_1High = newValue
          RequestParameterUpdate(5, true)
        end
      elsif index == 5 then
        oldValue = ChordAcceptanceLevel
        if oldValue != newValue then
          ChordAcceptanceLevel = newValue
          RequestParameterUpdate(8, true)
        end
      elsif index == 6 then
        oldValue = BassAnalysis_0Off_1On
        if oldValue != newValue then
          BassAnalysis_0Off_1On = newValue
          RequestParameterUpdate(25, true)
        end
      elsif index == 7 then
        oldValue = MicroTuning_0Off_1On
        if oldValue != newValue then
          MicroTuning_0Off_1On = newValue
          RequestParameterUpdate(24, true)
        end
      elsif index == 8 then
        oldValue = NaturalSeventh_0Off_1On
        if oldValue != newValue then
          NaturalSeventh_0Off_1On = newValue
          RequestParameterUpdate(22, true)
        end
      elsif index == 9 then
        oldValue = VoiceLeadingDelay_ms
        if oldValue != newValue then
          VoiceLeadingDelay_ms = newValue; RequestParameterUpdate(36, false)
        end
      elsif index == 10 then
        if ccVal >= 64 and not VLResetCCHigh then
          ResetVoiceLeadingMemory = 1; RequestParameterUpdate(37, false)
        end
        VLResetCCHigh = ccVal >= 64
      end
    end
  end
End
// The help area describes the last changed control without interrupting MIDI processing.
Function UpdateParameterHelp()
  var message : String = ""
  var requestedMode : Integer = VoiceMode_0Sorted_1Stable
  var level : Integer = ChordAcceptanceLevel
  var bassInputChannel : Integer = BassInputMidiChannel
  var divisiInputChannel : Integer = InputMidiChannel
  var controlChannel : Integer = ControlMidiChannel
  if HelpParameterIndex == 0 then
    if requestedMode == 0 then message = "Sorted: assign selected notes by descending pitch. Low/High priority chooses notes."
    elsif requestedMode == 1 then
      message = "Stable: notes keep their channels. A new attack replaces the oldest voice when full."
    else
      message = "Voice Leading: held and sustained voices keep their channels. Grouped new notes minimize movement from remembered pitches; full voices are protected. Short attacks and repetitions are preserved."
    end
    message = message + " Modes: 0 Sorted reorders voices; 1 Stable binds attacks and replaces the oldest full voice; 2 Voice Leading groups new attacks using persistent channel memory. Collection Time sets its window; played pitches are unchanged."
  elsif HelpParameterIndex == 1 then
    if Mode_0Fixed_1Input == 0 then message = "Fixed: tune relative to Fixed Root when Microtuning Enabled is On. Chord acceptance and natural seventh are bypassed."
    else
      message = "Input: all held Divisi notes determine the chord; eligible bass notes join when Bass Chord Analysis is On. Microtuning Enabled is the master switch."
    end
  elsif HelpParameterIndex == 2 then
    message = "Fixed Root: 1=C, 2=C#, 3=D, 4=D#, 5=E, 6=F, 7=F#, 8=G, 9=G#, 10=A, 11=A#, 12=B. Fixed mode only."
  elsif HelpParameterIndex == 3 then
    message = "Divisi Input: MIDI channel for ensemble notes. All held input notes enter chord analysis."
  elsif HelpParameterIndex == 4 then
    message = "Voice Count: use output channels 1 up to this number. All held input notes enter chord analysis. Reducing the count ends higher-channel voices. Voice Leading protects occupied channels; increasing the count does not revive silent held notes."
  elsif HelpParameterIndex == 5 then
    if Priority_0Low_1High == 0 then message = "Low Priority: Sorted selects the lowest held notes. Voice Leading selects the lowest new overflow notes; occupied voices stay protected. Stable ignores priority."
    else
      message = "High Priority: Sorted selects the highest held notes. Voice Leading selects the highest new overflow notes; occupied voices stay protected. Stable ignores priority."
    end
  elsif HelpParameterIndex == 6 then
    message = "Pitch Bend Range: must match the instrument's range in semitones for correct tuning offsets."
  elsif HelpParameterIndex == 7 then
    message = "Chord Delay: harmonic analysis waits after input changes. Stable attacks remain immediate; Voice Leading uses its separate collection time. Running note releases remain immediate."
  elsif HelpParameterIndex == 8 then
    if level == 0 then message = "Level 0: Major triads only, with the root in the bass."
    elsif level == 1 then message = "Level 1: Major triads, including inversions with the third or fifth in the bass."
    elsif level == 2 then message = "Level 2: Adds minor triads and their permitted inversions."
    elsif level == 3 then message = "Level 3: Adds power chords, sus2 and sus4. Suspended and power chords require root or fifth bass."
    elsif level == 4 then message = "Level 4: Adds major add9 and minor add9 chords."
    elsif level == 5 then message = "Level 5: Adds major7 and minor7. Minor7 requires the root in the bass."
    elsif level == 6 then message = "Level 6: Adds dominant7 chords with root, third or fifth in the bass."
    elsif level == 7 then message = "Level 7: Adds minor9 chords. This matches the V3.1 chord selection."
    else
      message = "Level 8: Adds major9, dominant9, dominant7sus4 and dominant9sus4. Existing bass rules still apply."
    end
  elsif HelpParameterIndex == 9 then
    if bassInputChannel == 0 then message = "Bass Input: external bass analysis and dedicated routing are disabled."
    elsif bassInputChannel == divisiInputChannel then message = "Bass Input: external bass is disabled because its input channel matches Divisi Input."
    else
      message = "Bass Input: route this channel to Bass Output. Bass Chord Analysis determines whether lower bass notes enter chord detection; playback continues when analysis is Off."
    end
  elsif HelpParameterIndex == 10 then
    message = "Bass Output: reserved channel 11-16. Highest held bass note sets its shared microtuning offset."
  elsif HelpParameterIndex == 11 then
    message = "Root Tuning: offset for the chord root. 10=1 cent; 153=15.3 cents. Natural seventh follows this offset."
  elsif HelpParameterIndex == 12 then
    message = "Minor Second: tuning offset 1 semitone above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 13 then
    message = "Major Second: tuning offset 2 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 14 then
    message = "Minor Third: tuning offset 3 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 15 then
    message = "Major Third: tuning offset 4 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 16 then
    message = "Perfect Fourth: tuning offset 5 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 17 then
    message = "Diminished Fifth: tuning offset 6 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 18 then
    message = "Perfect Fifth: tuning offset 7 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 19 then
    message = "Minor Sixth: tuning offset 8 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 20 then
    message = "Major Sixth: tuning offset 9 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 21 then
    message = "Minor Seventh: offset 10 semitones above root. 10=1 cent. Natural Seventh can override this for dominant7."
  elsif HelpParameterIndex == 22 then
    if NaturalSeventh_0Off_1On == 0 then message = "Natural Seventh Off: use Minor Seventh Tuning for every accepted chord."
    else
      message = "Natural Seventh On: accepted dominant7 in Input mode uses approximately 7:4, following Root Tuning. Other forms and Fixed mode keep normal tuning; Microtuning Enabled must be On."
    end
  elsif HelpParameterIndex == 23 then
    message = "Major Seventh: tuning offset 11 semitones above the chord root. 10=1 cent; positive raises, negative lowers."
  elsif HelpParameterIndex == 24 then
    if MicroTuning_0Off_1On == 0 then
      message = "Microtuning Off: all 16 micro-offsets are zero, including held Divisi, bass and melody notes. Fixed and Input tuning are disabled; normal pitch bend continues."
    else
      message = "Microtuning On: Fixed mode uses Fixed Root; Input mode tunes only accepted chords. Current held voices are updated without Stable or Voice Leading retriggers; normal pitch bend continues."
    end
  elsif HelpParameterIndex == 25 then
    if BassAnalysis_0Off_1On == 0 then
      message = "Bass Analysis Off: external bass does not influence chord recognition. Bass routing and playback continue, tuned relative to any accepted Divisi chord. Fixed mode is unaffected."
    else
      message = "Bass Analysis On: held external bass notes below the lowest held Divisi note join chord recognition. Higher bass notes do not count; bass playback continues. Fixed mode is unaffected."
    end
    if bassInputChannel == 0 then
      message = message + " External bass is disabled because Bass Input is 0."
    elsif bassInputChannel == divisiInputChannel then
      message = message + " External bass is disabled because Bass Input matches Divisi Input."
    end
  elsif HelpParameterIndex == 26 then
    if controlChannel == 0 then
      message = "CC remote control disabled (channel 0). Select 1-16 to enable assignments on that input channel. Manual controls remain available."
    else
      message = "CC Control: only input channel " + controlChannel + " changes assigned controls. Unique, non-reserved assigned CCs are consumed; other CCs keep normal routing. -1 means unassigned; CC0 is valid."
    end
  elsif HelpParameterIndex >= 27 and HelpParameterIndex <= 35 then
    message = GetCCAssignmentHelp(HelpParameterIndex - 27)
  elsif HelpParameterIndex == 36 then
    message = "Voice Leading Collection: 0-100 ms, default 20. The first new attack fixes the deadline; later attacks do not extend it. Running note releases are immediate. Buffered short notes keep their played duration; repetitions keep their timing. Changes apply to the next window. At 0 ms allocation can depend on input order."
  elsif HelpParameterIndex == 37 then
    message = "Memory Reset: set 1 to reset once; this control returns to 0. Free channels forget previous pitches; sounding channels retain their current note. Notes, pedal, pending events and tuning remain unchanged. Memory otherwise survives pauses and resets on activation, applied mode/input changes or Divisi panic."
  elsif HelpParameterIndex == 38 then message = GetCCAssignmentHelp(9)
  elsif HelpParameterIndex == 39 then message = GetCCAssignmentHelp(10)
  end
  if requestedMode != ActiveVoiceMode then
    message = message + " Mode change pending: release all Divisi keys and sustain; pending attacks and releases must finish."
  end
  if HelpParameterIndex < 27 then message = message + GetCCConfigurationWarning() end
  if VLOverflow then message = message + " Voice Leading buffer full: additional attacks were dropped; existing releases are preserved." end
  SetDisplayMessage(message)
  HelpUpdatePending = false
End
Function EvaluateAccompaniment()
  var voiceLimit : Integer = DivisiVoiceCount
  var i : Integer
  var voiceCount : Integer = 0
  var currentCh : Integer = 1
  var analyzedRoot : Integer
  analyzedRoot = FindRoot()
  MicroTuningActive = false
  if analyzedRoot >= 0 then
    GlobalActiveRoot = analyzedRoot
    if MicroTuning_0Off_1On == 1 then MicroTuningActive = true end
  end
  if ActiveVoiceMode == 1 or ActiveVoiceMode == 2 then
    // Keep channel assignments for sustained notes; stolen notes have no output.
    for i = 0; i < 128; i = i + 1 do
      TargetVoices[i] = AccompPlayingVoices[i]
      SelectedNotes[i] = 0
      if TargetVoices[i] > 0 then SelectedNotes[i] = 1 end
    end
    RefreshVoicePitchBend()
  else
    // Original allocation: select low/high notes, then assign in descending pitch order.
    for i = 0; i < 128; i = i + 1 do TargetVoices[i] = 0; SelectedNotes[i] = 0 end
    if Priority_0Low_1High == 0 then
      for i = 0; i < 128; i = i + 1 do
        if AccompHeldVelocities[i] > 0 and voiceCount < voiceLimit then
          SelectedNotes[i] = 1; voiceCount = voiceCount + 1
        end
      end
    else
      for i = 127; i >= 0; i = i - 1 do
        if AccompHeldVelocities[i] > 0 and voiceCount < voiceLimit then
          SelectedNotes[i] = 1; voiceCount = voiceCount + 1
        end
      end
    end
    for i = 127; i >= 0; i = i - 1 do
      if SelectedNotes[i] == 1 and currentCh <= voiceLimit and currentCh <= 10 then
        TargetVoices[i] = currentCh
        currentCh = currentCh + 1
      end
    end
    RefreshVoicePitchBend()
    for i = 0; i < 128; i = i + 1 do
      if AccompPlayingVoices[i] > 0 and TargetVoices[i] != AccompPlayingVoices[i] then
        SendNow(MakeNoteMessageEx(i, 0, AccompPlayingVoices[i]))
      end
    end
    for i = 0; i < 128; i = i + 1 do
      if TargetVoices[i] > 0 and TargetVoices[i] != AccompPlayingVoices[i] then
        SendNow(MakeNoteMessageEx(i, AccompHeldVelocities[i], TargetVoices[i]))
      end
      AccompPlayingVoices[i] = TargetVoices[i]
    end
  end
End
Function ScheduleChordEvaluation()
  var i : Integer
  var keysHeldCount : Integer = 0
  var delayMs : Integer = ChordDelay_ms
  for i = 0; i < 128; i = i + 1 do
    if AccompHeldVelocities[i] > 0 then keysHeldCount = keysHeldCount + 1 end
  end
  if keysHeldCount == 0 then
    EvaluationScheduled = false
    EvaluateAccompaniment()
  else
    TargetTime = TimeSinceStartup() + IntToFloat(delayMs)
    EvaluationScheduled = true
  end
End
Function StopMelodyChannel(ch : Integer)
  var i : Integer
  var maxEngineCh : Integer = DivisiVoiceCount
  // Also end notes held by the pedal after their keys have been released.
  // Do not send channel-wide All Notes Off on occupied Divisi outputs.
  if ch > maxEngineCh then SendNow(MakeControlChangeMessageEx(64, 0, ch)) end
  for i = 0; i < 128; i = i + 1 do
    if GetMelodyVelocity(ch, i) > 0 then SendNow(MakeNoteMessageEx(i, 0, ch)) end
    SetMelodyVelocity(ch, i, 0)
  end
  if ch > maxEngineCh then
    SendNow(MakeControlChangeMessageEx(123, 0, ch))
    ResetChannelPitchBend(ch)
  end
End
Function StopBassOutput()
  var i : Integer
  SendNow(MakeControlChangeMessageEx(64, 0, ActiveBassOutputMidiChannel))
  for i = 0; i < 128; i = i + 1 do
    if BassPlayingVelocities[i] > 0 then
      SendNow(MakeNoteMessageEx(i, 0, ActiveBassOutputMidiChannel))
    end
    BassPlayingVelocities[i] = 0
  end
  SendNow(MakeControlChangeMessageEx(123, 0, ActiveBassOutputMidiChannel))
  ResetChannelPitchBend(ActiveBassOutputMidiChannel)
End
// Check before each event so routing changes cannot leave stale trackers.
Function ApplyRoutingChanges()
  var newInput : Integer = InputMidiChannel
  var newBassInput : Integer = BassInputMidiChannel
  var newBassOutput : Integer = BassOutputMidiChannel
  var i : Integer
  if newBassInput == newInput then newBassInput = 0 end
  if newInput != ActiveInputMidiChannel then
    for i = 0; i < 128; i = i + 1 do
      if AccompPlayingVoices[i] > 0 then
        SendNow(MakeNoteMessageEx(i, 0, AccompPlayingVoices[i]))
      end
      AccompHeldVelocities[i] = 0; AccompPlayingVoices[i] = 0
      TargetVoices[i] = 0; SelectedNotes[i] = 0
    end
    for i = 1; i <= 10; i = i + 1 do
      if ChannelPlayingNotes[i] >= 0 then ChannelPlayingNotes[i] = -1 end
      VoiceStartOrder[i] = 0
    end
    if DivisiSustainDown then
      for i = 1; i <= DivisiVoiceCount; i = i + 1 do
        SendNow(MakeControlChangeMessageEx(64, 0, i))
        SendNow(MakeControlChangeMessageEx(120, 0, i))
      end
    end
    DivisiSustainDown = false; DivisiSustainValue = 0
    if newInput != ActiveBassOutputMidiChannel then StopMelodyChannel(newInput) end
    ClearVoiceLeadingState()
    ActiveInputMidiChannel = newInput
    EvaluationScheduled = false
    ParameterUpdatePending = true
  end
  if newBassInput != ActiveBassInputMidiChannel or newBassOutput != ActiveBassOutputMidiChannel then
    StopBassOutput()
    if newBassInput != ActiveBassInputMidiChannel then
      for i = 0; i < 128; i = i + 1 do BassHeldVelocities[i] = 0 end
      if newBassInput > 0 then StopMelodyChannel(newBassInput) end
    end
    if newBassOutput != ActiveBassOutputMidiChannel then StopMelodyChannel(newBassOutput) end
    ActiveBassInputMidiChannel = newBassInput
    ActiveBassOutputMidiChannel = newBassOutput
    UpdateMelodyPB()
    for i = 0; i < 128; i = i + 1 do
      if BassHeldVelocities[i] > 0 then
        SendNow(MakeNoteMessageEx(i, BassHeldVelocities[i], ActiveBassOutputMidiChannel))
        BassPlayingVelocities[i] = BassHeldVelocities[i]
      end
    end
    ParameterUpdatePending = true
  end
  ApplyVoiceMode()
  EnforceStableVoiceLimit()
End
// Disjoint callbacks identify the changed control and use one update path.
On ParameterValueChanged
  Matching VoiceMode_0Sorted_1Stable
  RequestParameterUpdate(0, true)
End
On ParameterValueChanged
  Matching Mode_0Fixed_1Input
  RequestParameterUpdate(1, true)
End
On ParameterValueChanged
  Matching FixedChordRoot
  RequestParameterUpdate(2, true)
End
On ParameterValueChanged
  Matching InputMidiChannel
  RequestParameterUpdate(3, true)
End
On ParameterValueChanged
  Matching DivisiVoiceCount
  RequestParameterUpdate(4, true)
End
On ParameterValueChanged
  Matching Priority_0Low_1High
  RequestParameterUpdate(5, true)
End
On ParameterValueChanged
  Matching PitchBendRange_Semi
  RequestParameterUpdate(6, true)
End
On ParameterValueChanged
  Matching ChordDelay_ms
  RequestParameterUpdate(7, true)
End
On ParameterValueChanged
  Matching ChordAcceptanceLevel
  RequestParameterUpdate(8, true)
End
On ParameterValueChanged
  Matching BassInputMidiChannel
  RequestParameterUpdate(9, true)
End
On ParameterValueChanged
  Matching BassOutputMidiChannel
  RequestParameterUpdate(10, true)
End
On ParameterValueChanged
  Matching Tune_1root
  RequestParameterUpdate(11, true)
End
On ParameterValueChanged
  Matching Tune_2minorSecond
  RequestParameterUpdate(12, true)
End
On ParameterValueChanged
  Matching Tune_3MajorSecond
  RequestParameterUpdate(13, true)
End
On ParameterValueChanged
  Matching Tune_4minorThird
  RequestParameterUpdate(14, true)
End
On ParameterValueChanged
  Matching Tune_5MajorThird
  RequestParameterUpdate(15, true)
End
On ParameterValueChanged
  Matching Tune_6PerfectFourth
  RequestParameterUpdate(16, true)
End
On ParameterValueChanged
  Matching Tune_7diminishedFifth
  RequestParameterUpdate(17, true)
End
On ParameterValueChanged
  Matching Tune_8PerfectFifth
  RequestParameterUpdate(18, true)
End
On ParameterValueChanged
  Matching Tune_9minorSixth
  RequestParameterUpdate(19, true)
End
On ParameterValueChanged
  Matching Tune_10MajorSixth
  RequestParameterUpdate(20, true)
End
On ParameterValueChanged
  Matching Tune_11minorSeventh
  RequestParameterUpdate(21, true)
End
On ParameterValueChanged
  Matching NaturalSeventh_0Off_1On
  RequestParameterUpdate(22, true)
End
On ParameterValueChanged
  Matching Tune_12MajorSeventh
  RequestParameterUpdate(23, true)
End
On ParameterValueChanged
  Matching MicroTuning_0Off_1On
  RequestParameterUpdate(24, true)
End
On ParameterValueChanged
  Matching BassAnalysis_0Off_1On
  RequestParameterUpdate(25, true)
End
On ParameterValueChanged
  Matching ControlMidiChannel
  RequestParameterUpdate(26, false)
End
On ParameterValueChanged
  Matching CC_VoiceMode
  RequestParameterUpdate(27, false)
End
On ParameterValueChanged
  Matching CC_RootMode
  RequestParameterUpdate(28, false)
End
On ParameterValueChanged
  Matching CC_FixedRoot
  RequestParameterUpdate(29, false)
End
On ParameterValueChanged
  Matching CC_VoiceCount
  RequestParameterUpdate(30, false)
End
On ParameterValueChanged
  Matching CC_Priority
  RequestParameterUpdate(31, false)
End
On ParameterValueChanged
  Matching CC_ChordAcceptanceLevel
  RequestParameterUpdate(32, false)
End
On ParameterValueChanged
  Matching CC_BassAnalysis
  RequestParameterUpdate(33, false)
End
On ParameterValueChanged
  Matching CC_MicroTuning
  RequestParameterUpdate(34, false)
End
On ParameterValueChanged
  Matching CC_NaturalSeventh
  RequestParameterUpdate(35, false)
End
On ParameterValueChanged
  Matching VoiceLeadingDelay_ms
  RequestParameterUpdate(36, false)
End
On ParameterValueChanged
  Matching ResetVoiceLeadingMemory
  RequestParameterUpdate(37, false)
End
On ParameterValueChanged
  Matching CC_VoiceLeadingDelay
  RequestParameterUpdate(38, false)
End
On ParameterValueChanged
  Matching CC_VoiceLeadingReset
  RequestParameterUpdate(39, false)
End
Function ProcessHarmonicUpdates()
  if ParameterUpdatePending then
    ParameterUpdatePending = false; EvaluateAccompaniment()
  end
  if EvaluationScheduled and TimeSinceStartup() >= TargetTime then
    EvaluationScheduled = false; EvaluateAccompaniment()
  end
End
On TimerTick(ms : Double)
  ApplyRoutingChanges(); ProcessHarmonicUpdates()
  if ActiveVoiceMode == 2 then RunVLEvents(); ApplyVoiceMode() end
  if HelpUpdatePending then UpdateParameterHelp() end
End
On NoteEvent(m : NoteMessage)
  var noteNum : Integer = GetNoteNumber(m)
  var vel : Integer = GetVelocity(m)
  var ch : Integer = GetChannel(m)
  ApplyRoutingChanges()
  if ActiveVoiceMode == 2 then ProcessHarmonicUpdates(); RunVLEvents() end
  if ch == ActiveInputMidiChannel then
    if ActiveVoiceMode == 2 then
      if IsNoteOn(m) and vel > 0 then
        AccompHeldVelocities[noteNum] = vel; QueueVLAttack(noteNum, vel)
      else
        AccompHeldVelocities[noteNum] = 0; MarkVLInputRelease(noteNum)
        RefreshVLReservations(); RefreshVoicePitchBend()
      end
    elsif ActiveVoiceMode == 1 then
      if IsNoteOn(m) and vel > 0 then
        AccompHeldVelocities[noteNum] = vel
        StartStableVoice(noteNum, vel)
      else
        AccompHeldVelocities[noteNum] = 0
        if AccompPlayingVoices[noteNum] > 0 then
          SendNow(MakeNoteMessageEx(noteNum, 0, AccompPlayingVoices[noteNum]))
          if not DivisiSustainDown then ClearStableVoice(noteNum) end
          RefreshVoicePitchBend()
        end
      end
    else
      if IsNoteOn(m) and vel > 0 then
        AccompHeldVelocities[noteNum] = vel
        if AccompPlayingVoices[noteNum] > 0 then
          SendNow(MakeNoteMessageEx(noteNum, 0, AccompPlayingVoices[noteNum]))
          AccompPlayingVoices[noteNum] = 0
        end
      else
        AccompHeldVelocities[noteNum] = 0
      end
    end
    ScheduleChordEvaluation()
    if ActiveVoiceMode == 2 then ProcessHarmonicUpdates(); RunVLEvents() end
    ApplyVoiceMode()
  elsif ActiveBassInputMidiChannel > 0 and ch == ActiveBassInputMidiChannel then
    if IsNoteOn(m) and vel > 0 then
      BassHeldVelocities[noteNum] = vel
      if BassPlayingVelocities[noteNum] > 0 then
        SendNow(MakeNoteMessageEx(noteNum, 0, ActiveBassOutputMidiChannel))
      end
      UpdateMelodyPB()
      SendNow(MakeNoteMessageEx(noteNum, vel, ActiveBassOutputMidiChannel))
      BassPlayingVelocities[noteNum] = vel
    else
      BassHeldVelocities[noteNum] = 0; BassPlayingVelocities[noteNum] = 0
      SendNow(MakeNoteMessageEx(noteNum, 0, ActiveBassOutputMidiChannel))
      UpdateMelodyPB()
    end
    ScheduleChordEvaluation()
  elsif ch != ActiveBassOutputMidiChannel then
    if IsNoteOn(m) and vel > 0 then
      SetMelodyVelocity(ch, noteNum, vel)
      UpdateMelodyPB()
      SendNow(m)
    else
      SetMelodyVelocity(ch, noteNum, 0)
      SendNow(m)
      UpdateMelodyPB()
    end
  end
End
On ControlChangeEvent(m : ControlChangeMessage)
  var i : Integer
  var ccNum : Integer = GetCCNumber(m)
  var ccVal : Integer = GetCCValue(m)
  var ch : Integer = GetChannel(m)
  var controlHandled : Boolean = HandleControlCC(ch, ccNum, ccVal)
  ApplyRoutingChanges()
  if controlHandled then
    if ParameterUpdatePending then
      ParameterUpdatePending = false
      EvaluateAccompaniment()
    end
    if HelpUpdatePending then UpdateParameterHelp() end
  else
    if ch == ActiveInputMidiChannel then
      if ccNum == 64 then
        DivisiSustainValue = ccVal
        DivisiSustainDown = ccVal >= 64
      end
      for i = 1; i <= DivisiVoiceCount; i = i + 1 do
        if i <= 10 then SendNow(MakeControlChangeMessageEx(ccNum, ccVal, i)) end
      end
      if ccNum == 64 and not DivisiSustainDown and ActiveVoiceMode == 1 then
        for i = 0; i < 128; i = i + 1 do
          if AccompHeldVelocities[i] == 0 and AccompPlayingVoices[i] > 0 then ClearStableVoice(i) end
        end
        RefreshVoicePitchBend()
      end
      if ccNum == 64 and not DivisiSustainDown and ActiveVoiceMode == 2 then
        for i = 1; i <= 10; i = i + 1 do
          if ChannelPlayingNotes[i] >= 0 and VLReleased[i] then
            ClearStableVoice(ChannelPlayingNotes[i]); VLChannelToken[i] = 0
          end
        end
        RefreshVLReservations(); RefreshVoicePitchBend()
      end
      if ccNum == 120 or ccNum == 123 then
        for i = 1; i <= DivisiVoiceCount; i = i + 1 do SendNow(MakeControlChangeMessageEx(64, 0, i)) end
        for i = 0; i < 128; i = i + 1 do
          if AccompPlayingVoices[i] > 0 then SendNow(MakeNoteMessageEx(i, 0, AccompPlayingVoices[i])) end
          AccompHeldVelocities[i] = 0; AccompPlayingVoices[i] = 0
          TargetVoices[i] = 0; SelectedNotes[i] = 0
        end
        for i = 0; i <= 10; i = i + 1 do ChannelPlayingNotes[i] = -1; VoiceStartOrder[i] = 0 end
        DivisiSustainDown = false; DivisiSustainValue = 0
        ClearVoiceLeadingState()
        ScheduleChordEvaluation()
      end
      ApplyVoiceMode()
    elsif ActiveBassInputMidiChannel > 0 and ch == ActiveBassInputMidiChannel then
      SendNow(MakeControlChangeMessageEx(ccNum, ccVal, ActiveBassOutputMidiChannel))
    elsif ch != ActiveBassOutputMidiChannel then
      SendNow(m)
    end
  end
End
On PitchBendEvent(m : PitchBendMessage)
  ApplyRoutingChanges()
  GlobalPBValue = GetPitchBendValue(m)
  SendPitchBendToChannels()
End
On MidiEvent(m : MidiMessage)
  var i : Integer
  var ch : Integer = GetChannel(m)
  ApplyRoutingChanges()
  if ch == ActiveInputMidiChannel then
    for i = 1; i <= DivisiVoiceCount; i = i + 1 do
      if i <= 10 then SendNow(WithChannel(m, i)) end
    end
  elsif ActiveBassInputMidiChannel > 0 and ch == ActiveBassInputMidiChannel then
    SendNow(WithChannel(m, ActiveBassOutputMidiChannel))
  elsif ch != ActiveBassOutputMidiChannel then
    SendNow(m)
  end
End

```