Module:MOS intervals

From Xenharmonic Wiki
Revision as of 08:17, 5 July 2023 by Ganaram inukshuk (talk | contribs) (Typing in either "ordinal" or "ordinals" uses ordinal names now)
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 intervals.

This module generates a table showing interval varieties and qualities for a given MOS scale.

Introspection summary for Module:MOS intervals 
Functions provided (2)
Line Function Params
8 mos_interval_to_step_count_string (step_pattern, mossteps)
46 mos_intervals_frame (invokable) (frame)
Lua modules required (3)
Variable Module Functions used
mos Module:MOS parse
new
brightest_mode
bright_gen
as_string
ord Module:Ordinal _ordinal
rat Module:Rational gcd
eq

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


local mos = require('Module:MOS')
local rat = require('Module:Rational')
local ord = require('Module:Ordinal')
local p = {}

-- Helper function that turns a mosstep into a step count as a string
-- EG, the 3-mosstep of "LLsLsLs" becomes "2L + s"
function p.mos_interval_to_step_count_string(step_pattern, mossteps)
	
	local L_count = 0
	local s_count = 0
	for i = 1, mossteps do
		local step =  string.sub(step_pattern, i, i)
		if step == "L" then
			L_count = L_count + 1
		elseif step == "s" then
			s_count = s_count + 1
		end
	end
	
	-- There are 9 combinations to write xL + ys, based on whether x and y are
	-- 0, 1, or greater than 1
	local return_string = ""
	if L_count == 0 and s_count == 0 then
		return_string = "0"
	elseif L_count == 0 and s_count == 1 then
		return_string = "s"
	elseif L_count == 0 and s_count > 1 then
		return_string = s_count .. "s"
	elseif L_count == 1 and s_count == 0 then
		return_string = "L"
	elseif L_count == 1 and s_count == 1 then
		return_string = "L + s"
	elseif L_count == 1 and s_count > 1 then
		return_string = "L + " .. s_count .. "s"
	elseif L_count > 1 and s_count == 0 then
		return_string = L_count .. "L"
	elseif L_count > 1 and s_count == 1 then
		return_string = L_count .. "L + s"
	else
		return_string = L_count .. "L + " .. s_count .. "s"
	end
	return return_string
end

