Post by ki on Jun 10, 2019 16:41:08 GMT
This script allows for on-the-fly midi matrix connection switching either between song parts or even while holding notes to play additional notes with different voicings.
Route several midi event sources (keyboards, sequencers or generative midi) sending on different midi channels to this StreamByter instance and feed the scripts midi output to several instruments, each limited to listen to only a single midi channel.
The Midi Matrix Switcher features 8 different setups, selected by the 8 buttons - the active setup is highlighted. Per default, all channels are routed to themselves.Use the ui button in the lower right of StreamByter to switch to the editor where one defines the output channel 1-16 for each input channel or 0 for input mute.
Switch between the three sub-page by pressing the 'page' button to access all 16 input channels. The edited and active set can be switched with the top left 'current set' pop.
On page 3/3 one can setup the control channel, where Roezata pattern changed notes are received.
To return to the main page, press StreamBytes UI button twice to first close the editor and then reopen the main page.
Midi Event Handling
All midi events (PC, PitchBend, AfterTouch, Note-On etc) are routed to their current destination. Note-Offs are NOT send to the current active destination (which might have changed since) but to the channel used during Note-On.
The script tracks all incomming and outgoing note-on/note-off events to prevent hanging or doubly send notes.
Due to the flexible routing, the same note of an output channel might be triggered and released simultaneously from several input channels. Only the first event triggers the note and afterwards only that input channel can release the note – all events for this note originating from other input channels are ignored.
The script tracks all incomming and outgoing note-on/note-off events to prevent hanging or doubly send notes.
Due to the flexible routing, the same note of an output channel might be triggered and released simultaneously from several input channels. Only the first event triggers the note and afterwards only that input channel can release the note – all events for this note originating from other input channels are ignored.
Setup example for AUM
* Connect AUMs keyboard (channel 1) to the scripts StreamByter instance
– Add synth A, connect to StreamByter and use AUMs midi filter to only read from channel 1
– Add synth B, connect to StreamByter and use AUMs midi filter to only read from channel 2
* In StreamByter, select setup 2 and press the UI button to edit that setup. Route channel 1 to 2.
* Switch back to the amin UI by pressing the UI button twice.
* Now select setup 1 and hold a chord on AUMs keyboard playing sounds from synth A
– While holding the chord, press setup 2 and play addtional notes with synth B.
– Synth A will play the chord until its notes are released.
Its as simple as press setup 1 for synth A notes and setup 2 for synth B notes.
– While holding the chord, press setup 2 and play addtional notes with synth B.
– Synth A will play the chord until its notes are released.
Its as simple as press setup 1 for synth A notes and setup 2 for synth B notes.
Another usage idea
Setup Fugue maschine to send its four playheads on four channels and use the scripts setup to mixup the routing to four instruments generating sound variations of the same running theme. The switching can be randomly automated using Rozeta particles sending the pattern change notes on the control channel.
# === Midi Matrix Switch V3 ===
# Live jam utility to switch midi matrix routings either between song
# parts or even while holding notes to play additional notes with
# different voicings.
#
# Route several midi event sources (keyboards, sequencers or generative
# midi) sending on different midi channels to this StreamByter instance
# and FEED the scripts midi output to several instruments, each limited
# to listen to only A single midi channel.
# The 8 button on the first UI page allow to instantly toggle between
# connection setups for all input senders and output receivers.
#
# To edit the connection setup use the slider button to change to the
# second UI page. The editor uses 3 pages to display a output channel
# slider for each of the 16 midi input channels. On the third page one
# can specify the control midi channel, on which Rozeta pattern change
# notes are received. A slider to set 0 means to mute that channel.
# Use the ‚page‘ button to toggle between the three edit pages and the
# ‚current set‘ popup to change between the 8 setups.
#
# -ki 10.06.2019
#
# V3 10.06.2019 Change: Changed var layout to circumvent some overwriting
# V2 09.06.2019 New: Rozeta pattern change control notes
# V1 07.06.2019 Initial version
if LOAD
alias 90 NOTE_ON_MSG
alias 80 NOTE_OFF_MSG
alias F0 SYSEX_MSG
alias MT MIDI_CMD
alias MC MIDI_CHANNEL
alias M0 MIDI_DATA
alias M1 MIDI_NOTE
alias M3 MIDI_SLIDER
alias Q0 SLIDER_SET_1
alias Q1 SLIDER_SET_2
alias Q2 SLIDER_SET_3
alias Q3 SLIDER_SET_4
alias Q4 SLIDER_SET_5
alias Q5 SLIDER_SET_6
alias Q6 SLIDER_SET_7
alias Q7 SLIDER_SET_8
alias Q8 SLIDER_SETUP
alias Q9 SLIDER_EDIT_A
alias QA SLIDER_EDIT_B
alias QB SLIDER_EDIT_C
alias QC SLIDER_PAGE
alias QD SLIDER_EDIT_D
alias QE SLIDER_EDIT_E
alias QF SLIDER_EDIT_F
alias QF SLIDER_CONTROL
alias 7 SLIDER_SET_8_NUM
alias 8 SLIDER_SETUP_NUM
alias C SLIDER_PAGE_NUM
alias F SLIDER_CONTROL_NUM
alias K00 matrix
alias J20 setup
alias J30 page
alias J40 controlChannel
alias W00 state
alias J00 rozetaTable
alias I00 tmp
alias I01 idx
alias I02 offset
alias I03 source
alias I04 ptr
alias I05 relayPtr
alias I06 relayChannel
alias I07 storedRelay
alias I08 originPtr
alias I09 storedOrigin
alias I0A origin
alias I20 evOffset
alias QI01 slider[idx]
alias KI02 matrix[offset]
alias KI03 matrix[source]
alias KI20 matrix[evOffset]
alias WI04 state[ptr]
alias WI05 state[relayPtr]
alias WI08 state[originPtr]
alias JI04 rozetaTable[ptr]
alias QM03 slider[MIDI_SLIDER]
DEFINE SYSEX_SLIDER_MSG F0 7D 01
Define PERSIST +P
Define MUTED 80
subroutine UpdateEditSliders all
assign SLIDER_EDIT_A = matrix[offset]
math offset = offset + 1
assign SLIDER_EDIT_B = matrix[offset]
math offset = offset + 1
assign SLIDER_EDIT_C = matrix[offset]
math offset = offset + 1
assign SLIDER_EDIT_D = matrix[offset]
if all != 0
math offset = offset + 1
assign SLIDER_EDIT_E = matrix[offset]
math offset = offset + 1
assign SLIDER_EDIT_F = matrix[offset]
end
end
subroutine UpdateEditPage0
set SLIDER_PAGE PAGE___1/3 +BUTTON
set SLIDER_EDIT_A CH_1 0 10
set SLIDER_EDIT_B CH_2 0 10
set SLIDER_EDIT_C CH_3 0 10
set SLIDER_EDIT_D CH_4 0 10
set SLIDER_EDIT_E CH_5 0 10
set SLIDER_EDIT_F CH_6 0 10
UpdateEditSliders 1
end
subroutine UpdateEditPage1
set SLIDER_PAGE PAGE___2/3 +BUTTON
set SLIDER_EDIT_A CH_7 0 10
set SLIDER_EDIT_B CH_8 0 10
set SLIDER_EDIT_C CH_9 0 10
set SLIDER_EDIT_D CH_10 0 10
set SLIDER_EDIT_E CH_11 0 10
set SLIDER_EDIT_F CH_12 0 10
math offset = offset + 6
UpdateEditSliders 1
end
subroutine UpdateEditPage2
set SLIDER_PAGE PAGE___3/3 +BUTTON
set SLIDER_EDIT_A CH_13 0 10
set SLIDER_EDIT_B CH_14 0 10
set SLIDER_EDIT_C CH_15 0 10
set SLIDER_EDIT_D CH_16 0 10
set SLIDER_EDIT_E +HIDE
set SLIDER_CONTROL CONTROL_CH 0 10
math SLIDER_CONTROL = controlChannel + 1
math offset = offset + C
UpdateEditSliders 0
end
subroutine UpdateUI
assign idx = 0
while idx < 8
if idx == setup
assign slider[idx] = 1
Else
assign slider[idx] = 0
end
math idx = idx + 1
end
math SLIDER_SETUP = setup + 1
math offset = setup * 10
if page == 0
UpdateEditPage0
end
if page == 1
UpdateEditPage1
end
if page == 2
UpdateEditPage2
end
end
# ————————————————————————- Persisted data
# Data storage for 8 setups with 16 routings
assign K00 = 1 2 3 4 5 6 7 8 9 A B C D E F 10 PERSIST
assign K10 = 1 2 3 4 5 6 7 8 9 A B C D E F 10 PERSIST
assign K20 = 1 2 3 4 5 6 7 8 9 A B C D E F 10 PERSIST
assign K30 = 1 2 3 4 5 6 7 8 9 A B C D E F 10 PERSIST
assign K40 = 1 2 3 4 5 6 7 8 9 A B C D E F 10 PERSIST
assign K50 = 1 2 3 4 5 6 7 8 9 A B C D E F 10 PERSIST
assign K60 = 1 2 3 4 5 6 7 8 9 A B C D E F 10 PERSIST
assign K70 = 1 2 3 4 5 6 7 8 9 A B C D E F 10 PERSIST
assign setup = 0 PERSIST
assign controlChannel = F PERSIST
# ————————————————————————- Initialization
assign rozetaTable = 0 0 1 1 2 3 3 4 4 5 5 6 7 7 8
set NAME Matrix_Switcher
set SLIDER_SET_1 SET_1 +toggle
set SLIDER_SET_2 SET_2 +toggle
set SLIDER_SET_3 SET_3 +toggle
set SLIDER_SET_4 SET_4 +toggle
set SLIDER_SET_5 SET_5 +toggle
set SLIDER_SET_6 SET_6 +toggle
set SLIDER_SET_7 SET_7 +toggle
set SLIDER_SET_8 SET_8 +toggle
set SLIDER_SETUP CURRENT_SET: 1 8 +MENU
assign page = 0
set SLIDER_DISPLAY 1
UpdateUI
# Initialize note state array
# This array contains 2 states for each note of each channel
# The lower 8 bits contain
# - 00-0F the output channel for an active note
# - 80 muted active note
# - FF No active note
# The upper 8 bits contain
# - 00-0F the input channel for an send note
# - FF No active note
assign ptr = 0
while ptr < $2048
assign idx = 0
while idx < 128
assign state[ptr] = FFFF
math ptr = ptr + 1
math idx = idx + 1
end
end
end
# ————————————————————————- Handle Midi Events
if MIDI_CMD < SYSEX_MSG
math evOffset = setup * 10
math evOffset = evOffset + MIDI_CHANNEL
assign relayChannel = matrix[evOffset]
math relayChannel = relayChannel - 1
if relayChannel == FFFF
# Mute is slider 0, resulting in FFFF
assign relayChannel = MUTED
end
if MIDI_CMD <= NOTE_ON_MSG
# NoteOn or NoteOff
math origin = MIDI_CHANNEL * 100
math relayPtr = MIDI_CHANNEL * 80
math relayPtr = relayPtr + MIDI_NOTE
math storedRelay = state[relayPtr] & 00FF
if MIDI_CMD == NOTE_ON_MSG
if storedRelay != FF
BLOCK # —— Ignore double note-on
Else
# note not yet playing
if MIDI_CHANNEL == controlChannel
if MIDI_NOTE >= $24
if MIDI_NOTE <= $36
math ptr = MIDI_NOTE - $24
assign setup = rozetaTable[ptr]
UpdateUI
assign relayChannel = MUTED
end
end
end
if relayChannel == MUTED
BLOCK # -— Ignore, muted
else
math originPtr = relayChannel * 80
math originPtr = originPtr + MIDI_NOTE
math storedOrigin = state[originPtr] & FF00
if storedOrigin != FF00
BLOCK # —- Ignore, blocked by other origin
Else
# output channel not playing, relay note
math MIDI_DATA = 90 + relayChannel
# Store origin of note in upper byte of state
math tmp = state[originPtr] & 00FF
math state[originPtr] = origin + tmp
end
end
# Store the relay channel in lower byte of state
math tmp = state[relayPtr] & FF00
math state[relayPtr] = tmp + relayChannel
end
else
# Note-Off
if storedRelay == FF
BLOCK # —— Ignore double note-off
else
if storedRelay == MUTED
Block # —— Ignore, muted
Else
math originPtr = storedRelay * 80
math originPtr = originPtr + MIDI_NOTE
math storedOrigin = state[originPtr] & FF00
if storedOrigin != origin
Block # —— Ignore, blocked by other origin
Else
# Relay note
math MIDI_DATA = 80 + storedRelay
# Mark origin as free in upper byte of state
math tmp = state[originPtr] & 00FF
math state[originPtr] = FF00 + tmp
end
end
# Mark relay channel as free in lower byte of state
math tmp = state[relayPtr] & FF00
math state[relayPtr] = tmp + FF
end
end
Else
# All other midi events just relay to channel defined in matrix
if relayChannel == MUTED
BLOCK
else
math M0 = MT + relayChannel
end
end
end
# ————————————————————————- Handle UI Slider Events
if MIDI_DATA == SYSEX_SLIDER_MSG
if MIDI_SLIDER < SLIDER_SETUP_NUM
# Change setup
assign setup = MIDI_SLIDER
assign page = 0
UpdateUI
end
if MIDI_SLIDER == SLIDER_SETUP_NUM
math tmp = SLIDER_SETUP - 1
if setup != tmp
assign setup = tmp
UpdateUI
end
Exit
end
if MIDI_SLIDER == SLIDER_PAGE_NUM
if page < 2
math page = page + 1
else
assign page = 0
end
UpdateUI
end
if MIDI_SLIDER > SLIDER_SETUP_NUM
# Edit settings
math offset = setup * 10
math tmp = page * 6
math offset = offset + tmp
if MIDI_SLIDER < SLIDER_PAGE_NUM
# sliders 9-B
math tmp = MIDI_SLIDER - 9
math offset = offset + tmp
assign matrix[offset] = slider[MIDI_SLIDER]
end
if MIDI_SLIDER > SLIDER_PAGE_NUM
if page != 2
# sliders D-F on page 0 & 1
math tmp = MIDI_SLIDER - A
math offset = offset + tmp
assign matrix[offset] = slider[MIDI_SLIDER]
else
# sliders D-F on page 2
if MIDI_SLIDER == SLIDER_EDIT_D
math offset = offset + 3
assign matrix[offset] = slider[MIDI_SLIDER]
end
if MIDI_SLIDER == SLIDER_CONTROL_NUM
math controlChannel = SLIDER_CONTROL - 1
end
end
end
end
end
BTW: There seems to a problem feeding AU parameter Q8 to change setups. If the AU param is changed directly using AUMs 'expose AU Param' it flawless and is instantly updated in the UI, But if driven for example by Rozeta LFOs CC via AUMs MIDI Control setup, the script starts to glitch and hanging notes occur.