Code
-- band equalizer
-- Constants
max_bunches_listed = 5
length_slider_max = 5
bunch_separator = "\n" -- '""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""\n'
function strength_compose(checkbox, slider)
if checkbox then
return math.min(slider * 10, 10)
else
return slider
end
end
function strength_decompose(strength)
if strength > 1.5 then
return true, strength / 10
else
return false, strength
end
end
function band_props_get(bi)
-- min_length and max_length because band_props needs it
local length = band.GetGoalLength(bi)
return { enabled=band.IsEnabled(bi), min_length=length, max_length=length, strength=band.GetStrength(bi) }
end
function band_props_key(b)
return (b.max_length == 0 and "z" or "n")..b.strength..(b.enabled and "+" or "-")
end
function describe_bunch(bunch)
local name = ""
if #bunch.bilist == 1 then
if bunch.max_length == 0 then
name = "zero length band "..bunch.bilist[1]
else
name = "band "..bunch.bilist[1]
end
elseif #bunch.bilist <= 7 then
if bunch.max_length == 0 then
name = "zero length bands "
else
name = "bands "
end
for i,bi in ipairs(bunch.bilist) do
if i > 1 then
name = name..", "
end
name = name..bi
end
else
if bunch.max_length == 0 then
name = #bunch.bilist.." similar zero length bands"
else
name = #bunch.bilist.." similar bands"
end
name = name.." including band "..bunch.bilist[1]
end
return name
end
function bunch_order(l, r)
if (l.max_length == 0) ~= (r.max_length == 0) then
return l.max_length == 0
end
if l.strength ~= r.strength then
return l.strength > r.strength
end
if l.enabled ~= r.enabled then
return l.enabled
end
return false
end
mission = {}
function mission.Init()
local bands = band.GetCount()
mission.bunchlist = {}
local band_key_to_bunchnr = {}
for bi=1,bands do
local band_props = band_props_get(bi)
local band_key = band_props_key(band_props)
local bunchnr = band_key_to_bunchnr[band_key]
local bunch
if bunchnr then
bunch = mission.bunchlist[bunchnr]
-- print("Adding band "..bi.." of length "..band_props.max_length.." to bunch #"..bunchnr.." with "..#bunch.bilist.." bamds of length "..bunch.max_length)
else
bunch = band_props
bunch.bilist = {}
table.insert(mission.bunchlist, bunch)
table.sort(mission.bunchlist, bunch_order)
band_key_to_bunchnr = {}
for i,bunch in pairs(mission.bunchlist) do
local band_key = band_props_key(bunch)
band_key_to_bunchnr[band_key] = i
end
end
table.insert(bunch.bilist, bi)
bunch.min_length = math.min(bunch.min_length, band_props.min_length)
bunch.max_length = math.max(bunch.max_length, band_props.max_length)
end
return bands > 0
end
function mission.Main()
while mission.Init() do
local main = dialog.CreateDialog("Band Equalizer")
local num_excess_bunches = math.max(0, #mission.bunchlist - max_bunches_listed)
for i=1,#mission.bunchlist-num_excess_bunches do
local bunch = mission.bunchlist[i]
local name = "Bunch #"..i..": "..describe_bunch(bunch)
if i > 1 then
main[" "..i] = dialog.AddLabel("")
end
main["l"..i] = dialog.AddLabel(name)
main["e"..i] = dialog.AddCheckbox("Enabled", bunch.enabled)
local strong, strength = strength_decompose(bunch.strength)
main["S"..i] = dialog.AddCheckbox("Strength boost x10", strong)
main["s"..i] = dialog.AddSlider(" Strength", math.min(strength+.001, 1.5), -0.01, 1.5, 2)
if bunch.min_length < bunch.max_length then
main["gmin"..i] = dialog.AddSlider(" Min goal length", bunch.min_length, bunch.min_length, bunch.max_length, 1)
main["gmax"..i] = dialog.AddSlider(" Max goal length", bunch.max_length, bunch.min_length, bunch.max_length, 1)
elseif 0 < bunch.max_length then
main["g"..i] = dialog.AddSlider(" Goal length", bunch.min_length, 0, math.ceil(math.max(bunch.min_length/length_slider_max), 1) * length_slider_max, 1)
end
end
if num_excess_bunches > 0 then
local num_excess_bands = 0
for i=max_bunches_listed+1,#mission.bunchlist do
local bunch = mission.bunchlist[i]
num_excess_bands = num_excess_bands + #bunch.bilist
end
if num_excess_bunches > 1 then
main.excess = dialog.AddLabel(bunch_separator..num_excess_bunches.." more bunches with "..num_excess_bands.." bands not listed")
elseif num_excess_bands > 1 then
main.excess = dialog.AddLabel(bunch_separator.."1 more bunch of "..num_excess_bands.. " bands not listed")
else
main.excess = dialog.AddLabel(bunch_separator.."1 more bamd not listed")
end
end
local ok_button, apply_button, refresh_button, merge_button = 1, 2, 3, 4
main.ok = dialog.AddButton("OK", ok_button)
main.apply = dialog.AddButton("Apply", apply_button)
main.refresh = dialog.AddButton("Refresh", refresh_button)
main.merge = dialog.AddButton("Merge", merge_button)
local update_dialog = false
repeat -- show dialog until user chose and - if needed - confirmed
local button = dialog.Show(main)
if button == 0 then
return
end
local apply = false
if button == refresh_button then
update_dialog = true
end
if button == merge_button then
local merge = dialog.CreateDialog("Merge bunches of bands")
for i=1,#mission.bunchlist do
local bunch = mission.bunchlist[i]
local name = "Bunch #"..i..": "..describe_bunch(bunch)
merge["m"..i] = dialog.AddCheckbox(name, true)
end
--merge.min = dialog.AddSlider("Merge from", #mission.bunchlist, 1, #mission.bunchlist, 0)
--merge.max = dialog.AddSlider("Merge to", #mission.bunchlist, 1, #mission.bunchlist, 0)
merge.ok = dialog.AddButton("Merge", 1)
merge.cancel = dialog.AddButton("Cancel", 0)
if dialog.Show(merge) == 1 then
local source_bunch
for i=1,#mission.bunchlist do
local bunch = mission.bunchlist[i]
if merge["m"..i].value then
if not source_bunch then
source_bunch = bunch
else
bunch.enabled = source_bunch.enabled
bunch.strength = source_bunch.strength
bunch.min_length = source_bunch.min_length
bunch.max_length = source_bunch.max_length
end
end
end
apply = true
end
end
if button == ok_button or button == apply_button then
local possibly_unintended = false
warn = dialog.CreateDialog("Warning")
local new_band_key_to_old_bunchnr = {}
for i=1,#mission.bunchlist-num_excess_bunches do
local bunch = mission.bunchlist[i]
bunch.strength = strength_compose(main["S"..i].value, main["s"..i].value)
if bunch.strength >= 0 then
bunch.enabled = main["e"..i].value
if bunch.min_length < bunch.max_length then
bunch.min_length = main["gmin"..i].value
bunch.max_length = main["gmax"..i].value
if bunch.min_length > bunch.max_length then
possibly_unintended = true
warn["l"..i] = dialog.AddLabel("Inconsistent length range for bunch #"..i..", using max")
bunch.min_length = bunch.max_length
end
elseif 0 < bunch.max_length then
bunch.min_length = main["g"..i].value
bunch.max_length = bunch.min_length
end
local band_key = band_props_key(bunch)
local conflicting_old_bunchnr = new_band_key_to_old_bunchnr[band_key]
if conflicting_old_bunchnr then
possibly_unintended = true
warn["l"..conflicting_old_bunchnr.."x"..i] = dialog.AddLabel("This will merge bunches #"..conflicting_old_bunchnr.." and #"..i)
else
new_band_key_to_old_bunchnr[band_key] = i
end
end
end
if possibly_unintended then
warn.ok = dialog.AddButton("OK", 1)
warn.cancel = dialog.AddButton("Cancel", 0)
apply = dialog.Show(warn) == 1
else
apply = true
end
end
if apply then
local doomed_bi = {}
for i,bunch in ipairs(mission.bunchlist) do
for j,bi in ipairs(bunch.bilist) do
if bunch.strength < 0 then
-- keep band indices intact for now
doomed_bi[bi] = true
else
if not band.IsEnabled(bi) and bunch.enabled then
print("Enabling band "..bi)
band.Enable(bi)
elseif band.IsEnabled(bi) and not bunch.enabled then
print("Disabling band "..bi)
band.Disable(bi)
end
if band.GetStrength(bi) > bunch.strength then
print("Weakening band "..bi)
band.SetStrength(bi, bunch.strength)
elseif band.GetStrength(bi) < bunch.strength then
print("Strengthening band "..bi)
band.SetStrength(bi, bunch.strength)
end
if band.GetGoalLength(bi) < bunch.min_length then
print("Stretching band "..bi)
band.SetGoalLength(bi, bunch.min_length)
elseif band.GetGoalLength(bi) > bunch.max_length then
print("Shortening band "..bi)
band.SetGoalLength(bi, bunch.max_length)
end
end
end
end
for bi = band.GetCount(), 1, -1 do
if doomed_bi[bi] then
print("Deleting band "..bi)
band.Delete(bi)
end
end
if button == ok_button then
return
end
update_dialog = true
end
until update_dialog
end
end
function mission.Exit(why)
if not mission.exiting then
mission.exiting = true
print(why)
end
end
function mission.Catch(what)
if what:match("Cancelled$") then
mission.Exit("Cancelled.")
else
print(what)
mission.Exit("Error!")
end
end
xpcall(mission.Main, mission.Catch)
mission.Exit("Finished.")