KeyStage Tutorial: The Scripting Language — Transcript
Full transcript
- 0:06Welcome to another tutorial video of
- 0:07Keystage. So in this video we are going
- 0:10to talk about the scripting language of
- 0:12Keystage. So scripts are outputs of
- 0:14custom translators which allow you to do
- 0:16much more advanced stuff. To start with
- 0:19let's start creating a custom translator
- 0:22on this track. Uh let's choose notes as
- 0:25our input
- 0:26and for output we choose script. Okay,
- 0:29so this is the script editor. Uh before
- 0:32we go into that, uh there is a new
- 0:34feature in 2.3
- 0:37which is these input conditions can also
- 0:40be chosen when the output is scripted.
- 0:42In the previous version, you won't be
- 0:43able to choose the input conditions. It
- 0:46doesn't have matter what input you have.
- 0:48For for example, if you have a pitch
- 0:49band, you can choose the input range for
- 0:51the pitch bands, input channels, and so
- 0:53on and so forth. So before the script is
- 0:56executed first these input conditions
- 0:58are checked and when those conditions
- 1:00are matched the script runs. So that's
- 1:03how it works. Okay. So before we start I
- 1:08strongly recommend you to connect a
- 1:10keyboard to your iPads to before working
- 1:13on the scripts. So I already connected a
- 1:15keyboard here and normally if you don't
- 1:19connect a keyboard you you have to use
- 1:21the virtual keyboard on on your iPad
- 1:24which is not really convenient. Uh
- 1:27connecting a keyboard makes it much
- 1:28easier because for example when you
- 1:31start typing uh autocompletes view
- 1:34appears and using up and down arrows you
- 1:37can choose between the options in the
- 1:38autocomplete view.
- 1:43Now we have cheat sheet here. This is
- 1:46also new in 2.3. We were going to go
- 1:49back to this cheat sheet. But what what
- 1:50this shows is all types of commands that
- 1:54you can write on on the scripting
- 1:57language. And these are all categorized
- 1:58in in a convenient way. For example, we
- 2:02had the variable types here. Then every
- 2:04time you choose one of the commands
- 2:07here, you can see the description of the
- 2:10command here. what it does.
- 2:12Okay, so let's write our first program.
- 2:16I'm going to go through the basics, but
- 2:17let's write our first program. Print
- 2:22hello world. So this is the first
- 2:24program that you always write in a
- 2:27computer language. So when I press a
- 2:29note,
- 2:30actually when I press a note and release
- 2:32the note, the hello appears in the log
- 2:35view here. So print actually writes into
- 2:38this log view here. So I can clear the
- 2:40lock here. Um why do I why does it
- 2:44happen when I press and release? Because
- 2:47in the input conditions both note and
- 2:49not off are selected here. Okay. So
- 2:52before I continue let me talk about
- 2:54commenting out lines. This is also
- 2:56standard in all computer languages. So
- 2:59now compiler completely disregards this
- 3:02line. Ignores this line. Now some basic
- 3:05rules about the scripting language. You
- 3:08can only write one comments on one line.
- 3:10You cannot have multiple comments on one
- 3:13line. For example, variable assignments,
- 3:16actions or whatever. You can have only
- 3:19one command. And now let's talk about
- 3:22variables first. So you can see there is
- 3:25this incoming variables. Since our input
- 3:28is a note,
- 3:31you can see the variable. So as I start
- 3:33playing
- 3:35when I press note note is on is true
- 3:39channel is zero. So note value and note
- 3:42velocity. So we get all the information
- 3:44of the input input signal. But for
- 3:46example if I
- 3:48uh let's assign
- 3:50one of the knobs here on my keyboard.
- 3:54If I actually choose control change 12.
- 3:56Now these incoming variables change. We
- 3:59have channel control change number and
- 4:01control change value. So you can monitor
- 4:04the incoming signals here and you we
- 4:06will be using these variables. So these
- 4:08are variables. We're going to talk about
- 4:10how we create variables. But let's talk
- 4:12about variables, variable types here. So
- 4:14we have different types of variables.
- 4:17We're going to go over them one by one.
- 4:19For now, what we're interested in is
- 4:21integers, floats, booleans, and strings.
- 4:26There are area versions of these
- 4:27variables, but we're going to talk about
- 4:29them later. So, these four types of
- 4:31variables, integers, floats, booleans,
- 4:33and strings are called basic variables.
- 4:36We're going to talk about non-basic
- 4:38variables at the end of this video, but
- 4:40for now, we we will be dealing with
- 4:42these four types of variables. And
- 4:44speaking of which, the input variables,
- 4:47incoming variables for the node signal
- 4:49are all in integer variables except this
- 4:52note on. This is a boo boolean variable.
- 4:54You can see that it's either true or
- 4:56false but all other variables are
- 4:58integers. Okay. So our first example is
- 5:01going to be a simple stakato. This is
- 5:04actually one of the uh custom
- 5:06translators in the database but we're
- 5:08going to build it from from scratch
- 5:10here. So uh first of all we want to
- 5:13apply the stacato only to note on
- 5:16signals. So I disabled note off. So
- 5:20right now when I press note this never
- 5:22becomes false because note off is
- 5:24filtered uh in the input conditions.
- 5:28Okay. So how do you write a staccato?
- 5:32All we need to do is send the note on
- 5:34signal and then send the note off signal
- 5:37after a certain period of time with a
- 5:39delay. So our first comment here is medi
- 5:44out. So as I type keep writing I see the
- 5:47options and media out is here.
- 5:49So what where is media out? Media out is
- 5:52here in the actions part in the actions
- 5:55menu. So these are all actions
- 5:58basically. So med sending a med signal
- 6:00is an action. Clearing a log is an
- 6:02action. So you can see the descriptions.
- 6:04So moving up up and down. We're not
- 6:06going to go through all these options.
- 6:08So throughout the tutorial we're going
- 6:11to use some of these actions but uh you
- 6:14can actually read the descriptions of
- 6:15all all of them here at the bottom.
- 6:18Okay. Okay. So media out. So what are we
- 6:20sending where we have medi message types
- 6:23here? We're going to send note on
- 6:26signal. So let's read the description.
- 6:28Note on medi type takes three
- 6:31parameters. The first parameter is
- 6:32channel. Second parameter is note and
- 6:34third parameter is velocity. So this is
- 6:36a common sense like these uh note on and
- 6:40not off signals are determined by these
- 6:43channel note value and velocity. So
- 6:47let's type
- 6:49note on. So a parenthesis appear. The
- 6:52first parameter is channel. So I write
- 6:55channel. So this is the incoming
- 6:57variable. You can see it here. This is
- 6:59your channel number,
- 7:02note value, comma.
- 7:07So right now when I press a note, the
- 7:10note on signal is is sent. But we want
- 7:14to send a note off signal with a delay.
- 7:17So media out
- 7:20note off. It's going to be the same
- 7:22channel, same note. Velocity doesn't
- 7:26matter in this case, but for some
- 7:28synthesizer, it actually does matter.
- 7:30And at the end of this, I'm going to add
- 7:33a command called delay. This is a
- 7:36function. Add the delay to the execution
- 7:38of the line. And we're going to talk
- 7:40about the rest of the description later.
- 7:43The number that you enter here will be
- 7:45considered as milliseconds. So let's say
- 7:49200 milliseconds.
- 7:51So right now
- 7:54even if I keep my notes pressed
- 7:58uh we just get the stato. So let's
- 8:00reduce this number to let's say 50
- 8:02milliseconds.
- 8:09Right now we have delay uh we have this
- 8:11staccato. So if I disable this,
- 8:15if I enable this,
- 8:18the stato works. Our next step is to add
- 8:21some conditionals. So where are
- 8:23conditionals? Conditionals and loops
- 8:25here. So this should be common sense for
- 8:27most of you guys who have some basic
- 8:30knowledge about any language bas
- 8:32basically C basic whatever they all have
- 8:36these
- 8:37core functions like the conditionals and
- 8:41loops it's going to start with
- 8:43conditional so it's easy
- 8:45so what I want to do here is I want to
- 8:49make the stato
- 8:52uh happen only at a high velocity
- 8:55And on low velocities I don't want this
- 8:58staccato to apply. Okay. So let's write
- 9:01if.
- 9:03So I want the velocity value to be above
- 9:07a certain number in order for this two
- 9:10lines to apply. So if velocity
- 9:15is greater than let's say 60.
- 9:20Uh well for some reason cuts doesn't
- 9:23work. I I'm going to work out work out
- 9:25this but copy works. So all I have to do
- 9:28is just copy and then uh delete the
- 9:31original and then paste. Okay. So if is
- 9:35greater than 60 we we had the stato.
- 9:38Well okay if it's less than 60 which
- 9:41means else
- 9:43we want to send the original signal. Now
- 9:46we have another uh nice
- 9:49uh command here actually and a specific
- 9:52special variable here which is called
- 9:54incoming metadata. So right now low
- 9:57velocities doesn't have stacato
- 10:02high velocities have stacato.
- 10:05Okay. So we're going to improve this uh
- 10:08by adding a slider to this an additional
- 10:11slider to this custom translator
- 10:13controlling the delay. I want to control
- 10:15the delay using a variable. So in order
- 10:18to create a variable, we just write the
- 10:20type of the variable and then name of
- 10:22the variable. So I'm going to say delay
- 10:25amount. So let's say we initiate it with
- 10:3050 and here we add
- 10:34delay amount. So right now nothing
- 10:36changes.
- 10:38We still have delay of 50 50
- 10:40milliseconds. Now instead of assigning
- 10:4350 here, I'm going to uh create a slider
- 10:48on the custom translator
- 10:52which is with this function. Okay, I
- 10:55haven't added the description of this
- 10:57function. I'm going to add it in the in
- 10:59in the future. But create a slider has
- 11:03four parameters. The first parameter is
- 11:05the name of the slider. So I'm going to
- 11:09just write delay. The second parameter
- 11:12is the variable that we are controlling
- 11:15with the slider. So delay amount is the
- 11:18variable. The third variable is the
- 11:21lower limit of this slider.
- 11:24Sorry, the first the third one is the
- 11:26upper, the fourth one is the lower limit
- 11:28of the slider. So let's say our highest
- 11:33uh delay can be 1 second and the lowest
- 11:36is let's say 30 seconds. Okay, my bad is
- 11:39this this has to be the other way
- 11:41around. So this should be lower, this
- 11:43should be higher. So that's why we need
- 11:45the descriptions. So let's say 30
- 11:47milliseconds for the lowest case and uh
- 11:52100 1,000 for the higher. And if we
- 11:55close this, now we have this delay
- 11:56slider added.
- 12:06Let's choose uh another instrument
- 12:08because piano it's not easy to let's
- 12:11choose module pro and choose a string.
- 12:15So let's choose a basic string.
- 12:25So yeah this is
- 12:28this is now the the custom translator in
- 12:31the database. This is exactly what the
- 12:33custom translator in the database base
- 12:35does. Okay before we continue I want to
- 12:38talk about these
- 12:41uh operators here. So most of these
- 12:44operators are just common sense like
- 12:47addition, subtraction but I want to
- 12:50point out some of the important things
- 12:52uh about variable types and how these
- 12:55operators convert variable types between
- 12:58variable types. So addition you can add
- 13:01integers and floats and the result is a
- 13:03float. So you you can see all the
- 13:05information here binary operator
- 13:07addition there's a special case we're
- 13:09going to talk about it later. And so
- 13:12these are the binary arithmetic
- 13:15operations.
- 13:16These are the comparison operations
- 13:19comparing numbers. The results are
- 13:21boolean. So this is one one example. We
- 13:24are comparing two numbers and the output
- 13:26is a boolean. So inside if we should
- 13:29always have a boolean variable and we
- 13:32can actually use and and or operations
- 13:35for to compare booleans. But you you get
- 13:38the point. So all these arithmetic
- 13:39operations gives us converts different
- 13:42variables to different variables and
- 13:44what we call a combination of these
- 13:48expressions
- 13:50operations along with these functions is
- 13:52called an expression. So each expression
- 13:56can be combination of different
- 13:57variables can be combination of special
- 13:59variables variables that we created and
- 14:01operations of these variables. But at
- 14:03the end we get one of the types here
- 14:06eventually and that expression when we
- 14:10use that expression in somewhere for
- 14:12example in the delay amount or here in
- 14:14if all these special cases for example
- 14:18the delay amount we should have a number
- 14:20variable here either a float or an
- 14:23integer. If you try to add a boolean
- 14:26variable here, for example, if I try to
- 14:28do the following, if delay equals
- 14:3156. So comparing two numbers results in
- 14:34a boolean variable,
- 14:36we have a type mismatch error because we
- 14:39should have an integer or actually a
- 14:41number here.
- 14:46Okay. Next, we're going to talk about
- 14:49loops and we're going to go through a
- 14:52different example.
- 14:53with on that loop. So, I'm going to
- 14:55erase this and I'm going to create a new
- 14:57custom translator. So, what I want to do
- 15:00here is I want to control the volume uh
- 15:04with one of my knobs here. But I don't
- 15:07want any jumps on the song volume. So,
- 15:11whenever I try to change the volume, if
- 15:13I jump from a value up to another value,
- 15:16I want the I want the song volume to
- 15:19change smoothly between those two
- 15:20values. Okay? So I have my knob here
- 15:24that I want to control. So it's control
- 15:26change 12. And as the output I choose
- 15:29scripts. Okay. We have two loop
- 15:32functions for and while. And we're going
- 15:35to be using while in this example. So
- 15:37what are we trying to achieve? We want
- 15:39to change the song volume uh to this
- 15:43control change value uh in a period of
- 15:46time. Uh changing the value smoothly. We
- 15:50will be iterating through uh a while
- 15:53loop while changing the song volume
- 15:55starting from the original value of the
- 15:58song volume up to or down to this
- 16:01control change value. Okay, first of all
- 16:03we need to compare our song volume.
- 16:06Okay, how do we access the song volume?
- 16:08If you go to cheat sheets, go to special
- 16:11variables. So these are the variables
- 16:12that keystage provides to access certain
- 16:15parameters or certain information inside
- 16:17keystage. Song volume media and song
- 16:20volume decibel are those one two of
- 16:22those parameters. So we can get the song
- 16:25volume as well as we can assign the song
- 16:27volume value here. So this is a special
- 16:30variable. You can see that special
- 16:31variable type of integer and the decibel
- 16:34version is type of floats. When you read
- 16:36the description here, song volume value
- 16:38in MIDI, the value 100 corresponds to 0
- 16:41decibel. Actually 127 corresponds to 6
- 16:44decibel, zero corresponds to minus
- 16:46infinity decibel. This is an assignable
- 16:49variable which means we can use the
- 16:51value and also assign different values
- 16:53to this changing the song. But for
- 16:56example, song name is read only which
- 16:59means we cannot assign a new name to the
- 17:01song using script. We can only use the
- 17:03name uh to compare with other strings.
- 17:06We're going to talk about them later.
- 17:07But right now, we're going to be using
- 17:08this med song volume med. Okay. We first
- 17:12need to compare this song volume with
- 17:14the initial value with with the value of
- 17:16this uh control chain single that we're
- 17:19sending. I'm going to create a variable
- 17:21called sign. And I'm going to be
- 17:25assigning either plus one or minus one
- 17:27depending on whether song volume is
- 17:30greater than this control change value
- 17:32or less than this control change value.
- 17:34So we want we are starting from the
- 17:36initial song volume value changing to
- 17:39control change. So if song volume midi
- 17:44is smaller than is less than the control
- 17:48change value, we're actually increasing
- 17:50the value. We're starting from song
- 17:52volume midi up to the control change
- 17:54value. We're going to be changing this
- 17:56smoothly. So sign is + one. Sign is one
- 18:01and otherwise
- 18:04sign is minus one.
- 18:08So minus one. So how are we using this
- 18:11sign? We're going to be adding this
- 18:12number through the iteration of while to
- 18:15a variable and that variable's initial
- 18:17value be will be our song volume mid. So
- 18:20let's first create another variable.
- 18:23Since we're always working with med
- 18:24signals, I'm just creating integer
- 18:26values. I'm going to assign say value.
- 18:29This value will change through the while
- 18:31loop. But the initial value is our song
- 18:35volume. Now while this value
- 18:41is not equal to control change value,
- 18:45what does that mean? our initial volume
- 18:47volume I don't our initial value is a
- 18:49song volume so I I'm assuming that the
- 18:53signal that we send is not the equal to
- 18:56the song volume this value is not equal
- 18:58so this while starts looping and inside
- 19:01this loop we we're going to add sign at
- 19:04every loop we're going to add sign to
- 19:06this value and at some point at some
- 19:09point the value will be equal to the
- 19:10control change value and we will going
- 19:12to go outside the loop and inside this
- 19:14loop we want to change the song value to
- 19:16the song volume. So I'm going to say
- 19:18song volume mid equals value
- 19:22and I'm going to be we're going to be
- 19:24adding volume
- 19:26equals value equals value plus sign. So
- 19:30we're adding the sign. So eventually the
- 19:32value will become equal to the control
- 19:34change value. So right now this seems
- 19:37like it's working but it actually
- 19:39doesn't. So if we go into the song
- 19:41mixer.
- 19:43So let's say we change the song volume
- 19:45to something like this and I move my
- 19:48slider
- 19:50and it just jumps. So it doesn't work.
- 19:53Why doesn't it work? It's because this
- 19:56while loop actually happens in like a 1
- 20:00million of a millisecond and the song
- 20:02volume changes
- 20:04uh continuously but in a extremely short
- 20:07period of time which we cannot observe.
- 20:10So we need to add delays to to these
- 20:13assignments in order to be able to
- 20:15observe this. So
- 20:18let's add a delay. Okay. So we have a
- 20:22problem here. If we actually execute
- 20:25this, this while loop starts, then the
- 20:28song volume changes its value from song
- 20:31volume med. And all these changes happen
- 20:34just 30 milliseconds after I execute
- 20:37this script. So we want to increase this
- 20:40delay at every iteration. In order to do
- 20:43that, I'm going to create another
- 20:45variable integer. Let's say counter
- 20:48equals zero. I'm going to multiply this
- 20:51with counter.
- 20:53So initially this will be with delay
- 20:55zero. So the song volume will be sent
- 20:58right away. But if I increase counter as
- 21:01well equals counter + one, the next
- 21:06iteration will be 30 mconds. The next
- 21:08iteration will be 60 mconds. So you get
- 21:10the point. So I press I compile it and
- 21:14let's see what happens. So I'm changing
- 21:16the volume as you see.
- 21:20Even if I So for example, I'm slightly
- 21:22moving my slider.
- 21:25So 30 milliseconds seems to be a bit
- 21:28slow. Let's turn it change it into 10
- 21:31milliseconds.
- 21:33So, I'm moving my slider drastically,
- 21:36but it's always
- 21:39going smoothly except there is a small
- 21:42slight problem happening here. So, you
- 21:44you so I'm moving my slider all and all
- 21:47just increasing and decreasing. You
- 21:50start seeing these jumps around. So, why
- 21:53do these jumps happen?
- 21:55The problem is
- 21:57this translator starts working as soon
- 22:01as it receives a control change value
- 22:03different than the song value. Then it
- 22:05goes through this iteration with these
- 22:06delay triggers. These delay triggers
- 22:09goes into a stack into a delay stack so
- 22:15to say and they they keep they they're
- 22:18executed
- 22:20with these delays. But the problem is if
- 22:22I keep changing my control change value
- 22:25more delays will start appearing. So for
- 22:28example I song volume is for example
- 22:31zero in mid strong med is zero and I
- 22:33send control change value 100. These
- 22:36loops starts happening and these
- 22:38assignments are put into the delay stack
- 22:41and starts operating. But but while
- 22:45these are still executing I change the
- 22:47control change value to like say 20.
- 22:50they they will still keep executing.
- 22:53These will still keep executing.
- 22:55Now what I need to do is as soon as this
- 22:58translator receives another control
- 23:00change value, I need to cancel all the
- 23:04existing delays. Now we have some
- 23:07special functions to perform that. If I
- 23:10go to actions here,
- 23:13you can see this cancel all delay
- 23:15triggers, cancel all delay triggers with
- 23:17index, cancel delete group triggers.
- 23:20We're going to talk about group triggers
- 23:22and all triggers later when we talk
- 23:23about non-local variables. Right now,
- 23:26we're interested in cancel delay
- 23:28translated triggers or cancel delay
- 23:30translated triggers with index. Let's
- 23:33start with this one. Cancel
- 23:36uh delayed translator triggers. So
- 23:38translator means this translator when uh
- 23:42this is executed whatever
- 23:45delay triggers are in the stack they are
- 23:48all cancelled all the drill triggers
- 23:50that are produced in this translator
- 23:52will be cancelled will will just vanish
- 23:55and then the new function start
- 23:57executing so if I now test my function
- 24:01test this translator so even if I
- 24:05drastically change the knob here
- 24:09it doesn't jump around. It just goes
- 24:11smoothly. So yeah, that's how we cancel
- 24:14uh existing delay triggers. Now there is
- 24:17another one that I want to talk about
- 24:20delay triggers with index. So let's say
- 24:23I choose index 2. Right now if I try to
- 24:27do this, it doesn't work. That cancel
- 24:30function doesn't work. And we still have
- 24:32these strange jumps all around because
- 24:35we didn't add an index to this delay. So
- 24:38if we go to cheat sheet and go to
- 24:41functions
- 24:43and check delay, it adds an delay to the
- 24:46execution of the line can have one or
- 24:49two parameters. So at right now we only
- 24:52have only one parameter which is the
- 24:54delay amount. The second parameter is
- 24:56the index of the delay. So we can add an
- 24:58index to this delay. this f this trigger
- 25:01actually. So if I choose two here
- 25:06and suppose in this function uh in this
- 25:08script we have another delay somewhere
- 25:10else and we don't want to cancel those
- 25:12delays. We just want to cancel these
- 25:13specific delays here with this uh index.
- 25:16So right now cancel direct trans uh
- 25:19delay translated triggers with index two
- 25:21will cancel all the delays inside this
- 25:25script with index two. So right now it
- 25:28works. So you see we don't have any
- 25:30jumps around.
- 25:35Next we talk about arrays. So arrays are
- 25:38collection of variables of of the chosen
- 25:41types. So if we go to cheat sheet and
- 25:43variable types you see we have the basic
- 25:46types and their array versions and we
- 25:49also have non-basic types and there so
- 25:50all types have array versions of them.
- 25:53So let's just give a simple example. int
- 25:55array let's say a okay how do we create
- 26:00an array we have that we have various
- 26:04ways to create arrays but the most
- 26:06convenient one is the list function we
- 26:09have the list here in functions the list
- 26:11function takes a bunch of uh val values
- 26:15and then output is the array type of of
- 26:18that variable so for for this we need to
- 26:20add integers so let's say 2 6 minus - 3
- 26:2689 and so on so forth. So but if we add
- 26:32another type here false for example a
- 26:35boolean type we get an error all
- 26:37parameters should have the same type
- 26:39which is in in area type or for example
- 26:43if
- 26:44we start adding like a different type
- 26:47like strings here okay all the types
- 26:50match with each other but it doesn't
- 26:51match with here so type mismatch. So
- 26:54let's view this uh in our
- 26:58log print a. By the way this language is
- 27:04case insensitive. So I created this as a
- 27:08capital A but it doesn't matter. I could
- 27:10have created this with lowerase A and in
- 27:13fact if I try to create another variable
- 27:15with lowerase A, we have an error and
- 27:19cannot be used as a variable name
- 27:20because the whole language is case in
- 27:23sense. we cannot use the same uh
- 27:25variable name even if it's lower case or
- 27:28upper case but if I try if I write type
- 27:30A here the autocomplete version will
- 27:33match with the initial variable name if
- 27:35I if I compile this so the input was
- 27:38note on and note off these are the
- 27:41indexes here index zero is 21 index one
- 27:45is six and so on and so forth now let's
- 27:48use these arrays in an example and we're
- 27:51going to talk about more about arrays
- 27:53and kind of they're different than the
- 27:56standard arrays in other language. This
- 27:57is actually a combination of arrays and
- 28:00dictionaries. Now in this example, I
- 28:02want to assign certain chords to certain
- 28:05nodes on my keyboard. So let's first
- 28:08create those chords as int arrays. Int
- 28:11array chord one. We cannot use numbers
- 28:15as variable names as well. Uh a chord.
- 28:18So for example, let's start with the
- 28:19major chord. the root
- 28:23third and the fifth
- 28:25and the second chord is the minor third
- 28:29and let's say we add the third chord is
- 28:31the power chord.
- 28:34So these are our three chords. Now I
- 28:37want to assign three notes on my
- 28:38keyboard. And whenever I press and hold
- 28:42one of those notes, for example, the
- 28:43note corresponding to chord one. I want
- 28:46all the other notes that I play on my
- 28:48keyboard playing the that chord. So
- 28:51right now I'm pressing the lowest note
- 28:53on my keyboard. And you see the note
- 28:54value is C2. So when I press C2 and hold
- 28:57it, when I play other notes on my
- 28:59keyboard, I want them to play major
- 29:01third chords based on that note. So I'm
- 29:05going to create another int array and
- 29:07I'm going to say this as current chord.
- 29:12Okay, currently this has zero elements.
- 29:16It doesn't have any elements. Now I want
- 29:18to change this current chord to one of
- 29:21these depending on the input number.
- 29:24Okay, we are both monitoring note and
- 29:26not off signals. This is important
- 29:27because we need both of them. Now if is
- 29:31not on. So let's start with this case.
- 29:33If we press the note, if our note
- 29:37equals comparison C2. Okay, what is the
- 29:42integer value of C2? It doesn't matter.
- 29:44I can just write type C2 here. This is
- 29:47one of the neat features of key stage
- 29:49scripting language. So we can actually
- 29:51use these note numbers uh anywhere in
- 29:54our calculations. So if it's C2, then
- 29:57current chord
- 30:00equals to chord one. So the this current
- 30:03code becomes chord one.
- 30:06Else if current value is C# 2. When I
- 30:10press enter, these brackets
- 30:12automatically appear.
- 30:14Current chord is chord 2. Note value
- 30:18equals D2. Then current chord equals
- 30:22chord three. Okay. Well, what about the
- 30:24other num other values? Else else we
- 30:27want to play the chord. So as long as we
- 30:29keep holding the notes, we want to play
- 30:31the chord. We have our current chords.
- 30:34What do what do we need to do? We need
- 30:36to go through the these pitch shifts on
- 30:40on the on the current chord and then add
- 30:42that those pitch shifts to this note
- 30:44value. Now I'm going to use my other
- 30:46loop function to demonstrate it. The for
- 30:49loop. The for loop has a special syntax
- 30:51and it has some neat features. for index
- 30:56actually let's say pitch it
- 31:00in current course
- 31:04now what happens here is whenever I
- 31:07write this line this variable this
- 31:10integer variable pitch shift is created
- 31:13and then it start iterating with through
- 31:16the values of the current code. Now one
- 31:18thing that we need to note about this
- 31:20scripting language is unlike other
- 31:22languages the scores of these variables
- 31:25are not bound to these curly brackets.
- 31:28So normally in in any language basically
- 31:31in C, C++ whatever whenever I create a
- 31:34variable inside a for loop or inside any
- 31:36curly brackets the scope of this
- 31:38variable are bound to these these curly
- 31:40brackets and outside these curly
- 31:42brackets we don't see this variable. We
- 31:43have to either redeclare it or not use
- 31:45it at all. But in this language pitch
- 31:48shift can be accessed through everywhere
- 31:50inside the script in inside the scripts.
- 31:52So this is something that you need to
- 31:54keep in mind. So we go through the
- 31:57pitches of the current note. I'm going
- 31:58to send MIDI notes with those pitch
- 32:00shifts. Note on. So we are in the note
- 32:03on zone. We send note on. Okay. What am
- 32:06I sending? I'm sending the original note
- 32:08value plus the pitch shifts and with
- 32:11original. Okay. I don't want to test
- 32:13this right now because I haven't
- 32:15actually done anything related to note
- 32:17off. We're going to have stock notes if
- 32:18I if I try to play this right now. Let's
- 32:21do some de debugging lines here. I'm
- 32:24going to print current chord here.
- 32:27Actually, I want to just do this print
- 32:30after I make the changes. I I press C1.
- 32:33Actually, I need to compile it. I press
- 32:35C1 and we get the major chord. I press
- 32:38C2, we get the minor chord. I press C3,
- 32:41we get the power chord. And right now
- 32:44the current chord is power chord. Okay.
- 32:46Now what happens if I release my notes
- 32:49else? So when I release the notes one of
- 32:53these C, C sharp and D2 notes, uh I want
- 32:57to just cancel the whole thing. So what
- 33:00do you what does it mean to cancel? I
- 33:02don't want to play any any chords. I
- 33:03just want to play regularly. Okay. So I
- 33:05need to add some additional variables to
- 33:08test to check certain things.
- 33:10I want to keep track of uh last pressed
- 33:15note.
- 33:16I want to keep track of the last pressed
- 33:18note here as long as it's one of these
- 33:20values. So if it's C2, last press note
- 33:24is equal to well note value. It's C2. So
- 33:27I'm just going to copy and paste this.
- 33:30So we keep track of the last press note
- 33:32here. One of these three nodes. And I
- 33:35want to check the following. When I
- 33:37release the note, if it's the last press
- 33:39note, I just want to cancel everything.
- 33:41I don't want to play a chords anymore.
- 33:43So, if not value equals last press note.
- 33:48So, how do we cancel? I am just going to
- 33:51remove all the pitch shift inside this
- 33:53current chord. How do we do that? We're
- 33:55going to talk about uh variable
- 33:58properties after we talk about other
- 34:00variable types. But right now, we I I
- 34:03just need to dive into this. Different
- 34:05types have different properties. Array
- 34:07is one of them. Uh without going into
- 34:09too much details, we have remove all
- 34:12objects action property of an array. So
- 34:14this is like an action applied to that
- 34:17specific array. Action property means
- 34:19that. So how do we do that action?
- 34:22Current dot remove all objects. So this
- 34:27is an action property. And when this
- 34:29line gets executed, the current code
- 34:31becomes just an empty code. Let's test
- 34:34this. Let's print current code here. Uh
- 34:37let's do the following. Let's also write
- 34:40print note release. And at the end of
- 34:44note on, we also add some print
- 34:46functions current.
- 34:48So let's try to debug this. Let's clear
- 34:51the log. I press C1 note pressed and I
- 34:54release. We got an empty array. So these
- 34:57logs keeps adding to the log. I can
- 35:01clear the log here. But we have a simple
- 35:02function if if you don't want to do this
- 35:05manually clear lock. So we have this
- 35:08action not function. So right now these
- 35:12logs do not add uh after another. Okay
- 35:16we have this print function. So we
- 35:18release all the current codes. Okay. But
- 35:20now when this is empty we have a problem
- 35:23here. We don't want to go through this
- 35:25iteration anymore because this is now an
- 35:27empty set an empty array. So we need to
- 35:30add a little check here. If current cord
- 35:34count. So this is another property which
- 35:37is the number of elements in inside that
- 35:39array equals zero. If it's empty, let's
- 35:43also do the add function. So if it's non
- 35:47empty, we go through this the pitch
- 35:49shifts. But if it's empty, I just want
- 35:51to send the original signal incoming med
- 35:54data. And here if we release the note we
- 35:57empty the current chord. Okay. But what
- 36:00what if we are on other notes else? Now
- 36:03we have to send the corresponding note
- 36:06of signals. Whether we are playing a
- 36:08chord or we are playing a single note.
- 36:10We have to send the corresponding note
- 36:11of signal. So we do the same thing here
- 36:13except we send note of signal instead of
- 36:15note on. So we just copy paste this
- 36:18whole line and we just change this to
- 36:21off. Okay. If I haven't skipped any
- 36:24step, this should work now. Let's Let's
- 36:26check.
- 36:32That's why we can't hear it.
- 36:34So, right now, I'm not I'm not pressing
- 36:36one of these special buttons. Let's
- 36:38actually open the editor. And
- 36:42now I'm pressing and holding
- 36:46C C2. I release C2. Now I'm pressing
- 36:51holding D2 sorry C sharp two we have the
- 36:55minor
- 36:57and we have the power chords.
- 37:03So that's how it works. So before I
- 37:05continue I want to talk about a couple
- 37:06of important points about local
- 37:09variables and their persistencies. So
- 37:13this is a tricky topic. So you realize
- 37:16that this last press node is initialized
- 37:18here but its value is retained through
- 37:21different iterations of this of the
- 37:23execution of this script editor. These
- 37:25local variables are created at the
- 37:27beginning of the compile and when we run
- 37:30key stage all the scripts are compiled
- 37:32automatically. those variables are
- 37:34created and that their values just
- 37:36retain as long as the the custom
- 37:38translators are not deleted until we
- 37:41move to another song or close key stage
- 37:43and then we reopen key stage their
- 37:45values are reseted again. So this is how
- 37:48the local variables work that that also
- 37:51works for arrays or integers or any
- 37:54type. But there is one exception here.
- 37:56The exception is when I create either a
- 37:59slider or switch. Let's actually also
- 38:02talk about switch here. So the switch
- 38:05variable controls boolean variables. Uh
- 38:08let's choose B boolean B. And I want to
- 38:11create a switch. Name it anything. I'm
- 38:15controlling the value variable B and we
- 38:18don't have upper and lower values for
- 38:20switches. So for switch creating
- 38:22switches we have a name and the variable
- 38:25that we're controlling. So this switch
- 38:27the value of this is retained or is
- 38:30stored inside this custom translator and
- 38:33even if we close keystage and reopen it
- 38:35this value will reappear and the same
- 38:38goes true with the uh sliders. So this
- 38:40is really important because when you
- 38:42create a custom translator and create
- 38:44some sliders here, you you assign
- 38:47certain values of to those sliders and
- 38:49switches and you want those values to
- 38:52retain between iterations of keystage or
- 38:54when you load different songs, you want
- 38:56those values to stay. So if a variable
- 39:00is assigned to a switch or slider, their
- 39:03values are stored in the custom
- 39:05translators, but otherwise their values
- 39:08only retain as long as that custom
- 39:09translator is active. We switch to
- 39:12another song and then back to the
- 39:13original song. No values, those values
- 39:15are reseted. So this is the persistency
- 39:18of values of local variables. One other
- 39:20thing that I want to talk about before I
- 39:22talk about the dictionary aspect of
- 39:24arrays uh I want to talk about this
- 39:27print function. So we have you realize
- 39:29that we added two lines here. But if I
- 39:33go into operators and look at plus we
- 39:37have a special case that I talked about
- 39:39before. This operator can be used
- 39:41between a string and any other object.
- 39:43The result is a string. So instead of
- 39:45doing this I can actually do the
- 39:47following. I add plus current chord.
- 39:50Let's do the same thing for note pressed
- 39:53instead of that I'm going to add plus
- 39:56current chord. So when I will press a
- 39:59note press one of these special notes.
- 40:02You see in the debugger we have not
- 40:04pressed a space and the value of the
- 40:07chord. So there's an space automatically
- 40:09added between each items here. So as
- 40:12long as one of these items is a string,
- 40:14the plus function basically adds the
- 40:17string version of the variable to the
- 40:19end of the first string. So let's talk
- 40:21about the dictionary aspect of array. So
- 40:23as I said the arrays here in key stage
- 40:25are not the simple arrays that you see
- 40:28in other languages. So let's create an
- 40:31int array. As long as we create an array
- 40:33and let's say we initialize it with list
- 40:36and print it, you can see the indexes
- 40:39here. So this is not just a a set of
- 40:42objects next to each other. We have
- 40:44indexes which means that this is
- 40:46actually a dictionary with integer keys
- 40:49technically speaking. So what we can do
- 40:51here is for example a we have a special
- 40:55notation here a5.
- 40:58We assign a number to the fifth index of
- 41:01a. So if I print a here let's delete
- 41:05this line. You see we added a new index
- 41:08here five. So we're skipping index three
- 41:10and four and we add index five. So this
- 41:12is not just a sequence of numbers. This
- 41:15is actually a set of numbers with
- 41:17indexes. So these arrays can be used as
- 41:19dictionaries. I'm not going to give a
- 41:21concrete example here but I'm just going
- 41:23to give a a case where you can use it.
- 41:26And we're going to be using a special
- 41:28variable which is press notes and press
- 41:31note velocities. These are arrays. You
- 41:33can see these are integer arrays which
- 41:35consists of the nodes that are entering
- 41:37into the into this translator. It
- 41:40doesn't matter whether we have a note
- 41:41input or any other input. These
- 41:45variables always change as long as we
- 41:47press note signals and those note
- 41:49signals goes into this script into this
- 41:52translator. So for example, if you
- 41:54monitor press notes, okay, the problem
- 41:57is we need to we need to send pitch
- 41:59signals to be able to trigger this
- 42:01translator. I'm holding three notes and
- 42:03when as I send pitch signals I can
- 42:06monitor the press notes that I'm
- 42:08pressing right now. So that's press note
- 42:10but for example at some point I may need
- 42:12to create a variable an integer array
- 42:17where the indexes are nodes and the
- 42:19values are velocities. For example, int
- 42:22array a. I'm going to go through the
- 42:25indexes of the press notes for index in
- 42:30press notes indices. So press nodes is a
- 42:34set of nodes and index starts from 0 1 2
- 42:363 depending on how many notes we're
- 42:39pressing. But we have another special
- 42:41variable press not velocities. So what
- 42:43I'm going to do here a press note index.
- 42:47Now we're getting the value the note
- 42:49value and use it as the index of a
- 42:52equals press notes velocities. We use
- 42:55the same index because they match the
- 42:58indices on the press notes velocities
- 42:59and press notes match. And if we print
- 43:03this now actually let's change this to
- 43:07notes. Okay, we have an error. Yeah, of
- 43:08course we haven't printed a yet. We we
- 43:12don't hear anything because we we are
- 43:14not sending any med signals when we
- 43:17press not. So I press a note. Huh? Okay.
- 43:21We are not resetting a. So we need to
- 43:23add a remove all object. So we want to
- 43:26just empty a at every iteration. So
- 43:29right now I pressed a note the note 81
- 43:32and it velocity.
- 43:39So right you see the indexes are note
- 43:41values and the velocities are so this is
- 43:44like a mapping. So we can use arrays as
- 43:47dictionaries or mappings. So speaking of
- 43:50the dictionary aspect of arrays there is
- 43:52an important function uh that I want to
- 43:55talk about actually a variable property
- 43:58again for arrays reindex. This is really
- 44:02important one because as I said arrays
- 44:05are dictionaries with indexes. Reindexes
- 44:08basically removes all empty indexes and
- 44:10then just squeeze the remaining values
- 44:14starting from zero preserving their
- 44:16order. So let's actually show you what
- 44:19what I mean. Of course in this example
- 44:21it's kind of weird. So let's actually
- 44:23create another example here. Int array a
- 44:27let's say a 1 equals something. Then we
- 44:33have a different index a 4 equals choose
- 44:36another number and a let's say 67
- 44:41equals -24.
- 44:44Okay. So if we print this we have index
- 44:47one corresponds to this index 4 and
- 44:49index uh 67. Now if we say a reindex and
- 44:55then print it again one becomes zero
- 44:57four becomes one 67 becomes two. So the
- 45:00all the empty indexes are removed and
- 45:02then this becomes a basic array.
- 45:04Reindexes means that it converts a
- 45:06dictionary into an array in in in some
- 45:09way. We also have functions like add or
- 45:11add to the beginning. So when add
- 45:13function adds an object to the end of
- 45:15the array and then automatically
- 45:16reindexes. So when you add you lose all
- 45:19the information about indices and you
- 45:21just have a basic array with indexes
- 45:23starting from 0 1 2 3 and so on. You
- 45:26have to really read these descriptions.
- 45:28And the remove functions also remove
- 45:30first object, remove last object also
- 45:32reindexes. Remove object at index
- 45:35doesn't reindex. So when we remove a
- 45:37specific index, we don't lose the index
- 45:39information. And we can also sort these
- 45:42arrays with ascending or descending
- 45:44order depending on their values and also
- 45:47they it's reindexed. So you have to be
- 45:49careful while you're using these
- 45:51properties. If you are using your array
- 45:54as dictionary, I suggest that you don't
- 45:56use these properties. You use them if
- 45:58you're using this as pure arrays. Before
- 46:01I move on to the next topic, I want to
- 46:03talk about two more things which are
- 46:04also related to arrays. So remember when
- 46:06we write media out, we had this incoming
- 46:10media data that we we wrote before and
- 46:13this was basically the med signal from
- 46:14the incoming nodes. Okay, but what is
- 46:16this incoming med data? If you look at
- 46:18it, it's the special variable with int
- 46:21array. So it's an integer array. Okay,
- 46:23what does this integer array contain?
- 46:25Let's print it incoming metadata. It's
- 46:28an array with three integers and as you
- 46:31guessed it's a med signal with three
- 46:32bytes. So this represents the bytes of
- 46:35the med signal. So as you know
- 46:38144 which is 90 in hexodimal form stands
- 46:44for not signal and 128 which is 80 in
- 46:47hexodimal stands for not signal. The
- 46:49second bite is the note value. So keep
- 46:52changing and the last one is the
- 46:53velocity value as you know. So the
- 46:55incoming med data is the array of MIDI
- 46:58signal. So this also gives us one more
- 47:01clue. MIDI out normally we have these
- 47:04MIDI types that we keep writing or
- 47:06incoming med data. But we can actually
- 47:08put any array here any integer array
- 47:11here. As long as that integer array
- 47:13represents a MIDI signal media out just
- 47:15works. So we can actually write lists
- 47:18something like 144 let's say 65 and 100.
- 47:24So this is a noton signal with note
- 47:26value 65 and with velocity 100. We can
- 47:28actually send med signals like that. And
- 47:30speaking of that we also have an
- 47:33important notation to represent that
- 47:35notation. I want to talk about system
- 47:37exclusive messages. So you know the
- 47:39system exclusive messages starts with f0
- 47:42and ends with f7. So let's actually read
- 47:44that uh again print incoming metadata
- 47:48real value of f0 is 240 and f7 is 247.
- 47:54So we can send system exclusive messages
- 47:57like that but we can actually write them
- 47:59in hexodimal form. So let's let me show
- 48:02you. We can actually create an int array
- 48:06for example
- 48:07cis x message.
- 48:09Yeah I'll just write cis x. I'm going to
- 48:12create a list. Let's say the first bite
- 48:14is so instead of writing uh 140 I'm
- 48:20going to write 0 x f0. So we can
- 48:25actually write hexadimal notations
- 48:28anywhere. Again it doesn't have to be
- 48:30just for since system exclusive message.
- 48:33It can be any message. So let's say 0 x
- 48:38I don't know a3
- 48:40and let's finish it with 0 x f7 and if I
- 48:46print this this is an exclusive message
- 48:49so we are okay we are printing two the
- 48:51first one is the incoming signal the
- 48:53second one as you see it starts with the
- 48:55240 and ends with 247
- 48:58so we can write system exclusive
- 49:01messages by hand using hexodimal
- 49:03notation if we
- 49:08Now let's talk about non-local
- 49:10variables. So non-local variables are
- 49:12really important when we want to have
- 49:14multiple custom translators working
- 49:16together. So let's start with creating a
- 49:18custom translator. Now we have this
- 49:21button called non-local variables. Okay,
- 49:24group variables, layer variables, strong
- 49:26variables and global variables. So we
- 49:27have four types of non local variables.
- 49:30And so what is a group variable? And
- 49:33when I tap here, create a new group
- 49:34variable. These are the eight basic
- 49:37types that we have the four basic types
- 49:39and their array versions. So we can
- 49:41create uh let's say an integer variable
- 49:44called test. We can actually see the
- 49:46same variables here when we so let's
- 49:49create another translator here. When we
- 49:52tap the output, this is this variables
- 49:54part and also the same goes as variables
- 49:57change in the input and we can actually
- 49:59see the variable test here as integer.
- 50:02Even if we are now on a different custom
- 50:04translator, we can see the variable that
- 50:06is created here. Group means all the
- 50:08translators in in this line here. For
- 50:11example, if we're in a parts, the
- 50:13translators here form a group. We if
- 50:15we're in song translators. So you see
- 50:17the point that the group is the
- 50:19translators that are working next to
- 50:20each other in whatever object they are
- 50:23in. For example, whether they're in a
- 50:24part or in a track or in a media LIS, it
- 50:27doesn't matter. Group means that. So for
- 50:29example, this translator can see test
- 50:32but the translators here can't see test
- 50:35here. For example, we can create the
- 50:37same variable name test here but with a
- 50:40different type. So in in this part the
- 50:43test variable that we created here is of
- 50:46type float array. Whereas in this track
- 50:50the variable even if we use the same
- 50:53name is now an integer. So you see the
- 50:55point the variable group variables are
- 50:58uh can be accessed by custom translator
- 51:01in the same group. But if we create by
- 51:03the way we can also create variables
- 51:05here. If you create song variable for
- 51:07example let's choose a variable with
- 51:10boolean array.
- 51:13Now this variable can be accessed
- 51:16anywhere in the song in this song. So if
- 51:18I type variable change song variable can
- 51:21be accessed here. We can delete those
- 51:23variables just as we delete anything.
- 51:26Let's delete both the group and song
- 51:28variable. The group variables I I
- 51:30already described. Layer variables are
- 51:32the variables that can be accessed from
- 51:34within this layer. The those layer
- 51:37variables can also be used in the layer
- 51:39translators. But from any other layer,
- 51:41you can't see the layer variables
- 51:43created in this layer. And the next step
- 51:45is the global variables. Global
- 51:46variables are variables that can be
- 51:48accessed from anywhere inside keystage,
- 51:51even from other songs. So those
- 51:53variables, we're going to give an
- 51:54example about global variables. Global
- 51:57variables can even be used inside media
- 52:00aliases since these are global objects
- 52:02as you know. So if you create custom
- 52:05translators inside these aliases and you
- 52:07want them to communicate with other
- 52:09translators in the song or anywhere, you
- 52:12can use global variables. And in any
- 52:14song, these global variables work. But
- 52:16for now, in our example, we are focused
- 52:18on group variables. These are the the
- 52:21ones that you probably will use most
- 52:23common. So in our example, we want to
- 52:26have something like an arpeggiator. Not
- 52:28exactly an arpeggiator, but a repeating
- 52:31note functionality using custom trans.
- 52:34So when I press a note, I want that note
- 52:37or when I press a chord, I want that
- 52:39chord to keep repeating with with a
- 52:41certain period until I release the
- 52:43notes. So this is what I want to
- 52:45achieve. And I need more than one custom
- 52:47translator to be able to achieve that.
- 52:48And I need those translators to
- 52:51communicate with each other using group
- 52:53variables. So first we need since we're
- 52:56talking about nodes, we want to create a
- 52:58translator with node inputs. Remember we
- 53:01talked about a special variable called
- 53:03press notes here and press notes
- 53:05velocities and actually channels are
- 53:07also stored on another special variable.
- 53:09But I don't care about channels right
- 53:11now since my keyboard only send notes
- 53:13from channel zero. Okay, these press
- 53:16nodes can be accessed through this
- 53:18custom translator. But the problem is I
- 53:21am going to need another translator here
- 53:24and I'm going to need those press nodes
- 53:26in this translator. The problem here
- 53:29what I so here's the structure that I'm
- 53:31going to do in this translator I want to
- 53:34store those notes and whether not
- 53:37pressed or not and I want to pass that
- 53:39information to this translator and this
- 53:41translator will keep repeating those
- 53:43notes over and over again. So what I
- 53:46need is I need to store those press
- 53:47notes. This is the first step and I need
- 53:50to have integer variables which are
- 53:53group variables because I need the other
- 53:55translator to access those variables. So
- 53:58I'm going to name current nodes instead
- 54:01of press node. I'm going to use this
- 54:03this name and I'm going to create
- 54:05another one to keep track of their
- 54:07velocities. Current node velocities
- 54:11current node velocities. So I'm going to
- 54:13store
- 54:15the press notes information in these
- 54:18global group variables. Okay. Why do we
- 54:21have a type mismatch? Current not
- 54:23integers. Yeah, we actually didn't
- 54:24create integer arrays. We create
- 54:26integers. So let's let's correct that.
- 54:33Okay. So yeah, I corrected. Uh so these
- 54:37are now integer variables, integer
- 54:39arrays. and current node velocities is
- 54:45pressed node velocities. So we store
- 54:46those information. Okay. Now I'm going
- 54:50to create a boolean variable this time
- 54:52again a group variable and call it uh
- 54:57there is
- 54:59a press note.
- 55:02So I created this boolean variable. So
- 55:03here's what I'm going to do. If not
- 55:07is on. If we're on nodes, if there isn't
- 55:11a pressed note, so this is the uh
- 55:14boolean negation. If there isn't a
- 55:16pressed node, we want that to be true.
- 55:20And when we release the note, okay, when
- 55:23we release a node, we need to check the
- 55:25number of notes that are currently
- 55:26pressed. If it's the last note that we
- 55:30release, then press notes count has to
- 55:33be equal to zero. If it's a zero, we
- 55:37forgot to add if. If it's zero, then
- 55:41we'll make this variable false, which
- 55:44means that we are no longer pressing any
- 55:46notes here. We're also going to use this
- 55:49uh boolean variable in in the other
- 55:52translator. Okay. So, right now we're
- 55:55keeping track of notes. We're keeping
- 55:56track of whether there is a press note
- 55:58or not. Now we need to start triggering
- 56:02the next translator here. To be able to
- 56:05do that, we're going to be using another
- 56:07global variable to trigger that. Now the
- 56:10type of variable here doesn't matter.
- 56:13I'm just going to choose boolean. I'm
- 56:14going to talk why. I'm going to talk
- 56:17about why. We don't need to choose
- 56:19boolean or we can choose any other
- 56:21variable here. So I'm going to call this
- 56:24trigger notes. This is like an action
- 56:27instead of a variable. So when do I want
- 56:30to trigger it? I want to trigger it when
- 56:33when we first pressed a node. When while
- 56:35there was no notes, this becomes true.
- 56:37And I want to trigger the other
- 56:39translator. So I'm going to just change
- 56:42the value to anything I want. It doesn't
- 56:45matter whether this is true or false
- 56:47because here's what I'm going what I'm
- 56:49going to do. So this is our first
- 56:50translator. Let's turn this basic. Now
- 56:53variable changed group variable trigger
- 56:57notes trigger notes changed I don't care
- 57:00whether it turned into false or true I
- 57:02just want to trigger this translator
- 57:04when we first pressed a note because why
- 57:07am I doing this when I first press a
- 57:10note I want the the loop to start but
- 57:13when I press a second note I don't want
- 57:15to re-trigger it I just want the loop to
- 57:17continue with the currently pressed not
- 57:19I don't want to trigger it again that's
- 57:21why I'm using this variable here. Okay.
- 57:24When the trigger not starts, I want to
- 57:27start a loop. So, when do I want to
- 57:30start a loop? If
- 57:33there's a press note, if there isn't a
- 57:35any press notes, I don't want to start
- 57:37any loop here. So, what I what do I want
- 57:40to do? I want to play the notes, the
- 57:43currently pressed notes. I want to send
- 57:45the note on signals and then note off
- 57:47signals after certain way like we did in
- 57:49the stato. So, what am I going to do? I
- 57:52need to go through for index in current
- 57:57nodes indices because we still need the
- 58:01values in the velocities the current
- 58:03node velocities that's why we're going
- 58:05through the indices not the values. So
- 58:07for all these indices we want the note
- 58:09value equals current nodes index and we
- 58:14also need the velocity value current
- 58:17node velocities
- 58:19index. So we had a note and velocity in
- 58:22this for loop. So we just going to send
- 58:24the med notes media out note on channel
- 58:28zero and I want to bother with channel
- 58:30note and I want to send the note off
- 58:33signal of the same note after certain
- 58:35delay note of zero note velocity with
- 58:41some delay. Let's say again 50
- 58:44milliseconds. So right now we just play
- 58:47one note. We're not really doing any
- 58:49repetition here. Now, here is the
- 58:51interesting trick. So, when does this
- 58:53translator trigger? When trigger notes
- 58:57change changes and as you see, we didn't
- 59:00use the value of the trigger notes
- 59:01inside. So, parameter shows the value of
- 59:03the trigger notes. We didn't use
- 59:04parameter here. But, so what I want to
- 59:07do, I sent the note signals and not of
- 59:09signals after certainly, but I want to
- 59:11retrigger it after with with some
- 59:13duration. So that duration will be the
- 59:16repetition of period of my notes. So
- 59:19what I'm going to do, I'm going to
- 59:20re-trigger itself. So trigger notes
- 59:23equals false. It doesn't matter whether
- 59:26I choose false or true, but with with a
- 59:28delay. And that delay, let's say 200
- 59:30millconds. So right now, as long as I
- 59:33have pressed nodes, we go into this
- 59:35translator, we send the note signals and
- 59:38there not of signals with delay. And
- 59:41then after 200 millconds, we trigger
- 59:43this again. And after 200 seconds we
- 59:45trigger this again. But as long as there
- 59:48is an press note when we release all the
- 59:50notes we we we stop this loop. So let's
- 59:52see how how this works now.
- 59:57So you see I first press a note
- 1:00:02and when I add a note to this
- 1:00:06the they they just work parallel. Even
- 1:00:10if I press the second note at a
- 1:00:12completely different time
- 1:00:16and I keep releasing the note and then
- 1:00:18we So let's actually do uh
- 1:00:22debugging. Let's just print something
- 1:00:24here.
- 1:00:26So
- 1:00:30when I release all the notes, we were no
- 1:00:32longer in this loop. Okay, let's improve
- 1:00:35this. I want to control this delay, but
- 1:00:38not with a slider on this custom
- 1:00:40translator, but with my knob on my
- 1:00:43keyboard. So, how do I do that? It's
- 1:00:46simple. I'm going to assign another
- 1:00:48variable to this delay. So, instead of
- 1:00:51200, I'm going to create another group
- 1:00:53variable. This time, an integer delay
- 1:00:56amount. And I'm going to change this to
- 1:00:59delay amount. Now, the problem is that
- 1:01:00the delay amount is zero. When when it's
- 1:01:03first created, the value is zero. So as
- 1:01:06soon as I move to a part, I want to
- 1:01:08reset that value to something. So here's
- 1:01:11what I'm going to do. I'm going to
- 1:01:12create another translator and choose
- 1:01:16when section is activated. I want to
- 1:01:19delay amount set delay amount. So you
- 1:01:23see both input and output are
- 1:01:25parametric. So that but I want a fixed
- 1:01:28value for for this delay amount. Let's
- 1:01:30say 40 uh let's say 50 milliseconds
- 1:01:33actually. let's say 100 milliseconds. So
- 1:01:37when we move to a section, this is resed
- 1:01:39to 100 millconds. But now I want to
- 1:01:41control this value, this parameter with
- 1:01:43another custom translator. So we add one
- 1:01:46last custom translator. We don't need a
- 1:01:48script with this one either. I want to
- 1:01:51use my control change 12 to control.
- 1:01:54Okay, we have two ways to do that. I can
- 1:01:58control the delay amount here. But if
- 1:02:00you don't use scripts, we're bound to
- 1:02:02these numbers 0 and 12 27. If you want
- 1:02:07to go beyond those numbers, we actually
- 1:02:09need scripts and we we have more control
- 1:02:11over here now. So my control change
- 1:02:13value here ranges between 0 and 120. But
- 1:02:18I don't want those values. So I want
- 1:02:22delay amount to be equal to let's say I
- 1:02:25want my delay amount to be equal to
- 1:02:29uh let's say 50
- 1:02:31minimum plus
- 1:02:34control change value. So right now this
- 1:02:37ranges between 50 and 1777.
- 1:02:40But if I multiply this let's say with
- 1:02:43four. Now this ranges between some other
- 1:02:46numbers. Let's see. Let's see on what
- 1:02:48range what is the range play amount.
- 1:02:53So it goes up to 558.
- 1:02:57Now if I start
- 1:03:02So I'm changing my knob. Changing to
- 1:03:09H.
- 1:03:11Okay. When I turn my knob all the way
- 1:03:14down, we we're having some something
- 1:03:16strange like the note cut off. Why is
- 1:03:18that? Well, that's because the note of
- 1:03:21signals delay amount is 50 like the
- 1:03:23minimum amount. So, we have to actually
- 1:03:25make it 40 less than 50 or maybe we
- 1:03:28should change that 50 to something as
- 1:03:29greater.
- 1:03:32So, now my note controls the gaps, the
- 1:03:35time gap between each note.
- 1:03:42Okay, our final topic is non-basic
- 1:03:45variables. This is a really advanced
- 1:03:47feature which allows us to control
- 1:03:49different aspects, different behaviors
- 1:03:52inside Keystage and you can even use
- 1:03:54scripts while you're editing your song.
- 1:03:57Okay, if you go to cheat sheets, you see
- 1:04:00we have these basic variables and we
- 1:04:02have non-basic variables and you see the
- 1:04:04names just are familiar like parts,
- 1:04:06layer, track, translator and more will
- 1:04:10be added to these variables as key stage
- 1:04:12keeps evolving and we also have player
- 1:04:15and the array versions of them as well.
- 1:04:17Okay, so let's start by creating a track
- 1:04:21variable for example. So track a
- 1:04:25now there are two important distinction
- 1:04:28between non-basic variables and basic
- 1:04:30variables. The first one is non-basic
- 1:04:33variables cannot be used as non-local
- 1:04:36variables. They are always local
- 1:04:37variables. We cannot use group variables
- 1:04:40non-basic variables as group global or
- 1:04:42song or layer variables. The second one
- 1:04:44is whenever we create an integer it
- 1:04:48automatically starts with the default
- 1:04:49value which is zero. When we create a
- 1:04:52string, it's an empty string. We always
- 1:04:54have a value of the basic variable. For
- 1:04:57non-basic variables, we don't have that.
- 1:05:00When we create a track A, this is a
- 1:05:02placeholder for an actual track inside
- 1:05:05keystage, but currently it doesn't point
- 1:05:08to any track and its value is nil. In
- 1:05:11technical terms, its value is nil. And
- 1:05:14this is true for all non-basic
- 1:05:15variables. As soon as we create them,
- 1:05:18they're actually pointers to actual
- 1:05:20objects inside Keystage. And right now,
- 1:05:22it doesn't hold any information. It
- 1:05:24doesn't point to an actual track. Okay,
- 1:05:26how do we make this point an actual
- 1:05:28track? First of all, we cannot create an
- 1:05:30actual track inside the scripting
- 1:05:32language. We can only create tracks
- 1:05:33here. And the same is true for all other
- 1:05:36objects. So, we can only point to an
- 1:05:38existing track here. So, how do we do
- 1:05:41that? So our special variables is the
- 1:05:44way that we access those actual objects.
- 1:05:47So for example, so by the way there is a
- 1:05:49filter here in all these. If I say
- 1:05:51track, we have two special variables,
- 1:05:54all tracks and current track. Okay. Uh
- 1:05:58all tracks is kind of obvious and all
- 1:06:00track is a special variable of track
- 1:06:01array. So we can access all tracks and
- 1:06:03put them in a track array. Okay. What is
- 1:06:06current track? Current track actually
- 1:06:08depends on where the custom translator
- 1:06:10is. Well, since it's on a track, it's
- 1:06:12obvious this we're talking about this
- 1:06:14track as the current track. While we're
- 1:06:16on a part, for example, this part, we're
- 1:06:18talking about again this track connected
- 1:06:20to that track. But on all other cases,
- 1:06:22for example, if you create a custom
- 1:06:24translator here with a script, current
- 1:06:25track doesn't point to anything. It it
- 1:06:27can't point to anything. So, it's always
- 1:06:29new. We can't use current track in a
- 1:06:32script inside song properties or layers
- 1:06:34or anywhere else. anywhere else other
- 1:06:36than a part or track and all the other
- 1:06:39things like parts, translators and so on
- 1:06:41and so forth. All these variables there
- 1:06:43is a current version current section.
- 1:06:45For example, if we're on a part, the
- 1:06:46current section is the section that we
- 1:06:48are on. If we're on a track, the current
- 1:06:51section points to the active section
- 1:06:53here. Now, the current section also
- 1:06:55works on song properties for example
- 1:06:57because the active section is global is
- 1:07:00a global information in inside the song.
- 1:07:02So for example we say a equals current
- 1:07:05track.
- 1:07:07So now a has a specific value. If you
- 1:07:10want to print a so our input is let's
- 1:07:14change the input to notes so we can
- 1:07:16monitor easier.
- 1:07:20Module pro channel one that's the name
- 1:07:22of our track. So if we actually name
- 1:07:24this to something else my track. And if
- 1:07:28we go into mood motor this becomes my
- 1:07:30track. By the way, we have another uh
- 1:07:33debugging functionality message to media
- 1:07:35monitor. This is also really convenient.
- 1:07:38Uh if you need to monitor certain
- 1:07:41properties, certain functions while you
- 1:07:43are not inside the editor, you can
- 1:07:45actually send those messages to media
- 1:07:48monitor. Especially if you're working
- 1:07:50with multiple custom translators and you
- 1:07:52need to monitor different variables in
- 1:07:53all those custom translators, you can
- 1:07:55use that uh debugging option message to
- 1:07:58media monitor. Okay, so we get the
- 1:08:00current track. What are we going to do
- 1:08:01with this current track variable? So how
- 1:08:04are we going to use it? That's really
- 1:08:06where these properties takes place. So
- 1:08:08we already have gone through properties
- 1:08:10of arrays and we gone through some
- 1:08:12actions or some variables like count
- 1:08:14indices and the same is true for all
- 1:08:17these types and in fact the track has
- 1:08:20lots of properties here. Okay, regular
- 1:08:23properties are divided into two
- 1:08:24categories read only and not read only.
- 1:08:27For example, name name is a read only
- 1:08:30property of the current track of a
- 1:08:32track. But unity volume midi is not read
- 1:08:35only. So this is very similar to special
- 1:08:37variables like we we did in the song
- 1:08:40volume. Some of them are read only like
- 1:08:42song name and some of them are not. The
- 1:08:45same is true for variable properties. In
- 1:08:48some of them we can assign values in
- 1:08:50some of them we can't. So let's do the
- 1:08:53this with current track and let's change
- 1:08:56this to my knob here and let's control
- 1:08:59the volume of this track. We don't need
- 1:09:01to assign this to a variable name. We
- 1:09:04can directly use the current track
- 1:09:06special variable unit volume midi equals
- 1:09:10current track again unit volume midi +
- 1:09:15one. So we're not using the value of
- 1:09:17control change value but as long as we
- 1:09:18change the value we just keep increasing
- 1:09:20the volume. So I'm just sending some
- 1:09:22random values or not the volume just
- 1:09:24keeps increasing as you see. So yeah you
- 1:09:26can use the values and assign the values
- 1:09:29for those uh which are not read only
- 1:09:33that's important. So like name is a read
- 1:09:36only property. So for example, if I try
- 1:09:38to do current track
- 1:09:41name equals something, it says this
- 1:09:45property is read only. Okay. Uh let's
- 1:09:49talk about properties of property. So
- 1:09:51let's actually try something
- 1:09:53interesting. So on this track I have
- 1:09:56three parts. Now I do current track. One
- 1:10:00of the properties of current track is
- 1:10:02parts. So you can actually have other
- 1:10:05non-basic variables as properties of
- 1:10:08these non-basic variables. So parts is
- 1:10:12an part array. Now we have an
- 1:10:15interesting functionality here. When I
- 1:10:17tap parts, so these are the properties
- 1:10:20of arrays. So let's say autosustain. Now
- 1:10:23this is an interesting situation because
- 1:10:25this is an part array. We have all the
- 1:10:28three parts and we are trying to control
- 1:10:31the auto sustain of all of them. Now
- 1:10:33this is one of the cool logics of
- 1:10:35operators or in also works for operators
- 1:10:39but also for assignments. When I try to
- 1:10:41assign let's say uh true here it's going
- 1:10:45to assign true to all of those auto
- 1:10:48things of all these parts. So we can
- 1:10:50assign values to arrays of the same
- 1:10:53value. So this it's not restricted to
- 1:10:55only properties. This is true for all
- 1:10:58types of arrays. So you can assign
- 1:11:00single values to arrays. All the object
- 1:11:03inside that array will be assigned that
- 1:11:05value. The same goes with the
- 1:11:07properties. So right now uh when I tap
- 1:11:10these parts their autosustains are off.
- 1:11:13As soon as I press notes all these parts
- 1:11:16auto sustains are turned on. Okay. Uh
- 1:11:19let's say these two have autosustain on
- 1:11:21and others are off. Let's try something
- 1:11:24more interesting. I am going to assign
- 1:11:28the negation of this. I'm assigning an
- 1:11:31array another array. Okay, that's okay.
- 1:11:34But I'm negating this array. So negating
- 1:11:37an array means that we're negating each
- 1:11:39boolean value inside that array. So it's
- 1:11:42the same logic like we can perform
- 1:11:44operations like negation or addition uh
- 1:11:47to arrays or between arrays and numbers.
- 1:11:51Uh so for example when I do this so this
- 1:11:54is on this is off this is on I press it
- 1:11:57it's just inversed I press it again
- 1:12:00again it's inverse so you get the point
- 1:12:02let's give another interesting example I
- 1:12:05want to have an interesting control over
- 1:12:08PDF so I have already loaded a PDF file
- 1:12:12let's open that it's a KCH user manual
- 1:12:15now in my controller I have two system
- 1:12:19exclusive messages so Let's let's show
- 1:12:21you that I want to scroll down
- 1:12:24automatically when I use that system
- 1:12:26exclusive message and with another
- 1:12:28message I want to scroll up. So for
- 1:12:30example if I do this is actually
- 1:12:32automatically detects that system
- 1:12:34exclusive message. So let's assign this
- 1:12:36for up. Oh actually let's just do down
- 1:12:40auto detect I have this single system
- 1:12:42exclusive message and as I keep pressed
- 1:12:45on that actually the problem is let's
- 1:12:49monitor meditor this ah I don't have a
- 1:12:52release functionality of this button so
- 1:12:54I cannot really use this. So I want to
- 1:12:56hold a button and as long as the button
- 1:12:58is hold I want to scroll down but the
- 1:13:00release functionality is cannot be
- 1:13:02detected on this keyboard. So I cannot
- 1:13:04use that. I have to use something. Okay,
- 1:13:05I'm just going to use a note. Then
- 1:13:08choose note. I'm going to choose this
- 1:13:10note as my input. Huh? Okay. Whenever I
- 1:13:15change the output, the input resets. So
- 1:13:17I have to reassign it. So for this note,
- 1:13:20note on will keep scrolling. Note off
- 1:13:23will stop scrolling. Okay. I'll again
- 1:13:26need a repetitive action. So this
- 1:13:29translator, let's see, I still have
- 1:13:31those group variables here, right?
- 1:13:32because I haven't changed the track.
- 1:13:35Let's delete all of them. So, I'm going
- 1:13:38to need another variable to detect my uh
- 1:13:41note input. So, it's I just need a
- 1:13:44boolean variable in this case. Note is
- 1:13:47pressed.
- 1:13:48Okay, so it's really easy. Note is
- 1:13:52pressed equals note is on. Okay, so this
- 1:13:56controls not is pressed. Now I'm going
- 1:13:59to show you another new feature in 2.3
- 1:14:02actually a really interesting input. I'm
- 1:14:05going to use it with a different purpose
- 1:14:07but audio meters. Normally this gives me
- 1:14:09the input signals or of this track
- 1:14:13pre-ffect input signal. And the
- 1:14:15important thing about these translators
- 1:14:17is whenever I have this translator, this
- 1:14:20output triggers continuously. Every
- 1:14:23frame, every sample frame, this triggers
- 1:14:26and these are the only inputs which
- 1:14:29allows you to have a continuous
- 1:14:31repetitive uh custom translator. So this
- 1:14:34script will just keep rolling on every
- 1:14:38sample frame. Of course, we don't want
- 1:14:40this to just work all all the time.
- 1:14:43That's why we added that uh control if
- 1:14:47note is pressed. So as long as note is
- 1:14:50pressed we we're going to execute this
- 1:14:53repetitive function. What I want to do I
- 1:14:55want to scroll down my PDF. Now I can do
- 1:14:58current section PDF position. Now this
- 1:15:01PDF position is position in terms of
- 1:15:04float. Zero means top of the document.
- 1:15:07One means top of the first page. Two
- 1:15:09means top of the second page. So it's
- 1:15:11intermediate values allows you to scroll
- 1:15:14the different positions on that. So I'm
- 1:15:16going to set PDF position equals current
- 1:15:19section PDF position plus okay I really
- 1:15:23need to choose a very small number here
- 1:15:25because as I said this iteration just
- 1:15:28keeps every frame. Okay let's test this.
- 1:15:32So as I press my notes it scrolls down
- 1:15:34as I release it just stops. uh since I'm
- 1:15:37at here I want to show you one other
- 1:15:40important uh convention I would say
- 1:15:43instead of writing current section PDF
- 1:15:46position I can just write PDF page
- 1:15:49position now you can see property of
- 1:15:53current section so properties of current
- 1:15:56objects either current track current
- 1:15:59part current section current player unit
- 1:16:02uh we can access those properties right
- 1:16:04away by just typing them and although
- 1:16:06automatically means that we are this
- 1:16:09this simply means current section dot
- 1:16:12PDF page position. So we can simply use
- 1:16:15this convention which allows us to
- 1:16:17access certain properties of the object
- 1:16:19that we are on much faster. Put a plus.
- 1:16:23Let's do minus this time. Right now we
- 1:16:25are scrolling up instead. Okay. I'm
- 1:16:28going to present a final example that I
- 1:16:31actually actively use in my setups. I
- 1:16:34have I have already created a project
- 1:16:36for this.
- 1:16:39Now uh this is a song file and we have
- 1:16:42the stems of this song file on each
- 1:16:45different player unit. So we have the
- 1:16:47lead vocals here, back vocals, guitars,
- 1:16:49bass, drums, keys and metronome and all
- 1:16:52of these are synced together. So for
- 1:16:54example, if I play the global play
- 1:16:58button, let's go into song mixer. All of
- 1:17:01them are started playing synchron in
- 1:17:02sync. So I can mute the monetronome.
- 1:17:08All of them are playing. So why am I why
- 1:17:10do I have these? So I have these in all
- 1:17:13my song files. Like for example, for the
- 1:17:16song file of Dhun, I have all these
- 1:17:18seven tracks. So first of all, the click
- 1:17:20track, I'm sending it to to the rest of
- 1:17:23the band to their inairs during live
- 1:17:25shows or also during our rehearsals. But
- 1:17:27for these stem files, first of all, I am
- 1:17:30using this while I'm practicing myself
- 1:17:32at home. I disabled the keys and played
- 1:17:36all the rest including the metronome and
- 1:17:38play on them during my uh practice at
- 1:17:40home. But during rehearsals sometimes
- 1:17:43our lead vocalist uh is not in stumble.
- 1:17:46So most of in most of our practices we
- 1:17:49don't have the lead vocalist there. So I
- 1:17:51had to along with the click track I had
- 1:17:54to send the lead vocals to the iners of
- 1:17:56all of us to to be able to play and
- 1:17:59sometimes our for example if our
- 1:18:00guitarist cannot make to the uh
- 1:18:03rehearsal we also give the backing track
- 1:18:05of the rehearsal. So I want to have a
- 1:18:08control of all these. So this global
- 1:18:10button start playing all of them
- 1:18:11together. What I want is I need a button
- 1:18:14on my keyboard and when I press that
- 1:18:17button, I want to just play the ones
- 1:18:20that are necessary for that rehearsal.
- 1:18:21And to be able to control that, I want
- 1:18:23to create a custom translator here in
- 1:18:25the song properties. And in fact, I can
- 1:18:27actually do that also in an input ls
- 1:18:30because it's going to be a global
- 1:18:31object. But this is an easier place to
- 1:18:33access. So I want to create a custom
- 1:18:35translator here uh with the inputs from
- 1:18:39my keyboard uh a control button, play
- 1:18:42and stop button with those buttons. So I
- 1:18:44can actually use the system exclusive
- 1:18:47buttons on my keyboard here to
- 1:18:48demonstrate that here in the scripts I
- 1:18:51have a bunch of switches here which
- 1:18:54controls these player units. So for
- 1:18:56example if the switch for guitar is on
- 1:18:58when I press the play button on my
- 1:19:00keyboard guitar starts playing. If bass
- 1:19:03is also on, bass and guitar play starts
- 1:19:05playing together. So I can control which
- 1:19:08player units to play from the switches
- 1:19:10here. So that's that's the general idea.
- 1:19:13Okay. Now I'm creating a custom
- 1:19:16translator and I want to control these
- 1:19:18player units. How how do I do that? One
- 1:19:21way is to create variables, global
- 1:19:24variables and then for on each of these
- 1:19:27translators I can have different custom
- 1:19:29translators with the same global
- 1:19:31variable so that I can have a connection
- 1:19:33between this custom translator and all
- 1:19:35the custom translators here. But the
- 1:19:37thing is I want to create a custom
- 1:19:38translator here where I can use in all
- 1:19:41of my projects because in all my
- 1:19:43projects I have these tracks and I don't
- 1:19:45want to add a global variable in all of
- 1:19:48these and have custom translators in all
- 1:19:50these play. I I want to have a much
- 1:19:53simpler way to control these. So So
- 1:19:56here's how I do it. First of all, let's
- 1:19:58press the play button on my keyboard.
- 1:20:00This is actually a play button which
- 1:20:02sends this is an exclusive message. So
- 1:20:05this will be the play functionality. Now
- 1:20:07here's what I want to do. I want to so
- 1:20:09when I press this play button I want to
- 1:20:12first of all I want to create some uh
- 1:20:14switches to be able to control those
- 1:20:16player units. So I need integers
- 1:20:18actually booleans for for each of the
- 1:20:20player units. So let's say lead vocals.
- 1:20:25Okay. I created these boolean variables
- 1:20:27and I created switches controlling those
- 1:20:29boolean variables. And as you remember,
- 1:20:31so if I close this window, I have all
- 1:20:33these switches controlling those
- 1:20:35variables. Okay, so I need to go through
- 1:20:37all the player units, all the tracks.
- 1:20:39And actually, I'm going to use the
- 1:20:41variable player unit for that. So for
- 1:20:44player, that's the name of my unit in
- 1:20:48all player units. So we we go through
- 1:20:52all the player units. If that player's
- 1:20:55name is equal to lead vocals but okay
- 1:20:58first of all we need to check this
- 1:21:00variable if lead vocals if the switch is
- 1:21:03on and we can actually do an and
- 1:21:06structure here if the switch is on and
- 1:21:08player track we need to access the track
- 1:21:12of the player units and you see actually
- 1:21:14there is a we can have the player units
- 1:21:16property as track and track as a player
- 1:21:18units property we can go both ways name
- 1:21:22equals lead vocals
- 1:21:24If both conditions are are true, then we
- 1:21:27start playing.
- 1:21:29So this is an action property and we're
- 1:21:31going to repeat this with all all the
- 1:21:34other uh player units. Okay. So I put
- 1:21:37all the conditions for each loaded
- 1:21:39vocals, back vocals. I have to make sure
- 1:21:42that these names exactly matches with
- 1:21:44the names of the track. So otherwise it
- 1:21:46doesn't work. So we always have to use
- 1:21:48the same names. But now I haven't
- 1:21:51actually uh set up the same thing for
- 1:21:54the stop button, but it's the same idea.
- 1:21:57So for example, I just want to play
- 1:21:59leads, guitars, and drums. And I press
- 1:22:02play. You see lead, guitar, and drums
- 1:22:05are on the playing. Let's stop it like
- 1:22:07this. And this time I'm going to play
- 1:22:10bass, lead, guitar, bass. And I can
- 1:22:14also, you know, assign rewind buttons
- 1:22:16and everything. I'm just going to do it
- 1:22:18like that. But now in this state leads,
- 1:22:21bass, drums and keys will start playing.
- 1:22:24Uh let's also add metronome.
- 1:22:30But you get the point. So yeah, using
- 1:22:33the non-basic variables and their
- 1:22:35properties, we can have tons and tons of
- 1:22:37controls. All you need to do is just go
- 1:22:39into the script editor and uh go into
- 1:22:42cheat sheets. So one thing you probably
- 1:22:45realize, you can access for example
- 1:22:47parts. What are the properties of parts?
- 1:22:48You can do that or you can uh do this on
- 1:22:52all. You can select all here. So you
- 1:22:54access all the properties of everything.
- 1:22:56Also there's this all button. This
- 1:22:58basically shows all the commands in
- 1:23:00keystage so far. And in every version
- 1:23:03there will be new variable types, new
- 1:23:05functions, new actions, new properties.
- 1:23:08So this will keep evolving
- 1:23:10automatically. So that's all about
- 1:23:12scripts.
About this transcript
This page contains the full transcript of KeyStage Tutorial: The Scripting Language by Ali Gokturk, generated from the public captions YouTube serves with the video. The transcript has 13,065 words across 1,875 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.
What you can do with it
Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to choose a caption track available on the video.
Free YouTube transcript tool
YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, caption language selection, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.