1
+ function plugindef ()
2
+ finaleplugin .Author = " Jacob Winkler"
3
+ finaleplugin .Copyright = " CC0 https://creativecommons.org/publicdomain/zero/1.0/"
4
+ finaleplugin .Version = " 1.0"
5
+ finaleplugin .Date = " 2024-01-27"
6
+ finaleplugin .HandlesUndo = true
7
+ finaleplugin .RequireSelection = true
8
+ return " Note Filter..." , " Note Filter" , " Deletes notes above or below a specified note"
9
+ end
10
+
11
+ local note_entry = require (" library.note_entry" )
12
+ local configuration = require (" library.configuration" )
13
+ local str = finale .FCString ()
14
+
15
+ config = {
16
+ above_below = " below" ,
17
+ pitch_string = " C4"
18
+ }
19
+
20
+ local script_name = " pitch_filter"
21
+ configuration .get_user_settings (script_name , config , true )
22
+
23
+ local function simple_input (title , text )
24
+ local return_value = finale .FCString ()
25
+ local min_width = 160
26
+ --
27
+ local function format_ctrl (ctrl , h , w , st )
28
+ ctrl :SetHeight (h )
29
+ ctrl :SetWidth (w )
30
+ str .LuaString = st
31
+ ctrl :SetText (str )
32
+ end -- function format_ctrl
33
+ --
34
+ local title_width = string.len (title ) * 6 + 54
35
+ if title_width > min_width then min_width = title_width end
36
+ local text_width = string.len (text ) * 6
37
+ if text_width > min_width then min_width = text_width end
38
+ --
39
+ str .LuaString = title
40
+ local dialog = finale .FCCustomLuaWindow ()
41
+ dialog :SetTitle (str )
42
+ local descr = dialog :CreateStatic (0 , 0 )
43
+ format_ctrl (descr , 16 , min_width , text )
44
+ local below_check = dialog :CreateCheckbox (70 , 0 )
45
+ str .LuaString = " Below"
46
+ below_check :SetText (str )
47
+ local above_check = dialog :CreateCheckbox (120 , 0 )
48
+ str .LuaString = " Above"
49
+ above_check :SetText (str )
50
+ local input_edit = dialog :CreateEdit (180 , - 3 )
51
+ format_ctrl (input_edit , 20 , 30 , config .pitch_string )
52
+ dialog :CreateOkButton ()
53
+ dialog :CreateCancelButton ()
54
+ --
55
+ local function set_checkboxes ()
56
+ if config .above_below == " below" then
57
+ below_check :SetCheck (1 )
58
+ above_check :SetCheck (0 )
59
+ else
60
+ below_check :SetCheck (0 )
61
+ above_check :SetCheck (1 )
62
+ end
63
+ end
64
+
65
+ set_checkboxes ()
66
+
67
+ local function callback (ctrl )
68
+ if ctrl :GetControlID () == below_check :GetControlID () then
69
+ if below_check :GetCheck () == 1 then
70
+ config .above_below = " below"
71
+ else
72
+ config .above_below = " above"
73
+ end
74
+ set_checkboxes ()
75
+ elseif ctrl :GetControlID () == above_check :GetControlID () then
76
+ if above_check :GetCheck () == 1 then
77
+ config .above_below = " above"
78
+ else
79
+ config .above_below = " below"
80
+ end
81
+ set_checkboxes ()
82
+ end
83
+ end -- callback
84
+ --
85
+ dialog :RegisterHandleCommand (callback )
86
+
87
+ --
88
+ if dialog :ExecuteModal (nil ) == finale .EXECMODAL_OK then
89
+ input_edit :GetText (return_value )
90
+ config .pitch_string = return_value .LuaString
91
+ str .LuaString = " Filter Notes " .. config .above_below .. " " .. return_value .LuaString
92
+ configuration .save_user_settings (script_name , config )
93
+ finenv .StartNewUndoBlock (str .LuaString , false )
94
+ return return_value .LuaString
95
+ end
96
+
97
+ end -- function simple_input
98
+
99
+ local function expand_rests () -- inspired by Carl Vine
100
+ local i = 0
101
+ while i < 4 do
102
+ local delete_next = false
103
+ for e in eachentrysaved (finenv .Region ()) do
104
+ local measure = finale .FCMeasure ()
105
+ measure :Load (e .Measure )
106
+ local timesig = measure :GetTimeSignature ()
107
+ local compound = timesig :IsCompound ()
108
+ if e :IsRest () and compound == false then
109
+ if delete_next then -- last note was expanded
110
+ e .Duration = 0
111
+ delete_next = false -- start again
112
+ elseif e .Duration < finale .HALF_NOTE then
113
+ if e .MeasurePos % finale .HALF_NOTE == 0 and e :Next ():IsRest () then
114
+ e .Duration = e .Duration + e :Next ().Duration
115
+ delete_next = true
116
+ elseif e .Duration < finale .QUARTER_NOTE then
117
+ if e .MeasurePos % finale .QUARTER_NOTE == 0 and e :Next ():IsRest () then
118
+ e .Duration = e .Duration + e :Next ().Duration
119
+ delete_next = true
120
+ elseif e .Duration < finale .NOTE_8TH then
121
+ if e .MeasurePos % finale .NOTE_8TH == 0 and e :Next ():IsRest () then
122
+ e .Duration = e .Duration + e :Next ().Duration
123
+ delete_next = true
124
+ elseif e .Duration < finale .NOTE_16TH then
125
+ if e .MeasurePos % finale .NOTE_16TH == 0 and e :Next ():IsRest () then
126
+ e .Duration = e .Duration + e :Next ().Duration
127
+ delete_next = true
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ i = i + 1
136
+ end
137
+ end
138
+
139
+ local function pitch_to_midi (pitch )
140
+ local pitch_table = { " C" , " D" , " E" , " F" , " G" , " A" , " B" }
141
+ local midi_table = {0 , 2 , 4 , 5 , 7 , 9 , 11 }
142
+ local length = (string.len (pitch ))
143
+ local octave = string.sub (pitch , - 1 )
144
+ local pitch_name = string.sub (pitch , 1 , length - 1 )
145
+ local pitch_base = string.sub (pitch_name , 1 , 1 )
146
+ pitch_base = string.upper (pitch_base )
147
+ local midi_note = 0
148
+ local alter = 0
149
+ --
150
+ if string.len (pitch_name ) == 2 then
151
+ local accidental = string.sub (pitch_name , - 1 )
152
+ if accidental == " #" then
153
+ alter = 1
154
+ elseif accidental == " b" then
155
+ alter = - 1
156
+ end
157
+ end
158
+ for i in pairs (pitch_table ) do
159
+ if pitch_table [i ] == pitch_base then
160
+ midi_note = midi_table [i ] + alter + (12 * octave ) + 12
161
+ end
162
+ end
163
+ return midi_note
164
+ end -- pitch_to_midi()
165
+
166
+ local function filter_keyswitch ()
167
+ --
168
+ str .LuaString = " Filter notes"
169
+ --
170
+ local pitch = simple_input (" Note Filter v" .. finaleplugin .Version ,str .LuaString )
171
+
172
+ if nil == pitch then
173
+ goto bypass
174
+ end
175
+
176
+ local note_filter = pitch_to_midi (pitch )
177
+ for entry in eachentrysaved (finenv .Region ()) do
178
+ if entry :IsNote () then
179
+ if entry .Count > 1 then
180
+ local process_entry = 1
181
+ while process_entry == 1 do
182
+ local lowest_note = entry :CalcLowestNote (nil )
183
+ local highest_note = entry :CalcHighestNote (nil )
184
+ if lowest_note then
185
+ if lowest_note :CalcMIDIKey () < note_filter and config .above_below == " below" then
186
+ note_entry .delete_note (lowest_note )
187
+ elseif highest_note :CalcMIDIKey () > note_filter and config .above_below == " above" then
188
+ note_entry .delete_note (highest_note )
189
+ else
190
+ process_entry = 0
191
+ end
192
+ else
193
+ process_entry = 0
194
+ end
195
+ end
196
+ else
197
+ for note in each (entry ) do
198
+ if (note :CalcMIDIKey () < note_filter and config .above_below == " below" ) or
199
+ (note :CalcMIDIKey () > note_filter and config .above_below == " above" ) then
200
+ note_entry .delete_note (note )
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+ expand_rests ()
207
+ :: bypass::
208
+ end -- filter keyswitch
209
+
210
+ filter_keyswitch ()
0 commit comments