function p.mos_intervals_frame(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 brightest and darkest modes for the mos
	local brightest_mode = mos.brightest_mode(input_mos)
	local darkest_mode = string.reverse(brightest_mode)
	
	-- Get the number of steps per period and equave
	local steps_per_equave = (input_mos.nL + input_mos.ns)
	local steps_per_period = steps_per_equave / rat.gcd(input_mos.nL, input_mos.ns)
	
	-- Get the step counts for the bright and dark generators
	local bright_gen = mos.bright_gen(input_mos)
	local steps_per_bright_gen = bright_gen['L'] + bright_gen['s']
	local steps_per_dark_gen = steps_per_period - steps_per_bright_gen
	
	-- Get the scale sig
	local scale_sig = mos.as_string(input_mos)
	
	-- Is there a mos prefix? Should ordinals be used?
	-- For 5L 2s, that would be no to prefixes and yes to ordinals, by default.
	-- For any other mos, that would be yes to prefixes and no to ordinals, by
	-- default. The default prefix is "mos".
	local mos_prefix = "mos"
	local use_ordinals = false
	if scale_sig == "5L 2s" then
		use_ordinals = true
		mos_prefix = ""
	end
	if string.len(frame.args['Interval Notation']) > 0 then
		use_ordinals = frame.args['Interval Notation'] == "ordinals" or frame.args['Interval Notation'] == "ordinal"
	end
	if frame.args['MOS Prefix'] == "NONE" then
		mos_prefix = ""
	elseif string.len(frame.args['MOS Prefix']) > 0 then
		mos_prefix = frame.args['MOS Prefix']
	end
	
	-- Create the table, starting with the headers
	local result = '{| class="wikitable"\n'
	result = result .. '|+\n'
	result = result .. scale_sig .. ' interval varieties\n'
	result = result .. '! rowspan="2" |Interval class\n'
	result = result .. '! colspan="2" |Large variety\n'
	result = result .. '! colspan="2" |Small variety\n'
	result = result .. '|-\n'
	result = result .. '! Size\n'
	result = result .. '! Quality\n'
	result = result .. '!Size\n'
	result = result .. '!Quality\n'
	
	-- First row is the unison
	result = result .. "|-\n"
	if use_ordinals and mos_prefix == "" then
		result = result .. "|'''1st (unison)'''\n"
	elseif use_ordinals and mos_prefix ~= "" then
		result = result .. "|'''" .. mos_prefix .. "-1st (unison)'''\n"
	else
		result = result .. "|'''0-" .. mos_prefix .. "step (unison)'''\n"
	end
	result = result .. "|0\n"
	result = result .. "|Perfect\n"
	result = result .. "|0\n"
	result = result .. "|Perfect\n"
	
	-- Successive rows are the mossteps, starting at the 1-mosstep
	-- Name the interval according to whether it's major/minor or
	-- perf/aug/dim; for nL ns mosses, it's only major/minor for any
	-- non-period intervals.
	local is_nL_ns = input_mos.nL == input_mos.ns
	for i = 1, steps_per_equave - 1 do
		-- If i corresponds to the bright generator, then the large size is
		-- perfect and the small size is diminished.
		-- If i corresponds to the dark generator, then the large size is
		-- augmented and the small size is perfect.
		-- If i corresponds to the period, then the large and small sizes are
		-- the same and they're perfect. This also applies to the equave.
		-- For any other interval, and for generators for nL ns mosses, the
		-- large size is major and the small size is minor.
		result = result .. "|-\n"
		if i % steps_per_period == steps_per_bright_gen and not is_nL_ns then
			if use_ordinals and mos_prefix == "" then
				result = result .. "|'''" .. ord._ordinal(i + 1) .. "'''\n"
			elseif use_ordinals and mos_prefix ~= "" then
				result = result .. "|'''" .. mos_prefix .. "-" .. ord._ordinal(i + 1) .. "'''\n"
			else
				result = result .. "|'''" .. i .. "-" .. mos_prefix .. "step'''\n"
			end
			result = result .. "|" .. p.mos_interval_to_step_count_string(brightest_mode, i) .. "\n"
			result = result .. "|Perfect\n"
			result = result .. "|" .. p.mos_interval_to_step_count_string(darkest_mode, i) .. "\n"
			result = result .. "|Diminished\n"
			
		elseif i % steps_per_period == steps_per_dark_gen and not is_nL_ns then
			if use_ordinals and mos_prefix == "" then
				result = result .. "|'''" .. ord._ordinal(i + 1) .. "'''\n"
			elseif use_ordinals and mos_prefix ~= "" then
				result = result .. "|'''" .. mos_prefix .. "-" .. ord._ordinal(i + 1) .. "'''\n"
			else
				result = result .. "|'''" .. i .. "-" .. mos_prefix .. "step'''\n"
			end
			result = result .. "|" .. p.mos_interval_to_step_count_string(brightest_mode, i) .. "\n"
			result = result .. "|Augmented\n"
			result = result .. "|" .. p.mos_interval_to_step_count_string(darkest_mode, i) .. "\n"
			result = result .. "|Perfect\n"
			
		elseif i % steps_per_period == 0 and i ~= mossteps_per_equave then
			if use_ordinals and mos_prefix == "" then
				result = result .. "|'''" .. ord._ordinal(i + 1) .. "'''\n"
			elseif use_ordinals and mos_prefix ~= "" then
				result = result .. "|'''" .. mos_prefix .. "-" .. ord._ordinal(i + 1) .. "'''\n"
			else
				result = result .. "|'''" .. i .. "-" .. mos_prefix .. "step (period)'''\n"
			end
			result = result .. "|" .. p.mos_interval_to_step_count_string(brightest_mode, i) .. "\n"
			result = result .. "|Perfect\n"
			result = result .. "|" .. p.mos_interval_to_step_count_string(darkest_mode, i) .. "\n"
			result = result .. "|Perfect\n"
			
		else
			if use_ordinals and mos_prefix == "" then
				result = result .. "|" .. ord._ordinal(i + 1) .. "\n"
			elseif use_ordinals and mos_prefix ~= "" then
				result = result .. "|" .. mos_prefix .. "-" .. ord._ordinal(i + 1) .. "\n"
			else
				result = result .. "|" .. i .. "-" .. mos_prefix .. "step\n"
			end
			result = result .. "|" .. p.mos_interval_to_step_count_string(brightest_mode, i) .. "\n"
			result = result .. "|Major\n"
			result = result .. "|" .. p.mos_interval_to_step_count_string(darkest_mode, i) .. "\n"
			result = result .. "|Minor\n"
		end
	end
	
	-- Manually add the last row of the table (this is for the equave)
	result = result .. "|-\n"
	if use_ordinals and mos_prefix == "" then
		result = result .. "|'''" .. ord._ordinal(steps_per_equave + 1)
	elseif use_ordinals and mos_prefix ~= "" then
		result = result .. "|'''" .. mos_prefix .. "-" .. ord._ordinal(steps_per_equave + 1)
	else
		result = result .. "|'''" .. steps_per_equave .. "-" .. mos_prefix .. "step"
	end
	
	-- Is the equave 2/1?
	if rat.eq(input_mos.equave, 2) then
		result = result .. " (octave)'''\n"
	else
		result = result .. " (equave)'''\n"
	end
	
	-- Add the last cells
	result = result .. "|" .. p.mos_interval_to_step_count_string(brightest_mode, steps_per_equave) .. "\n"
	result = result .. "|Perfect\n"
	result = result .. "|" .. p.mos_interval_to_step_count_string(darkest_mode, steps_per_equave) .. "\n"
	result = result .. "|Perfect\n"
	result = result .. "|}"
	
	return result
end

return p