MidiDesigner "Stream Byter" plugin

stream byter - binary logic math - midi messages - midi designer pro - dk70 - UltraEdit Wordfile

The Stream Byter plugin for Midi Designer can be used to reroute and alter MIDI messages before the MIDI stream hits the app. Stream Byter was licensed from Audeonic's MidiFire app in 2018. Development continued for the MidiFire app. However, the version in Midi Designer has not yet been updated (as of 2020) and is therefore a little behind. So take note that newer functions, reflected in Audeonic's Stream Byter manual, give an ERROR in the MidiDesigner Plugin version.

The lack of the newer "Rules" (read "Commands") in MidiDesigner will not get in the way of your programming needs at all. The only difference is, that they would have made life easier. Still it's a lot of fun to use.

Manual
You can stop studying the Audeonic University Stream Byter manual roughly at "Defines, Subroutines and Includes". These functions are not yet implemented in the MidiDesigner plugin. For a primer, start with this MIDI Bridge Stream Byter manual (MIDI Bridge is an older product from Audeonic).

From Audeonic: StreamByter is a plugin for creating custom MIDI effects on iOS & MacOS. StreamByter is ported from our MidiFire (=routing app) MIDI processing environment. An iOS 11 device and a suitable AU host app such as AUM, apeMatrix, Cubasis or Sequencism is required to use the AU variant on an iDevice.

The Stream Byter in Midi Designer is the "Stream Byter II" version that exists in Audeonic’s MidiFire app (iOS/macOS). Stream Byter input and output rules are saved and shared with your layout. Any MIDI Designer Pro 2 user may open a layout with rules someone else has authored free of charge. There's an in-app purchase to author or edit Stream Byter rules.

MidiDesigner Stream Byter version
In october 2020 I investigated the differences between the Audeonic manual pages and MidiDesigner (v2.165).
Here is a list of "rules" from the Audeonic manual, and the current implementation in MidiDesigner:

Audeonic explanation   MidiDesigner implementation
# comment #  
ALIAS custom names for variables -- n/i
ASSIGN assign a value to a variable ASS max 16 conseq. Array elements in allocation
BLOCK block an event -- n/i
DEFINE simple code macro -- n/i
EXIT finish processing immediately -- n/i
IF/ELSE conditional logic IF
END
ELSE not implemented
(operators, see below)
KEY send keystroke -- n/i
LOG log a message to the event monitor -- n/i *
MATH perform mathematical calculations MAT (math functions, see below)
CALC MATH synonym   n/i
SEND send a message SND max 16 values (see quick manual).
split SysEx into multi line with +F flag.

--FLAGS--
+I (inject)
do not Send this to SB MIDI Out,
but send it to SB MIDI IN

+ F (force)
no autocheck for message validity
(used for multiline split SysEx messages)
SET set system vars -- n/i
SUBROUTINE create a re-usable subroutine -- n/i
WHILE loops IF <..> +L
END
loops max 128 times for safety

* not implemented, though All MIDI communication is automatically printed to the MidiDesigner LOG screen.

SET not implemented (see Nil Velocity Rewrite), so use '9X XX 00 = 8X' for Note Off rewrite,
because the Note On check 'if MT == 90' could be Note off, when Velocity == 00.

Decimal values not implemented. MIDI Array index "MC" can be accessed, because the reserved variable MC does not exist.

To summarize:
- Use only HEX and Note values, decimal values are not implemented
- The reserved MIDI Variables "MC" and "MT" are not implemented


SYNTAX

INPUT RULES = OUTPUT RULES FLAGS
[Midi input stream rules match]   [process this Midi input stream]  
The Input Rules process MIDI after it comes into MIDI Designer, before it is parsed and affects your controls.   For the MIDI IN
The Output Rules process MIDI before it hits MIDI Designer
For the MIDI OUT
The Output Rules process MIDI before it hits your Wi-Fi, bluetooth, virtual and hardware MIDI destinations.
+C
+B
+D
all X's on the LEFT side are wildcards   all X's on the RIGHT side are referers to the corresponding incoming bytes on the LEFT side  

FLAGS

    LEFT RIGHT
no flag : match the input stream string.
trash the incoming message after processing.
reuse incoming values to rebuild the message.
send the new message to the app.
+C : match the input stream string.
let the incoming message pass, this results in 2 messages.
same as above.
+B : match the input stream string.
trash the incoming message.
type 'XX'
and do nothing
+D : delay an event by nn milliseconds  

