Module:MOS tunings: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
m added default step ratios
Ganaram inukshuk (talk | contribs)
mNo edit summary
Line 65: Line 65:
end
end
step_ratio_range = tamnams.lookup_step_ratio_range(step_ratios[index_of_lowest], step_ratios[index_of_highest]) or "Tunings"
step_ratio_range = (tamnams.lookup_step_ratio_range(step_ratios[index_of_lowest], step_ratios[index_of_highest]) .. " Tunings") or "Tunings"
elseif #step_ratios == 1 then
elseif #step_ratios == 1 then
step_ratio_range = (tamnams.lookup_step_ratio(step_ratios[1]) or string.format("%s/%s", step_ratios[1][1], step_ratios[1][2])) .. " Tuning"
step_ratio_range = (tamnams.lookup_step_ratio(step_ratios[1]) or string.format("%s/%s", step_ratios[1][1], step_ratios[1][2])) .. " Tuning"

Revision as of 19:00, 22 August 2024

Module documentation[view] [edit] [history] [purge]
This module should not be invoked directly; use its corresponding template instead: Template:MOS tunings.

This module displays the cent values of a MOS scale's degrees under various tunings, or step ratios, as well as the nearest JI ratios represented by those scale degrees.

Introspection summary for Module:MOS tunings 
Functions provided (5)
Line Function Params
22 capitalize_first (text)
26 preprocess_step_ratio_range (step_ratios)
81 _mos_tunings (main) (input_mos, mos_prefix, mos_abbrev, step_ratios)
151 mos_tunings (invokable) (frame)
174 tester none
Lua modules required (5)
Variable Module Functions used
et Module:ET as_string
mos Module:MOS new
modal_union
as_string
mos_to_et
interval_to_et_steps
interval_to_cents
parse
tamnams Module:TAMNAMS lookup_step_ratio_range
lookup_step_ratio
degree_quality
verify_prefix
verify_abbrev
tip Module:Template input parse parse_numeric_pairs
yesno Module:Yesno yesno

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


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

-- Rewritten/simplified module replacement for Module:MOS degrees
-- A new template is chosen because it's a better name than the old one and is
-- far easier to maintain than the old one.

-- TODO:
-- - Header capitalization
-- - Links
-- - Text align
-- - Step ratio input (only the simple ratios are being displayed)
-- - JI ratio input??
-- - Diatonic interval category lookup?!!

-- Helper function
-- Capitalizes the first character of a string
function p.capitalize_first(text)
	return string.upper(string.sub(text, 1, 1)) .. string.sub(text, 2, -1)
end

function p.preprocess_step_ratio_range(step_ratios)
	local step_ratios = step_ratios or { {6,1} }
	local step_ratio_range = ""
	
	-- If the step ratios are 2/1, 3/1, and 3/2 in that order, then they are
	-- the simple step ratios: basic, hard, and soft
	if #step_ratios == 3 then
		if step_ratios[1][1] == 2 and step_ratios[1][2] == 1
			and step_ratios[2][1] == 3 and step_ratios[2][2] == 1
			and step_ratios[3][1] == 3 and step_ratios[3][2] == 2 then
			return "Simple Tunings"
		end
	end
	
	-- If there are multiple step ratios, find the smallest and largest ratios
	-- and find the step ratio range. If there's only one step ratio, then only
	-- find the step ratio name for that ratio, if there is one. If there are 
	-- zero step ratios, then it's a table of scale degrees.
	if #step_ratios > 1 then
		local index_of_lowest  = -1
		local index_of_highest = -1
		local lowest_hardness  =  1/0
		local highest_hardness = -1/0
		
		-- Update lowest step ratio
		for i = 1, #step_ratios do
			local current_hardness = step_ratios[i][1] / step_ratios [i][2]
		
			-- If the current hardness is smaller than the lowest recorded
			-- hardness, update. If the current hardness is larger than the
			-- highest recorded hardness, update.
			if current_hardness < lowest_hardness then
				lowest_hardness = current_hardness
				index_of_lowest = i
			end
			if current_hardness > highest_hardness then
				highest_hardness = current_hardness
				index_of_highest = i
			end
		end
		
		step_ratio_range = (tamnams.lookup_step_ratio_range(step_ratios[index_of_lowest], step_ratios[index_of_highest]) .. " Tunings") or "Tunings"
	elseif #step_ratios == 1 then
		step_ratio_range = (tamnams.lookup_step_ratio(step_ratios[1]) or string.format("%s/%s", step_ratios[1][1], step_ratios[1][2])) .. " Tuning"
	else
		step_ratio_range = "Tunings"
	end
	
	-- Capitalize first letter
	step_ratio_range = p.capitalize_first(step_ratio_range)
	
	return step_ratio_range
