Skip to content

Commit 60da1b9

Browse files
committed
Update dynamic_levels.lua
Now works on "hidden" dynamics as well as visible ones. The choice to enable creation of new dynamic expressions is added to dialog and preserved. Other minor tweaks.
1 parent bde59d0 commit 60da1b9

File tree

1 file changed

+70
-41
lines changed

1 file changed

+70
-41
lines changed

src/dynamic_levels.lua

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,29 @@ function plugindef()
44
finaleplugin.Author = "Carl Vine"
55
finaleplugin.AuthorURL = "https://carlvine.com/lua"
66
finaleplugin.Copyright = "https://creativecommons.org/licenses/by/4.0/"
7-
finaleplugin.Version = "0.09"
8-
finaleplugin.Date = "2024/06/19"
7+
finaleplugin.Version = "0.10"
8+
finaleplugin.Date = "2024/07/17"
99
finaleplugin.MinJWLuaVersion = 0.70
1010
finaleplugin.Notes = [[
11-
Make dynamic marks in the selection louder or softer by stages.
11+
Make dynamic marks in the selection louder or softer in stages.
1212
This functionality is buried within __JWChange__ but is useful
13-
and I thought was worth bringing nearer the surface.
13+
enough to bring closer to the surface.
1414
This script works similarly but allows jumping up to 9 _levels_ at once.
1515
The dynamic range is from __pppppp__ to __ffffff__, though scores using
16-
older (non-_SMuFL_) fonts are restricted to the range __pppp__-__ffff__.
16+
older (non-__SMuFL__) fonts are restricted to the range __pppp__-__ffff__.
1717
1818
To repeat the previous level shift without a confirmation dialog
19-
hold down [Shift] when starting the script.
19+
hold down [_Shift_] when starting the script.
2020
]]
2121
return "Dynamic Levels...",
2222
"Dynamic Levels",
2323
"Make dynamic marks in the selection louder or softer by stages"
2424
end
2525

