Module:Infobox MOS: Difference between revisions

Ganaram inukshuk (talk | contribs)
Moved categories to helper function
ArrowHead294 (talk | contribs)
m No need to use NBSP on ones that aren't displayed
 
(218 intermediate revisions by 5 users not shown)
Line 1: Line 1:
local p = {}
local p = {}
local i = require('Module:Interval')
local u = require('Module:Utils')
local rat = require('Module:Rational')
local MOS = require('Module:MOS')
local ET = require('Module:ET')
local xp = require('Module:Xenpaper')
local infobox = require('Module:Infobox')


local common_suffix = {
local getArgs = require("Module:Arguments").getArgs
['3/2'] = 'f',
local ib = require("Module:Infobox")
['2'] = 'o',
local kbvis = require("Module:Keyboard vis")
['2/1'] = 'o',
local mos = require("Module:MOS")
['3'] = 't',
local tamnams = require("Module:TAMNAMS")
['3/1'] = 't',
local tip = require("Module:Template input parse")
}
local xp = require("Module:Xenpaper")
local yesno = require("Module:Yesno")


local common_ratio = {
-- TODO: REWRITE. REFACTOR. AGAIN.
['f'] = rat.new(3, 2),
['o'] = 2,
['t'] = 3,
}


function gcd(x, y)
-- TODO: bugfix tamnams lookup breaking for mosses with 5 steps or less; interim
if x < y then
-- fix currently implemented shows smaller mosses as descending from itself.
x, y = y, x
 