Rules are evaluated top to bottom and the results of each rule are fed into the next (unless the clone flag is set).

SYNTAX (verbose)
vertical table

LEFT SIDE (INCOMING): IF incoming matches THIS
normally 3 bytes
exceptions: Cx, Dx, F3-FE: 1 or 2 bytes; F0, F1: > 3 bytes)
= RIGHT SIDE (OUTGOING) SND THIS rewritten message
1st byte 1st nibble type var N: match Note Off (8) or On (9)
var X: match "all event types"
range 8-F: range of types to match
  var X: preserve corresponding incoming nibble
2nd nibble channel var X: match "any channel"
range 0-F: range of channels to match
  var X: preserve corresponding incoming nibble
2nd byte   name var XX: match "any name"   var XX: copy byte 2 of incoming
var X3: replace byte 2 with byte 3
flag +C: copy byte (2 and) 3 of incoming (opt)
3rd byte   value var XX: match "any value"   var XX: copy byte 3 of incoming
var X2: replace byte 3 with byte 2
flag +C: copy byte 2 and 3 of incoming
LEFT: all X's are wildcards   RIGHT: all X's are referers

IF (LEFT SIDE matches string) {
  SND RIGHT SIDE composed string
}

SYNTAX (verbose)
same, but horizontal table

LEFT SIDE (INCOMING): IF incoming matches THIS
normally 3 bytes
exceptions: Cx, Dx, F3-FE: 1 or 2 bytes; F0, F1: > 3 bytes)
  RIGHT SIDE (OUTGOING) SND THIS rewritten message
1st byte 2nd byte 3rd byte   1st byte 2nd byte 3rd byte
Type and Channel nibble Name byte Value byte = Type and Channel nibble Name byte Value byte
#: type number #: channel number ##: name nr ##: value nr   #: type number #: channel number ##: name nr ##: value nr
X: all types X: all channels XX: any name XX: any value   X: copy 1st nibble
from incoming
X: copy 2nd nibble
from incoming
XX: copy 2nd byte
from incoming
XX: copy 3rd byte
from incoming
8-F: type range 0-F: channel range 00-7F: range 00-7F: range       X3: copy 3rd byte
from incoming
X2: copy 2nd byte
from incoming
N: Note On or Off                
LEFT: all X's are wildcards   RIGHT: all X's are referers

Range example '8, A, C' does not work.

Examples

- Match CC $7 from every channel and add CC $8 to the MID stream with the same value
BX 06 XX = BX 07 XX +C
BX 06    = XX 07    +C # this is identical

- Match all CC from Ch 1 and copy Name/Value to Ch 2
B0 = B1 +C

- Match all CC from Ch 1 and move the Name/Value to Ch 2
B0 = X1

- Match all CC $8 from Ch 1 and move the Name/Value to CC $7 on Ch 2
B0 07 = B1 06
B0 07 XX = B1 06 XX # identical
B0 07 = X1 06 # identical, only type the name/vals that need to be changed

- Rewrite all Note On/Off Msg from Ch 1 to Ch 2
N0 = X1 # can not reuse N on the right side

- Merge all Note On/Off from all Channels to Ch 1
NX = X0

- Block all Mod Wheel msg
B0 01 = XX +B

PERMITTED VARIABLE NAMES (ALPHABETICAL)

name purpose
0-9, A-F hexadecimal numbers. reserved
^A-^G note names
BP current tempo of the MidiFire Clock
G global array
I, J, K, L local array
M midi message array
MC midi channel number
ML midi message length
MT midi type (1st nibble of 1st byte)
second nibble is always 0
N LEFT: note off or on wildcard (8 or 9)
RIGHT: not available
P precision array
PO current MidiFire clock position
Q MidiFire GUI control
R returns random number
T timer
W wide array
X LEFT: wildcards
RIGHT: referers
Z arguments array in SUB
ZN nr of arguments in SUB

ARRAY VARIABLES
indices start from 0.
0 is hex and $0 is decimal

name array type indices values example
I, J, K, L local 256 [0-255]
    [00-FF]
2-bytes values (unsigned 16 bits int) L00-LFF (hex)
L$0-L$255 (dec)
G global 256 2-bytes values (unsigned 16 bits int) G00-GFF
W wide 2048 [0-7FF] 2-bytes values (unsigned 16 bits int) W0-W7FF
W$0-W$2047
P precision 256 4-bytes values (signed 32 bits int) P00-PFF
T timer 8 timing calculations in milliseconds
(16-bit, max 65535 seconds)
T00-T07

