Module:MOS data

From Xenharmonic Wiki
Jump to navigation Jump to search

Documentation transcluded from /doc
Note: Do not invoke this module directly; use the corresponding template instead: Template:MOS data.
Icon-Todo.png Todo: Documentation

local mos = require("Module:MOS")
local rat = require("Module:Rational")
local tamnams = require("Module:TAMNAMS")
local mosi = require("Module:MOS intervals")
local mosg = require("Module:MOS genchain")
local mosmd = require("Module:MOS mode degrees")
local p = {}

-- Combination module/template of:
-- - Mos intervals
-- - Mos genchain
-- - Mos mode degrees

-- Mos intervals description
function p.mos_intervals_description(input_mos, mos_prefix)
	local input_mos = input_mos or mos.new(5,2)
	local mos_prefix = mos_prefix or "dia"
	local scalesig = mos.as_string(input_mos)
	
	-- How many periods?
	-- What are the period intervals?
	local period_count = mos.period_count(input_mos)
	local period_interval = mos.period(input_mos)
	local period_intervals = {}
	for i = 1, period_count + 1 do
		local interval = mos.interval_mul(period_interval, i-1)
		table.insert(period_intervals, interval)
	end
	
	-- As a sentence piece
	local period_intervals_as_text = ""
	if #period_intervals == 2 then
		period_intervals_as_text = string.format("%s and %s", tamnams.interval_quality(period_intervals[1], input_mos, "none", mos_prefix), tamnams.interval_quality(period_intervals[2], input_mos, "none", mos_prefix))
	else
		for i = 1, period_count do
			period_intervals_as_text = period_intervals_as_text
				.. string.format("%s, ", tamnams.interval_quality(period_intervals[i], input_mos, "none", mos_prefix))
		end
		period_intervals_as_text = period_intervals_as_text
			.. string.format("and %s", tamnams.interval_quality(period_intervals[period_count + 1], input_mos, "none", mos_prefix))
	end
	
	local result = string.format("The intervals of %s are named after the number of mossteps (L and s) they subtend.", scalesig)
	
	local period_interval_desc = ""
	if period_count == 1 then
		period_interval_desc = "the root and " .. (rat.eq(input_mos.equave, 2) and "octave" or "equave")
	else
		period_interval_desc = "the period intervals"
	end
	result = result
		.. string.format(" Each interval, apart from %s (%s), has two varieties, or sizes, each.", period_interval_desc, period_intervals_as_text)
	
	result = result
		.. " Interval varieties are named major and minor for the large and small sizes, respectively"
		.. (input_mos.nL == input_mos.ns and "." or ", and augmented, perfect, and diminished for the scale's generators.")
	
	return result
end

-- Mos genchain description
function p.mos_genchain_description(input_mos, mos_prefix)
	local input_mos = input_mos or mos.new(6,2)
	local mos_prefix = mos_prefix or "mos"
	local scalesig = mos.as_string(input_mos)
	
	-- How many periods?
	-- What are the period intervals?
	local period_count = mos.period_count(input_mos)
	local period_interval = mos.period(input_mos)
	local period_intervals = {}
	for i = 1, period_count + 1 do
		local interval = mos.interval_mul(period_interval, i-1)
		table.insert(period_intervals, interval)
	end
	
	-- As a sentence piece
	local period_intervals_as_text = ""
	if #period_intervals == 2 then
		period_intervals_as_text = string.format("%s and %s", tamnams.degree_quality(period_intervals[1], input_mos), tamnams.degree_quality(period_intervals[2], input_mos))
	else
		for i = 1, period_count do
			period_intervals_as_text = period_intervals_as_text
				.. string.format("%s, ", tamnams.degree_quality(period_intervals[i], input_mos))
		end
		period_intervals_as_text = period_intervals_as_text
			.. string.format("and %s", tamnams.degree_quality(period_intervals[period_count + 1], input_mos))
	end
	
	-- What is the bright gen and at what scale degrees do you stack from?
	local bright_gen_as_text = tamnams.interval_quality(mos.bright_gen(input_mos), input_mos)
	local result = string.format("A chain of bright generators, each a %s, produces the following scale degrees.", bright_gen_as_text)
	
	-- How long is the chain?
	local period_step_count = mos.period_step_count(input_mos)
	result = result
		.. " "
		.. string.format("A chain of %s bright generators %scontains the scale degrees of one of the modes of %s.", period_step_count, (period_count == 1 and "" or "from each period "), scalesig)
	
	-- What scales are produced when the chain is extended further?
	local child_scale_chain_length = input_mos.nL + input_mos.nL + input_mos.ns
	local child_scale_1, child_scale_2 = mos.child_mosses(input_mos)
	result = result
		.. " "
		.. string.format("Expanding %s chain to %s scale degrees produces the modes of either %s (for soft-of-basic tunings) or %s (for hard-of-basic tunings).",
			(period_count == 1 and "the" or "each"),
			child_scale_chain_length / period_count,
			mos.as_string(child_scale_1),
			mos.as_string(child_scale_2)
		)
		
	return result
end

function p._mos_data(input_mos, mos_prefix, mos_abbrev)
	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 mosi_desc = p.mos_intervals_description(input_mos, mos_prefix)
	local mosg_desc = p.mos_genchain_description(input_mos, mos_prefix)
	
	local mosi_table = mosi._mos_intervals(input_mos, mos_prefix, mos_abbrev)
	local mosg_table = mosg._mos_genchain(input_mos, mos_prefix, mos_abbrev, true)		-- Autocollapse for now
	local mosmd_table = mosmd._mos_mode_degrees(input_mos, mos_prefix, mos_abbrev)
	
	local result = ""
	
	result = result
		.. "=== Intervals ===\n"
		.. mosi_desc
		.. "\n"
		.. mosi_table
		.. "\n\n"
		.. "=== Generator chain ===\n"
		.. mosg_desc
		.. "\n"
		.. mosg_table
		.. "\n\n"
		.. "=== Modes ===\n"
		.. "\n"
		.. mosmd_table
		
	return result
end

function p.mos_data(frame)
	local scalesig = frame.args["Scale Signature"]
	local mos_prefix = frame.args["MOS Prefix"]
	local mos_abbrev = frame.args["MOS Abbrev"]

	-- 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_data(input_mos, mos_prefix, mos_abbrev)
end

return p