Module:MOS mode degrees

From Xenharmonic Wiki
Revision as of 06:26, 19 July 2024 by Ganaram inukshuk (talk | contribs) (Removed unused functions)
Jump to navigation Jump to search
Module documentation[view] [edit] [history] [purge]
This module should not be invoked directly; use its corresponding template instead: Template:MOS mode degrees.

This module creates a table of the scale degrees for each mode of a MOS or MODMOS scale.

Introspection summary for Module:MOS mode degrees 
Functions provided (4)
Line Function Params
19 cell_color (interval, input_mos)
53 _mos_mode_degrees (main) (input_mos, mos_prefix, mode_names, use_default_mode_names)
136 _modmos_mode_degrees (input_mos, mos_prefix, step_pattern, mode_names, use_default_mode_names)
222 mos_mode_degrees (invokable) (frame)
Lua modules required (3)
Variable Module Functions used
mos Module:MOS new
period_step_count
interval_step_count
interval_chroma_count
modes_by_brightness
modes_to_step_matrices
as_string
equave_step_count
mode_rotations
mode_rotations_to_step_matrices
parse
tamnams Module:TAMNAMS mos_mode_udps
mos_mode_cpos
decode_quality
mode_rotation_udps
lookup_prefix
tip Module:Template input parse parse_entries

No function descriptions were provided. The Lua code may have further information.


local mos = require("Module:MOS")
local tip = require("Module:Template input parse")
local tamnams = require("Module:TAMNAMS")
local p = {}

-- Global variables for cell colors
-- Colors are as follows:
-- Large intervals are yellow, small intervals are blue, augmented intervals are dark yellow, and diminished intervals are dark blue
p.cell_color_none = "NONE"				-- For cells that don't have a color (default cell color applies)
p.cell_color_perfect_size = "NONE"		-- Only applies for periods, including the root and equave
p.cell_color_lg_altered_size = "#BDD7EE"
p.cell_color_large_size      = "#DDEBF7"
p.cell_color_small_size      = "#FCE4D6"
p.cell_color_sm_altered_size = "#F8CBAD"


-- A simplified version of calculate_row_colors
-- Finds the row color for a single cell
function p.cell_color(interval, input_mos)
	local interval = interval or {["L"] = 3, ["s"] = 1}
	local input_mos = input_mos or mos.new(5, 2)
	
	local period_step_count = mos.period_step_count(input_mos)
	local interval_step_count = mos.interval_step_count(interval)
	local chroma_count = mos.interval_chroma_count(interval, input_mos)
	
	local is_period_interval = interval_step_count % period_step_count == 0
	
	local color = p.cell_color_none
	if is_period_interval then
		if chroma_count > 0 then
			color = p.cell_color_lg_altered_size
		elseif chroma_count == 0 then
			color = p.cell_color_none
		elseif chroma_count < 0 then
			color = p.cell_color_sm_altered_size
		end
	else
		if chroma_count > 0 then
			color = p.cell_color_lg_altered_size
		elseif chroma_count == 0 then
			color = p.cell_color_large_size
		elseif chroma_count == -1 then
			color = p.cell_color_small_size
		elseif chroma_count < -1 then
			color = p.cell_color_sm_altered_size
		end
	end
	return color
end

