Module:MOS modes: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
Undo revision 148879 by ArrowHead294 (talk) Undo recent changes as they broke default names and udp
Tag: Undo
ArrowHead294 (talk | contribs)
mNo edit summary
 
(26 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local p = {}
local mos = require("Module:MOS")
local mos = require("Module:MOS")
local rat = require("Module:Rational")
local rat = require("Module:Rational")
local tamnams = require("Module:TAMNAMS")
local tip = require("Module:Template input parse")
local utils = require("Module:Utils")
local utils = require("Module:Utils")
local tip = require("Module:Template input parse")
local yesno = require("Module:Yesno")
local yesno = require("Module:Yesno")
local p = {}


-- TODO:
-- TODO:
-- - Add ability to autocollapse on large mos pages (say, more than 12 modes)
-- - Add ability to autocollapse on large mos pages (say, more than 12 modes)
-- - Code cleanup, adopt new functions where needed (low priority)


-- "Main" function
-- "Main" function
Line 14: Line 15:
function p._mos_modes(input_mos, mode_names, headers, entries, is_collapsed)
function p._mos_modes(input_mos, mode_names, headers, entries, is_collapsed)
local is_collapsed = is_collapsed == true
local is_collapsed = is_collapsed == true
local input_mos = input_mos or mos.new(5,2)
local mode_names = mode_names or {}
local headers = headers or {}
local entries = entries or {}
-- Mos is entered as a scale signature "xL ys" or "xL ys<p/q>" since the mos module can parse that format
-- Get UDPs and CPOs
local input_mos = input_mos or mos.parse(scale_sig)
local udps = tamnams.mos_mode_udps(input_mos)
local cpos = tamnams.mos_mode_cpos(input_mos)
-- Get the mos's mode names, if given
-- Get the mos's modes
-- Mode names are entered as a semicolon-delimited list
local mode_names = mode_names or {}
-- Get the mos's modes and the mode count
local mos_modes = mos.modes_by_brightness(input_mos)
local mos_modes = mos.modes_by_brightness(input_mos)
local periods = mos.period_count(input_mos)
local mossteps_per_period = mos.period_step_count(input_mos)


-- This is for entering multiple columns of info, if a single column of mode names isn't enough
-- Check whether to add mode names
-- For n headers, the number of entries must match the number of modes times the number of headers
local add_mode_names = #mode_names == #mos_modes
-- or else column data won't be added
local headers = headers or {}
local entries = entries or {}


-- To determine whether to add additional columns, determine whether the number of entries
-- Check whether the number of headers times the number of modes equals the
-- and the number of columns are greater than zero, and if so, determine whether the number of entries
-- number of entries. Supplementary info can only be added if this condition
-- is equal to the number of headers times the number of modes
-- is met. Limited to 3 columns of supplementary info.
local add_columns = #headers > 0 and #entries > 0
local add_columns = #headers > 0 and #entries > 0
if add_columns then
if add_columns then
add_columns = add_columns and #mos_modes * #headers == #entries
add_columns = add_columns and #mos_modes * #headers == #entries and #headers <= 3
end
end
-- Get the number of mossteps in the bright gen
-- Table caption
-- Used for calculating rotational order
local scale_sig = mos.as_string(input_mos)
local bright_gen = mos.bright_gen(input_mos)
local mossteps_per_bright_gen = mos.bright_gen_step_count(input_mos)
-- Make a table with a column for the mode (as a string of L's and s's) and UDP
-- Start of table
local result = "{| class=\"wikitable sortable mw-collapsible" .. (is_collapsed and " mw-collapsed\"\n" or "\"\n")
local result = "{| class=\"wikitable sortable center-2 center-3 mw-collapsible" .. (is_collapsed and " mw-collapsed\"\n" or "\"\n")
.. "|+ style=\"font-size: 105%;\" | Modes of " .. mos.as_string(input_mos) .. "\n" -- To create the scale signature with〈〉instead of <>
.. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("Modes of %s\n", scale_sig)
.. "|-\n"
.. "|-\n"
.. "! [[UDP]] !! Rotational order !! Step pattern"
-- Table headers
result = result
.. "! [[UDP]]"
.. " !! Cyclic<br />order"
.. " !! Step<br />pattern"
-- If there are mode names (if the mode names array is not nil), then add a column for mode names
-- Add header for mode names, if provided.
if #mode_names == #mos_modes then
if add_mode_names then
result = result .. " !! class=\"unsortable\" | Mode names"
result = result .. " !! class=\"unsortable\" | Mode names"
end
end
-- Add columns
-- Add column headers for supplementary info, if provided.
-- If mode names and columns are used, mode names come first
if add_columns then
if add_columns then
for i = 1, #headers do
for i = 1, #headers do
result = result .. " !! class=\"unsortable\" | " .. headers[i]
result = result .. string.format(" !! class=\"unsortable\" | %s", headers[i])
end
end
end
end
Line 68: Line 67:
-- Enter each row
-- Enter each row
-- As of coding, mos mode listings are fairly inconsistent or nonexistent, but consist of
-- the UDP, step pattern, and any mode names(s) in some order
-- This table orders them as UDP, step pattern, and mode names, as that's more common
for i = 1, #mos_modes do
for i = 1, #mos_modes do
result = result .. "|-\n"
result = result .. "|-\n"
-- Add the UDP, formatted as up|dp(p), where u is the number of bright generators going up,
-- Add the UDP, brightness order, and the mode's step pattern
-- d is the number of bright generators going down, and p is the number of periods
result = result .. string.format("| %s || %s || %s",
-- Omit p if p = 1
udps[i], cpos[i], mos_modes[i])
local gens_down = (i - 1) * periods
local gens_up = (#mos_modes - i) * periods
local udp_as_text = ""
if periods == 1 then
udp_as_text = gens_up .. "&#124;" .. gens_down
else
udp_as_text = gens_up .. "&#124;" .. gens_down .. "(" .. periods .. ")"
end
result = result .. "| " .. udp_as_text
-- Add the mode's rotational order
local bright_gens_up = mossteps_per_bright_gen * (i - 1)
local rotational_order = bright_gens_up % mossteps_per_period + 1
result = result .. " || " .. rotational_order
 
-- Add the mode's step pattern
result = result .. " || " .. mos_modes[i]
-- Add the mode's name, if given
-- Add the mode's name, if given
if #mode_names == #mos_modes then
if add_mode_names then
result = result .. " || " .. mode_names[i]
result = result .. string.format(" || %s", mode_names[i])
end
end
Line 104: Line 83:
for j = 1, #headers do
for j = 1, #headers do
local index = (i - 1) * #headers + j
local index = (i - 1) * #headers + j
result = result .. " || " .. entries[index]
result = result .. string.format(" || %s", entries[index])
end
end
end
end
Line 117: Line 96:
-- Wrapper function; to be called by template
-- Wrapper function; to be called by template
function p.modes_table(frame)
function p.modes_table(frame)
-- Mos is entered as a scale signature "xL ys" or "xL ys<p/q>" since the mos module can parse that format
local scale_sig = frame.args["Scale Signature"] or "5L 2s"
local scale_sig = frame.args["Scale Signature"] or "5L 2s"
local input_mos = mos.parse(scale_sig)
local input_mos = mos.parse(scale_sig)
Line 126: Line 104:
local mode_names = nil
local mode_names = nil
if scale_sig == "5L 2s" then
if scale_sig == "5L 2s" then
mode_names_unparsed = "Lydian; Ionian (major); Mixolydian; Dorian; Aeolian (minor); Phrygian; Locrian"
mode_names = {
mode_names = tip.parse_entries(mode_names_unparsed)
"Lydian",
else
"Ionian (major)",
mode_names_unparsed = frame.args["Mode Names"]
"Mixolydian",
mode_names = tip.parse_entries(mode_names_unparsed)
"Dorian",
"Aeolian (minor)",
"Phrygian",
"Locrian"
}
end
-- Get mode names entered
if #frame.args["Mode Names"] ~= 0 then
mode_names = tip.parse_entries(frame.args["Mode Names"], "$")
end
end


-- This is for entering multiple columns of info, if a single column of mode names isn't enough
-- Get supplementary info
-- For n headers, the number of entries must match the number of modes times the number of headers
-- or else column data won't be added
local headers_unparsed = frame.args["Table Headers"]
local headers_unparsed = frame.args["Table Headers"]
local headers = tip.parse_entries(headers_unparsed)
local headers = tip.parse_entries(headers_unparsed, "$")
local entries_unparsed = frame.args["Table Entries"]
local entries = tip.parse_entries(entries_unparsed, "$")
local is_collapsed = yesno(frame.args["Collapsed"], false)
local debugg = yesno(frame.args["debug"])
local result = p._mos_modes(input_mos, mode_names, headers, entries, is_collapsed)
-- Current means of adding entries is unmaintainable; to be deprecated.
if headers_unparsed ~= "" and entries_unparsed ~= "" then
result = result .. "[[Category:Pages with deprecated template parameters]]"
end
local entries_unparsed = frame.args["Table Entries"]
-- Debugger option
local entries = tip.parse_entries(entries_unparsed)
if debugg == true then
result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>"
end
local is_collapsed = yesno(frame.args["Collapsed"], false)
return frame:preprocess(result)
 
return p._mos_modes(input_mos, mode_names, headers, entries, is_collapsed)
end
end


return p
return p

Latest revision as of 12:45, 1 June 2025

Module documentation[view] [edit] [history] [purge]
Note: Do not invoke this module directly; use the corresponding template instead: Template:MOS modes.

This template is used for mos pages to automatically generate and list that mos's modes, along with the modes' UDP (up-down-period) notation.


local p = {}

local mos = require("Module:MOS")
local rat = require("Module:Rational")
local tamnams = require("Module:TAMNAMS")
local tip = require("Module:Template input parse")
local utils = require("Module:Utils")
local yesno = require("Module:Yesno")

-- TODO:
-- - Add ability to autocollapse on large mos pages (say, more than 12 modes)

-- "Main" function
-- To be called by wrapper
function p._mos_modes(input_mos, mode_names, headers, entries, is_collapsed)
	local is_collapsed = is_collapsed == true
	local input_mos = input_mos or mos.new(5,2)
	local mode_names = mode_names or {}
	local headers = headers or {}
	local entries = entries or {}
	
	-- Get UDPs and CPOs
	local udps = tamnams.mos_mode_udps(input_mos)
	local cpos = tamnams.mos_mode_cpos(input_mos)
	
	-- Get the mos's modes
	local mos_modes = mos.modes_by_brightness(input_mos)

	-- Check whether to add mode names
	local add_mode_names = #mode_names == #mos_modes

	-- Check whether the number of headers times the number of modes equals the
	-- number of entries. Supplementary info can only be added if this condition
	-- is met. Limited to 3 columns of supplementary info.
	local add_columns = #headers > 0 and #entries > 0
	if add_columns then
		add_columns = add_columns and #mos_modes * #headers == #entries and #headers <= 3
	end
	
	-- Table caption
	local scale_sig = mos.as_string(input_mos)
	
	-- Start of table
	local result = "{| class=\"wikitable sortable center-2 center-3 mw-collapsible" .. (is_collapsed and " mw-collapsed\"\n" or "\"\n")
		.. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("Modes of %s\n", scale_sig)
		.. "|-\n"
		
	-- Table headers
	result = result
		.. "! [[UDP]]"
		.. " !! Cyclic<br />order"
		.. " !! Step<br />pattern"
	
	-- Add header for mode names, if provided.
	if add_mode_names then
		result = result .. " !! class=\"unsortable\" | Mode names"
	end
	
	-- Add column headers for supplementary info, if provided.
	if add_columns then
		for i = 1, #headers do
			result = result .. string.format(" !! class=\"unsortable\" | %s", headers[i])
		end
	end
	
	result = result .. "\n"
	
	-- Enter each row
	for i = 1, #mos_modes do
		result = result .. "|-\n"
		
		-- Add the UDP, brightness order, and the mode's step pattern
		result = result .. string.format("| %s || %s || %s",
			udps[i], cpos[i], mos_modes[i])
		
		-- Add the mode's name, if given
		if add_mode_names  then
			result = result .. string.format(" || %s", mode_names[i])
		end
		
		-- Add columns if given
		if add_columns then
			for j = 1, #headers do
				local index = (i - 1) * #headers + j
				result = result .. string.format(" || %s", entries[index])
			end
		end
		
		result = result .. "\n"
	end
	
	result = result .. "|}"
	return result
end

-- Wrapper function; to be called by template
function p.modes_table(frame)
	local scale_sig = frame.args["Scale Signature"] or "5L 2s"
	local input_mos = mos.parse(scale_sig)
	
	-- Get the mos's mode names, if given
	-- Mode names are entered as a semicolon-delimited list
	-- 5L 2s gets default names
	local mode_names = nil
	if scale_sig == "5L 2s" then
		mode_names = { 
			"Lydian",
			"Ionian (major)",
			"Mixolydian",
			"Dorian",
			"Aeolian (minor)",
			"Phrygian",
			"Locrian"
		}
	end
	
	-- Get mode names entered
	if #frame.args["Mode Names"] ~= 0 then
		mode_names = tip.parse_entries(frame.args["Mode Names"], "$")
	end

	-- Get supplementary info
	local headers_unparsed = frame.args["Table Headers"]
	local headers = tip.parse_entries(headers_unparsed, "$")
	local entries_unparsed = frame.args["Table Entries"]
	local entries = tip.parse_entries(entries_unparsed, "$")
	local is_collapsed = yesno(frame.args["Collapsed"], false)
	local debugg = yesno(frame.args["debug"])
	local result = p._mos_modes(input_mos, mode_names, headers, entries, is_collapsed)
	
	-- Current means of adding entries is unmaintainable; to be deprecated.
	if headers_unparsed ~= "" and entries_unparsed ~= "" then
		result = result .. "[[Category:Pages with deprecated template parameters]]"
	end
	
	-- Debugger option
	if debugg == true then
		result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>"
	end
	
	return frame:preprocess(result)
end

return p