Welcome to the second article on learning to code with JW Lua.
In the first article, we learned some of the basics of the lua language, including datatypes and for loops. Today, we’re going to take that knowledge and apply it with creating an actual script that changes every notehead in a selected region.
Sure will beat using the Special Tools!
So let’s dive right in.
For Loops, Part 2
Last time, we learned about for loops like this that iterate through a series of numbers.
for i = 1, 10, 1 do
print(i)
end
To recap, we learned that this specific loop will print out all the numbers from 1 to 10. The variable i
starts at one, then increments by one each loop, and the loop stops when i
gets to 10.
for variable = start, end, increment do
-- looped code goes here
-- and can reference "variable"
end
Well, that’s actually only one of two main types of for loops. Now here’s the second kind of loop (one that is used all the time in JW Lua).
For-in Loops
for key, value in pairs(table) do
-- looped code goes here
-- can reference "key" and/or "value"
end
Ok, so similar, yet quite different. The idea is similar as we are still looping through a collection of data. But instead of looping through a series of numbers, we can loop through data in a table.
Tables are just a collection of data
Ok, let’s try one. Go back to the online lua coding editor we used last week, and type this:
local pi_table = {3, 1, 4, 1, 5, 9, "easy as pi"}
for key, value in pairs(pi_table) do
print(value)
end
Look at the output. What do you notice?
That’s right! It prints out all the items in pi_table
in order! Exciting!
Actually exciting, though. This basic mechanic is what you’re going to use in literally 90% of your scripts in JW Lua. I won’t get into too much detail about the specifics of this type of for loop (like why you have both a key and value, but only use the value) right now because JW Lua has it’s own variant. But we’ll learn that eventually.
Consider this like opening up a subplot in a tv drama. It’s those story hooks that keep reeled in for seasons later.
And we’re going to get into how to write this type of loop in JW Lua, later today, but first, let’s actually write some code in JW Lua.
Your First JW Lua Script
Before we go further, I just want to preface this because there’s going to be a lot that you won’t know right away. You won’t know why we’re doing some things or what everything means. And that’s ok. We’re going to learn as we go by diving head first into the deep end.
If you don’t have JW Lua installed yet, go ahead and install it. And if you haven’t read my Getting Started with JW Lua, go ahead and give that a quick read right now.
The, go to the development tab and paste this code in the editor:
function plugindef()
-- This function and the 'finaleplugin' namespace
-- are both reserved for the plug-in definition.
finaleplugin.MinFinaleVersion = "2010"
finaleplugin.Author = "Nick Mazuk"
finaleplugin.Copyright = "CC0 1.0"
finaleplugin.Version = "1.00"
finaleplugin.Date = "December 22, 2018"
finaleplugin.AuthorURL = "https://nickmazuk.com"
finaleplugin.AuthorEmail = "https://nickmazuk.com/Contact/"
return "My First JW Lua Script", "My First JW Lua Script", "Creates a popup that says \"I ran my first JW Lua script!\""
end
print('Hello, world!')
Then, hit “Run Script”.
Great! You’ll see in the “Execution Output” that it now says “Hello, world!”! And given that you know how to print things, that would make sense. In fact, if we were to write any of the lua code we’ve learned so far below line 13, it would run and output as we’d expect.
Go ahead, have some fun coding in this environment!
Your First JW Lua For Loop
Now, we’re going to do something we’ve been waiting for since the beginning: interacting with Finale.
So on line 14, write the following code:
for entry in eachentrysaved(finenv.Region()) do
print('Note!')
end
Let’s break it down.
- finenv.Region(): This gets the info for the selected region in Finale
- eachentrysaved: Acts like
pairs
from before, where in this case it gets each note entry in the region - entry: This is the actual note entry object, kind of like
i
that we used before
Given that, what do you think this script does?
I’ll let you know in:
5…
4…
3…
2…
1…
So, as you might have guessed, this script will print “Note!” for ever note in a highlighted section of music.
Run it. See for yourself!
This would really be a great time to try things out. Try different notes and rhythms and prove to yourself that it really does loop for each note in the selected region. Because spoiler: it also counts rests. Yep, rests are considered an entry just like notes are an entry. It won’t matter for this article, but it will matter very soon…
Changing Noteheads to “x” Noteheads
Let’s have some fun. Let’s actually change something in the Finale document. You’re going to keep the same loop as you had before, but add this code inside of it.
for entry in eachentrysaved(finenv.Region()) do
local highestnote = entry:CalcHighestNote(nil)
local notehead = finale.FCNoteheadMod()
notehead:EraseAt(highestnote)
notehead.CustomChar = 192
notehead.Resize = 80
notehead:SaveAt(highestnote)
end
And here’s a breakdown of what it does:
for entry in eachentrysaved(finenv.Region()) do
-- get the highest note for each entry (e.g., in a chord)
local highestnote = entry:CalcHighestNote(nil)
-- create a notehead object
local notehead = finale.FCNoteheadMod()
-- remove any old notehead data for this entry
notehead:EraseAt(lowestnote)
notehead:EraseAt(highestnote)
-- define the notehead to be an "x"
notehead.CustomChar = 192
-- resize the notehead to 80% to match the previous noteheads
notehead.Resize = 80
-- save this notehead to the note entry (i.e., make the note have the custom notehead)
notehead:SaveAt(highestnote)
end
And there you go!
Next Steps
Homework
Ok, so at the end of each article, there will be some homework for you to do to help you learn. Last week it was creating more loops to do interesting math. This week, it’s about modifying the script we created.
Here are the challenges for you to see how well you understand what’s going on.
- Make the resulting noteheads smaller
- Make the resulting noteheads larger
- Change the character of the noteheads
- Make the lowest notehead in a chord change (instead of the highest)
- Change both the highest and lowest notehead (advanced)
Footnotes
There’s a lot to go over each week as we get moving, so inevitably there’s some content that’s almost essential but gets left out. Therefore, each week I’m going to be posting footnotes to explain these gaps. Here are a few you may want to check out this week:
- What is an algorithm in code?
- What do pairs, key, and value really mean with for in loops?
- What does eachentrysaved really mean?
- Why does eachentrysaved include rests?
- How did I know to use 192 as the character?
Next Time
That’s it for this week. Next week we’re going to change the characters based on the pitch of each note.
But before you get there, make sure to do the homework and feel free to leave your solutions in the comments below. I look forward to reading them!
Nick
Nick Mazuk is a composer and trombonist in Los Angeles. He mainly freelances by creating arrangements and original compositions for orchestra. In Finale, he specializes in automating tasks so he can achieve great results fast. Nick’s YouTube channel provides more tips and tricks for speeding up your Finale workflow.
risolti anche questi esercizi.
li ho trovati più semplici.
these exercises also solved.
I found them simpler.
I know very little English, anyway thanks