Module:MOS tunings: Difference between revisions
Ratio search looks for ratios between 1/1 and 2/1; if ratios above 2/1 are needed, then the previous ratios are raised up by 2/1 and added |
ArrowHead294 (talk | contribs) mNo edit summary |
||
| (128 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
local p = {} | |||
local et = require("Module:ET") | |||
local jira = require("Module:JI ratios") | |||
local mos = require("Module:MOS") | local mos = require("Module:MOS") | ||
local tamnams = require("Module:TAMNAMS") | local tamnams = require("Module:TAMNAMS") | ||
local | local tip = require("Module:Template input parse") | ||
local yesno = require("Module:Yesno") | local yesno = require("Module:Yesno") | ||
-- Rewritten/simplified module replacement for Module:MOS degrees | -- Rewritten/simplified module replacement for Module:MOS degrees | ||
| Line 13: | Line 13: | ||
-- TODO: | -- TODO: | ||
-- - JI ratio search | -- - Relegate JI ratio input to sorting entered ratios; this is to decouple | ||
-- - Diatonic interval category lookup? | -- ratio search from ratio sorting/filtering. | ||
-- - Diatonic interval category lookup? | |||
-- Helper function | -- Helper function | ||
| Line 48: | Line 49: | ||
end | end | ||
return step_ratios | return step_ratios | ||
end | end | ||
| Line 117: | Line 65: | ||
and step_ratios[2][1] == 2 and step_ratios[2][2] == 1 | and step_ratios[2][1] == 2 and step_ratios[2][2] == 1 | ||
and step_ratios[3][1] == 3 and step_ratios[3][2] == 1 then | and step_ratios[3][1] == 3 and step_ratios[3][2] == 1 then | ||
return "Simple Tunings", {{2,1}, {3,1}, {3,2}} | return "Simple Tunings", {{2, 1}, {3, 1}, {3, 2}} | ||
end | end | ||
end | end | ||
| Line 149: | Line 97: | ||
-- Preprocess step ratios | -- Preprocess step ratios | ||
function p.preprocess_ji_ratios(input_mos, modal_union, step_ratios, ji_ratios) | function p.preprocess_ji_ratios(input_mos, modal_union, step_ratios, ji_ratios, tolerance) | ||
-- Calculate the avegrage step ratio | |||
local avg_step_ratio = {0, 0} | local avg_step_ratio = {0, 0} | ||
for i = 1, #step_ratios do | for i = 1, #step_ratios do | ||
| Line 158: | Line 107: | ||
avg_step_ratio[1] = avg_step_ratio[1] / #step_ratios | avg_step_ratio[1] = avg_step_ratio[1] / #step_ratios | ||
avg_step_ratio[2] = avg_step_ratio[2] / #step_ratios | avg_step_ratio[2] = avg_step_ratio[2] / #step_ratios | ||
avg_step_ratio[1] = avg_step_ratio[1] / avg_step_ratio[2] | |||
-- Normalize step ratio to be x:1, accounting for 1:0 | |||
if avg_step_ratio[2] ~= 0 then | |||
avg_step_ratio[1] = avg_step_ratio[1] / avg_step_ratio[2] | |||
avg_step_ratio[2] = avg_step_ratio[2] / avg_step_ratio[2] | |||
else | |||
avg_step_ratio[1] = 1 | |||
avg_step_ratio[2] = 0 | |||
end | |||
-- Calculate the tolerance, the range in which ratios can be accepted +/- | |||
-- from an et-step. (ET may be a non-integer value, since the L:s ratio is | |||
-- normalized to x:1.) | |||
-- Tolerance is how many cents away from an et-step a ratio can be. This is | |||
-- by default 30% of the small step size, and maxes out at 30 cent. Can be | |||
-- overridden with a custom tolerance value. | |||
local steps_in_et = input_mos.nL * avg_step_ratio[1] + input_mos.ns * avg_step_ratio[2] | |||
local tolerance = tolerance or math.min((mos.equave_to_cents(input_mos) / steps_in_et) * 0.30, 30) | |||
-- Calculate the cent values for each interval in the modal union | |||
local cent_values = {} | local cent_values = {} | ||
for i = 1, #modal_union do | for i = 1, #modal_union do | ||
| Line 166: | Line 132: | ||
end | end | ||
local | local sorted_ratios = jira.sort_by_closeness_to_cent_values(ji_ratios, cent_values, tolerance) | ||
return sorted_ratios | return sorted_ratios | ||
end | end | ||
-- Main function | -- Main function | ||
function p._mos_tunings(input_mos, mos_prefix, mos_abbrev, step_ratios, ji_ratios) | function p._mos_tunings(input_mos, mos_prefix, mos_abbrev, step_ratios, ji_ratios, tolerance, footnotes, is_collapsed) | ||
local input_mos = input_mos or mos.new(5,2) | local input_mos = input_mos or mos.new(5,2) | ||
local mos_prefix = mos_prefix or "mos" | local mos_prefix = mos_prefix or "mos" | ||
local mos_abbrev = mos_abbrev or "m" | local mos_abbrev = mos_abbrev or "m" | ||
local step_ratios = step_ratios or { {2,1}, {3,1}, {3,2} } | local step_ratios = step_ratios or {{2, 1}, {3, 1}, {3, 2}} | ||
local ji_ratios = ji_ratios or {} | |||
local tolerance = tolerance or nil | |||
local is_collapsed = is_collapsed == true | |||
local footnotes = footnotes or "(footnotes here)" | |||
-- Scalesig | |||
local scale_sig = mos.as_string(input_mos) | local scale_sig = mos.as_string(input_mos) | ||
| Line 208: | Line 156: | ||
-- Preprocess JI ratios | -- Preprocess JI ratios | ||
local | local modal_union = mos.modal_union(input_mos) | ||
local | --local sorted_ji_ratios, search_info = p.preprocess_ji_ratios(input_mos, modal_union, step_ratios, ji_ratios, tolerance) | ||
-- Create table | -- Create table | ||
local result = "{| class=\"wikitable sortable right-all left-1 left-2\"\n" | local result = "{| class=\"wikitable sortable right-all left-1 left-2 mw-collapsible" .. (is_collapsed and " mw-collapsed\"\n" or "\"\n") | ||
-- Table caption | -- Table caption | ||
result = result .. | result = result .. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("%s of %s\n", step_ratio_range, scale_sig) | ||
.. "|-\n" | |||
-- First row of headers | -- First row of headers | ||
-- First two headers span two rows | -- First two headers span two rows | ||
result = result .. "! rowspan=\"2\" class=\"unsortable\" | Scale degree\n" | result = result | ||
.. "! rowspan=\"2\" class=\"unsortable\" | Scale degree\n" | |||
.. "! rowspan=\"2\" class=\"unsortable\" | Abbrev.\n" | |||
-- Headers for tunings; these span two cols | -- Headers for tunings; these span two cols | ||
| Line 232: | Line 181: | ||
end | end | ||
local et_as_string = et.as_string(mos. | local et_as_string = et.as_string(mos.as_et(input_mos, step_ratios[i])) | ||
local header_text = string.format("%s<br>[[%s]]", step_ratio_as_text, et_as_string) | local header_text = string.format("%s<br />[[%s]]", step_ratio_as_text, et_as_string) | ||
result = result .. string.format("! colspan=\"2\" | %s\n", header_text) | result = result .. string.format("! colspan=\"2\" | %s\n", header_text) | ||
end | end | ||
-- Headers for JI ratios; this spans two rows | -- Headers for JI ratios; this spans two rows | ||
result = result .. "! rowspan=\"2\" class=\"unsortable\" | Approx. | -- Commented out, pending rewrite | ||
--[[ | |||
if #ji_ratios ~= 0 then | |||
result = result .. "! rowspan=\"2\" class=\"unsortable\" | Approx. ratios*\n" | |||
end | |||
]]-- | |||
result = result .. "|-\n" | result = result .. "|-\n" | ||
-- Second row of headers | -- Second row of headers | ||
for i = 1, #step_ratios do | for i = 1, #step_ratios do | ||
result = result .. "! Steps\n" | result = result .. "! style=\"border-right: none;\" class=\"unsortable\" | Steps\n" | ||
result = result .. "! | result = result .. "! style=\"border-left: none; text-align: right;\" | ¢\n" | ||
end | end | ||
| Line 257: | Line 211: | ||
result = result | result = result | ||
.. "|-\n" | .. "|-\n" | ||
.. string.format("| %s | .. string.format("| %s\n", degree_name) | ||
.. string.format("| %s\n", degree_abbrev) | |||
-- Add cells for each interval's tunings | -- Add cells for each interval's tunings | ||
| Line 266: | Line 221: | ||
result = result | result = result | ||
.. " " | --.. string.format("\n| %s\\%s\n| %.1f", step_count, input_mos.nL * step_ratio[1] + input_mos.ns * step_ratio[2], cents) | ||
.. string.format("|| %s\\%s | .. string.format("| style=\"border-right: none;\" | %s\\%s\n", step_count, input_mos.nL * step_ratio[1] + input_mos.ns * step_ratio[2]) | ||
.. string.format("| style=\"border-left: none;\" | %.1f\n", cents) | |||
end | end | ||
-- Add cells for JI ratios | -- Add cells for JI ratios | ||
-- | --[[ | ||
-- Commented out, pending rewrite | |||
if #ji_ratios ~= 0 then | |||
-- Ratios link to their respective pages, and are comma-delimited. | |||
local ratios_as_text = jira.ratios_as_string(sorted_ji_ratios[i], true, ", ") | |||
result = result .. "| style=\"text-align: left;\" | " .. string.format("%s\n", ratios_as_text) | |||
end | end | ||
]]-- | |||
end | end | ||
-- End of table | -- End of table, plus footnotes | ||
result = result .. "|}" | result = result .. "|}" | ||
--[[ | |||
-- Commented out, pending rewrite | |||
if #ji_ratios ~= 0 then | |||
-- Make footnote text smaller than the rest of the text to avoid confusion with paragraph text | |||
result = result .. string.format("<span style=\"font-size: 0.75em;\">* %s</span>", footnotes) | |||
end | |||
]]-- | |||
return result | return result | ||
end | |||
-- Parse step ratios passed into template | |||
-- If the unparsed string is blank, default to the simple tunings. | |||
-- If the unparsed string is any of the step ratio range names, list the named | |||
-- ratios that fall within that range. | |||
-- if the unparsed string is blank, don't show any ratios. | |||
-- If the ratios is a list, parse it. | |||
function p.parse_step_ratios(unparsed) | |||
local parsed = {} | |||
local lookup_table = { | |||
["Central Spectrum"] = {{4, 3}, {3, 2}, {5, 3}, {2, 1}, {5, 2}, {3, 1}, {4, 1}}, | |||
["Simple Tunings"] = {{2, 1}, {3, 1}, {3, 2}}, | |||
["Soft-of-basic"] = {{4, 3}, {3, 2}, {2, 1}}, | |||
["Ultrasoft"] = {{6, 5}, {5, 4}, {4, 3}}, | |||
["Parasoft"] = {{4, 3}, {7, 5}, {3, 2}}, | |||
["Quasisoft"] = {{3, 2}, {8, 5}, {5, 3}}, | |||
["Minisoft"] = {{5, 3}, {7, 4}, {2, 1}}, | |||
["Hyposoft"] = {{3, 2}, {5, 3}, {2, 1}}, | |||
["Hypohard"] = {{2, 1}, {5, 2}, {3, 1}}, | |||
["Minihard"] = {{2, 1}, {7, 3}, {5, 2}}, | |||
["Quasihard"] = {{5, 2}, {8, 3}, {3, 1}}, | |||
["Parahard"] = {{3, 1}, {7, 2}, {4, 1}}, | |||
["Ultrahard"] = {{4, 1}, {5, 1}, {6, 1}}, | |||
["Hard-of-basic"] = {{2, 1}, {3, 1}, {4, 1}}, | |||
} | |||
if unparsed == "" then | |||
parsed = lookup_table["Simple Tunings"] | |||
elseif unparsed == "NONE" then | |||
parsed = {} | |||
else | |||
parsed = lookup_table[unparsed] or tip.parse_numeric_pairs(unparsed) | |||
end | |||
return parsed | |||
end | |||
-- Parse JI ratios passed into template | |||
-- If the unparsed string corresponds to a list of JI ratios ("a/b; c/d; e/f"), | |||
-- then parse it as a list of ratios. If it's not that, parse it as search | |||
-- args. If the text is "NONE", then there should be no ratios passed in. | |||
-- If the unparsed string is an empty string, return nil. (This is so the | |||
-- wrapper function can go by default search args.) | |||
function p.parse_ji_ratios(unparsed, equave) | |||
local ratios = nil | |||
local search_args = nil | |||
if unparsed == "" then | |||
search_args = {["Int Limit"] = 50, ["Tenney Height"] = 8; ["Complements Only"] = true} -- Defualt search args if no args were passed in | |||
ratios = jira.search_by_args_within_equave(equave, search_args) | |||
elseif unparsed == "NONE" then | |||
search_args = {} | |||
ratios = {} | |||
elseif string.match(unparsed, "Int Limit:") then | |||
search_args = jira.parse_search_args(unparsed) -- Search requires at the absolute least an int limit, so see if there's "Int Limit" | |||
ratios = jira.search_by_args_within_equave(equave, search_args) | |||
else | |||
search_args = {} | |||
ratios = jira.parse_ratios(unparsed) | |||
end | |||
return ratios, jira.search_footnotes(search_args) | |||
end | end | ||
| Line 294: | Line 317: | ||
function p.mos_tunings(frame) | function p.mos_tunings(frame) | ||
-- Get params | -- Get params | ||
local scalesig | local scalesig = frame.args["Scale Signature"] | ||
local mos_prefix | local input_mos = mos.parse(scalesig) | ||
local mos_abbrev | local mos_prefix = tamnams.verify_prefix(input_mos, frame.args["MOS Prefix"]) | ||
local | local mos_abbrev = tamnams.verify_abbrev(input_mos, frame.args["MOS Abbrev"]) | ||
local step_ratios = | local is_collapsed = yesno(frame.args["Collapsed"], false) | ||
local | local step_ratios = p.parse_step_ratios(frame.args["Step Ratios"]) | ||
local tolerance = tonumber(frame.args["Tolerance"]) | |||
local debugg = yesno(frame.args["debug"]) | |||
-- | --local ji_ratios, footnotes = p.parse_ji_ratios(frame.args["JI Ratios"], input_mos.equave) | ||
local | |||
local result = p._mos_tunings(input_mos, mos_prefix, mos_abbrev, step_ratios, ji_ratios, tolerance, footnotes, is_collapsed) | |||
if debugg == true then | |||
result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>" | |||
end | |||
return frame:preprocess(result) | |||
return | |||
end | end | ||
function p.tester() | function p.tester() | ||
local range, ratios = p.preprocess_step_ratios({ {7,1}, {3,1}, {2,1} } | local range, ratios = p.preprocess_step_ratios({{7, 1}, {3, 1}, {2, 1}}) | ||
local input_mos = mos.parse("9L 4s<7/2>") | local input_mos = mos.parse("9L 4s<7/2>") | ||
--return p.preprocess_ji_ratios(input_mos, mos.modal_union(input_mos), {{2,1}}, ji_ratios) | --return p.preprocess_ji_ratios(input_mos, mos.modal_union(input_mos), {{2,1}, {3,2}, {5,3}}, ji_ratios) | ||
--return ji_ratios | --return ji_ratios | ||
return p._mos_tunings(input_mos) | return p._mos_tunings(input_mos) | ||