-- Helper function
-- Concatenates the contents of two tables into one
-- This doesn't have a return value; rather, the first table passed has the
-- second table's contents added to it.
function p.concatenate_tables(t1, t2)
for i=1, #t2 do
t1[#t1 + 1] = t2[i]
end
end
while y > 0 do
x, y = y, x % y
end
return x
end
end


function display_range (arg1, arg2, arg3, arg4) --If arg1 < arg2, then display "arg3 to arg4"; otherwise display "arg4 to arg3".
-- Helper function
if arg1 < arg2 then
-- Annotates a section header with the equave as superscript text.
return arg3 .. ' to ' .. arg4
function p.annotate_section_header(input_mos, section_header)
else
local et_suffix = mos.et_suffix(input_mos)
return arg4 .. ' to ' .. arg3
return (et_suffix == "edo"
and string.format('<div style=\"margin-top: 0.6em;\"><b>%s</b></div>', section_header)
or  string.format('<div style=\"margin-top: 0.6em;\"><b>%s</b><sup><abbr title=\"In steps of %s\">(%s)</sup></div>', section_header, et_suffix, et_suffix)
)
end
 
-- Helper function
-- Create a keyboard visualization, based on the Halberstadt keyboard layout
function p.kb_vis(input_mos)
local input_mos = input_mos or mos.new(5, 2)
local brightest_mode = mos.brightest_mode(input_mos)
local vis = ""
if input_mos.nL + input_mos.ns < 40 then
vis = kbvis.vis_small(brightest_mode)
end
end
end
function round(num, numDecimalPlaces)
return {{["Header"] = vis}}
  local mult = 10^(numDecimalPlaces or 0)
  return math.floor(num * mult + 0.5) / mult
end
end


-- Helper function
-- Helper function
-- Adds categories
-- Adds categories
function p.categorize(tuning)
function p.categorize(input_mos)
local tuning = tuning or "5L 2s"
local input_mos = input_mos or mos.new(5, 2)
local input_mos = MOS.parse(tuning)
-- Add to category of abstact mosses
-- Add to category of abstact mosses
local categories = "[[Category:Abstract MOS patterns]]"
local categories = " [[Category:Abstract MOS patterns]]"
-- Add tuning category
categories = categories .. "\n" .. string.format('[[Category:%s]]', tuning)
-- Add notecount category if the notecount is greater than 3
-- Add notecount category if the notecount is greater than 3
local notecount = input_mos.nL + input_mos.ns
local notecount = input_mos.nL + input_mos.ns
if notecount > 3 then
if notecount > 3 then
categories = categories .. "\n" .. string.format('[[Category:%d-tone scales]]', notecount)
categories = categories .. string.format(" [[Category:%d-tone scales]]", notecount)
end
end
-- Add to nonoctave category if the equave is nonoctave
-- If the mos is octave-equivalent, add appropriate tamnams-named categories
if not rat.eq(input_mos.equave, rat.new(2)) then
-- Otherwise, add to nonoctave category
categories = categories .. "\n" .. '[[Category:Nonoctave]]'
if mos.is_octave_equivalent(input_mos) then
-- Caveats:
-- - Only octave-equivalent mos names are used as categories.
-- - Monowood and biwood are excluded (for now).
-- - Mosses whose notecounts > 10 and periods < 5 are categorized under
--  the closest tamnams-named ancestor.
local ancestor_mos = tamnams.find_ancestor(input_mos)
local tamnams_name = tamnams.lookup_name(ancestor_mos)
if tamnams_name ~= nil then
categories = categories .. string.format(" [[Category:%s]]", tamnams_name)
end
else
categories = categories .. " [[Category:Nonoctave]]"
end
end
Line 71: Line 85:
end
end


-- Main function
-- Helper function
-- To be called by wrapper
-- Creates adjacent links for mos, found by +/-1 large or +/- small steps
function p._infobox_MOS(tuning, debug_mode, other_names)
function p.adjacent_links(input_mos)
local tuning = tuning or "5L 2s"
local input_mos = input_mos or mos.new(1, 1)
local debug_mode = debug_mode == true
local other_names = other_names or nil
local adjacent_mosses = {
local mos = MOS.parse(tuning)
mos.new(input_mos.nL - 1, input_mos.ns - 1, input_mos.equave), -- UL
mos.new(input_mos.nL    , input_mos.ns - 1, input_mos.equave), -- U
mos.new(input_mos.nL + 1, input_mos.ns - 1, input_mos.equave), -- UR
mos.new(input_mos.nL - 1, input_mos.ns    , input_mos.equave), -- L
mos.new(input_mos.nL + 1, input_mos.ns    , input_mos.equave), -- R
mos.new(input_mos.nL - 1, input_mos.ns + 1, input_mos.equave), -- DL
mos.new(input_mos.nL    , input_mos.ns + 1, input_mos.equave), -- D
mos.new(input_mos.nL + 1, input_mos.ns + 1, input_mos.equave), -- DR
}
local equave = mos.equave
local adjacent_links = {
local equave_link = ' (' .. rat.as_ratio(equave) .. '-equivalent)'
mos.is_valid(adjacent_mosses[1]) and string.format("[[%s|&#x2196;&nbsp;%s]]", mos.as_long_string(adjacent_mosses[1], false), mos.as_string(adjacent_mosses[1]), true) or "",
local equave_disp = '⟨' .. rat.as_ratio(equave) .. '⟩'
mos.is_valid(adjacent_mosses[2]) and string.format("[[%s|&#x2191;&nbsp;%s]]", mos.as_long_string(adjacent_mosses[2], false), mos.as_string(adjacent_mosses[2]), true) or "",
if rat.eq(equave, 2) then
mos.is_valid(adjacent_mosses[3]) and string.format("[[%s|%s&nbsp;&#x2197;]]", mos.as_long_string(adjacent_mosses[3], false), mos.as_string(adjacent_mosses[3]), true) or "",
equave_link = ''
mos.is_valid(adjacent_mosses[4]) and string.format("[[%s|&#x2190;&nbsp;%s]]", mos.as_long_string(adjacent_mosses[4], false), mos.as_string(adjacent_mosses[4]), true) or "",
equave_disp = ''
mos.is_valid(adjacent_mosses[5]) and string.format("[[%s|%s&nbsp;&#x2192;]]", mos.as_long_string(adjacent_mosses[5], false), mos.as_string(adjacent_mosses[5]), true) or "",
mos.is_valid(adjacent_mosses[6]) and string.format("[[%s|&#x2199;&nbsp;%s]]", mos.as_long_string(adjacent_mosses[6], false), mos.as_string(adjacent_mosses[6]), true) or "",
mos.is_valid(adjacent_mosses[7]) and string.format("[[%s|&#x2193;&nbsp;%s]]", mos.as_long_string(adjacent_mosses[7], false), mos.as_string(adjacent_mosses[7]), true) or "",
mos.is_valid(adjacent_mosses[8]) and string.format("[[%s|%s&nbsp;&#x2198;]]", mos.as_long_string(adjacent_mosses[8], false), mos.as_string(adjacent_mosses[8]), true) or ""
}
return adjacent_links
end
 
-- TODO: Cleanup and adopt mos functions
-- Helper function
-- Produces section entries for scale sturcture
-- Section is returned as a jagged array and return value must be merged into
-- a larger array.
function p.scale_structure(input_mos)
local input_mos = input_mos or mos.new(5, 2)
local equave_as_string = mos.equave_as_string(input_mos)
local equave_in_cents  = mos.equave_to_cents(input_mos)
local number_of_periods = mos.period_count(input_mos)
local period_as_string = ""
if number_of_periods == 1 then
period_as_string = equave_as_string
else
period_as_string = mos.reduced_period_to_et_string(input_mos, "")
end
end
local nL = mos.nL
local period_in_cents = equave_in_cents / number_of_periods
local ns = mos.ns
local has_prev_y = (ns > 1)
local step_pattern = string.format("...%d steps...", input_mos.nL + input_mos.ns)
local has_prev_x = (nL > 1)
if input_mos.nL + input_mos.ns <= 40 then
local prev_L, next_L, prev_s,next_s, prev_L_prev_s, prev_L_next_s, next_L_prev_s, next_L_next_s = '', '', '', '', '', '', '', ''
local brightest_mode = mos.brightest_mode(input_mos)
next_s = '[[' .. nL .. 'L ' .. (ns + 1) .. 's' .. equave_link .. '|↓' .. nL .. 'L ' .. (ns + 1) .. 's' .. equave_disp .. ']]'
step_pattern = string.format("<abbr title=\"Brightest mode\">%s</abbr><br /><abbr title=\"Darkest mode\">%s</abbr>", brightest_mode, string.reverse(brightest_mode))
next_L = '[[' .. (nL + 1) .. 'L ' .. ns .. 's' .. equave_link .. '|' .. (nL + 1) .. 'L ' .. ns .. 's' .. equave_disp .. '→]]'
next_L_next_s = '[[' .. (nL + 1) .. 'L ' .. (ns + 1) .. 's' .. equave_link .. '|' .. (nL + 1) .. 'L ' .. (ns + 1) .. 's' .. equave_disp .. '↘]]'
if has_prev_y then
prev_s = '[[' .. nL .. 'L ' .. (ns - 1) .. 's' .. equave_link .. '|↑' .. nL .. 'L ' .. (ns - 1) .. 's' .. equave_disp .. ']]'
next_L_prev_s = '[[' .. (nL + 1) .. 'L ' .. (ns - 1) .. 's' .. equave_link .. '|' .. (nL + 1) .. 'L ' .. (ns - 1) .. 's' .. equave_disp .. '↗]]'
end
if has_prev_x then
prev_L = '[[' .. (nL - 1) .. 'L ' .. ns .. 's' .. equave_link .. '|←' .. (nL - 1) .. 'L ' .. ns .. 's' .. equave_disp .. ']]'
prev_L_next_s = '[[' .. (nL - 1) .. 'L ' .. (ns + 1) .. 's' .. equave_link .. '|↙' .. (nL - 1) .. 'L ' .. (ns + 1) .. 's' .. equave_disp .. ']]'
end
if has_prev_y and has_prev_x then
prev_L_prev_s = '[[' .. (nL - 1) .. 'L ' .. (ns - 1) .. 's' .. equave_link .. '|↖' .. (nL - 1) .. 'L ' .. (ns - 1) .. 's' .. equave_disp .. ']]'
end
end
local adjacent_links = {
local section_header = "Scale structure"
[1]=prev_L_prev_s,  
local section_entries = {
[2]=prev_s,  
{ ["Header"] = string.format("<div style=\"margin-top: 0.6em;\"><b>%s</b></div>", section_header)},
[3]=next_L_prev_s,  
{ ["Header"] = "[[Step pattern]]", ["Data"] = step_pattern},
[4]=prev_L,  
{ ["Header"] = "[[Equave]]"      , ["Data"] = string.format("%s (%.1f{{c}})", equave_as_string, equave_in_cents) },
[5]=next_L,  
{ ["Header"] = "[[Period]]"      , ["Data"] = string.format("%s (%.1f{{c}})", period_as_string, period_in_cents) }
[6]=prev_L_next_s,  
}
[7]=next_s,  
 
[8]=next_L_next_s
return section_entries
end
 
-- Helper function
-- Produces generator ranges for scale
-- Section is returned as a jagged array and return value must be merged into
-- a larger array.
function p.generator_sizes(input_mos)
local input_mos = input_mos or mos.new(5, 2,3)
local bright_min_in_steps = mos.bright_gen_to_et_string(input_mos, {1, 1}, "")
local bright_max_in_steps = mos.bright_gen_to_et_string(input_mos, {1, 0}, "")
local dark_min_in_steps  = mos.dark_gen_to_et_string  (input_mos, {1, 0}, "")
local dark_max_in_steps  = mos.dark_gen_to_et_string  (input_mos, {1, 1}, "")
local bright_min_in_cents = mos.bright_gen_to_cents(input_mos, {1, 1})
local bright_max_in_cents = mos.bright_gen_to_cents(input_mos, {1, 0})
local dark_min_in_cents = mos.dark_gen_to_cents(input_mos, {1, 0})
local dark_max_in_cents = mos.dark_gen_to_cents(input_mos, {1, 1})
local section_header = p.annotate_section_header(input_mos, "Generator size")
local section_entries = {
{ ["Header"] = section_header},
{ ["Header"] = "[[Bright]]", ["Data"] = string.format("%s to %s (%.1f{{c}} to %.1f{{c}})", bright_min_in_steps, bright_max_in_steps, bright_min_in_cents, bright_max_in_cents) },
{ ["Header"] = "[[Dark]]"  , ["Data"] = string.format("%s to %s (%.1f{{c}} to %.1f{{c}})", dark_min_in_steps, dark_max_in_steps, dark_min_in_cents, dark_max_in_cents) },
}
}
local period = rat.as_ratio(equave)
return section_entries
d = gcd(nL, ns)
end
if d > 1 then
 
period = ET.backslash_display(ET.new(d, equave), 1)
-- Helper function
end
-- Produces section entries for tamnams info
local pattern = MOS.brightest_mode(mos)
-- Conditions for tamnams info inclusion:
local collapsed_et = ET.new(nL, equave)
-- - Scale is octave-equivalent.
local abstract_bright_gen = MOS.bright_gen(mos)
-- - Scales within the "named range" (6-10 notes, or is 1L 1s or 2L 2s) have
local collapsed_bright_steps = abstract_bright_gen['L']
--  a tamnams name.
local collapsed_dark_steps = nL/d - collapsed_bright_steps
-- - Scales with a notecount greater than 10 and no more than 5 periods have
local equalized_et = ET.new(nL + ns, equave)
--  a tamnams-named ancestor.
local equalized_bright_steps = abstract_bright_gen['L'] + abstract_bright_gen['s']
-- - Scales with a notecount greater than 10 and more than 5 periods don't have
local equalized_dark_steps = (nL + ns)/d - equalized_bright_steps
--  a tamnams-named ancestor, but will report what nL ns mos they descend from.
-- Section is returned as a jagged array and return value must be merged into
-- a larger array.
function p.tamnams_information(input_mos)
local input_mos = input_mos or mos.new(6,6,3)
local structure_data = {}
-- If a mos is octave-equivalent and has 10 or fewer steps, then it has a
-- tamnams name/prefix/abbrev.
-- If a mos is octave-equivalent, has more than 10 steps, and is not a root
-- mos nL ns, then:
-- - If it has 5 periods or less, then its closest ancestor has a tamnams
--  name.
-- - If it has more than 5 periods, then it relates to a root mos nL ns that
--  has more than 10 steps (ancestor therefore has no tamnams name).
-- If a mos is not octave-equivalent, then it may have a tamnams name (if
-- step count is 5 or less; currently unsupported) or not.
local is_octave_equivalent  = mos.is_octave_equivalent(input_mos)
local has_tamnams_name      = tamnams.lookup_name(input_mos) ~= nil
local is_within_named_range  = mos.step_count(input_mos) <= 10
local is_within_period_count = mos.period_count(input_mos) <= 5
local is_root_mos = input_mos.nL == input_mos.ns
table.insert(structure_data, {
local section_header = "TAMNAMS information"
'Brightest mode',
local section_entries = nil
pattern
if is_octave_equivalent and has_tamnams_name then
})
section_entries = {
table.insert(structure_data, {
{ ["Header"] = string.format("<div style=\"margin-top: 0.6em;\"><b>%s</b></div>", section_header) },
'Period',
{ ["Header"] = "[[TAMNAMS#Mos_pattern_names | Name]]"    , ["Data"] = tamnams.lookup_name  (input_mos) },
period
{ ["Header"] = "[[TAMNAMS#Mos_pattern_names | Prefix]]"  , ["Data"] = tamnams.lookup_prefix(input_mos) .. "-" },
})
{ ["Header"] = "[[TAMNAMS#Mos_pattern_names | Abbrev.]]" , ["Data"] = tamnams.lookup_abbrev(input_mos) }
table.insert(structure_data, {
}
'Range for [[bright]] [[generator]]',
elseif is_octave_equivalent and not has_tamnams_name and not is_root_mos and not is_within_named_range then
display_range(collapsed_bright_steps/collapsed_et.size, equalized_bright_steps/equalized_et.size,  
-- Lookup closest named ancestor mos
collapsed_bright_steps .. ET.backslash_modifier(collapsed_et) .. ' (' .. round(ET.cents(collapsed_et, collapsed_bright_steps), 1) .. '¢)',  
local ancestor_mos, ratio_1, ratio_2, generations = tamnams.find_ancestor_info(input_mos)
equalized_bright_steps .. ET.backslash_modifier(equalized_et) .. ' (' .. round(ET.cents(equalized_et, equalized_bright_steps), 1) .. '¢)'
)
-- Link to ancestor mos
})
local ancestor_link = mos.as_link(ancestor_mos)
table.insert(structure_data, {
'Range for [[dark]] [[generator]]',
-- Lookup step ratio range
display_range(collapsed_dark_steps/collapsed_et.size, equalized_dark_steps/equalized_et.size,
local step_ratio_range = string.format("%s:%s to %s:%s", ratio_1[1], ratio_1[2], ratio_2[1], ratio_2[2])
collapsed_dark_steps .. ET.backslash_modifier(collapsed_et) .. ' (' .. round(ET.cents(collapsed_et, collapsed_dark_steps), 1) .. '¢)',  
local range_name = tamnams.lookup_step_ratio_range(ratio_1, ratio_2)
equalized_dark_steps .. ET.backslash_modifier(equalized_et) .. ' (' .. round(ET.cents(equalized_et, equalized_dark_steps), 1) .. '¢)'
local step_ratio_range_entry = range_name == nil and step_ratio_range or string.format("%s (%s)", step_ratio_range, range_name)
)
})
local ancestor_name = tamnams.lookup_name(ancestor_mos)
local tamnams = MOS.tamnams_name[MOS.as_string(mos)]
local ancestor_entry = ancestor_name == nil and ancestor_link or string.format("%s (%s)", ancestor_link, ancestor_name)
if tamnams ~= nil then
table.insert(structure_data, {
section_entries = {
'[[TAMNAMS#Mos pattern names|TAMNAMS name]]',
{ ["Header"] = string.format("<div style=\"margin-top: 0.6em;\"><b>%s</b></div>", section_header) },
tamnams
{ ["Header"] = "Related to" , ["Data"] = ancestor_entry },
})
{ ["Header"] = "With tunings", ["Data"] = step_ratio_range_entry }
end
}
local pfx = MOS.tamnams_prefix[MOS.as_string(mos)]
if pfx ~= nil then
table.insert(structure_data, {
'[[TAMNAMS#Mos pattern names|TAMNAMS prefix]]',
pfx
})
end
if nL == ns then
parent_mos_link = 'none'
else
parent_mos_link = '[[' .. math.min(nL, ns) .. 'L ' .. math.max(nL, ns)-math.min(nL, ns) .. 's' .. equave_link .. '|' 
.. math.min(nL, ns) .. 'L ' .. math.max(nL, ns)-math.min(nL, ns) .. 's' .. equave_disp .. ']]'
end
table.insert(structure_data, {
'Parent MOS',
parent_mos_link
})
table.insert(structure_data, {
'[[Operations on MOSes#Sister_MOS|Sister MOS]]',
'[[' .. ns .. 'L ' .. nL .. 's' .. equave_link .. '|' .. ns .. 'L ' .. nL .. 's' .. equave_disp .. ']]'
})
table.insert(structure_data, {
'Daughter MOSes',
'[[' .. nL + ns .. 'L ' .. nL .. 's' .. equave_link .. '|'  
.. nL + ns .. 'L ' .. nL .. 's' .. equave_disp .. ']], [['
.. nL .. 'L ' .. nL + ns .. 's' .. equave_link.. '|' .. nL .. 'L ' .. nL + ns .. 's' .. equave_disp .. ']]'
})
if other_names ~= nil then
table.insert(structure_data, {
'Other names', other_names
})
end
end
-- Compute et generators by taking mediants from collapsed and equalized generators
local supersoft_et = ET.new(1*collapsed_et.size + 3*equalized_et.size, equave, nil)
return section_entries
local supersoft_bright_steps = 1*collapsed_bright_steps + 3*equalized_bright_steps
end
local supersoft_gen = ET.backslash_display(supersoft_et, supersoft_bright_steps)
 
local supersoft_gen_cents = round(ET.cents(supersoft_et, supersoft_bright_steps), 1)
-- Helper function
-- Adds a section for scale names
function p.other_names(other_names)
local other_names = other_names or {"p-chromatic", "hard diatonic"}
local soft_et = ET.new(1*collapsed_et.size + 2*equalized_et.size, equave, nil)
local section_header = "Other names"
local soft_bright_steps =  1*collapsed_bright_steps + 2*equalized_bright_steps
local soft_gen = ET.backslash_display(soft_et, soft_bright_steps)
local soft_gen_cents = round(ET.cents(soft_et, soft_bright_steps), 1)
local semisoft_et = ET.new(2*collapsed_et.size + 3*equalized_et.size, equave, nil)
if #other_names == 0 then
local semisoft_bright_steps = 2*collapsed_bright_steps + 3*equalized_bright_steps
return nil
local semisoft_gen = ET.backslash_display(semisoft_et, semisoft_bright_steps)
else
local semisoft_gen_cents = round(ET.cents(semisoft_et, semisoft_bright_steps), 1)
local scale_names = ""
for i=1, #other_names do
scale_names = scale_names .. other_names[i]
if i ~= #other_names then
scale_names = scale_names .. "<br />"
end
end
local section_entries = {
{ ["Header"] = string.format("<div style=\"margin-top: 0.6em;\"><b>%s</b></div>", section_header)},
{ ["Header"] = "Name(s)", ["Data"] = scale_names}
}
return section_entries
end
end
 
-- Helper function
-- Produces section for related scales
-- Section is returned as a jagged array and return value must be merged into
-- a larger array.
function p.related_scales(input_mos)
local input_mos = input_mos or mos.new(5, 2)
local basic_et = ET.new(1*collapsed_et.size + 1*equalized_et.size, equave, nil)
-- Produce the mos's relatives
local basic_bright_steps = 1*collapsed_bright_steps + 1*equalized_bright_steps
local parent_mos, sister_mos, soft_child_mos, hard_child_mos, neutral_mos, soft_flought_mos, hard_flought_mos
local basic_gen = ET.backslash_display(basic_et, basic_bright_steps)
parent_mos = mos.parent(input_mos)
local basic_gen_cents = round(ET.cents(basic_et, basic_bright_steps), 1)
sister_mos = mos.sister(input_mos)
soft_child_mos, hard_child_mos = mos.children(input_mos)
neutral_mos = mos.neutralized(input_mos)
soft_flought_mos, hard_flought_mos = mos.interleaved(input_mos)
local semihard_et = ET.new(3*collapsed_et.size + 2*equalized_et.size, equave, nil)
-- Produce links to those relatives; parent and sister links have extra
local semihard_bright_steps = 3*collapsed_bright_steps + 2*equalized_bright_steps
-- checks to make sure they're valid mosses:
local semihard_gen = ET.backslash_display(semihard_et, semihard_bright_steps)
-- - If the input mos is a root mos (nL ns), then it has no parent
local semihard_gen_cents = round(ET.cents(semihard_et, semihard_bright_steps), 1)
-- - If the input mos is a root mos, then it's its own sister
local is_nL_ns = input_mos.nL == input_mos.ns
local parent_scalesig      = is_nL_ns and "none" or mos.as_link(parent_mos)
local sister_scalesig      = is_nL_ns and (mos.as_string(input_mos) .. " (self)") or mos.as_link(sister_mos)
local soft_scalesig        = mos.as_link(soft_child_mos)
local hard_scalesig        = mos.as_link(hard_child_mos)
local neutral_scalesig      = mos.as_link(neutral_mos)
local soft_flought_scalesig = mos.as_link(soft_flought_mos)
local hard_flought_scalesig = mos.as_link(hard_flought_mos)
local hard_et = ET.new(2*collapsed_et.size + 1*equalized_et.size, equave, nil)
local section_header = "Related MOS scales"
local hard_bright_steps = 2*collapsed_bright_steps + 1*equalized_bright_steps
local section_entries = {
local hard_gen = ET.backslash_display(hard_et, hard_bright_steps)
{ ["Header"] = string.format("<div style=\"margin-top: 0.6em;\"><b>%s</b></div>", section_header)},
local hard_gen_cents = round(ET.cents(hard_et, hard_bright_steps), 1)
{ ["Header"] = "[[Operations_on_MOSes#Parent_MOS | Parent]]"        , ["Data"] = parent_scalesig },
{ ["Header"] = "[[Operations_on_MOSes#Sister_MOS | Sister]]"        , ["Data"] = sister_scalesig },
{ ["Header"] = "[[Operations_on_MOSes#Daughter_MOS | Daughters]]"    , ["Data"] = soft_scalesig .. ", " .. hard_scalesig },
{ ["Header"] = "[[Operations_on_MOSes#Neutralization | Neutralized]]", ["Data"] = neutral_scalesig},
{ ["Header"] = "[[Flought_scale | 2-Flought]]"                      , ["Data"] = soft_flought_scalesig .. ", " .. hard_flought_scalesig }
}
local superhard_et = ET.new(3*collapsed_et.size + 1*equalized_et.size, equave, nil)
return section_entries
local superhard_bright_steps = 3*collapsed_bright_steps + 1*equalized_bright_steps
end
local superhard_gen = ET.backslash_display(superhard_et, superhard_bright_steps)
 
local superhard_gen_cents = round(ET.cents(superhard_et, superhard_bright_steps), 1)
-- Helper function
-- Produces simple equal tunings
-- Includes xenpaper links
function p.equal_tunings(input_mos)
local input_mos = input_mos or mos.new(5, 2)
local bright_gen = mos.bright_gen(input_mos)
local step_ratios = {
{ 1, 1 },
{ 4, 3 },
{ 3, 2 },
{ 5, 3 },
{ 2, 1 },
{ 5, 2 },
{ 3, 1 },
{ 4, 1 },
{ 1, 0 }
}
local et_data = {}
local section_header = p.annotate_section_header(input_mos, "Equal tunings")
table.insert(et_data, {
local section_entries = {{ ["Header"] = section_header }}
'[[TAMNAMS#Step ratio spectrum|Supersoft]] (L:s = 4:3)',
'[[' .. ET.as_string(supersoft_et) .. '|' .. supersoft_gen .. ']] (' .. supersoft_gen_cents .. '¢)'
})
table.insert(et_data, {
'[[TAMNAMS#Step ratio spectrum|Soft]] (L:s = 3:2)',
'[[' .. ET.as_string(soft_et) .. '|' .. soft_gen .. ']] (' .. soft_gen_cents .. '¢)'
})
table.insert(et_data, {
'[[TAMNAMS#Step ratio spectrum|Semisoft]] (L:s = 5:3)',
'[[' .. ET.as_string(semisoft_et) .. '|' .. semisoft_gen .. ']] (' .. semisoft_gen_cents .. '¢)'
})
table.insert(et_data, {
'[[TAMNAMS#Step ratio spectrum|Basic]] (L:s = 2:1)',
'[[' .. ET.as_string(basic_et) .. '|' .. basic_gen .. ']] (' .. basic_gen_cents .. '¢)'
})
table.insert(et_data, {
'[[TAMNAMS#Step ratio spectrum|Semihard]] (L:s = 5:2)',
'[[' .. ET.as_string(semihard_et) .. '|' .. semihard_gen .. ']] (' .. semihard_gen_cents .. '¢)'
})
table.insert(et_data, {
'[[TAMNAMS#Step ratio spectrum|Hard]] (L:s = 3:1)',
'[[' .. ET.as_string(hard_et) .. '|' .. hard_gen .. ']] (' .. hard_gen_cents .. '¢)'
})
table.insert(et_data, {
'[[TAMNAMS#Step ratio spectrum|Superhard]] (L:s = 4:1)',
'[[' .. ET.as_string(superhard_et) .. '|' .. superhard_gen .. ']] (' .. superhard_gen_cents .. '¢)'
})
-- Create xenpaper links
local step_ratios = { {4,3}, {3,2}, {5,3}, {2,1}, {5,2}, {3,1}, {4,1} }
local step_ratio_names = { "Supersoft", "Soft", "Semisoft", "Basic", "Semihard", "Hard", "Superhard" }
local xenpaper_links = {}
for i = 1, #step_ratios do
for i = 1, #step_ratios do
local link = string.format("[%s %s]", xp.mosstep_pattern_to_xenpaper_link(pattern, step_ratios[i], mos.equave), step_ratio_names[i])
local step_ratio = step_ratios[i]
table.insert(xenpaper_links, link)
local ed_as_string = mos.et_string(input_mos, step_ratio)
local gen_in_steps = mos.bright_gen_to_et_string(input_mos, step_ratio, "")
local gen_in_cents = mos.bright_gen_to_cents(input_mos, step_ratio)
local step_ratio_name = tamnams.lookup_step_ratio(step_ratio)
step_ratio_name = step_ratio_name:gsub("^%l", string.upper)
local xenpaper_link = xp.mosstep_pattern_to_xenpaper_link(mos.brightest_mode(input_mos), step_ratios[i], input_mos.equave)
local caption = string.format("[[%s]] [%s (<span style=\"white-space: nowrap;\">L:s = %d:%d</span>)]", step_ratio_name, xenpaper_link, step_ratio[1], step_ratio[2])
local text = string.format("[[%s|%s]] (%.1f{{c}})", ed_as_string, gen_in_steps, gen_in_cents)
table.insert(section_entries, { ["Header"] = caption, ["Data"] = text })
end
end


local title = nL .. 'L ' .. ns .. 's' .. equave_disp
return section_entries
result = p.build(
end
title,
 
structure_data,
-- New "main" function
et_data,
function p._infobox_mos(input_mos)
adjacent_links,
local input_mos = input_mos or mos.new(4, 5, 3)
xenpaper_links
local other_names_unparsed = ""
)
if not debug_mode then
local other_names_parsed = tip.parse_entries(other_names_unparsed) or tip.parse_entries(other_names_unparsed, ",")
result = result .. "\n\n" .. p.categorize(tuning)
local sections = {}
-- Keyboard visualization
local kb_vis = p.kb_vis(input_mos)
p.concatenate_tables(sections, kb_vis)
-- Scale structure section
local scale_structure = p.scale_structure(input_mos)
p.concatenate_tables(sections, scale_structure)
-- Interval range section
--local step_sizes = p.step_sizes(tuning_parsed)
--p.concatenate_tables(sections, step_sizes)
-- Generator sizes section
local gen_sizes = p.generator_sizes(input_mos)
p.concatenate_tables(sections, gen_sizes)
-- Tamnams info section, if applicable
local tamnams_info = p.tamnams_information(input_mos)
if tamnams_info ~= nil then
p.concatenate_tables(sections, tamnams_info)
end
-- Other names section, if applicable
local other_names_section = p.other_names(other_names_parsed)
if other_names_section ~= nil then
p.concatenate_tables(sections, other_names_section)
end
end
return result
-- Related scales section
local related_scales = p.related_scales(input_mos)
p.concatenate_tables(sections, related_scales)
-- Equal tunings section
local equal_tunings = p.equal_tunings(input_mos)
p.concatenate_tables(sections, equal_tunings)
-- Adjacent links
local adjacent_links = p.adjacent_links(input_mos)
local args = {
["Adjacent Links"] = adjacent_links,
["Title"] = mos.as_long_string(input_mos),
["Rows"] = sections,
["name"] = "Infobox MOS"
}
return ib._infobox(args)
--return sections
end
end


-- Wrapper function
-- Wrapper function
function p.infobox_MOS(frame)
function p.infobox_MOS(frame)
local args = getArgs(frame)
local tuning = frame.args['Tuning']
-- "Scale Signature" is preferred; "Tuning" is supported for legacy purposes
local other_names = frame.args['Other names'] or nil
local unparsed = args["Tuning"] or args["Scale Signature"]
local debug_mode = frame.args['debug'] ~= nil
local input_mos = mos.parse(unparsed)
local other_names = args["othernames"] or nil
local debug_mode = yesno(args["debug"], false)
local wtext = yesno(frame.args["wtext"] or args["wtext"])
return p._infobox_MOS(tuning, debug_mode, other_names)
local result = p._infobox_mos(input_mos)
 
if not debug_mode then
end
result = result .. p.categorize(input_mos)
 
function p.build(title, structure_entries, et_entries, adjacent_links, xenpaper_links)
local s = '<div style="\n' ..
'border: 1px solid #999;\n' ..
'margin: 0;\n' ..
'margin-left: 1em;\n' ..
'margin-bottom: 0.5em;\n' ..
'padding: 0.5em;\n' ..
'background-color: #f0f0f0;\n' ..
'min-width: 15em;\n' ..
'float: right;\n' ..
'max-width: 100%;\n' ..
'overflow: auto;\n' ..
'">\n' ..
'{| width="100%" style="border-collapse: collapse;"\n' ..
'|+ style="font-weight: bold; text-align: center;" | '
s = s .. '<table style="width: 100%; margin: 0"><tr>'
.. '<td style="width: 15%; text-align: left; white-space: nowrap; font-size: smaller">'
.. adjacent_links[1]
.. '</td>'
.. '<td style="\n'
.. 'width: 50%; padding-left: 1em; padding-right: 1em; text-align: center; font-size: smaller">'
.. adjacent_links[2]
.. '\n</td><td style="width: 15%; text-align: right; white-space: nowrap; font-size: smaller">'
.. adjacent_links[3]
.. '</td>'
.. '\n<tr>'
.. '<td style="width: 15%; text-align: left; white-space: nowrap; font-size: smaller">'
.. adjacent_links[4]
.. '</td>'
.. '<td style="width: 50%; padding-left: 1em; padding-right: 1em; text-align: center">'
.. title
.. '</td>'
.. '<td style="width: 15%; text-align: right; white-space: nowrap; font-size: smaller">'
.. adjacent_links[5]
.. '</td>'
.. '</tr>'
.. '<tr>'
.. '<td style="width: 15%; text-align: left; white-space: nowrap; font-size: smaller">'
.. adjacent_links[6]
.. '</td><td style="\n'
.. 'width: 50%; padding-left: 1em; padding-right: 1em; text-align: center; font-size: smaller">'
.. adjacent_links[7]
.. '\n</td>'
.. '<td style="width: 15%; text-align: right; white-space: nowrap; font-size: smaller">'
.. adjacent_links[8]
.. '</td>'
.. '</tr>'
..  '</table>'
s = s .. '\n'
for i, entry in ipairs(structure_entries) do
if #entry > 1 then
local caption = entry[1]
local text = entry[2]
s = s .. '|-\n' ..
'| style="text-align:right; padding-right: 0.25em" | ' .. caption .. '\n' ..
'| style="background-color: white; padding-left: 0.25em; font-weight: bold" | ' .. text .. '\n'
elseif #entry == 1 then
local text = entry[1]
s = s .. '|-\n'
.. '| colspan="2" style="text-align: center;" | ' .. text .. '\n'
end
end
end
s = s .. '|-\n'
    .. '|colspan="2" style="text-align:center;"| <b>Equal tunings</b>\n'
if wtext then
for i, entry in ipairs(et_entries) do
result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>"
if #entry > 1 then
local caption = entry[1]
local text = entry[2]
s = s .. '|-\n' ..
'| style="text-align:right; padding-right: 0.25em" | ' .. caption .. '\n' ..
'| style="background-color: white; padding-left: 0.25em; font-weight: bold" | ' .. text .. '\n'
elseif #entry == 1 then
local text = entry[1]
s = s .. '|-\n'
.. '| colspan="2" style="text-align: center;" | ' .. text .. '\n'
end
end
end
s = s .. '|-\n'
    .. '|colspan="2" style="text-align:center;"| <small>Brightest-mode tunings on \'\'xenpaper\'\'</small>\n'
return frame:preprocess(result)
    .. '|-\n'
s = s .. '| colspan="2" style="text-align:center;"|<small>'
for i = 1, #xenpaper_links do
s = s .. xenpaper_links[i] .. " "
end
s = s .. "</small>\n"
s = s .. '|}</div>'
return s
end
end


return p
return p