end

-- Main function
function p._mos_tunings(input_mos, mos_prefix, mos_abbrev, step_ratios)
	local input_mos = input_mos or mos.new(5,2)
	local mos_prefix = mos_prefix or "mos"
	local mos_abbrev = mos_abbrev or "m"
	local step_ratios = step_ratios or { {2,1}, {3,1}, {3,2} }
	
	local modal_union = mos.modal_union(input_mos)
	local scale_sig = mos.as_string(input_mos)
	
	-- Create table
	local result = "{| class=\"wikitable sortable\"\n"
	
	-- Table caption
	local step_ratio_range = p.preprocess_step_ratio_range(step_ratios)
	result = result	.. string.format("|+ style=\"font-size: 105%%; white-space: nowrap;\" | %s of %s\n", step_ratio_range, scale_sig)
	
	-- First row of headers
	-- First two headers span two rows
	result = result .. "! rowspan=\"2\" class=\"unsortable\" | Scale degree\n"
	result = result .. "! rowspan=\"2\" class=\"unsortable\" | Abbrev.\n"
	
	-- Headers for tunings; these span two cols
	for i = 1, #step_ratios do
		local step_ratio_as_text = tamnams.lookup_step_ratio(step_ratios[i]) or string.format("%s:%s", step_ratios[i][1], step_ratios[i][2])
		step_ratio_as_text = p.capitalize_first(step_ratio_as_text)
		local et_as_string = et.as_string(mos.mos_to_et(input_mos, step_ratios[i]))
		
		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)
	end
	result = result .. "|-\n"
	
	-- Second row of headers
	for i = 1, #step_ratios do
		result = result .. "! Steps\n"
		result = result .. "! Cents\n"
	end
	
	-- Add a for each scale degree
	for i = 1, #modal_union do
		local interval = modal_union[i]
		
		-- Add cells for the degree names
		local degree_name   = tamnams.degree_quality(interval, input_mos, "sentence-case", mos_prefix)
		local degree_abbrev = tamnams.degree_quality(interval, input_mos, "abbrev"       , mos_abbrev)
		
		result = result
			.. "|-\n"
			.. string.format("| %s || %s", degree_name, degree_abbrev)
	
		-- Add cells for each interval's tunings
		for j = 1, #step_ratios do
			local step_ratio = step_ratios[j]
			local step_count = mos.interval_to_et_steps(interval, step_ratio)
			local cents = mos.interval_to_cents(interval, input_mos, step_ratio)
			
			result = result
				.. " "
				.. string.format("|| %s\\%s || %.1f", step_count, input_mos.nL * step_ratio[1] + input_mos.ns * step_ratio[2], cents)
		end
		result = result .. "\n"
	end
	
	-- End of table
	result = result .. "|}"
	
	return result
end

-- Wrapper function; to be called by template
function p.mos_tunings(frame)
	-- Get params
	local scalesig   = frame.args["Scale Signature"]
	local mos_prefix = frame.args["MOS Prefix"]
	local mos_abbrev = frame.args["MOS Abbrev"]
	local collapsed  = yesno(frame.args["Collapsed"], false)		-- Currently does nothing
	local step_ratios = tip.parse_numeric_pairs(frame.args["Step Ratios"])
	
	-- If the table is size zero, defualt to simple tunings
	if #step_ratios == 0 then
		step_ratios = tip.parse_numeric_pairs("2/1; 3/1; 3/2")
	end
	
	-- Parse scalesig
	local input_mos = mos.parse(scalesig)
	
	-- Verify name/prefix/abbrev
	mos_prefix = tamnams.verify_prefix(input_mos, mos_prefix)
	mos_abbrev = tamnams.verify_abbrev(input_mos, mos_abbrev)

	return p._mos_tunings(input_mos, mos_prefix, mos_abbrev, step_ratios)
end

function p.tester()
	return tip.parse_numeric_pairs("")
end

return p