Module:Scale tree: Difference between revisions
CompactStar (talk | contribs) Created page with "function p.infobox_ET(frame) return "test" end" |
ArrowHead294 (talk | contribs) mNo edit summary |
||
| (130 intermediate revisions by 4 users not shown) | |||
| Line 1: | Line 1: | ||
function p. | local MOS = require("Module:MOS") | ||
local ET = require("Module:ET") | |||
local rat = require("Module:Rational") | |||
local sb = require("Module:SB tree") | |||
local utils = require("Module:Utils") | |||
local yesno = require("Module:yesno") | |||
local p = {} | |||
-- Helper function that parses entries from a semicolon-delimited string and returns them in an array | |||
-- TODO: Separate this and parse_pairs into its own module of helper functions, as they're included | |||
-- in various modules at this point | |||
function p.parse_entries(unparsed) | |||
local parsed = {} | |||
-- for entry in string.gmatch(unparsed, "([^;]+)") do | |||
for entry in string.gmatch(unparsed, "([^%$]+)") do | |||
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1") | |||
table.insert(parsed, trimmed) -- Add to array | |||
end | |||
return parsed | |||
end | end | ||
-- Helper function that parses pairs of elements separated by a colon | |||
-- A pair must be two elements or it will be returned as an empty array | |||
function p.parse_pair(unparsed) | |||
local parsed = {} | |||
for entry in string.gmatch(unparsed, "([^:]+)") do | |||
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1") | |||
table.insert(parsed, trimmed) -- Add to array | |||
end | |||
if #parsed == 2 then | |||
return parsed | |||
else | |||
return {} | |||
end | |||
end | |||
-- Function that takes a list of semicolon-delimited pairs and returns a map | |||
-- (or dictionary or associative array) of key-value pairs | |||
-- Each entry is colon-delimited as key : pair | |||
function p.parse_kv_pairs(unparsed) | |||
-- Tokenize the string of unparsed pairs | |||
local parsed = p.parse_entries(unparsed) | |||
-- Then tokenize the tokens into key-value pairs | |||
local pairs_ = {} | |||
for i = 1, #parsed do | |||
local pair = p.parse_pair(parsed[i]) | |||
if #pair == 2 then | |||
pairs_[pair[1]] = pair[2] | |||
end | |||
end | |||
return pairs_ | |||
end | |||
-- Rewrite of scale tree function (has bugfixes and new formatting) | |||
function p._scale_tree(input_mos, depth, comments) | |||
local input_mos = input_mos or MOS.new(5, 2) | |||
local depth = depth or 5 | |||
local comments = comments or {} | |||
local equave = input_mos.equave | |||
local L = input_mos.nL -- Large steps in mos | |||
local s = input_mos.ns -- Small steps in mos | |||
local n = utils._gcd(L, s) -- Number of periods | |||
local abstract_bright_gen = MOS.bright_gen(input_mos) | |||
local step_ratios = sb.sb_tree_ratios(depth) | |||
local depths = sb.sb_tree_depths(depth) | |||
-- What is the equave suffix (edo, edt, edf, ed-p/q) | |||
local equave_suffix = "" | |||
if rat.eq(input_mos.equave, rat.new(2)) then | |||
equave_suffix = "o" | |||
elseif rat.eq(input_mos.equave, rat.new(3)) then | |||
equave_suffix = "t" | |||
elseif rat.eq(input_mos.equave, rat.new(3, 2)) then | |||
equave_suffix = "f" | |||
else | |||
equave_suffix = rat.as_ratio(input_mos.equave) | |||
end | |||
-- Default comments for TAMNAMS-named step ratios | |||
local default_comments = {} | |||
local mos_as_string = MOS.as_string(input_mos) | |||
default_comments["1/1"] = string.format("'''Equalized %s'''", mos_as_string) | |||
default_comments["4/3"] = string.format("'''Supersoft %s'''", mos_as_string) | |||
default_comments["3/2"] = string.format("'''Soft %s'''", mos_as_string) | |||
default_comments["5/3"] = string.format("'''Semisoft %s'''", mos_as_string) | |||
default_comments["2/1"] = string.format("'''Basic %s'''", mos_as_string) | |||
default_comments["5/2"] = string.format("'''Semihard %s'''", mos_as_string) | |||
default_comments["3/1"] = string.format("'''Hard %s'''", mos_as_string) | |||
default_comments["4/1"] = string.format("'''Superhard %s'''", mos_as_string) | |||
default_comments["1/0"] = string.format("'''Collapsed %s'''", mos_as_string) | |||
-- Append boundary of proper scales to basic comment, if applicable | |||
-- Monosmall mosses and knL ns mosses are always proper, but all other mosses | |||
-- are proper if the step ratio is within the soft-of-basic range | |||
if n < s then | |||
default_comments["2/1"] = default_comments["2/1"] .. "<br />Scales with tunings softer than this are proper" | |||
end | |||
-- Produce table header for the comments | |||
local comments_header_text = "Comments" | |||
if s == 1 then | |||
comments_header_text = comments_header_text .. "<sup><abbr title=\"Every tuning produces a proper scale.\">(always proper)</abbr></sup>" | |||
elseif s == n and n > 1 then | |||
comments_header_text = comments_header_text .. "<sup><abbr title=\"Every true-MOS tuning produces a proper scale.\">(always proper)</abbr></sup>" | |||
end | |||
-- Table headers | |||
-- There are 6 columns: | |||
-- - Steps of ED | |||
-- - Bright and dark gens in cents | |||
-- - Step ratio and hardness | |||
-- - Comments | |||
local result = "{| class=\"wikitable center-all\"\n" | |||
.. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("Scale tree and tuning spectrum of %s\n", mos_as_string) | |||
.. "|-\n" | |||
.. string.format("! rowspan=\"2\" colspan=\"%d\" | Generator<sup><abbr title=\"In steps of ed%s.\">(ed%s)</abbr></sup>\n", depth + 1, equave_suffix, equave_suffix) | |||
.. "! colspan=\"2\" | Cents\n" | |||
.. "! colspan=\"2\" | Step ratio\n" | |||
.. "! rowspan=\"2\" | " .. comments_header_text .. "\n" | |||
.. "|-\n" | |||
.. "! Bright\n" | |||
.. "! Dark\n" | |||
.. "! L:s\n" | |||
.. "! Hardness\n" | |||
-- Rounding is done using string.format, to 3 decimal places (%.3f) | |||
-- Create each row of the table | |||
for i = 1, #step_ratios do | |||
local step_ratio = step_ratios[i] | |||
local steps_per_equave = step_ratio[1] * L + step_ratio[2] * s | |||
local steps_per_period = steps_per_equave / n | |||
local et = ET.new(steps_per_equave, equave) | |||
-- Calculate the bright gen and cent value | |||
local bright_generator_steps = step_ratio[1] * abstract_bright_gen["L"] + step_ratio[2] * abstract_bright_gen["s"] | |||
local bright_generator_cents = ET.cents(et, bright_generator_steps) | |||
-- Calculate dark generator step count and cent value | |||
local dark_generator_steps = steps_per_period - bright_generator_steps | |||
local dark_generator_cents = ET.cents(et, dark_generator_steps) | |||
-- New row | |||
result = result .. "|-\n" | |||
-- Cells for bright generator, as steps in et | |||
local current_depth = depths[i] | |||
for i = 1, depth + 1 do | |||
result = result .. "| " | |||
if i == current_depth then | |||
result = result .. string.format("[[%s|%d\\%s]]", ET.as_string(et), bright_generator_steps, et.size) | |||
end | |||
result = result .. "\n" | |||
end | |||
-- Cells for generators in cents | |||
result = result .. string.format("| %.3f\n", bright_generator_cents) | |||
result = result .. string.format("| %.3f\n", dark_generator_cents) | |||
-- Cell for step ratio | |||
result = result .. string.format("| %d:%d\n", step_ratio[1], step_ratio[2]) | |||
-- Cell for hardness, with divide-by-zero check | |||
local hardness = "" | |||
if step_ratio[2] == 0 then | |||
hardness = "→ ∞" | |||
else | |||
hardness = string.format("%.3f", step_ratio[1] / step_ratio[2]) | |||
end | |||
result = result .. string.format("| %s\n", hardness) | |||
-- Cell for comment | |||
-- Default comments are on their own line before custom comments | |||
local key = step_ratios[i][1] .. "/" .. step_ratios[i][2] -- The step ratio is (literally and figuratively) the key to add comments! | |||
local comment = "" | |||
local default_comment = default_comments[key] or "" | |||
local custom_comment = comments[key] or "" | |||
if default_comment == "" then | |||
comment = custom_comment | |||
else | |||
comment = default_comment .. "<br />" .. custom_comment | |||
end | |||
result = result .. string.format("| style=\"text-align: left;\" | %s\n", comment) | |||
end | |||
result = result .. "|}" | |||
return result | |||
end | |||
function p.scale_tree(frame) | |||
local mos = MOS.parse(frame.args["tuning"]) | |||
local depth = frame.args["depth"] or 5 | |||
local comments_unparsed = frame.args["Comments"] or "" | |||
local comments = p.parse_kv_pairs(comments_unparsed) or {} | |||
local out_str = p._scale_tree(mos, depth, comments) | |||
local debugg = yesno(frame.args["debug"]) | |||
return frame:preprocess(debugg == true and "<pre>" .. out_str .. "</pre>" or out_str) | |||
end | |||
return p | |||