@@ -3,32 +3,36 @@ function plugindef()
3
3
finaleplugin .Author = " Carl Vine"
4
4
finaleplugin .AuthorURL = " https://carlvine.com/lua/"
5
5
finaleplugin .Copyright = " https://creativecommons.org/licenses/by/4.0/"
6
- finaleplugin .Version = " 0.70 "
7
- finaleplugin .Date = " 2023/07/25 "
6
+ finaleplugin .Version = " 0.83 "
7
+ finaleplugin .Date = " 2024/01/27 "
8
8
finaleplugin .MinJWLuaVersion = 0.60
9
- finaleplugin .Notes = [[
10
- This script provides rapid entry of simple or complex
9
+ finaleplugin .Notes = [[
10
+ This script allows rapid creation of simple or complex
11
11
time signatures with a few keystrokes.
12
12
It supports composite numerators like [3+2+3/16] and can join
13
- with further composites (e.g. [3+2+3/16]+[1/4]+[5+4/8]).
14
- "Display only" time signatures can be equally complex and set without using a mouse.
13
+ easily with extra composites (e.g. [3+2+3/16]+[1/4]+[5+4/8]).
14
+ "Display only" time signatures can be equally complex and set without using a mouse.
15
+
15
16
At startup the time signature of the first selected measure is shown.
16
- Click the "Clear All" button to revert to a simple 4/4 with no other options.
17
+ To revert to a simple 4/4 with no other options click the "Clear All" button or type [x].
18
+ To read these script notes click the [?] button or type [q].
19
+ To respace notes on completion click the "Respace" button or type [r].
17
20
18
21
All measures in the current selection will be assigned the new time signature.
22
+ Use this feature to quickly copy the initial meter throughout the selection.
19
23
If just one measure is selected only it will be changed.
20
24
21
- "Bottom" numbers (denominators) are the usual "note" numbers: 2, 4, 8, 16, 32, 64.
25
+ "Bottom" numbers (denominators) are the usual "note" numbers: 2, 4, 8, 16, 32, or 64.
22
26
"Top" numbers (numerators) are integers, optionally joined by '+' signs for composite meters.
23
- Multiples of 3 automatically convert to compound signatures so [9/16] will
24
- convert to three groups of dotted 8ths.
25
- To prevent automatic compounding, instead of the bottom 'note' number enter its EDU value
26
- (quarter note = 1024; eighth note = 512 etc).
27
+ Numerators that are multiples of 3 automatically convert to compound signatures
28
+ so [9/16] will convert to three groups of dotted 8ths.
29
+ To prevent automatic compounding, instead of the bottom 'note' number enter its
30
+ EDU value (quarter note = 1024; eighth note = 512; sixteenth = 256 etc).
27
31
28
32
Empty and zero "Top" numbers will be ignored.
29
- If "Secondary" numbers are zero then "Tertiary" values are ignored.
33
+ "Tertiary" values will be ignored if "Secondary" numbers are blank or zero.
30
34
]]
31
- return " Meter Set Numeric" , " Meter Set Numeric" , " Set the Meter Numerically"
35
+ return " Meter Set Numeric... " , " Meter Set Numeric" , " Set the Meter Numerically"
32
36
end
33
37
34
38
--[[
@@ -45,130 +49,196 @@ end
45
49
}
46
50
]]
47
51
52
+ local info_notes = [[
53
+ This script allows rapid creation of simple or complex
54
+ time signatures with a few keystrokes.
55
+ It supports composite numerators like [3+2+3/16] and can join
56
+ easily with extra composites (e.g. [3+2+3/16]+[1/4]+[5+4/8]).
57
+ "Display only" time signatures can be equally complex and set without using a mouse.
58
+ **
59
+ At startup the time signature of the first selected measure is shown.
60
+ To revert to a simple 4/4 with no other options click the "Clear All" button or type [x].
61
+ To read these script notes click the [?] button or type [q].
62
+ To respace notes on completion click the "Respace" button or type [r].
63
+ **
64
+ All measures in the current selection will be assigned the new time signature.
65
+ Use this feature to quickly copy the initial meter throughout the selection.
66
+ If just one measure is selected only it will be changed.
67
+ **
68
+ "Bottom" numbers (denominators) are the usual "note" numbers: 2, 4, 8, 16, 32, or 64.
69
+ "Top" numbers (numerators) are integers, optionally joined by '+' signs for composite meters.
70
+ Numerators that are multiples of 3 automatically convert to compound signatures
71
+ so [9/16] will convert to three groups of dotted 8ths.
72
+ To prevent automatic compounding, instead of the bottom 'note' number enter its
73
+ EDU value (quarter note = 1024; eighth note = 512; sixteenth = 256 etc).
74
+ **
75
+ Empty and zero "Top" numbers will be ignored.
76
+ "Tertiary" values will be ignored if "Secondary" numbers are blank or zero.
77
+ ]]
78
+ info_notes = info_notes :gsub (" \n %s*" , " " ):gsub (" *" , " \n " )
79
+ .. " \n (v" .. finaleplugin .Version .. " )"
80
+
48
81
local mixin = require (" library.mixin" )
49
82
local configuration = require (" library.configuration" )
83
+ local library = require (" library.general_library" )
84
+ local script_name = library .calc_script_name ()
85
+
50
86
local config = {
51
87
note_spacing = true ,
52
88
window_pos_x = false ,
53
89
window_pos_y = false ,
54
90
}
55
- local script_name = " meter_set_numeric"
56
91
configuration .get_user_settings (script_name , config , true )
57
92
58
- function blank_meter ()
93
+ local function blank_meter ()
59
94
return { -- empty composite meter record
60
- main = { top = {}, bottom = {} },
61
- display = { top = {}, bottom = {} }
95
+ main = { top = {}, bottom = {} },
96
+ display = { top = {}, bottom = {} }
62
97
}
63
98
end
64
99
65
- function dialog_set_position (dialog )
100
+ local function dialog_set_position (dialog )
66
101
if config .window_pos_x and config .window_pos_y then
67
102
dialog :StorePosition ()
68
103
dialog :SetRestorePositionOnlyData (config .window_pos_x , config .window_pos_y )
69
104
dialog :RestorePosition ()
70
105
end
71
106
end
72
107
73
- function dialog_save_position (dialog )
108
+ local function dialog_save_position (dialog )
74
109
dialog :StorePosition ()
75
110
config .window_pos_x = dialog .StoredX
76
111
config .window_pos_y = dialog .StoredY
77
112
configuration .save_user_settings (script_name , config )
78
113
end
79
114
80
- function user_chooses_meter (meter , rgn )
115
+ local function user_chooses_meter (meter , rgn )
81
116
local x = { 0 , 70 , 130 , 210 , 280 , 290 } -- horizontal grid
82
- local label = {" PRIMARY" , " (+ SECONDARY)" , " (+ TERTIARY)" }
83
- local label_off = { 0 , - 37 , - 21 } -- horizontal offset for these names (ranged right)
117
+ local label = { -- data type descriptors and (range right) horizontal offset
118
+ { " PRIMARY" , 0 }, -- name, horiz (left) offset
119
+ { " (+ SECONDARY)" , - 37 },
120
+ { " (+ TERTIARY)" , - 21 }
121
+ }
84
122
local y_middle = 118 -- window vertical mid-point
85
123
local offset = finenv .UI ():IsOnMac () and 3 or 0
124
+ local name = plugindef ():gsub (" %.%.%." , " " ) -- remove trailing dots
125
+ local function show_info () finenv .UI ():AlertInfo (info_notes , " About " .. name ) end
126
+ local box , save_text = {}, {} -- user's edit-box entry responses
86
127
local message = " m. " .. rgn .StartMeasure -- measure selection information
87
128
if rgn .StartMeasure ~= rgn .EndMeasure then -- multiple measures
88
129
message = " m" .. message .. " -" .. rgn .EndMeasure
89
130
end
131
+ local respace_check -- pre-declare the "respace" checkbox
132
+
90
133
local y = 0
91
- local dialog = mixin .FCXCustomLuaWindow ():SetTitle (plugindef ())
92
- -- STATIC TEXT ELEMENTS
93
- local function cstat (nx , ny , nwide , ntext )
94
- local dx = (type (nx ) == " number" ) and x [nx ] or tonumber (nx )
95
- return dialog :CreateStatic (dx , ny ):SetWidth (nwide ):SetText (ntext )
96
- end
97
- local function join (values ) -- join integers from the meter.top array with '+' signs
98
- if not values or # values == 0 then return " 0" end
99
- return table.concat (values , " +" )
100
- end
101
- local shadow = cstat (" 1" , y + 1 , x [3 ], " TIME SIGNATURE" )
102
- if shadow .SetTextColor then shadow :SetTextColor (153 , 153 , 153 ) end
103
- cstat (1 , y , x [3 ], " TIME SIGNATURE" )
134
+ local dialog = mixin .FCXCustomLuaWindow ():SetTitle (name )
135
+ local function cstat (nx , ny , nwide , ntext , id )
136
+ local dx = (type (nx ) == " number" ) and x [nx ] or tonumber (nx )
137
+ return dialog :CreateStatic (dx , ny , id ):SetWidth (nwide ):SetText (ntext )
138
+ end
139
+ local function join (values ) -- join integers from the meter.top array with '+' signs
140
+ if not values or # values == 0 then return " 0" end
141
+ return table.concat (values , " +" )
142
+ end
143
+ local function clear_entries ()
144
+ for j = 0 , 6 , 6 do
145
+ for i = 1 , 3 do
146
+ local n = (j == 0 and i == 1 ) and 4 or 0
147
+ box [j + i ]:SetText (n )
148
+ box [j + i + 3 ]:SetInteger (n )
149
+ save_text [j + i ] = n
150
+ save_text [j + i + 3 ] = n
151
+ end
152
+ end
153
+ box [1 ]:SetKeyboardFocus ()
154
+ end
155
+ local function key_check (id )
156
+ local s = box [id ]:GetText ():lower ()
157
+ if s :find (" [^0-9+]" ) then
158
+ if s :find (" x" ) then
159
+ clear_entries ()
160
+ else
161
+ if s :find (" [?q]" ) then
162
+ show_info ()
163
+ elseif s :find (" r" ) then
164
+ local n = respace_check :GetCheck ()
165
+ respace_check :SetCheck ((n + 1 ) % 2 )
166
+ end
167
+ box [id ]:SetText (save_text [id ])
168
+ end
169
+ elseif s ~= " " then
170
+ save_text [id ] = s -- save newly entered text
171
+ end
172
+ end
173
+
174
+ cstat (1 , y + 1 , x [3 ], " TIME SIGNATURE" , " main" )
104
175
cstat (3 , y , x [3 ], " TOP" )
105
176
cstat (4 , y , x [3 ], " BOTTOM" )
106
177
cstat (6 , 25 , 80 , " Selection:" )
107
178
cstat (6 , 40 , 80 , message )
179
+ dialog :CreateButton (x [5 ], y ):SetWidth (80 ):SetText (" Clear All (x)" )
180
+ :AddHandleCommand (function () clear_entries () end )
108
181
y = y_middle
109
182
cstat (x [2 ] - 30 , y - 30 , 330 , " 'TOP' entries can include integers joined by '+' signs" )
110
183
dialog :CreateHorizontalLine (x [1 ], y - 9 , x [5 ] + 50 )
111
184
dialog :CreateHorizontalLine (x [1 ], y - 8 , x [5 ] + 50 )
112
- shadow = cstat (" 1" , y + 1 , x [3 ], " DISPLAY SIGNATURE" )
113
- if shadow .SetTextColor then shadow :SetTextColor (153 , 153 , 153 ) end
114
- cstat (1 , y , x [3 ], " DISPLAY SIGNATURE" )
185
+ cstat (1 , y + 1 , x [3 ], " DISPLAY SIGNATURE" , " second" )
115
186
cstat (3 , y , 150 , " (set to '0' for none)" )
116
- dialog :CreateButton (x [5 ] + 60 , y ):SetText (" ?" ):SetWidth (20 )
117
- :AddHandleCommand (function ()
118
- finenv .UI ():AlertInfo (finaleplugin .Notes :gsub (" %s+" , " " ), " About " .. plugindef ())
119
- end )
120
- -- USER EDIT BOXES
187
+
188
+ -- COMPOSITE TIME SIG BOXES
121
189
y = 0
122
- local box = {} -- user's edit-box entry responses
123
190
for jump = 0 , 6 , 6 do
124
191
local t_sig = (jump == 0 ) and meter .main or meter .display
125
192
for group = 1 , 3 do
126
193
y = y + 20
127
194
local id = group + jump
128
- box [id ] = dialog :CreateEdit (x [3 ], y - offset , tostring ( id ) )
195
+ box [id ] = dialog :CreateEdit (x [3 ], y - offset )
129
196
:SetText (join (t_sig .top [group ])):SetWidth (65 )
130
- box [id + 3 ] = dialog :CreateEdit (x [4 ], y - offset , tostring (id + 3 ))
197
+ :AddHandleCommand (function () key_check (id ) end )
198
+ box [id + 3 ] = dialog :CreateEdit (x [4 ], y - offset )
131
199
:SetInteger (t_sig .bottom [group ] or 0 ):SetWidth (65 )
132
- cstat (x [2 ] + label_off [group ], y , 56 - label_off [group ], label [group ])
200
+ :AddHandleCommand (function () key_check (id + 3 ) end )
201
+ cstat (x [2 ] + label [group ][2 ], y , 56 - label [group ][2 ], label [group ][1 ])
202
+ save_text [id ] = box [id ]:GetText ()
203
+ save_text [id + 3 ] = box [id + 3 ]:GetText ()
133
204
end
134
205
y = y_middle
135
206
end
136
- dialog :CreateCheckbox (x [3 ], y_middle + 85 , " spacing" ):SetText (" Respace notes on completion" )
137
- :SetCheck (config .note_spacing and 1 or 0 ):SetWidth (170 )
138
- local clear_button = dialog :CreateButton (x [5 ], 0 ):SetWidth (80 ):SetText (" Clear All" )
139
- clear_button :AddHandleCommand (function ()
140
- box [1 ]:SetText (" 4" )
141
- box [4 ]:SetInteger (4 )
142
- for _ , i in ipairs ({2 , 3 , 7 , 8 , 9 }) do -- "clear" the other edit boxes
143
- box [i ]:SetText (" 0" )
144
- box [i + 3 ]:SetInteger (0 )
145
- end
146
- box [1 ]:SetKeyboardFocus ()
147
- end
148
- )
207
+ y = y + 60
208
+ dialog :CreateButton (x [5 ] + 60 , y , " q" ):SetText (" ?" ):SetWidth (20 )
209
+ :AddHandleCommand (function () show_info () end )
210
+ y = y + 25
211
+ respace_check = dialog :CreateCheckbox (x [3 ], y ):SetText (" Respace notes on completion (r)" )
212
+ :SetCheck (config .note_spacing and 1 or 0 ):SetWidth (185 )
149
213
dialog :CreateOkButton ()
150
214
dialog :CreateCancelButton ()
151
215
local choices = {} -- convert edit box values to 12-element table
152
- dialog :RegisterInitWindow (function () box [1 ]:SetKeyboardFocus () end )
153
- -- "TOP" values are strings, "BOTTOMS" are integers
154
- dialog :RegisterHandleOkButtonPressed (function (self )
155
- for count = 0 , 6 , 6 do -- "main" then "display" t_sig values
216
+ dialog :RegisterInitWindow (function (self )
217
+ box [1 ]:SetKeyboardFocus ()
218
+ local q = self :GetControl (" q" )
219
+ local bold = q :CreateFontInfo ():SetBold (true )
220
+ q :SetFont (bold )
221
+ self :GetControl (" main" ):SetFont (bold )
222
+ self :GetControl (" second" ):SetFont (bold )
223
+ end )
224
+ dialog :RegisterHandleOkButtonPressed (function ()
225
+ for count = 0 , 6 , 6 do -- "main" then "display" time_sig values
156
226
for group = 1 , 3 do
157
227
local id = count + group
158
- choices [id ] = self : GetControl ( tostring ( id )) :GetText () or " 0"
228
+ choices [id ] = box [ id ] :GetText () or " 0"
159
229
if choices [id ] == " " then choices [id ] = " 0" end
160
- choices [id + 3 ] = math.abs ( self : GetControl ( tostring ( id + 3 )) :GetInteger () ) or 0
230
+ choices [id + 3 ] = box [ id + 3 ] :GetInteger () or 0
161
231
end
162
232
end
163
- config .note_spacing = (self :GetControl (" spacing" ):GetCheck () == 1 )
164
- dialog_save_position (self ) -- save window position and config choices
233
+ config .note_spacing = (respace_check :GetCheck () == 1 )
165
234
end )
235
+ dialog :RegisterCloseWindow (function (self ) dialog_save_position (self ) end )
166
236
dialog_set_position (dialog )
167
237
local ok = dialog :ExecuteModal (nil ) -- run the dialog
168
238
return ok , choices
169
239
end
170
240
171
- function encode_current_meter (time_sig , sub_meter )
241
+ local function encode_current_meter (time_sig , sub_meter )
172
242
local function numerators_treble (top_table )
173
243
for j = 1 , # top_table do
174
244
top_table [j ] = top_table [j ] * 3
@@ -211,7 +281,7 @@ function encode_current_meter(time_sig, sub_meter)
211
281
end
212
282
end
213
283
214
- function copy_meter_from_score (measure_number )
284
+ local function copy_meter_from_score (measure_number )
215
285
local measure = finale .FCMeasure ()
216
286
measure :Load (measure_number )
217
287
local meter = blank_meter ()
@@ -222,16 +292,16 @@ function copy_meter_from_score(measure_number)
222
292
return meter
223
293
end
224
294
225
- function is_power_of_two (num )
295
+ local function is_power_of_two (num )
226
296
local current = 1
227
297
while current < num do
228
298
current = current * 2
229
299
end
230
300
return current == num
231
301
end
232
302
233
- function convert_choices_to_meter (choices , meter )
234
- if choices [1 ] == " 0" or choices [4 ] == 0 then
303
+ local function convert_choices_to_meter (choices , meter )
304
+ if choices [1 ] == " 0" or choices [1 ] == " " or choices [ 4 ] == 0 then
235
305
return " Primary time signature cannot be zero"
236
306
end
237
307
for jump = 0 , 6 , 6 do -- 'main' meter then 'display' meter
@@ -273,7 +343,7 @@ function convert_choices_to_meter(choices, meter)
273
343
return " " -- no error
274
344
end
275
345
276
- function new_composite_top (sub_meter )
346
+ local function new_composite_top (sub_meter )
277
347
local composite_top = finale .FCCompositeTimeSigTop ()
278
348
for count = 1 , 3 do
279
349
if not sub_meter [count ] or sub_meter [count ][1 ] == 0 then break end
@@ -286,7 +356,7 @@ function new_composite_top(sub_meter)
286
356
return composite_top
287
357
end
288
358
289
- function new_composite_bottom (sub_meter )
359
+ local function new_composite_bottom (sub_meter )
290
360
local composite_bottom = finale .FCCompositeTimeSigBottom ()
291
361
for count = 1 , 3 do
292
362
if not sub_meter [count ] or sub_meter [count ] == 0 then break end
@@ -297,8 +367,7 @@ function new_composite_bottom(sub_meter)
297
367
return composite_bottom
298
368
end
299
369
300
-
301
- function fix_new_top (composite_top , time_sig , numerator )
370
+ local function fix_new_top (composite_top , time_sig , numerator )
302
371
if composite_top ~= nil then -- COMPOSITE top
303
372
time_sig :SaveNewCompositeTop (composite_top )
304
373
else
@@ -310,7 +379,7 @@ function fix_new_top(composite_top, time_sig, numerator)
310
379
end
311
380
end
312
381
313
- function fix_new_bottom (composite_bottom , time_sig , denominator )
382
+ local function fix_new_bottom (composite_bottom , time_sig , denominator )
314
383
if composite_bottom ~= nil then -- COMPOSITE bottom
315
384
time_sig :SaveNewCompositeBottom (composite_bottom )
316
385
else
@@ -322,7 +391,7 @@ function fix_new_bottom(composite_bottom, time_sig, denominator)
322
391
end
323
392
end
324
393
325
- function create_new_meter ()
394
+ local function create_new_meter ()
326
395
local region = mixin .FCMMusicRegion ()
327
396
region :SetRegion (finenv .Region ()):SetStartMeasurePosLeft ():SetEndMeasurePosRight ()
328
397
@@ -338,7 +407,7 @@ function create_new_meter()
338
407
main = { top = nil , bottom = nil },
339
408
display = { top = nil , bottom = nil }
340
409
}
341
- for _ , kind in ipairs ( {" main" , " display" }) do
410
+ for _ , kind in ipairs {" main" , " display" } do
342
411
if meter [kind ].top [1 ] and (# meter [kind ].top > 1 or # meter [kind ].top [1 ] > 1 ) then
343
412
composites [kind ].top = new_composite_top (meter [kind ].top )
344
413
if # meter [kind ].bottom > 1 then
0 commit comments