T: Each time you refer to a timer variable, the value returned will be the number of milliseconds elapsed since that timer variable was last referenced. The first time you refer to a timer variable after a scene load, it will return 0 milliseconds. The values of variables are not reset when you press the 'Install Rules' button, but they are reset during a scene load.

R/O variables that contains a copy of the current midi message

M midi message array 65536 (M0-MFFFF) unsigned 8 bit M0A - 11th message byte (hex)
M$10 - 11th msg byte (dec)
ML --- length of midi message    
MC --- midi channel number 0-F returns F0 if the message is not a channel message
(eg. the F-range)
alert! C (hex) = 13 (dec)
to get the 13th element of a midi message use M$12

- TQ5 v1.0, MIDI OUT rules: "MT and MC doesn't seem to work in MD"
- which is quite fortunate, because $12 doesn't seem to work either.
  nearly made me decide to trash streambyter development.
MT --- midi type/status [8-F] first nibble of the first byte (channel removed)
- TQ5 v1.0, MIDI OUT rules: &quot;MT and MC doesn't seem to work in MD&quot;

other variables

R returns random number 0-nn nn = hex number or variable name
BP current tempo of the MidiFire Dynamic Clock module * 100   BPM 127.32 = value 12732
PO current MidiFire clock position in milliseconds   32 bit signed value

indirect array addressing

GL0 global array G, index is that of the value stored in variable L0
MG03 MIDI message array, index is that of the value stored in the variable G03

VALUES
Values come in different flavours

Values can be:
- a literal hex value: 0A (hex)
- a literal decimal value: $10 (dec) DOES NOT WORK IN STREAM BYTER
- a literal negative decimal value: $-10 (dec) DOES NOT WORK IN STREAM BYTER
- a note literal: ^C-2 (from C-2 to G8), ex. ^C3 ^G#6 ^Bb0 ^Bb-1 ^D-2 ^G2
- the contents of another variable

ASSIGN
As in: the LET command in BASIC

ASS L0 = 5
ASS L1 = 70 23 2 A7 $51 L02 4 3 ^C1 # Fill L01-09 with hex hex hex hex dec var hex hex note

MATH
Mathematical functions. like ASS but with operators.

MAT I0 = I0 + 1 # Add
MAT I0 = I0 - 1 # Subtract
MAT I0 = I0 * 3 # Multiply
MAT I0 = I0 / 2 # Divide
MAT I0 = I0 % 2 # Mod

And Bit operators

MAT I0 = I0 & 0F # And
MAT I0 = I0 | 0F # Or
MAT I0 = I0 ^ FF # Xor

See bit operations on the page Binary Logic Math.

SEND
Send a MIDI stream of max. 16 bytes.
Split SysEx messages into separate Send-commands

SND B0 01 L01 # Send CC $2 to Channel 2, get the value from L01
SND 90 C2 7F # Send Note C2 On to Channel 1, Velocity 127

 


PROGRAMMING STUFF

CONDITIONAL BLOCK
can be nested

IF M0 == C0
  # do something
  # ELSE is not implemented, use a variable or XOR
END

OPERATORS

==, !=, <, <=, >, >=
note that = means assign (ASS), and == means compare (IF, WHILE)

IF-CLAUSE STRUCTURE

<value> <OPERATOR> <value> [<value> [<value>] [<value>]] [+L[OOP]]

If more than one value is specified after the operator (max 4), then the left hand value should be an Array and each right hand variable corresponds to an incremental index of the Array.

IF M12 == 64 32 G01
  # do something
END

# Same as:
# IF (M12==64) AND (M13==32) AND (M14==G01) THEN

Another fun example: get the index for K from variable I

ASS K0 = 1 2 3 4 5 6 7 # indices K0-K6 are filled
ASS I0 = 3

IF KI0 == 4 5 6
  # this will be true
END

# the conditional above works out to be the same as
IF K3 == 4
  IF K4 == 5
    IF K5 == 6
    END
  END
END

CONDITIONAL LOOPS (WHILE)
use the +L flag in an IF

ASS I0 = 0
IF I0 < 10 +L
  MAT I0 = I0 + 1
END

HEADER OF CODE
LOAD command, is executed before any MIDI has arrived, so the M variable is still empty

IF LOAD
  ALIAS ...
  DEFINE ...
  SUB ...

  
  SEND C0 01 +D2000
END

 

copyright: tinyloops.com - contact