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 ----