-- Create a table of a mos's degrees
function p._mos_mode_degrees(input_mos, mos_prefix, mode_names, use_default_mode_names)
	local input_mos = input_mos or mos.new(5, 2)
	local mos_prefix = mos_prefix or "mos"
	local mode_names = mode_names or nil
	local use_default_mode_names = use_default_mode_names == 1
	
	-- Get the modes as strings and step vectors
	local step_patterns = mos.modes_by_brightness(input_mos)
	local step_matrices = mos.modes_to_step_matrices(input_mos)
	
	-- Get the scale sig
	local scale_sig = mos.as_string(input_mos)
	
	-- Get the brightness (UDP) and rotational orderings
	local brightness_order = tamnams.mos_mode_udps(input_mos)
	local rotational_order = tamnams.mos_mode_cpos(input_mos)

	-- Equave step count; needed for degree column count
	local equave_step_count = mos.equave_step_count(input_mos)
	
	-- Create table
	local result = "{| class=\"wikitable sortable\"\n"
		.. string.format("|+ style=\"font-size: 105%%;\" | Scale degree qualities of %s modes\n", scale_sig)
	
	-- Add table headers for first row
		.. "! rowspan=\"2\" | UDP "
		.. "!! rowspan=\"2\" | Rotational Order "
		.. "!! rowspan=\"2\" | Step pattern"
	
	-- Add mode names if present
	local mode_names_given = (mode_names ~= nil and #mode_names == #step_patterns) or use_default_mode_names
	if mode_names_given then
		result = result .. " !! rowspan=\"2\" class=\"unsortable\" | Mode names"
	end
	
	-- Add header for scale degrees
	result = result .. string.format(" !! colspan=\"%d\" class=\"unsortable\" | Scale degree (%sdegree)\n", equave_step_count + 1, mos_prefix)
	
	-- Add second row of headers
	result = result .. "|- class=\"unsortable\"\n"
		.. "! 0"
	for i = 1, equave_step_count do
		result = result .. string.format(" !! %d", i)
	end
	
	result = result .. "\n"
	
	-- Add table contents
	for i = 1, #step_patterns do
		result = result .. "|-\n"
		
		-- Add brightness order (as UDP), rotational order, and step pattern
			.. string.format("| %s || %s || %s", brightness_order[i], rotational_order[i], step_patterns[i])
		
		-- Add mode name if given
		if mode_names_given then
			if use_default_mode_names then
				result = result .. string.format(" || %s %s", scale_sig, brightness_order[i])
			else
				result = result .. string.format(" || %s", mode_names[i])
			end
		end
		
		-- Add scale degrees with cell coloring
		for j = 1, #step_matrices[i] do
			local current_interval = step_matrices[i][j]
			local degree_quality = tamnams.decode_quality(current_interval, input_mos, "shortened")
			
			local cell_color = p.cell_color(current_interval, input_mos)
			local style_code = cell_color == p.cell_color_none and "" or string.format("style=\"background: %s;\" | ", cell_color)
			
			result = result .. string.format(" || %s%s", style_code, degree_quality)
		end
		result = result .. "\n"
	end
	
	-- End of table
	result = result .. "|}"
	
	return result
end

-- Create a table of a modmos's degrees
function p._modmos_mode_degrees(input_mos, mos_prefix, step_pattern, mode_names, use_default_mode_names)
	local input_mos = input_mos or mos.new(5, 2)
	local mos_prefix = mos_prefix or "mos"
	local step_pattern = step_pattern or "LsLLsAs"
	local mode_names = mode_names or nil
	local use_default_mode_names = use_default_mode_names == 1
	
	-- Get the modes and step matrices for the modmos
	local step_patterns = mos.mode_rotations(step_pattern)
	local step_matrices = mos.mode_rotations_to_step_matrices(step_pattern)
	
	-- Get the scale sig
	local scale_sig = mos.as_string(input_mos)
	
	-- Get the UDPs (with alterations)
	local udps = tamnams.mode_rotation_udps(step_pattern, input_mos)
	
	-- Equave step count; needed for degree column count
	local equave_step_count = mos.equave_step_count(input_mos)
	
	-- Create table
	local result = '{| class="wikitable sortable"\n'
	local result = result .. string.format("|+ style=\"font-size: 105%%;\" | Scale degree qualities of %s modes (step pattern of %s)\n", scale_sig, step_pattern)
	
	-- Add table headers for first row
	-- Headers should be on their own line for ease of reading code
		.. "! rowspan=\"2\" | UDP and alterations "
		.. "!! rowspan=\"2\" | Rotational Order "
		.. "!! rowspan=\"2\" | Step pattern"
	
	-- Add mode names if present
	local mode_names_given = (mode_names ~= nil and #mode_names == #step_patterns) or use_default_mode_names
	if mode_names_given then
		result = result .. " !! rowspan=\"2\" class=\"unsortable\" | Mode names"
	end
	
	-- Add header for scale degrees
	result = result .. string.format(" !! colspan=\"%d\" class=\"unsortable\" | Scale degree (%sdegree)\n", equave_step_count + 1, mos_prefix)
	
	-- Add second row of headers
	result = result .. "|- class=\"unsortable\"\n"
		.. "! 0"
	for i = 1, equave_step_count do
		result = result .. string.format(" !! %d", i)
	end
	
	result = result .. "\n"
	
	-- Add table contents
	for i = 1, #step_patterns do
		result = result .. "|-\n"
		
		-- Add brightness order (as UDP), rotational order, and step pattern
			.. string.format("| %s || %s || %s", udps[i], i, step_patterns[i])
		
		-- Add mode name if given
		if mode_names_given then
			if use_default_mode_names then
				result = result .. string.format(" || %s %s", scale_sig, udps[i])
			else
				result = result .. string.format(" || %s", mode_names[i])
			end
		end
		
		-- Add scale degrees with cell coloring
		for j = 1, #step_matrices[i] do
			local current_interval = step_matrices[i][j]
			local degree_quality = tamnams.decode_quality(current_interval, input_mos, "shortened")
			
			local cell_color = p.cell_color(current_interval, input_mos)
			local style_code = cell_color == p.cell_color_none and "" or string.format("style=\"background: %s;\" | ", cell_color)
			
			result = result .. string.format(" || %s%s", style_code, degree_quality)
		end
		result = result .. "\n"
	end
	
	-- End of table
	result = result .. "|}"
	
	return result
end

-- Function to be called as part of a template
-- Note that there are two "main" functions: one for mosses, one for modmosses
-- Whichever is called is based on the step pattern entered
function p.mos_mode_degrees(frame)
	-- Default param for input mos is 5L 2s
	local input_mos = mos.parse(frame.args["Scale Signature"]) or mos.new(2, 5, 2)
	
	-- Get the scale sig; for calculating the mos prefix
	local scale_sig = mos.as_string(input_mos)
	
	-- Default param for mos prefix
	-- If "NONE" is given, no prefix will be used
	-- If left blank, try to find the appropriate mos prefix, or else defualt to "mos"
	-- If not left blank, use the prefix passed in instead
	local mos_prefix = "mos"
	if frame.args["MOS Prefix"] == "NONE" then
		mos_prefix = ""
	elseif string.len(frame.args["MOS Prefix"]) == 0 then
		mos_prefix_lookup = tamnams.lookup_prefix(input_mos) or ""
		if string.len(mos_prefix_lookup) ~= 0 then
			mos_prefix = mos_prefix_lookup
		end
	else
		mos_prefix = frame.args["MOS Prefix"]
	end
	
	-- Get the step pattern
	local step_pattern = frame.args["MODMOS Step Pattern"]
	
	-- Get the mode names
	local mode_names = nil
	-- Default names for 5L 2s modes
	if scale_sig == "5L 2s" and step_pattern == "LsLLsAs" then
		mode_names = { "Harmonic minor", "Locrian #6", "Ionian augmented", "Dorian #4", "Phrygian dominant", "Lydian #2", "Locrian b4 bb7" }
	elseif scale_sig == "5L 2s" and #step_pattern == 0 then
		mode_names = { "Lydian", "Ionian (major)", "Mixolydian", "Dorian", "Aeolian (minor)", "Phrygian", "Locrian" }
	end
	
	-- If mode names are given, use those instead
	-- If using default mode names (scalesig+udp), those names are auto-added by the relevant function
	local use_default_names = 0
	if #frame.args["Mode Names"] ~= 0 then
		if frame.args["Mode Names"] == "Default" then
			use_default_names = 1
		else
			mode_names = tip.parse_entries(frame.args["Mode Names"])
		end
	end
	
	-- If a modmos step pattern was never provided, call the function mos_mode_degrees
	-- Otherwise, call the function modmos_mode_degrees
	local result = ""
	if step_pattern == "" then
		result = p._mos_mode_degrees(input_mos, mos_prefix, mode_names, use_default_names)
	elseif #step_pattern == input_mos.nL + input_mos.ns then
		result = p._modmos_mode_degrees(input_mos, mos_prefix, step_pattern, mode_names, use_default_names)
	end
	
	return result
end

return p