Post by nic on May 3, 2019 18:47:51 GMT
What are variables?
A variable is just a little bit of memory for storing bits of data as needed. StreamByter uses variable arrays which are a sequence of variables numbered from 0. Think of them as a hotel of rooms that may or may not have guests staying in them, hence their contents are... variable.
There are four main variable arrays and they are each given a single letter to identify them: I, J, K and L. Each array element (room) can hold a number between 0 and 65535 (that's 16 bit unsigned) and each array has 256 elements, numbered 0 sequentially to 255. So, we have 4 hotels called I, J K and L and each hotel has 256 rooms and each room is given a unique whole number from 0 to 255. Of note is that room 1 is to the right of room 0 and so on...
To put the number 12 in room 47 in hotel 'L' you simply say:
assign L$47 = $12
Those $ characters above tell StreamByter you want to use decimals for the numbers, because without the $, StreamByter wants hexadecimal (hex) numbers. Hex is more concise when dealing with data and second nature to MIDI programmers, but not everyone likes hex, so StreamByter gives you the choice. The following is the exact same as the above but using the short rule name and hex numbers instead:
ASS L2F = 0C
So, now that the number 12 is encamped in room 47 in hotel L, we can refer to L$47 elsewhere in our script and while number 12 is staying there, that is the value that will be used. To put another number in a room just ASSIGN again.
We can copy the occupant of one room to another:
assign L$47 = L$30
or between hotels:
assign L$47 = J$32
Any number without an array letter before it is a literal (see below for more detail on literals). In the examples above, $12 is a literal; what you see is what you get.
Being able to refer to a selection of rooms that are all next to each other is very useful if you want to do something multiple times to each room in order. It makes all the difference to say 'clean rooms 0 to 15' instead of 'clean room 0, then clean room 1, then clean room 2 ... and finally clean room 15', right? You can assign multiple values in one go to a bunch of elements that are next to each other:
ASSIGN I$12 = 1 2 3 L$47
Which is the same as:
assign I$12 = 1
assign I$13 = 2
assign I$14 = 3
assign I$15 = L$47
You can assign up to 16 elements with the one ASSIGN rule.
That can be really handy for specifying lookup tables, which brings us to indirect variables - where the room numbers themselves are also variable.
Say we want to create a crazy custom velocity curve that can't be drawn, where every even velocity should become the max velocity and every odd velocity should be set to minimum velocity.
In MIDI, each note can have a velocity from 1 to 127 (it's in the third byte of a 'note on' message), so we can create a 'table' of 127 elements each having the required velocity value that we wish to impose using the ASSIGN rule like so using the 'L' array:
ASS L00 = 00 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L10 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L20 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L30 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L40 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L50 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L60 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L70 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
I have used short form and hex above, because it looks more 'neat' to my eyes, but you can use decimals/long form if you prefer. ('7F' above is 127 in decimal). You might also notice that above I said there are 127 possible velocities, yet the table has 128 entries. This is because a velocity of 0 is interpreted as a note off, so we do not want to remap that to a velocity with value, as we would get lumbered with stuck notes. So the very first entry in our table (the value in L0) is 0 so that we leave those note offs alone.
Now, whenever a note on event comes in, we can look at the velocity value of that message and then 'lookup' the velocity value we want to remap to using an indirect variable!
Assuming the incoming MIDI message is contained in a variable array called 'M' (it is, but we are getting ahead of ourselves), we can replace the velocity value in M2 (third byte of array M, which is numbered from 0, remember) with the corresponding value from our 128 entry lookup table:
assign M2 = LM2
Wot?
We are assigning something to 'M2' (3rd byte of message), that's simple enough.
However where we have LM2, then this means: 'get whatever number is in M2 and use that as the index (room) number in the L array' (our lookup table), so if M2 had a value of 1 that means we really want the value of L1, which is 7F (or 127). This feature is pretty important to understand for proficient StreamByter programming. It's just like an indirect reference in Excel or an array subscript variable in just about any programming language
Remembering who/what each array variable is by the letter and number can become tedious. StreamByter lets you give your own meaningful name to any variable (literal, standard or indirect) and then use that name to refer to it from then on. This is done with the ALIAS rule. Defining and using an alias is simple:
alias I0 middleC # middleC now refers to I0
# we can use 'middleC' elsewhere in the code
# instead of I0
assign middleC = $64
if M0 == middleC
set lb0 middleC +N
end
There are other arrays available in StreamByter language:
The Message array is examined in more detail here
This gives you the basics of what variable arrays are, how they are referred to in code and what they contain. Further articles will explore how you actually use variables in anger.
A variable is just a little bit of memory for storing bits of data as needed. StreamByter uses variable arrays which are a sequence of variables numbered from 0. Think of them as a hotel of rooms that may or may not have guests staying in them, hence their contents are... variable.
There are four main variable arrays and they are each given a single letter to identify them: I, J, K and L. Each array element (room) can hold a number between 0 and 65535 (that's 16 bit unsigned) and each array has 256 elements, numbered 0 sequentially to 255. So, we have 4 hotels called I, J K and L and each hotel has 256 rooms and each room is given a unique whole number from 0 to 255. Of note is that room 1 is to the right of room 0 and so on...
To put the number 12 in room 47 in hotel 'L' you simply say:
assign L$47 = $12
Those $ characters above tell StreamByter you want to use decimals for the numbers, because without the $, StreamByter wants hexadecimal (hex) numbers. Hex is more concise when dealing with data and second nature to MIDI programmers, but not everyone likes hex, so StreamByter gives you the choice. The following is the exact same as the above but using the short rule name and hex numbers instead:
ASS L2F = 0C
So, now that the number 12 is encamped in room 47 in hotel L, we can refer to L$47 elsewhere in our script and while number 12 is staying there, that is the value that will be used. To put another number in a room just ASSIGN again.
We can copy the occupant of one room to another:
assign L$47 = L$30
or between hotels:
assign L$47 = J$32
Any number without an array letter before it is a literal (see below for more detail on literals). In the examples above, $12 is a literal; what you see is what you get.
Being able to refer to a selection of rooms that are all next to each other is very useful if you want to do something multiple times to each room in order. It makes all the difference to say 'clean rooms 0 to 15' instead of 'clean room 0, then clean room 1, then clean room 2 ... and finally clean room 15', right? You can assign multiple values in one go to a bunch of elements that are next to each other:
ASSIGN I$12 = 1 2 3 L$47
Which is the same as:
assign I$12 = 1
assign I$13 = 2
assign I$14 = 3
assign I$15 = L$47
You can assign up to 16 elements with the one ASSIGN rule.
That can be really handy for specifying lookup tables, which brings us to indirect variables - where the room numbers themselves are also variable.
Say we want to create a crazy custom velocity curve that can't be drawn, where every even velocity should become the max velocity and every odd velocity should be set to minimum velocity.
In MIDI, each note can have a velocity from 1 to 127 (it's in the third byte of a 'note on' message), so we can create a 'table' of 127 elements each having the required velocity value that we wish to impose using the ASSIGN rule like so using the 'L' array:
ASS L00 = 00 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L10 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L20 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L30 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L40 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L50 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L60 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
ASS L70 = 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F 01 7F
I have used short form and hex above, because it looks more 'neat' to my eyes, but you can use decimals/long form if you prefer. ('7F' above is 127 in decimal). You might also notice that above I said there are 127 possible velocities, yet the table has 128 entries. This is because a velocity of 0 is interpreted as a note off, so we do not want to remap that to a velocity with value, as we would get lumbered with stuck notes. So the very first entry in our table (the value in L0) is 0 so that we leave those note offs alone.
Now, whenever a note on event comes in, we can look at the velocity value of that message and then 'lookup' the velocity value we want to remap to using an indirect variable!
Assuming the incoming MIDI message is contained in a variable array called 'M' (it is, but we are getting ahead of ourselves), we can replace the velocity value in M2 (third byte of array M, which is numbered from 0, remember) with the corresponding value from our 128 entry lookup table:
assign M2 = LM2
Wot?
We are assigning something to 'M2' (3rd byte of message), that's simple enough.
However where we have LM2, then this means: 'get whatever number is in M2 and use that as the index (room) number in the L array' (our lookup table), so if M2 had a value of 1 that means we really want the value of L1, which is 7F (or 127). This feature is pretty important to understand for proficient StreamByter programming. It's just like an indirect reference in Excel or an array subscript variable in just about any programming language
Remembering who/what each array variable is by the letter and number can become tedious. StreamByter lets you give your own meaningful name to any variable (literal, standard or indirect) and then use that name to refer to it from then on. This is done with the ALIAS rule. Defining and using an alias is simple:
alias I0 middleC # middleC now refers to I0
# we can use 'middleC' elsewhere in the code
# instead of I0
assign middleC = $64
if M0 == middleC
set lb0 middleC +N
end
There are other arrays available in StreamByter language:
- The 'G' (Global) array is shared amongst all StreamByter instances in an AUv3 host/MidiFire/StreamByter, so if you set G0 in one StreamByter then that value will also be set in another StreamByter's 'G' array.
- Sometimes 256 elements is not enough. The 'W' (Wide) array has 2048 elements.
- The 'P' (Precision) array has signed 32 bit integers as elements for doing maths with larger numbers
- The 'M' (Message) array is unsigned 8bit bytes and represents the current MIDI message
The Message array is examined in more detail here
This gives you the basics of what variable arrays are, how they are referred to in code and what they contain. Further articles will explore how you actually use variables in anger.