Values and Variables
Everything a script works with is a value. The 64 that a filter cutoff sends, the name Synthbrass that a DX7 stores in its voice, the question of whether the timer is running or not. Lua keeps each of these as a value, and each value has a type that tells Lua what can be done with it.
Lua has very few types, which is one of the reasons it is so quick to learn. For now we need four of them: numbers, strings, booleans and nil. Tables and functions have chapters of their own.
Numbers
A number is what MIDI is made of, so we start there. Lua writes numbers the way you would expect, and it understands the hexadecimal form we used in the MIDI part of this book:
print(60)
print(0x90)
print(3.5)
print(-12)The log shows 60, 144, 3.5 and -12. Note the second line: 0x90 is the Note On status byte from the MIDI Messages chapter, and Lua simply treats it as the number 144. You can write a SysEx message in a script in the same hexadecimal you see in the MIDI Console.
The arithmetic is the usual +, -, * and /, with two more that we will use constantly. % gives the remainder after a division, and // divides and throws the remainder away. Together they are the bit shifting and masking of the More Bits & Bytes chapter in a friendlier costume:
local value = 1000
print(value // 128) -- 7, the upper 7 bits (MSB)
print(value % 128) -- 104, the lower 7 bits (LSB)
print(7 * 128 + 104) -- 1000 againThat is exactly how a 14-bit value is split into two MIDI data bytes and put back together. The text after -- is a comment; Lua ignores it and so can you, although I would encourage you to write them.
One detail of the controller is worth knowing. The Electra One build of Lua keeps whole numbers and fractional numbers apart: 7 // 2 gives 3, while 7 / 2 gives 3.5. Everything the firmware hands you, a MIDI value, a control id, a number of milliseconds, is a whole number. Use // when you want to stay in that world, and math.floor() when you have a fraction and need a whole number back.
Strings
A string is a piece of text in quotes. Double or single quotes, it makes no difference, as long as they match:
print("Cutoff")
print('Synthbrass')Two operations cover most of what a script does with text. The .. operator glues strings together, and it will happily take a number as one of its halves. The # operator tells you how long a string is:
local name = "Synthbrass"
print("Voice: " .. name)
print(#name)The first line prints Voice: Synthbrass, and the second prints 10, which is no accident: a DX7 voice name is always ten characters.
When the text should follow a fixed pattern, string.format is the tool. It takes a pattern with placeholders and fills them in. %d stands for a whole number, %s for a string, %.1f for a number with one decimal place:
print(string.format("%d ms", 740))
print(string.format("%.1f dB", -6.02))
print(string.format("%02X %02X", 0x90, 60))The last one prints 90 3C, which is how you turn bytes back into the hexadecimal you are used to from the MIDI Console. We will lean on string.format heavily when we come to formatting what a control shows.
Booleans
A boolean is a value that is either true or false, and nothing else. A switch control is a boolean in disguise, and so is the question whether the timer is on:
local playing = false
print(playing)
print(timer.isEnabled())Both lines print false: the timer of a new preset is off until a script turns it on. Booleans come into their own in the next chapter, when the script starts making decisions.
Nil
nil is Lua's word for nothing here. A variable you have never given a value holds nil. A text box that has no override yet gives you nil when you ask for it. It is not an error to have nil, it is simply the absence of a value, and a well-behaved script checks for it before going further:
local lastNote
print(lastNote == nil)This prints true. We have declared a variable and given it nothing, which is a perfectly ordinary thing to do when the value will arrive later, for instance when the first MIDI message comes in.
Variables
A variable is a name for a value. You create one by assigning to it, and you can assign something else to it later:
local cutoff = 64
print(cutoff)
cutoff = cutoff + 10
print(cutoff)Names can contain letters, digits and underscores, cannot start with a digit, and are case sensitive: cutoff and Cutoff are two different variables. Lua does not care how you name things, but the people reading your script do, and that includes you in six months. cutoff is a better name than c.
Local and global
You will have noticed the word local in front of most of the variables above. A local variable exists only inside the block where it was created: inside a function, inside a loop, or, at the top level, inside the script. A variable without local is global and visible everywhere.
The habit to build is to write local by default. Two global variables with the same name in different parts of a script quietly overwrite each other, and that is a mistake that takes a long afternoon to find.
There is one place where a global is exactly what you want. When a control's Formatter field names a function, the controller looks that function up by its name, and it can only find a global one. We will come back to this in the chapter on functions. For plain values, use local.
A small convention from the tutorials rounds this off. Values that never change, such as where a name starts in a patch dump, are written in capitals and placed at the top of the script, so that they read as settings:
NAME_START = 152
NAME_LENGTH = 10
print("The name occupies bytes " .. NAME_START .. " to "
.. (NAME_START + NAME_LENGTH - 1))Lua does not know that these are constants; the capitals are a promise you make to the reader. It is a promise worth keeping.
What comes next
Values, and names for them, are the vocabulary. In the next chapter we start building sentences: a script that looks at a value and decides what to do about it.