Module:MOS degrees

From Xenharmonic Wiki
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 degrees.
Module:MOS degrees is deprecated and has been replaced by Module:MOS tunings. Further use of this module is not advised. This module is kept for historical purposes and should not be deleted.
Introspection summary for Module:MOS degrees 
Functions provided (1)
Line Function Params
12 mos_degrees_frame (invokable) (frame)
Lua modules required (5)
Variable Module Functions used
et Module:ET new
cents
mos Module:MOS parse
new
as_string
bright_gen
mosnot Module:MOS notation parse_step_ratio
parse_udp
mos_nomacc_chain
mos_degree_chain
mosstep_and_chroma_to_note_name
mosstep_and_quality_to_degree
rat Module:Rational gcd
cents
utils Module:Utils _round_dec

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


local mos = require('Module:MOS')
local et = require('Module:ET')
local rat = require('Module:Rational')
local utils = require('Module:Utils')
local mosnot = require('Module:MOS notation')		-- Contains the important functions
local p = {}

-- Algorithm:
-- Use the input mos, udp, and step ratio to find the genchains
-- Using the genchains and UDP, find the mos's intervals/degrees
-- Format the result as a table
function p.mos_degrees_frame(frame)
	-- Default parameters for input mos and step ratio (5L 2s and 2:1 step ratio)
	local input_mos_unparsed = frame.args['Scale Signature']
	local input_mos = mos.parse(input_mos_unparsed) or mos.new(2, 5, 2)
	
	-- Step ratio
	local step_ratio = { 2, 1 }
	if string.len(frame.args['Step Ratio']) > 0 then
		step_ratio = mosnot.parse_step_ratio(frame.args['Step Ratio'])
	end
	
	-- Get the number of mossteps per period and equave
	local mossteps_per_equave = input_mos.nL + input_mos.ns
	local periods_per_equave = rat.gcd(input_mos.nL, input_mos.ns)
	local mossteps_per_period = mossteps_per_equave / periods_per_equave
	
	-- If certain params were left blank and the scalesig is 5L 2s, the default
	-- params will be for standard notation
	local scale_sig = mos.as_string(input_mos)
	
	-- The default UDP corresponds to the middle mode. For mosses with an even
	-- number of modes, there are two middle modes, so use the brighter of the
	-- two instead.
	-- If it's 5L 2s, default to the second-brightest mode.
	local udp = { periods_per_equave * math.ceil((mossteps_per_period - 1)/ 2), periods_per_equave * math.floor((mossteps_per_period - 1) / 2) }
	if scale_sig == "5L 2s" then
		udp = { 5, 1 }
	end
	if string.len(frame.args['UDP']) > 0 then
		udp = mosnot.parse_udp(frame.args['UDP'])
	end
	local generators_up = udp[1]
	local generators_down = udp[2]
	
	
	-- Get note symbols
	-- If this param was blank, default to diamond-mos; limited to 17 note names
	-- But if it's blank and the scalesig is 5L 2s, default to standard notation
	-- This order of operations allows for overriding standard notation for 5L 2s
	local note_symbols_main = "JKLMNOPQRSTUVWXYZ"
	local note_symbols = string.sub(note_symbols_main, 1, mossteps_per_equave)
	if scale_sig == "5L 2s" then
		note_symbols = "CDEFGAB"
	end
	-- If a value was entered, override the default value
	if string.len(frame.args['Note Symbols']) > 0 then
		note_symbols = frame.args['Note Symbols']
	end
	
	-- Get accidental symbols
	-- If this param was blank, default to diamond-mos symbols & and @
	-- unless the mos is 5L 2s, then it's sharp and flat # and b
	-- This order of operations allows for overriding standard notation for 5L 2s
	local chroma_plus_symbol = "&"
	local chroma_minus_symbol = "@"
	if scale_sig == "5L 2s" then
		chroma_plus_symbol = "#"
		chroma_minus_symbol = "b"
	end
	-- If value(s) were entered, override the default values
	if string.len(frame.args['Sharp Symbol']) > 0 then
		chroma_plus_symbol = frame.args['Sharp Symbol']
	end
	if string.len(frame.args['Flat Symbol']) > 0 then
		chroma_minus_symbol = frame.args['Flat Symbol']
	end
	
	-- Override values for testing
	--[[
	local input_mos = mos.new(5, 2, 2)
	local step_ratio = { 2, 1 }
	local udp = { 5, 1 }
	local note_symbols = "CDEFGAB"
	local chroma_plus_symbol = "#"
	local chroma_minus_symbol = "b"
	local mossteps_per_equave = input_mos.nL + input_mos.ns
	local periods_per_equave = rat.gcd(input_mos.nL, input_mos.ns)
	local mossteps_per_period = mossteps_per_equave / periods_per_equave
	local scale_sig = mos.as_string(input_mos)
	]]--
	
	-- How long are the initial genchain lengths? (These correspond to the UDP)
	local gens_up_per_period = udp[1] / periods_per_equave
	local gens_dn_per_period = udp[2] / periods_per_equave
	
	-- How long should the genchains be?
	-- The length should be such that:
	-- - Every non-root interval is shown in its small, large, augmented, and
	--   diminished size.
	-- - The root and equave are shown in their perfect sizes, followed by their
	--   augmented and diminished sizes respectively.
	-- - Any non-root non-equave periods are shown in their perfect, augmented,
	--   and diminished sizes.
	-- To do this requires going up 2x+2y generators, and down the same amount.
	-- Going up x+y gens from the root reaches every scale degree's large size,
	-- plus the augmented root, then going up x+y-1 more gens reaches each
	-- augmented degree. Same is true for going down to get minor/dim degrees.
	local asc_chain_length = input_mos.nL * 2 + input_mos.ns
	local des_chain_length = input_mos.nL * 2 + input_mos.ns
	
	-- Get the genchains
	local asc_genchain = mosnot.mos_nomacc_chain(input_mos, gens_up_per_period, asc_chain_length, true)
	local des_genchain = mosnot.mos_nomacc_chain(input_mos, gens_dn_per_period, des_chain_length, false)
	
	-- Get the degrees
	local asc_degrees = mosnot.mos_degree_chain(input_mos, asc_chain_length, true)
	local des_degrees = mosnot.mos_degree_chain(input_mos, des_chain_length, false)
	
	-- Format the output as a table, starting with the header row
	local result = '{| class="wikitable sortable"\n'

	-- Produce the headers
	local steps_in_et = input_mos.nL * step_ratio[1] + input_mos.ns * step_ratio[2]
	local et_for_mos = et.new(steps_in_et, input_mos.equave)
	result = result .. "! Scale degree !! Steps !! Cents !! Note name on ".. string.sub(note_symbols, 1, 1) .. "\n"
	
	-- How many esteps per period? Per bright/dark gen?
	local esteps_per_period = steps_in_et / periods_per_equave
	local bright_gen = mos.bright_gen(input_mos)
	local esteps_per_bright_gen = bright_gen['L'] * step_ratio[1] + bright_gen['s'] * step_ratio[2]
	local esteps_per_dark_gen = esteps_per_period - esteps_per_bright_gen
	
	-- Add the rows
	local step_ratio_gcd = rat.gcd(step_ratio[1], step_ratio[2])		-- GCD of the sizes of L and s, in case L:s isn't simplified
	local cents_per_equave = rat.cents(input_mos.equave)				-- Equave in cents
	
	-- For each period, add a row containing a scale degree, step count, cent
	-- value, and note name from the ascending genchain, then do the same with
	-- the descending genchain, in reverse and skipping the perfect root and
	-- raising any other root by one period. Repeat for all other periods.
	-- For the last period, add the perfect root as the perfect equave.
	-- TODO: Formatting (rounding cent values, row coloring, etc)
	for i = 1, periods_per_equave do
		-- Add degrees from ascending chain
		for j = 1, asc_chain_length do
			local note = asc_genchain[i][j]
			local mossteps = note['mossteps']
			local chromas = note['chromas']
			local quality = asc_degrees[i][j]['quality']
			
			-- Find the note name
			local note_symbol = string.sub(note_symbols, mossteps + 1, mossteps + 1)
			local note_name = mosnot.mosstep_and_chroma_to_note_name(mossteps, chromas, note_symbol, chroma_plus_symbol)
			
			-- Find the degree name
			-- If the degree is the 0-mosdegree, say it's the unison
			local degree_name = mosnot.mosstep_and_quality_to_degree(mossteps, quality)
			if mossteps == 0 then
				degree_name = degree_name .. " (unison)"
			end
			
			-- Find the estep count
			local estep_count = ((j - 1) * esteps_per_bright_gen) % esteps_per_period + (i - 1) * esteps_per_period
			
			-- Find the cent value, rounded
			local cent_value = et.cents(et_for_mos, estep_count)
			cent_value = utils._round_dec(cent_value, 1)
			
			-- Add the row
			result = result .. "|-\n" 
			result = result .. "| " .. degree_name .. "\n"
			result = result .. "| " .. estep_count .. "\n"
			result = result .. "| " .. cent_value  .. \n"
			result = result .. "| " .. note_name   .. "\n"
		end
		
		-- Calculate the stop value for the for loop as being 1 or 2, depending
		-- on whether this is the last period or not
		local stop_value = 1
		if i ~= periods_per_equave then
			stop_value = stop_value + 1
		end
		
		-- Add degrees from descending chain
		for j = des_chain_length, stop_value, -1 do
			local note = des_genchain[i][j]
			local mossteps = note['mossteps']
			local chromas = note['chromas']
			local quality = des_degrees[i][j]['quality']
			
			-- Find the note name
			-- If the mosstep is the root of the period, add a period to it
			local note_symbol = string.sub(note_symbols, mossteps + 1, mossteps + 1)
			if mossteps % mossteps_per_period == 0 then
				mossteps = mossteps + mossteps_per_period
			end
			local note_name = mosnot.mosstep_and_chroma_to_note_name(mossteps, chromas, note_symbol, chroma_minus_symbol)
			
			-- Find the degree name
			-- If the degree corresponds to the equave, say it's the equave
			local degree_name = mosnot.mosstep_and_quality_to_degree(mossteps, quality)
			
			-- Find the estep count
			local estep_count = ((j - 1) * esteps_per_dark_gen) % esteps_per_period + (i - 1) * esteps_per_period
			
			-- Find the cent value
			local cent_value = et.cents(et_for_mos, estep_count)
			cent_value = utils._round_dec(cent_value, 1)
			
			-- If the mosstep is for the equave, add that name
			if mossteps == mossteps_per_equave then
				degree_name = degree_name .. " (equave)"
			end
			
			-- If the note corresponds to the root, say it's the equave instead
			if cent_value == 0 then
				cent_value = cents_per_equave
				estep_count = steps_in_et
				note_name = string.sub(note_symbols, 1, 1) .. " (one equave up)"
			end
			
			-- Add the row
			result = result .. "|-\n" 
			result = result .. "| " .. degree_name .. "\n"
			result = result .. "| " .. estep_count .. "\n"
			result = result .. "| " .. cent_value  .. \n"
			result = result .. "| " .. note_name   .. "\n"
		end
	end
	
	result = result .. "|}"
	
	return result
end
	
return p