Skip to content

Commit 61a7f82

Browse files
committed
Create pitch_filter.lua
- Passes checks under 'linting' branch - Creates a dialog that lets the user specify a pitch, then filter out all notes above or below that pitch
1 parent f0170d1 commit 61a7f82

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

src/pitch_filter.lua

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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

Comments
 (0)