Here are some complete scripts that should get you you a quick start to [[jwlua:development|programming with JW Lua]]. The [[jwlua:fullscripts|full scripts page]] also contains some more extensive, fully-featured scripts.
==== Resize Measures in a Region ====
This script will resize the measures in the selected region by the percent (given on the first script line).
resizepercent = 90
region = finale.FCMusicRegion()
region:SetCurrentSelection()
selectedmeasures = finale.FCMeasures()
selectedmeasures:LoadRegion(region)
for measure in each(selectedmeasures) do
measure.Width = measure.Width * resizepercent / 100
end
selectedmeasures:SaveAll()
Detailed explanation:
* Line 1: Sets the variable ''resizepercent'' to 90
* Line 2: Creates a region object (of the ''FCMusicRegion'' class) and assigns it to the variable ''region''. The creation of objects always use a dot as separator.
* Line 3: Gets the currently selected region for the region object. Since it's a method (or function), the separator is a colon.
* Line 4: Creates a measure collection object (of the ''FCMeasures'' class) and assigns it to the variable ''selectedmeasures''. Again, using the dot as a separator.
* Line 5: Loads the measures in the selection to the measure collection object. Colon as separator, since it's a method call.
* Line 6: Starts looping through all the loaded measures, using the special ''each()'' function. For each loop instance, the variable ''measure'' will contain a measure object (of the ''FCMeasure'' class, note the singular naming here!).
* Line 7: Sets the ''Width'' property of the measure, based on the earlier width value. Since its a property, a dot is used as the separator. (An equivalent to ''measure.Width = x'' would be to use the method call ''measure:SetWidth(x)'', now with the colon.)
* Line 8: The end closure of the loop (that started at line 6)
* Line 9: Saves all the measures in the measure collection object. (Using a colon, since it's a method.)
----
==== Assure Page Size Consistency ====
The script below will assure that the width, height and resize settings are consistent for all pages in each part. It scans through every part in the document (including the score view) and sets all subsequent pages to the first page settings. The script doesn't touch the margin settings, and it will allow for different page sizes for each part.
For this script, please note that the ''onepart:SwitchTo()'' call (which sets the internal editing focus to another part) **must** be followed by a subsequent ''onepart:SwitchBack()'' call (to return the editing focus to the visible part).
allparts = finale.FCParts()
allparts:LoadAll()
for onepart in each (allparts) do
onepart:SwitchTo()
pageone = finale.FCPage()
pageone:Load(1)
allpages = finale.FCPages()
allpages:LoadAll()
for apage in each(allpages) do
if apage.ItemNo > 1 then
apage.Width = pageone.Width
apage.Height = pageone.Height
apage.Percent = pageone.Percent
apage.HoldMargins = pageone.HoldMargins
apage:Save()
end
end
onepart:SwitchBack()
end
----
==== Find all Double Barlines ====
This script will report all measures in the document that has a double barline:
allmeasures = finale.FCMeasures()
allmeasures:LoadAll()
for measure in each(allmeasures) do
if measure.Barline == finale.BARLINE_DOUBLE then
print ("Double barline found in measure ", measure.ItemNo)
end
end
Technical note: in the PDK Framework documentation, you'll find the ''BARLINE_DOUBLE'' constant within the ''FCMeasure'' scope. However, when using //JW Lua//, all such constants are in the ''finale'' namespace.
A sample output from this script could be found at the bottom of this [very early beta] screen shot:
{{:wiki:jwlua:jwlua_beta0_01-output.png |}}
----
==== Single Pitch ====
This script turns note entries into a single pitch, very much like in Finale's built-in plug-in:
pitchstring = finale.FCString()
pitchstring.LuaString = "A#5"
writtenpitch = true
for e in eachentrysaved(finenv.Region()) do
if e:IsNote() then
-- Quick trick to assure single notes at chords
e:MakeRest()
e:MakeNote()
-- Set the pitch of the single note
e:GetItemAt(0):SetString(pitchstring, nil, writtenpitch)
e.CheckAccidentals = true -- Assure proper accidental refresh
end
end
A couple of notes for this script:
* The lines ''pitchstring = finale.FCString()'' and ''pitchstring.LuaString = "A#5"'' will create a PDK Framework string object (which is what PDK Framework usually uses for strings) and assign it to the string ''A#5''.
* The line ''writtenpitch = true'' can be changed to ''writtenpitch = false'' to set the sounding pitch instead.
* The ''for ... do'' loop is producing entries with the ''eachentrysaved()'' iterator. ''eachentrysaved()'' needs (at least) a region as parameter, and the built-in //JW Lua// shortcut ''finenv.Region()'' is used to get the currently selected region. ''eachentrysaved()'' will automatically save the entry object back to Finale after processing.
* Using the ''MakeRest()'' method for a note entry will remove all notes from a chord, when creating a rest. Calling the ''MakeNote()'' method afterwards will assure that one note exist on a note entry. So, using these calls in succession will turn all chords into single notes (each note entry will also be reset to middle C pitch as a result).
* The line ''e:GetItemAt(0):SetString(pitchstring, nil, writtenpitch)'' will do 2 things in succession. First, it will recieve the first note object (of the FCNote class, and PDK Framework uses 0-based indexes for all collections) from a chord, and since we are sure that there will be at least one note in the entry, there's no need for error checking the return value. The second part is to set the returned note's pitch as a string (the second parameter in the call is using ''nil'', which signals to automatically use the key signature found at the entry).
* Setting the ''CheckAccidentals'' property is needed everytime the pitch of a note entry frame is changed in Finale. (This will make Finale refresh the displayed accidentals for the note entry frame.) Otherwise, accidentals might be changed internally in Finale without displaying visually in the document.
----
==== String Harmonics ====
Here's a script that will create visual string harmonics on all perfect 4ths in the selected region. One difference compared to Finale's built-in solution is that it makes the diamond notehead a little larger to better match the normal notehead.
for entry in eachentrysaved(finenv.Region()) do
-- Only process 2-note chords
if (entry.Count ~= 2) then goto continue end
-- Only process (enharmonically) perfect 4ths
local highestnote = entry:CalcHighestNote(nil)
local lowestnote = entry:CalcLowestNote(nil)
local mididiff = highestnote:CalcMIDIKey() - lowestnote:CalcMIDIKey()
if mididiff ~= 5 then goto continue end
-- Create a notehead modification object
local notehead = finale.FCNoteheadMod()
-- Remove any old notehead data for this entry
notehead:EraseAt(lowestnote)
notehead:EraseAt(highestnote)
-- Create a diamond character that is 110% of the default size
notehead.CustomChar = 79
notehead.Resize = 110
notehead:SaveAt(highestnote)
-- Jump label here:
::continue::
end
One specific note about the script above is that it uses ''eachentrysaved'' (**not** ''eachentry'') as the entry iterator. Since the entry contains specific flags about which kind of alterations records that should load for the entry, these flags must be set accordingly. When the modification record is linked back to an entry that is later saved through the iterator, all this will be taken care of automatically.
The script above will play back correctly when using //Human Playback// on a staff defined for strings. For non-HP documents, you might want to expand the script further. The ''FCPerformanceMod'' class provides playback modifications for each notehead, and the ''FCNote.Playback'' property can mute single notes (when HP isn't used).
----
==== Change Hairpin Opening Size ====
This script will change the hairpin opening size (for the selected region's hairpins) to a smaller one. The script uses an opening size 24 EVPUs.
local marks = finale.FCSmartShapeMeasureMarks()
-- Load all smart shape marks within the region and remove duplicate references
marks:LoadAllForRegion(finenv.Region(), true)
for mark in each(marks) do
-- Make a smart shape object (of the FCSmartShape class) for the mark
local smartshape = mark:CreateSmartShape()
if smartshape:IsHairpin() then
-- Get the FCSmartShapeCtrlPointAdjust sub object
-- (which is part of the FCSmartShape)
local adjust = smartshape:GetCtrlPointAdjust()
-- Set the hairpin opening width to 24 EVPUs
adjust.ControlPoint1OffsetY = 24
adjust.CustomShaped = true
-- Save the smart shape change
smartshape:Save()
end
end
Since smart shapes can span multiple measures and cross to other staves, a smart shape occurrence in a measure is marked with a ''FCSmartShapeMeasureMark'' object. Each such object points to a "master" smart shape object of the ''FCSmartShape'' class. If marks are loaded from multiple measures of a document, it's likely that there will be marks that points to the same smart shape. The methods in ''FCSmartShapeMeasureMarks'' can remove any duplicate smart shape references. In this example, removal of duplicate references are made by using ''true'' as the second parameter to ''marks:LoadAllForRegion''.
The ''FCSmartShape'' class is huge in size and functionality. In //JW Lua//, parts of the class functionality is accessed by sub object classes. In the example above, ''smartshape:GetCtrlPointAdjust()'' is called to access the ''FCSmartShapeCtrlPointAdjust '' sub object for control point adjustments.
----
==== Text Block Line Spacing ====
This script will change the line spacing in all "title" page text blocks to 90%.
The line spacing is located in the ''FCTextBlock'' class, which is a separate object that is linked to the page text.
-- Load all page-attached texts
pagetexts = finale.FCPageTexts()
pagetexts:LoadAll()
for pagetext in each(pagetexts) do
-- Create the text string
local str = pagetext:CreateTextString()
-- Check if the title text insert exists in the string
if str:ContainsLuaString("^title()") then
-- Create (and load) the connected FCTextBlock data
local textblock = pagetext:CreateTextBlock()
-- Set the line spacing to 90 % and save
textblock.LineSpacingIsPercent = true
textblock.LineSpacing = 90
textblock:Save()
end
end
----
==== Set the File Info text ====
This script sets the composer in the File Info to "My Name".
-- Create a FCFileInfoText object
local text = finale.FCFileInfoText()
-- Create a FCString object
local str = finale.FCString()
str.LuaString = "My Name"
text:SetText(str)
-- Save the object as a composer text insert
text:SaveAsComposer()
----
==== Note Duration Statistics ====
The following script counts how many occurrences each duration has within the selected region. Rests are not counted.
The script relies heavily on Lua's powerful table feature (used both for counting, presenting the sorted result, and as a lookup table for strings), and the fact that any key that hasn't been defined in a table is ''nil''.
-- Set up an empty table for duration statistics
local durationstatistics = {}
for e in eachentry(finenv.Region()) do
if e:IsNote() then
if durationstatistics[e.Duration] == nil then
-- No statistics exists for this duration - create a counter
durationstatistics[e.Duration] = 1
else
-- Increase the counter for this duration
durationstatistics[e.Duration] = durationstatistics[e.Duration] + 1
end
end
end
-- A look-up table for durations.
-- Add other values to the table if necessary (can be added as number values as well).
-- Quarter Note is 1024, Dotted Quarter Note is 1536, etc
local durationtable =
{
[finale.EIGHTH_NOTE] = "8th Notes",
[finale.QUARTER_NOTE] = "Quarter Notes",
[finale.HALF_NOTE] = "Half Notes",
[finale.WHOLE_NOTE] = "Hole Notes"
}
-- Report the statistics sorted by duration
for key, value in pairsbykeys(durationstatistics) do
if durationtable[key] == nil then
-- Not a duration with a known "text version" - output EDU value instead
print("EDU duration", key, ":", value)
else
print(durationtable[key], ":", value)
end
end
----
==== Change Tuplets from Preferences ====
The following script will set the tuplets in the region to use the number and shape style from the tuplet preferences (in the Document Options).
For "standard" preferences (where there aren't multiple instances of the preference data), load them with 1.
-- Load the tuplet preferences
local tupletprefs = finale.FCTupletPrefs()
tupletprefs:Load(1)
for e in eachentry(finenv.Region()) do
-- Speed/memory optimization - only load tuplets if
-- the "tuplet start flag" is set for the entry.
if e.TupletStartFlag then
-- Load all tuplets that starts on the note entry
local tuplets = e:CreateTuplets()
for tuplet in each(tuplets) do
-- Change number/shape styles to what's in the preferences:
tuplet.ShapeStyle = tupletprefs.ShapeStyle
tuplet.NumberStyle = tupletprefs.NumberStyle
-- Save tuplet
tuplet:Save()
end
end
end
----
==== Append Note Entry ====
This script appends a note entry of a specific pitch to the first measure and staff of the current selection.
-- Load existing notes in the cell
local region = finenv.Region()
notecell = finale.FCNoteEntryCell(region.StartMeasure, region.StartStaff)
notecell:Load()
-- Append to layer 1, add 1 entry
entry = notecell:AppendEntriesInLayer(1, 1)
if entry then
entry.Duration = finale.QUARTER_NOTE
entry.Legality = true -- Must be set for a created note
entry:MakeNote()
local note = entry:GetItemAt(0)
-- Assign a specific pitch to the note
local pitch = finale.FCString()
pitch.LuaString = "Bb4"
-- Set pitch, using the note entry measure's key signature.
-- Also use sounding pitch.
note:SetString(pitch, nil, false)
notecell:Save()
end
----
==== Create Page Text in One Part Only ====
This script creates a page text that's only visible in the part/score where it was created.
To unlink page text data in Finale, the data first needs to be saved. After that, that same object needs to be re-saved again for each part (including the score) - but with modified ''Visible'' property when needed.
To save to a specific part, use the ''SwitchTo'' and ''SwitchBack'' method for a part. These methods **must** be used in pairs, so the editing focus returns to the original state.
-- Create a FCString with the raw Enigma string
local stringobject = finale.FCString()
stringobject.LuaString = "^font(Times New Roman)^size(12)^nfx(0)My Text"
-- Create a page text object
-- Set it up to top/center on the first page
local pagetext = finale.FCPageText()
pagetext.HorizontalAlignment = finale.TEXTHORIZALIGN_CENTER
pagetext.VerticalAlignment = finale.TEXTVERTALIGN_TOP
pagetext.HorizontalPos = 0
pagetext.VerticalPos = 0
pagetext.Visible = true
pagetext.FirstPage = 1
pagetext.LastPage = 1
-- Save the raw text and text block first
pagetext:SaveNewTextBlock(stringobject)
-- Save the page text to the page 1 storage
pagetext:SaveNew(1)
-- Load all parts
local allparts = finale.FCParts()
allparts:LoadAll()
-- Parse through all parts and RESAVE the existing page text object
for part in each(allparts) do
-- Page text will only be visible in the part/score where it was created
pagetext.Visible = part:IsCurrent()
part:SwitchTo()
pagetext:Save()
part:SwitchBack()
end
----
==== Search for text expressions ====
This script searches for a substring among the text expression definitions and displays the expressions to the user (using Finale's own dialog), one by one. As long as the user cancels the dialog box, the script will continue to display the next expression def found in the list. If the user hits Assign, the script will stop.
The script consists of 4 parts:
- Display the search dialog
- Load the text expression definitions
- Build the match table
- Display the Expression Definition dialog to the user
The ''FCUI:DisplayTextExpressionDialog()'' method displays the ''ItemNo'' of a text expression definition.
Please note that when a substring is matched, all Enigma command tags are first removed with ''FCString:TrimEnigmaTags()''. If it wouldn't do that, the script would also match "on" on a "^font()" Enigma tag, for example.
-- Display user dialog to get the search text
local dialog = finenv.UserValueInput()
dialog.Title = "Enter Search String"
dialog:SetTypes("String")
dialog:SetDescriptions("Text expression search string")
local results = dialog:Execute()
if not results then return end
if results[1] == "" then return end
-- Load all the text expression defs
local exprdefs = finale.FCTextExpressionDefs()
exprdefs:LoadAll()
-- Init a table for all the found expressions
local foundexpressions = {}
-- Search all text expression defs
for exprdef in each(exprdefs) do
local exprstring = exprdef:CreateTextString()
exprstring:TrimEnigmaTags()
if exprstring:ContainsLuaString(results[1]) then
-- Add matching text expression def to table
table.insert(foundexpressions, exprdef)
end
end
-- Display Finale's expression selection dialog box
-- for each found expression.
-- Continue if the user cancels the dialog box.
for k, v in pairs(foundexpressions) do
if finenv.UI():DisplayTextExpressionDialog(v.ItemNo) ~= 0 then
-- User confirmed the dialog - exit
return
end
end
----
==== Swap Staves ====
This script swaps the contents between the top and bottom staff in the selected region, by using Finale's copy/paste mechanism (using clip files). Finale's Edit filters are not used for these methods.
-- Get the selected region
local region = finenv.Region()
-- Get the top and bottom staff
local topstaff = region.StartStaff
local bottomstaff = region.EndStaff
-- Make sure that the swap can take place
if topstaff < 1 then return end
if bottomstaff < 1 then return end
if topstaff == bottomstaff then return end
-- Create a top region
local topregion = finale.FCMusicRegion()
topregion:SetRegion(region)
topregion.EndStaff = topstaff
-- Create a bottom region
local bottomregion = finale.FCMusicRegion()
bottomregion:SetRegion(region)
bottomregion.StartStaff = bottomstaff
-- Copy the music to clip files
topregion:CopyMusic()
bottomregion:CopyMusic()
-- Paste top contents to the bottom staff
topregion.StartStaff = bottomstaff
topregion.EndStaff = bottomstaff
topregion:PasteMusic()
-- Paste bottom contents to the top staff
bottomregion.StartStaff = topstaff
bottomregion.EndStaff = topstaff
bottomregion:PasteMusic()
-- Release the clip files
topregion:ReleaseMusic()
bottomregion:ReleaseMusic()
-- Make sure the original region is visually restored
region:SetInDocument()
----
==== Staff Range ====
This script searches through the selection and presents the highest and lowest note for each staff. It uses 4 different Lua tables (named ''lowestnotes'', ''highestnotes'' for the MIDI key numbers and ''lowestnotestrings'', ''highestnotestrings'' for the pitch string representations), where each table use the staff number as the index. If the index (staff number) doesn't appear in a table, there is no statistical information about the staff.
The method used to verify the highest/lowest note is by using the enharmonic MIDI key number.
Since ''pitchstring'' is one single object through the whole processing (which saves memory and is faster), the tables with pitch strings stores the Lua string versions.
-- Use sounding pitch
local written_pitch = false
-- Table with highest/lowest MIDI notes for each staff
local lowestnotes = {}
local highestnotes = {}
-- Tables with the pitch strings for highest/lowest notes
local lowestnotestrings = {}
local highestnotestrings = {}
local region = finenv.Region()
-- Use a single object for the string pitch result (=faster)
local pitchstring = finale.FCString()
for entry in eachentry(region) do
if entry:IsNote() then
-- Get the staff number where the entry is placed
local staffnumber = entry.Staff
-- Parse through all notes in the note entry (=chord)
for note in each(entry) do
-- Use the enharmonic MIDI note to check the pitch
local midikeynumber = note:CalcMIDIKey()
-- Fill the FCString object that represent the pitch string
note:GetString (pitchstring, nil, false, written_pitch)
-- See if the note is lowest on the staff
if not lowestnotes[staffnumber] or lowestnotes[staffnumber] > midikeynumber then
lowestnotes[staffnumber] = midikeynumber
lowestnotestrings[staffnumber] = pitchstring.LuaString
end
-- See if the note is the highest on the staff
if not highestnotes[staffnumber] or highestnotes[staffnumber] < midikeynumber then
highestnotes[staffnumber] = midikeynumber
highestnotestrings[staffnumber] = pitchstring.LuaString
end
end
end
end
-- Statistics output
for k, v in pairs(lowestnotes) do
-- Construct a staff name to display
local staff = finale.FCStaff()
staff:Load(k)
local namestr = staff:CreateDisplayFullNameString()
local fullstaffname = namestr.LuaString
if fullstaffname == "" then
fullstaffname = "#" .. k
end
-- Display the statistics for a staff
print (fullstaffname, "- lowest note", lowestnotestrings[k], " highest note", highestnotestrings[k])
end
----
==== Add a Staff Style ====
This example adds a staff style to the staff ID 1 in the document, between measures 3 and 5. It assumes that the staff ID 1 exists in the document and it also assumes that staff style definition ID 2 exists.
(The current betas can't yet get the staff style definitions and their settings, that will come in a later beta).
local ssa = finale.FCStaffStyleAssign()
ssa.StyleID = 2 -- Document-specific staff style definition ID
ssa.StartMeasure = 3
ssa.StartMeasurePos = 0
ssa.EndMeasure = 5
ssa:SetEndMeasurePosRight()
ssa:SaveNew(1) -- Save to staff ID 1
----
==== Add a Staff ====
This script adds a staff to the current document and changes it to "Violin" in the Score Manager. It does not set the playback info.
local staffID = finale.FCStaves.Append()
if staffID then
-- Load the created staff
local staff = finale.FCStaff()
staff:Load(staffID)
-- Set the virtual instrument (in the Score Manager) to violin:
staff.InstrumentUUID = finale.FFUUID_VIOLIN
staff:Save()
end
----
==== Hide Articulations ====
The script below hides all articulations in the selected region. It requires Finale 2014b or later.
for e in eachentry(finenv.Region()) do
local artics = e:CreateArticulations()
for a in each(artics) do
a.Visible = false
a:Save()
end
end
----
==== Double Precision Time Signatures ====
This script changes the time signature to the "double precision" for the whole document. For example, a 2/4 measure becomes 4/8.
local measures = finale.FCMeasures()
measures:LoadAll()
for m in each(measures) do
local timesig = m.TimeSignature
timesig.Beats = timesig.Beats * 2
timesig.BeatDuration = timesig.BeatDuration / 2
m:Save()
end
----
==== Open File Names ====
This sample script displays the operating system's dialog box for file name selection and prints all file names that were selected by the user.
Please note that the initialization of the ''FCFileOpenDialog'' require a ''FCUI'' object. Use ''finenv.UI()'' to get the global UI object, as in the example.
This script requires beta version 0.22 or later.
-- Create the dialog object
local dialog = finale.FCFileOpenDialog(finenv.UI())
-- Set the window title
local windowtitle = finale.FCString()
windowtitle.LuaString = "Open some files"
dialog:SetWindowTitle(windowtitle)
-- Set the filter
local filter = finale.FCString()
filter.LuaString = "*.txt"
local filterdescr = finale.FCString()
filterdescr.LuaString = "Text Files"
dialog:AddFilter(filter, filterdescr)
-- Allow multiple file selection
dialog.MultiSelect = true
-- Display the dialog to the user
if dialog:Execute() then
-- Get the names of the selected files and display the names
local filenames = finale.FCStrings()
dialog:GetFileNames(filenames)
for fname in each(filenames) do
print (fname.LuaString)
end
end
----
==== Break Secondary Beams ====
The following script (which requires beta version 0.22 or later) breaks secondary beams. The ''min_beamed_duration'' controls the largest note entry duration that should break. The ''duration_alignment'' controls how often the secondary beam breaks should appear. The reason the script verifies the note entries against the double ''min_beamed_duration'' value, is because dotted durations are also allowed durations.
Please note that the script uses the ''eachentrysaved'' iterator (rather than ''eachentry''). This is required since the entries needs to resave to attach correctly to new created data.
In this example, the secondary beams break all the way to the eight note (primary) beam.
local min_beamed_duration = finale.THIRTYSECOND_NOTE
local duration_alignment = finale.EIGHTH_NOTE
-- This function returns true if the secondary beams should break
-- before the note entry
function IsBreakableEntry(entry)
-- Check that the current entry is valid for secondary beam breaks:
if entry.BeamBeat then return false end
if entry:IsRest() then return false end
if entry.Duration >= min_beamed_duration * 2 then return false end
-- Check that the previous note entry is valid for secondary beam breaks:
local previousentry = entry:Previous()
if not previousentry then return false end
if previousentry:IsRest() then return false end
if previousentry.Duration >= min_beamed_duration * 2 then return false end
-- Return true if the measure position is on eight note boundary
return ((entry.MeasurePos % duration_alignment) == 0)
end
for noteentry in eachentrysaved(finenv.Region()) do
local sbbm = finale.FCSecondaryBeamBreakMod()
sbbm:SetNoteEntry(noteentry)
if IsBreakableEntry(noteentry) then
local loaded = sbbm:LoadFirst()
sbbm:SetBreakAll(true)
if loaded then
-- Save existing data
sbbm:Save()
else
-- Create new data
sbbm:SaveNew()
end
end
end
----
==== Mute Selected Staves ====
This script mutes the note layers in the selected staves and unmutes the note layers outside the selected staves. It uses the FCStaff object to get the FCInstrumentPlaybackData, where the instrument data for the Score Manager is stored.
The script requires beta 0.22 or later.
-- Set the full document as a region
local fulldocregion = finale.FCMusicRegion()
fulldocregion:SetFullDocument()
-- Get the selected region
local region = finenv.Region()
-- Loop through all staves in the full document
for slot = fulldocregion.StartSlot, fulldocregion.EndSlot do
-- Convert from slot number to staff number/ID
local staffnumber = region:CalcStaffNumber(slot)
-- Load staff object
local staff = finale.FCStaff()
staff:Load(staffnumber)
-- Create a FCInstrumentPlaybackData object for the staff
local playbackdata = staff:CreateInstrumentPlaybackData()
-- Go through all 4 layers and process the note layer
for layer = 1, 4 do
local layerdef = playbackdata:GetNoteLayerData(layer)
-- Mute in region, play outside region
layerdef.Play = not region:IsStaffIncluded(staffnumber)
end
-- Save the playback settings
playbackdata:Save()
end
----
==== Measurement Units ====
Sometimes it's convenient to use a specific measurement in a script. However, since Finale only uses EVPUs internally, all other kinds of units need to be converted to EVPUs. The ''FCString:GetMeasurement()'' method can convert a string to EVPUs, so one solution could be a wrapper function, like in the example below.
The string conversion supports the same measurement suffixes that Finale supports (''e'' for EVPUs, ''c'' for centimeters, ''i'' for inches, etc).
This code sample would set the page width to 20 centimeters for all pages in the current score/part view.
If the string sent to the ''ToEvpus()'' function has no suffix, the current default measurement will be assumed.
function ToEvpus(text)
local str = finale.FCString()
str.LuaString = text
return str:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT)
end
for p in loadall(finale.FCPages()) do
p.Width = ToEvpus("20c")
p:Save()
end
----
==== Move TAB Numbers ====
This sample moves the tablature (TAB) numbers from one string to another string (while keeping the fret numbers) in the current selection. A dialog box appears where the user can specify the number of strings to move the numbers. A negative number (such as ''-1'') would result in an upwards movement.
To keep the fret number between strings, two steps are required: transposition of the note and moving the note to another string. The ''eachentrysaved()'' iterator is used (rather than ''eachentry()'') to resave the transposed note.
This script requires beta version 0.26 or later.
-- Show dialog box
local dialog = finenv.UserValueInput()
dialog.Title = "Move TAB Numbers"
dialog:SetTypes("Number")
dialog:SetDescriptions("Move x strings downwards:")
dialog:SetInitValues(1)
local dlgresult = dialog:Execute()
if not dlgresult then return end
local stringadd = dlgresult[1]
-- Browse through the note entries in the selection:
local tabinstrument = finale.FCFretInstrumentDef()
for entry in eachentrysaved(finenv.Region()) do
local staffspec = finale.FCCurrentStaffSpec()
if staffspec:LoadForEntry(entry) and staffspec:IsTablature() then
if tabinstrument:Load(staffspec.FretInstrumentDefID) then
for note in each(entry) do
local mod = finale.FCTablatureNoteMod()
if mod:LoadAt(note) then
-- Calculated the MIDI offset difference between the strings:
local originalstring = tabinstrument:GetStringTuning(mod.StringNumber)
local otherstring = tabinstrument:GetStringTuning(mod.StringNumber + stringadd)
local midioffset = originalstring - otherstring
-- Transpose:
local newmidipitch = note:CalcMIDIKey() - midioffset
note:SetMIDIKey(newmidipitch)
-- Change the string:
mod.StringNumber = mod.StringNumber + stringadd
-- Save the new string number:
mod:Save()
end
end
end
end
end
----
==== Using Unicode ====
JW Lua supports Unicode for Finale 2012 and above (as long as the font supports it). Umlauts, cyrillic text, Japanese characters, etc can be mixed with traditional western characters.
This sample script sets the composer text insert to include both a cyrillic and western version of the composer's name.
This script requires beta 0.27 or later.
-- Assure that the script exits directly on pre-2012 Finale versions:
if finenv.FinaleVersion < 2012 then return end
--Set composer text to use both cyrillic and western version
-- of the composer's name. Put a new line character between.
local str = finale.FCString()
str.LuaString = "Сергей Прокофьев\r(Sergei Prokofiev)"
-- Set an save the text insert:
local fileinfotext = finale.FCFileInfoText()
fileinfotext:SetText(str)
fileinfotext:SaveAsComposer()
----
==== Align Repeat Brackets ====
The following script example aligns the top lines of the repeat brackets within the selected region to the topmost position. It does not look at staves with independently moved bracket positioning.
All ending repeat brackets and backward repeat brackets are first scanned for the highest vertical position. The initial "highest position" is set to a extremely low value, to make sure that a true top position is found. After that, all the brackets are set to the highest vertical position found.
The script requires beta 0.29 or higher.
local region = finenv.Region()
if region:IsEmpty() then return end
-- Scan to find the highest bracket position in the selected measures:
local highest = -10000
local count = 0
for m = region.StartMeasure, region.EndMeasure do
local endingrepeat = finale.FCEndingRepeat()
local backwardrepeat = finale.FCBackwardRepeat()
if endingrepeat:Load(m) then
if endingrepeat.VerticalTopBracketPosition > highest then
highest = endingrepeat.VerticalTopBracketPosition
end
count = count + 1
end
if backwardrepeat:Load(m) then
if backwardrepeat.TopBracketPosition > highest then
highest = backwardrepeat.TopBracketPosition
end
count = count + 1
end
end
if count > 1 then
-- Set the new bracket positions:
for m = region.StartMeasure, region.EndMeasure do
local endingrepeat = finale.FCEndingRepeat()
local backwardrepeat = finale.FCBackwardRepeat()
if endingrepeat:Load(m) then
endingrepeat.VerticalTopBracketPosition = highest
endingrepeat:Save()
end
if backwardrepeat:Load(m) then
backwardrepeat.TopBracketPosition = highest
backwardrepeat:Save()
end
end
end
----
==== Swap Metatool Key Assignment ====
The following script swaps the expression metatool keyboard assignments for the keys '1' and '2'. Metatools are saved to the (ASCII) key number, and the Lua ''string.byte()'' function is used to find the key number. Simply resaving to the other key number will do a swap of the metatool keys. Beta version 0.40 or later is required for this sample code.
-- Load all existing metatool assignments for the Expression Tool
local mas = finale.FCMetatoolAssignments()
mas:LoadAllForMode(finale.MTOOLMODE_EXPRESSION)
-- Find the current expression metatools for keyboard keys '1' and '2'
metatool1 = mas:FindKeystroke(string.byte("1"))
metatool2 = mas:FindKeystroke(string.byte("2"))
-- Swap the keyboard mapping for these 2 objects
if (metatool1 ~= nil) then
metatool1:SaveAsKeystroke(string.byte("2"))
end
if (metatool2 ~= nil) then
metatool2:SaveAsKeystroke(string.byte("1"))
end
----