2626
local hotkey = { -- customise hotkeys (lowercase only)
27-
direction = "z", -- toggle Louder/Softer
28-
show_info = "q",
27+
direction = "z", -- toggle Louder/Softer
28+
create_new = "e", -- toggle create_new
29+
show_info = "q",
2930
}
3031
local config = {
3132
direction = 0, -- 0 == "Louder", 1 = "Softer"
@@ -78,7 +79,7 @@ local function get_staff_name(staff_num)
7879
return str
7980
end
8081

81-
local function track_selection()
82+
local function set_bounds()
8283
local bounds = { -- primary region selection boundaries
8384
"StartStaff", "StartMeasure", "StartMeasurePos",
8485
"EndStaff", "EndMeasure", "EndMeasurePos",
@@ -102,18 +103,20 @@ local function track_selection()
102103
end
103104

104105
local function create_dynamics_alert(dialog)
105-
local msg = "Do you want this script to create "
106-
.. "additional dynamic expressions as required? "
107-
.. "(A positive reply will be saved and used if this question arises again)."
106+
local msg = "The required replacement dynamic doesn't exist in this file. "
107+
.. "Do you want this script to create "
108+
.. "additional dynamic expressions as required? "
109+
.. "(You can change this decision later in the dialog window.)"
108110
local ui = dialog and dialog:CreateChildUI() or finenv.UI()
109111
return ui:AlertYesNo(msg, name) == finale.YESRETURN
110112
end
111113

112-
local function create_dyn_def(expression_text)
114+
local function create_dynamic_def(expression_text, hidden)
113115
local cat_def = finale.FCCategoryDef()
114116
cat_def:Load(1) -- default "DYNAMIC" category
115117
local finfo = finale.FCFontInfo()
116118
cat_def:GetMusicFontInfo(finfo)
119+
finfo.EnigmaStyles = finale["ENIGMASTYLE_" .. (hidden and "HIDDEN" or "PLAIN")]
117120
local str = finale.FCString()
118121
str.LuaString = "^fontMus"
119122
.. finfo:CreateEnigmaString(finale.FCString()).LuaString
@@ -127,58 +130,75 @@ local function create_dyn_def(expression_text)
127130
return ted:GetItemNo() -- save new item number
128131
end
129132

133+
local function is_hidden_exp(exp_def)
134+
local str = exp_def:CreateTextString()
135+
return str:CreateLastFontInfo().Hidden
136+
end
137+
130138
local function change_dynamics(dialog)
131139
if finenv.Region():IsEmpty() then
132140
local ui = dialog and dialog:CreateChildUI() or finenv.UI()
133141
ui:AlertError("Please select some music\nbefore running this script", name)
134142
return
135143
end
136-
local found = {} -- collate matched dynamic expressions
137-
local match_count = 0
144+
local found = { show = {}, hide = {} } -- collate matched dynamic expressions
145+
local match_count = { show = 0, hide = 0 }
138146
local shift = config.levels -- how many dynamic levels to move?
139147
if config.direction == 1 then shift = -shift end -- softer not louder
140148
local dyn_len = library.is_font_smufl_font() and 3 or 2 -- dynamic max string length
141-
-- match all target dynamics from existing expressions
142-
local exp_defs = mixin.FCMTextExpressionDefs()
143-
exp_defs:LoadAll()
144-
for exp_def in each(exp_defs) do
145-
if exp_def.CategoryID == 1 and exp_def.UseCategoryFont then -- "standard" dynamic?
146-
local str = exp_def:CreateTextString()
147-
str:TrimEnigmaTags()
148-
if str.LuaString:len() <= dyn_len then -- within max dynamic length
149-
for i, v in ipairs(dyn_char) do -- check all dynamic glyphs
150-
if not found[i] and str.LuaString == utf8.char(v) then
151-
found[i] = exp_def.ItemNo -- matched char
152-
match_count = match_count + 1
149+
150+
-- match all target dynamics from existing expressions
151+
local function match_dynamics(hidden) -- hidden is true or false
152+
local mode = hidden and "hide" or "show"
153+
local exp_defs = mixin.FCMTextExpressionDefs()
154+
exp_defs:LoadAll()
155+
for exp_def in each(exp_defs) do
156+
if exp_def.CategoryID == 1 and hidden == is_hidden_exp(exp_def) then
157+
local str = exp_def:CreateTextString()
158+
str:TrimEnigmaTags()
159+
if str.LuaString:len() <= dyn_len then -- within max dynamic length
160+
for i, v in ipairs(dyn_char) do -- check all dynamic glyphs
161+
if not found[mode][i] and str.LuaString == utf8.char(v) then
162+
found[mode][i] = exp_def.ItemNo -- matched char
163+
match_count[mode] = match_count[mode] + 1
164+
end
165+
end
153166
end
154167
end
168+
if match_count[mode] >= #dyn_char then break end -- all collected
155169
end
156170
end
157-
if match_count >= #dyn_char then break end -- all collected
158-
end
171+
match_dynamics(true)
172+
match_dynamics(false)
159173
-- scan the selection for dynamics and change them
160-
finenv.StartNewUndoBlock(string.format("%s %s%d %s", name,
174+
finenv.StartNewUndoBlock(string.format("Dynamics %s%d %s",
161175
(config.direction == 0 and "+" or "-"), config.levels, selection)
162176
)
163177
for e in loadallforregion(mixin.FCMExpressions(), finenv.Region()) do
164178
if expression.is_dynamic(e) then
165179
local exp_def = e:CreateTextExpressionDef()
166-
if exp_def and exp_def.UseCategoryFont then -- "standard" dynamic?
180+
if exp_def then
181+
local hidden = is_hidden_exp(exp_def)
182+
local mode = hidden and "hide" or "show"
167183
local str = exp_def:CreateTextString()
168184
str:TrimEnigmaTags()
169185
if str.LuaString:len() <= dyn_len then -- dynamic length
170186
for i, v in ipairs(dyn_char) do -- look for matching dynamic
171187
local target = math.min(math.max(1, i + shift), #dyn_char)
172188
if str.LuaString == utf8.char(v) then -- dynamic match
173-
if found[target] then -- replacement exists
174-
e:SetID(found[target]):Save()
189+
if found[mode][target] then -- replacement exists
190+
e:SetID(found[mode][target]):Save()
175191
else -- create new dynamic
176192
if not config.create_new then -- ask permission
177193
config.create_new = create_dynamics_alert(dialog)
178194
end
179195
if config.create_new then -- create missing dynamic exp_def
180-
found[target] = create_dyn_def(utf8.char(dyn_char[target]))
181-
e:SetID(found[target]):Save()
196+
if dialog then -- update checkbox condition
197+
dialog:GetControl("create_new"):SetCheck(1)
198+
end
199+
local t = utf8.char(dyn_char[target])
200+
found[mode][target] = create_dynamic_def(t, hidden)
201+
e:SetID(found[mode][target]):Save()
182202
end
183203
end
184204
break -- all done for this target dynamic
@@ -196,7 +216,7 @@ local function run_the_dialog()
196216
local y, m_offset = 0, finenv.UI():IsOnMac() and 3 or 0
197217
local save = config.levels
198218
local ctl = {}
199-
local dialog = mixin.FCXCustomLuaWindow():SetTitle(name:sub(1, 7))
219+
local dialog = mixin.FCXCustomLuaWindow():SetTitle("Dynamics")
200220
-- local functions
201221
local function yd(diff) y = y + (diff or 20) end
202222
local function show_info()
@@ -214,6 +234,9 @@ local function run_the_dialog()
214234
if s:find("[^1-9]") then
215235
if s:find(hotkey.show_info) then show_info()
216236
elseif s:find(hotkey.direction) then flip_direction()
237+
elseif s:find(hotkey.create_new) then
238+
local c = ctl.create_new
239+
c:SetCheck((c:GetCheck() + 1) % 2)
217240
end
218241
else
219242
save = s:sub(-1) -- save last entered char only
@@ -223,7 +246,7 @@ local function run_the_dialog()
223246
local function on_timer() -- track changes in selected region
224247
for k, v in pairs(saved_bounds) do
225248
if finenv.Region()[k] ~= v then -- selection changed
226-
track_selection() -- update selection tracker
249+
set_bounds() -- update selection tracker
227250
break -- all done
228251
end
229252
end
@@ -232,7 +255,7 @@ local function run_the_dialog()
232255
yd()
233256
-- RadioButtonGroup
234257
local labels = finale.FCStrings()
235-
labels:CopyFromStringTable({"Louder", "Softer"})
258+
labels:CopyFromStringTable{ "Louder", "Softer" }
236259
ctl.direction = dialog:CreateRadioButtonGroup(0, y + 1, 2)
237260
:SetText(labels):SetWidth(55):SetSelectedItem(config.direction)
238261
local softer = ctl.direction:GetItemAt(1) -- 2nd button
@@ -245,9 +268,14 @@ local function run_the_dialog()
245268
yd(21)
246269
ctl.q = dialog:CreateButton(110, y):SetText("?"):SetWidth(20)
247270
:AddHandleCommand(function() show_info() end)
271+
yd(21)
272+
ctl.create_new = dialog:CreateCheckbox(0, y, "create_new")
273+
:SetText("Enable creation of new\ndynamic expressions")
274+
:SetWidth(150):SetCheck(config.create_new and 1 or 0)
275+
:SetHeight(30)
248276
-- wrap it up
249-
dialog:CreateOkButton():SetText("Apply")
250-
dialog:CreateCancelButton()
277+
dialog:CreateOkButton() :SetText("Apply")
278+
dialog:CreateCancelButton():SetText("Close")
251279
dialog:RegisterInitWindow(function(self)
252280
self:SetTimer(config.timer_id, 125)
253281
local bold = ctl.q:CreateFontInfo():SetBold(true)
@@ -259,6 +287,7 @@ local function run_the_dialog()
259287
dialog:RegisterHandleOkButtonPressed(function()
260288
config.direction = ctl.direction:GetSelectedItem()
261289
config.levels = ctl.levels:GetInteger()
290+
config.create_new = (ctl.create_new:GetCheck() == 1)
262291
change_dynamics(dialog)
263292
end)
264293
dialog:RegisterCloseWindow(function(self)
@@ -272,7 +301,7 @@ local function dynamic_levels()
272301
configuration.get_user_settings(script_name, config, true)
273302
local qim = finenv.QueryInvokedModifierKeys
274303
local mod_key = qim and (qim(finale.CMDMODKEY_ALT) or qim(finale.CMDMODKEY_SHIFT))
275-
track_selection() -- track current selected region
304+
set_bounds() -- track current selected region
276305
--
277306
if mod_key then
278307
change_dynamics(nil)

0 commit comments

Comments
 (0)