This page lists JW Lua programming topics.
Every programming reference seems to require a “Hello, world!” example, so here it is in JW Lua:
print ("Hello, world!")
The fastest way to get a feel for how the JW Lua script language works is probably to read and test existing source code material, such as the scripts on the example page. When the basics of JW Lua has been covered, there are also some larger scripts on the page with fully-featured scripts.
Make small changes to the script code and try and see what happens when you run it.
JW Lua maps the Lua script language to the PDK Framework (created by Jari Williamsson). For more info about how to interpret the PDK Framework documentation for scripting in JW Lua, please refer to this page.
All accessible PDK Framework classes starts with the FC
prefix, such as FCMeasure
, FCMusicRegion
, FCNoteEntry
, etc.
The methods names (and as a result, the Lua properties) in the PDK Framework are in CamelCase. The constants are all-uppercase.
JW Lua is based on Lua 5.2. More information about the Lua computer language (and books about Lua) is available at the Lua home page, including the Lua 5.2 Reference Manual and the On-line version of "Programming in Lua", First Edition (this book covers Lua 5.0, but for JW Lua programming purposes, it should cover what you need.)
Lua is case sensitive. The basic Lua syntax is very similar to other computer languages and Lua is an easy language to learn. To be able to write plug-in scripts there are just a few concepts in Lua you need to be aware of, such as:
for … do … end
loopsif … then … end
conditional statementsHowever, to really take advantage of the full power of Lua, there are other very powerful tools (such as iterators, closures and coroutines) to explore.
JW Lua includes all the standard Lua modules (string
, math
, file
, etc), so they can be used in any JW Lua script, such as :
print (math.random(1, 10))
All functionality that accesses Finale through the Lua/PDK Framework resides within the finale
namespace (namespaces use the dot separator).
For example:
page = finale.FCPage()
The finenv
namespace has been created to provide “programming shortcuts” to some objects that are often needed for a Finale scripts. finenv
currently contains these functions:
Member | Description |
---|---|
finenv.Region() | Returns an object with the currently selected region (in the document/part currently in editing scope), without the need for any other method calls. |
finenv.UI() | Returns the global “user interface” object (of the FCUI class). The FCUI class contains Finale and system-global tasks, such as displaying alert boxes, sounding a system beep, or getting the width of the screen, etc. |
finenv.UserValueInput() | Creates and returns a dialog object to be used for simple user input. |
finenv.StartNewUndoBlock() | Ends the currently active Undo/Redo block in Finale and starts a new one with a new undo text. First parameter (a Lua string) is the name of the new Undo/Redo block. Second parameter (optional, default is true ) is a boolean, indicating if the edits in the previous Undo/Redo block should be stored (=true) or cancelled (=false). Finale will only store Undo/Redo blocks that contains edit changes to the documents. Available in beta 0.36 and above. |
finenv.FinaleVersion | A read-only property with the running Finale “year” version, such as 2011, 2012, etc. |
finenv.RawFinaleVersion | A read-only property with the full Finale version number. It's constructed as 4 bytes with different version info. The highest byte is the major version, the next is subversion, etc. Use this only if you need the revision number of a specific major Finale version. |
finenv.MajorVersion | A read-only property with the major version number of JW Lua. Beta versions return 0, version 1.xx gives 1, etc |
finenv.MinorVersion | A read-only property with the minor version number of JW Lua. A version 1.07 would give 7, etc. |
finenv.StringVersion | A read-only property with the full “printed” version number of JW Lua, as a string. |
finenv.ConsoleIsAvailable | A read-only property that will return true if there's a console available for print() statements. Scripts that runs from the Finale menu don't have a console. Available in beta v0.28 and above. |
JW Lua supports dialog boxes to the user through the finenv.UserValueInput() call. Programming of the dialog boxes is explained in full detail on this page.
When you access Finale data in JW Lua, the data is internally handled in a way that sometimes is very different from how a Finale user experience the data. This section lists some internal concepts in Finale that a JW Lua programmer should be aware of.
Internally, Finale stores its database, historically called Enigma data which is loaded/saved from/to the database. Different data record types have different access parameters. However, the PDK Framework “hides” all calls to the database through its classes and methods.
Some data (such as the “page spec” record, which describes a page with its width, height, etc) is really simple and has no linked connections to other data in the database. Other types of data demands more, and might consist of linked data records across the database (for example an expression added to the score in Finale, which contains a number of different data records).
Note entries however, are not accessed through Finale's usual database calls. They are accessed through a concept called the TGF entry frame, where all note entries in a single measure+staff+layer are accessed through one variable-sized data record. The TGF frame can actually be browsed directly in Finale, using Finale's Edit Frame dialog box (in the Speedy Entry Tool).
A note entry can contain multiple notes (which are the different pitches in a chord), but the notes are just sub-data in the note entry.
Finale makes extensive use of bit flags (access through properties in JW Lua) to signal that the data should behave in a certain way.
However, when connecting some data types to measures and note entries, Finale also uses some internal flags to mark if the database should be accessed for the specific measure/entry. These flags are optimization flags, to speed up the performance in Finale. In JW Lua and the PDK Framework, all optimization flags ends with Flag
.
Example: if a note entry has articulations, there's a specific optimization flag for that is accessed through the FCNoteEntry.ArticulationFlag
property.
Finale will edit the document and part that is currently has the editing focus. Technically, the editing focus might be different from what the user sees on the screen (the visual focus), so a script can edit data in a document/part that's not currently visible. However, as a plug-in script programmer, it's extremely important that you make sure that the editing focus and the visual focus is identical when the plug-in script ends.
An important concept in the PDK Framework is the collection. Usually, collection classes ends with a plural 's' version of the “single-object” version. For example:
measure = finale.FCMeasure() -- A single measure measures = finale.FCMeasures() -- A collection of multiple measures
Collections are not compatible with Lua tables, but they can be converted to Lua tables with the coll2table() function (see below).
A cell in the PDK Framework termonology is a reference to a single measure on a single specific staff. The concept is used with the FCCell
and FCNoteEntryCell
classes, but affects other many classes as well. The coordinate system for cells (at object creation, for example) always is x, y, which in the cell concept means measure_number, staff_number.
To really integrate a plug-in script with Finale (and JW Lua) - so it behaves like an independent plug-in - the script can describe the script to JW Lua through the plugindef()
function.
Please note that JW Lua can handle everything in the plugindef()
function automatically, by using the Plug-in Def dialog box.
The plugindef()
function is an optional function that only should do a maximum of 2 things:
finaleplugin
namespace environment to further describe the plug-in (see below).
A simple plugindef()
implementation might look like this:
function plugindef() finaleplugin.RequireSelection = true finaleplugin.CategoryTags = "Rest, Region" return "Hide Rests", "Hide Rests", "Hides all rests in the selected region." end
plugindef()
is considered to be a reserved name in the global namespace. If the script has a function named plugindef()
, it might be called at any time (not only during script execution) by JW Lua to gather information about the plug-in. The plugindef()
function can NOT have dependencies outside the function itself.
All aspects regarding the plugindef()
execution is optional, but for a plug-in script that's going to be used repeatedly, the minimum should be to return a plug-in name, undo string and short description.
The plugindef() function can return a maximum of 3 return values (all of them should be strings):
&
character can be used before a letter to specify that letter as a mnemonic character keystroke when the script appears in Finale's plug-in menu. (Example: “My &Plug-in” would make p
the shortcut mnemonic key.)Again, all these return values are optional.
The finaleplugin
namespace is a reserved and defined namespace for the plugindef()
execution. It can both contain properties that affect how the script will run and properties that further describes the script to the outside world.
Please note that since the execution of plugindef()
is completely silent (no errors are displayed on failure), make absolute sure that all spellings are correct (including correct upper/lower case).
The finaleplugin
properties should only be set in the plugindef()
function.
The properties are discussed in details on the finaleplugin properties page.
The output of the standard Lua print
function has been redirected to the JW Lua window, so all output from the print
function will show up there:
JW Lua adds some some functions to the global namespace that are specially designed for plug-in script programming. They are listed below:
coll2table()
transforms a PDK Framework collection object into a Lua-style table, so the items can be used with the Lua standard functions for tables (such as the standard table.
library). The resulting table is 1-based (not 0-based as the PDK Framework collections). For example:
-- Load all measure objects allmeasures = finale.FCMeasures() allmeasures:LoadAll() -- Convert to table measuretable = coll2table(allmeasures) -- Sort the measure objects according to their width (widest measure first) table.sort(measuretable, function (a,b) return (a.Width > b.Width) end) -- Print the sorted result for i, v in ipairs(measuretable) do print ("Item", i, "is measure number", v.ItemNo, "with the width", v.Width) end
dumpproperties()
creates a table consisting of all available properties for an object and their values. The keys in the table is the property names; the values in the table are the property values. Use the pairsbykeys()
iterator (see below) to get the properties sorted in alphabetical order.
page = finale.FCPage() page:Load(1) properties = dumpproperties(page) for k, v in pairsbykeys(properties) do print (k, "=", v) end
each()
is the general iterator “factory” for PDK Framework collection objects. It feeds the for
loop with all the elements of the collection:
-- Print the widths for all the pages allpages = finale.FCPages() allpages:LoadAll() for v in each(allpages) do print ("Page", v.ItemNo, "has the width", v.Width) end
eachbackwards()
does the same as the each()
iterator, but parses the elements backwards starting from the end of the collection. It feeds the for
loop with all the elements of the collection. This iterator is available in beta version 0.31 and later.
-- Print the widths for all the pages, starting from the last page allpages = finale.FCPages() allpages:LoadAll() for v in eachbackwards(allpages) do print ("Page", v.ItemNo, "has the width", v.Width) end
eachentry()
feeds a for
loop with all the note entry objects in a region, without saving them back. Mirror entries are processed with eachentry()
.
First parameter to this function is the region to process, where you could use finenv.Region()
to get the current selection.
The second parameter is optional, but can be used to indicate the note entry layer(s) to load in Finale. The default is to load all visible layers. These values are available:
Value | Description |
---|---|
-3 | Don't load any entries. |
-2 | The non-visible layers(s). |
-1 | All layers, regardless if they're visible or not. |
0 | All visible layer(s). This is the default. |
1 through 4 | Load only the one-based layer number. The layer is loaded regardless of the layer visibility. |
A bit mask combination of hex values 0x100, 0x200, 0x400, 0x800 | The layers 1-4 in any combination. |
Example:
counter = 0 for e in eachentry(finenv.Region()) do if e:IsRest() then counter = counter + 1 end end print ("The region contains", counter, "rest entries.")
eachentrysaved()
feeds a for
loop with all the note entry objects in a region and automatically saves the entries back to Finale after processing. Only use this function when the entries actually needs to be saved. It requires the same parameter(s) as eachentry()
(see above). Mirror entries are not processed with eachentrysaved()
.
One other task that can be automatically done with eachentrysaved()
is to delete entries. Just set the duration to 0, and eachentrysaved()
will automatically delete the entry prior to saving it.
-- Let eachentrysaved() delete all rests in the selected region for e in eachentrysaved(finenv.Region()) do if e:IsRest() then e.Duration = 0 end end
Due to the way the TGF frame works, the note entry is not saved directly after each loop turn, but only when a frame has been fully processed.
eachcell()
feeds a for
loop with all the cell coordinates for a region. Partially selected measures are treated as being selected. The first coordinate is the measure, the second is the staff ID. eachcell()
requires a region as the parameter, the easiest way is to refer to finenv.Region()
to get the currently selected region.
Example:
for m, s in eachcell(finenv.Region()) do print ("Measure: ", m, "Staff: ", s) end
loadall()
feeds a for
loop with the elements from a collection, typically where the data hasn't been loaded. The supplied collection must support the LoadAll()
method. This is a useful shortcut when the collection itself isn't important, but rather all the items in the collection.
Example:
for m in loadall(finale.FCMeasures()) do print ("Measure width: ", m.Width) end
loadallforregion()
feeds a for
loop with the elements from a collection, based on a region. The first argument is a created collection, typically without any loaded objects. The second argument is the region.
The supplied collection must support the LoadAllForRegion()
method. This is a useful shortcut when the collection itself isn't important, but rather all the items in the collection.
Example:
local region = finenv.Region() for e in loadallforregion(finale.FCExpressions(), region) do print ("Expression's measure:", e.Measure) end
pairsbykeys()
feeds a for
loop with the table elements, where the elements appear sorted by key. It returns the key/value pair just like Lua's own pairs()
iterator.
Example:
t = {} t["test"] = 12 t["a test"] = 100 for k, v in pairsbykeys(t) do print (k, "=", v) end
The Lua language handles memory through a garbage collection: the memory are handled automatically by Lua. However, there are a couple of points that a plug-in programmer should be aware of.
local
as often as possible. This becomes particularly important when group scripts are used (global variables survives to the subsequent scripts in the group as well), or when using libraries.Create
methods are handled automatically by JW Lua (and not by the Lua interpreter) and those memory objects are released after the full script has been run.Create
methods could sometimes result in a huge number of object waiting for release (for example in a tight loop), Lookup
classes (classes that ends with Lookup
) are available as a faster and more memory-efficient approach.FCCellMetrics
or FCNoteEntryMetrics
class, make sure to call the FreeMetrics()
method separately for the loaded object as soon as the data is no longer is needed. Finale allocate loaded metrics data internally, and metrics with a garbage collector can otherwise lead to problems in scripts where lots of metrics data are loaded without release.nil
. That might benefit the performance in large scripts with huge collections of data (since it signals to the garbage collector that it can free the allocated memory).mymeasure.Width
is equivalent to mymeasure[“Width”]
. This alternative syntax might be useful when referencing properties dynamically through the code.