Module:MOS intervals: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
Adopt tamnams module for lookup
Ganaram inukshuk (talk | contribs)
Adopt tamnams and mos module functions
Line 6: Line 6:
local tamnams = require('Module:TAMNAMS')
local tamnams = require('Module:TAMNAMS')
local p = {}
local p = {}
-- TODO:
-- - Adopt mos arithmetic functions provided by mos module
-- 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
-- Helper function
-- Produces the cent range of a mosstep
function p.mos_interval_to_cent_range(step_pattern, mossteps, input_mos)
local step_pattern = step_pattern or "LLsLLLs"
local mossteps = mossteps or 1
local input_mos = input_mos or mos.new(5, 5, 2)
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
-- The range of a small step is from 0 to 1 step of the equalized mos
-- The range of a large step is from 1 step of equalized to 1 step of collapsed
-- These ranges do not apply if the mosstep interval is for a period; instead
-- it's a fixed value.
local equave_in_cents = rat.cents(input_mos.equave)
local s_size_min = 0
local s_size_max = equave_in_cents / (input_mos.nL + input_mos.ns)
local L_size_min = s_size_max
local L_size_max = equave_in_cents / input_mos.nL
local mossteps_per_period = (input_mos.nL + input_mos.ns) / utils._gcd(input_mos.nL, input_mos.ns)
local is_period = mossteps % mossteps_per_period == 0
local result = ""
if not is_period then
local min_cents = L_count * L_size_min + s_count * s_size_max
local max_cents = L_count * L_size_max + s_count * s_size_min
result = string.format("%.1f¢ to %.1f¢", math.min(min_cents, max_cents), math.max(min_cents, max_cents))
else
local period_in_cents = equave_in_cents / utils._gcd(input_mos.nL, input_mos.ns)
local number_of_periods = mossteps / mossteps_per_period
result = string.format("%.1f¢", period_in_cents * number_of_periods)
end
return result
end


-- Main function; to be called by wrapper
-- Main function; to be called by wrapper
Line 101: Line 16:
local scale_sig = mos.as_string(input_mos)
local scale_sig = mos.as_string(input_mos)
-- Get the brightest and darkest modes for the mos
-- Get the brightest and darkest modes as step matrices
local brightest_mode = mos.brightest_mode(input_mos)
local bright_step_matrix = mos.mode_to_step_matrix(mos.brightest_mode(input_mos))
local darkest_mode = string.reverse(brightest_mode)
local dark_step_matrix = mos.mode_to_step_matrix(mos.darkest_mode(input_mos))
-- Get the number of steps per period and equave
-- Get the number of steps per period and equave
local steps_per_equave = (input_mos.nL + input_mos.ns)
local equave_step_count = mos.equave_step_count(input_mos)
local steps_per_period = steps_per_equave / utils._gcd(input_mos.nL, input_mos.ns)
local period_step_count = mos.period_step_count(input_mos)
-- Get the step counts for the bright and dark generators
-- Get the step counts for the bright and dark generators
local bright_gen = mos.bright_gen(input_mos)
local bright_gen_step_count = mos.bright_gen_step_count(input_mos)
local steps_per_bright_gen = bright_gen['L'] + bright_gen['s']
local dark_gen_step_count = mos.dark_gen_step_count(input_mos)
local steps_per_dark_gen = steps_per_period - steps_per_bright_gen
-- Create the table, starting with the headers
-- Create the table, starting with the headers
Line 126: Line 40:
result = result .. '! Range in cents\n'
result = result .. '! Range in cents\n'
-- First row is the unison
-- Write each row
result = result .. "|-\n"
for i = 1, #bright_step_matrix do
result = result .. "| '''0-" .. mos_prefix .. "step (root)'''\n"
 
result = result .. "| Perfect 0-" .. mos_prefix .. "step\n"
-- Compare the bright and dark intervals. If they're the same, then the
result = result .. "| 0\n"
-- current interval class is a period interval.
result = result .. string.format("| %.1f¢\n", 0)
local current_bright_interval = bright_step_matrix[i]
result = result .. string.format("| P0ms\n")
local current_dark_interval = dark_step_matrix[i]
local is_period = mos.interval_eq(current_bright_interval, current_dark_interval)
-- Successive rows are the mossteps, starting at the 1-mosstep
-- Name the interval according to whether it's major/minor or
-- If it's a period interval, then there is only one row to write.
-- perf/aug/dim; for nL ns mosses, it's only major/minor for any
-- Otherwise, there are two rows to write.
-- non-period intervals.
if is_period then
local is_nL_ns = input_mos.nL == input_mos.ns
for i = 1, steps_per_equave - 1 do
if i % steps_per_period == steps_per_bright_gen and not is_nL_ns then
-- If i corresponds to the bright generator, then the large size is
-- perfect and the small size is diminished. Add two rows.
result = result .. "|-\n"
result = result .. "|-\n"
result = result .. '| rowspan="2" ' .. "| '''" .. i .. "-" .. mos_prefix .. "step'''\n"
result = result .. string.format("| '''%s-%sstep'''\n", i-1, mos_prefix)
result = result .. "| Diminished " .. i .. "-" .. mos_prefix .. "step\n"
result = result .. string.format("| %s\n" , tamnams.interval_quality(current_bright_interval, input_mos))
result = result .. "| " .. p.mos_interval_to_step_count_string(darkest_mode, i) .. "\n"
result = result .. string.format("| %s\n" , mos.interval_as_string(current_bright_interval))
result = result .. string.format("| %s\n", p.mos_interval_to_cent_range(darkest_mode, i, input_mos))
result = result .. string.format("| %\n", mos.interval_to_cents(current_bright_interval, input_mos, {1,1}))
result = result .. string.format("| d%ims\n", i)
result = result .. string.format("| %s\n" , tamnams.interval_quality(current_bright_interval, input_mos, "abbrev", "m"))
result = result .. "|-\n"
else
result = result .. "| Perfect " .. i .. "-" .. mos_prefix .. "step\n"
result = result .. "| " .. p.mos_interval_to_step_count_string(brightest_mode, i) .. "\n"
result = result .. string.format("| %s\n", p.mos_interval_to_cent_range(brightest_mode, i, input_mos))
result = result .. string.format("| P%ims\n", i)
elseif i % steps_per_period == steps_per_dark_gen and not is_nL_ns then
-- If i corresponds to the dark generator, then the large size is
-- augmented and the small size is perfect. Add two rows.
result = result .. "|-\n"
result = result .. '| rowspan="2" ' .. "|'''" .. i .. "-" .. mos_prefix .. "step'''\n"
result = result .. "| Perfect " .. i .. "-" .. mos_prefix .. "step\n"
result = result .. "| " .. p.mos_interval_to_step_count_string(darkest_mode, i) .. "\n"
result = result .. string.format("| %s\n", p.mos_interval_to_cent_range(darkest_mode, i, input_mos))
result = result .. string.format("| P%ims\n", i)
result = result .. "|-\n"
result = result .. "|-\n"
result = result .. "| Augmented " .. i .. "-" .. mos_prefix .. "step\n"
result = result .. string.format("| rowspan=\"2\" | '''%s-%sstep'''\n", i-1, mos_prefix)
result = result .. "| " .. p.mos_interval_to_step_count_string(brightest_mode, i) .. "\n"
result = result .. string.format("| %s\n" , tamnams.interval_quality(current_dark_interval, input_mos))
result = result .. string.format("| %s\n", p.mos_interval_to_cent_range(brightest_mode, i, input_mos))
result = result .. string.format("| %s\n" , mos.interval_as_string(current_dark_interval))
result = result .. string.format("| A%ims\n", i)
result = result .. string.format("| %.3f¢ to %.3f¢\n", mos.interval_to_cents(current_dark_interval, input_mos, {1,1}), mos.interval_to_cents(current_dark_interval, input_mos, {1,0}))
result = result .. string.format("| %s\n" , tamnams.interval_quality(current_dark_interval, input_mos, "abbrev", "m"))
elseif i % steps_per_period == 0 and i ~= mossteps_per_equave then
-- 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. Add one row.
result = result .. "|-\n"
result = result .. "|-\n"
result = result .. "| '''" .. i .. "-" .. mos_prefix .. "step (period)'''\n"
result = result .. string.format("| %s\n" , tamnams.interval_quality(current_bright_interval, input_mos))
result = result .. "| Perfect " .. i .. "-" .. mos_prefix .. "step\n"
result = result .. string.format("| %s\n" , mos.interval_as_string(current_bright_interval))
result = result .. "| " .. p.mos_interval_to_step_count_string(darkest_mode, i) .. "\n"
result = result .. string.format("| %.3f¢ to %.3f¢\n", mos.interval_to_cents(current_bright_interval, input_mos, {1,1}), mos.interval_to_cents(current_dark_interval, input_mos, {1,0}))
result = result .. string.format("| %s\n", p.mos_interval_to_cent_range(darkest_mode, i, input_mos))
result = result .. string.format("| %s\n" , tamnams.interval_quality(current_bright_interval, input_mos, "abbrev", "m"))
result = result .. string.format("| P%ims\n", i)
else
-- For any other interval, and for generators for nL ns mosses, the
-- large size is major and the small size is minor. Add two rows.
result = result .. "|-\n"
result = result .. '| rowspan="2" ' .. "|" .. i .. "-" .. mos_prefix .. "step\n"
result = result .. "| Minor " .. i .. "-" .. mos_prefix .. "step\n"
result = result .. "|" .. p.mos_interval_to_step_count_string(darkest_mode, i) .. "\n"
result = result .. string.format("| %s\n", p.mos_interval_to_cent_range(darkest_mode, i, input_mos))
result = result .. string.format("| m%ims\n", i)
result = result .. "|-\n"
result = result .. "| Major " .. i .. "-" .. mos_prefix .. "step\n"
result = result .. "| " .. p.mos_interval_to_step_count_string(brightest_mode, i) .. "\n"
result = result .. string.format("| %s\n", p.mos_interval_to_cent_range(brightest_mode, i, input_mos))
result = result .. string.format("| M%ims\n", i)
end
end
end
 
-- Add the last row of the table, which is either the equave or octave
local equave_name = ""
if rat.eq(input_mos.equave, 2) then
result = result .. "|-\n"
result = result .. "| '''" .. steps_per_equave .. "-" .. mos_prefix .. "step (octave)'''\n"
result = result .. "| Perfect " .. steps_per_equave .. "-" .. mos_prefix .. "step'''\n"
result = result .. "| " .. p.mos_interval_to_step_count_string(darkest_mode, steps_per_equave) .. "\n"
result = result .. string.format("| %.1f¢\n", 1200)
result = result .. string.format("| P%ims\n", steps_per_equave)
else
result = result .. "|-\n"
result = result .. "| '''" .. steps_per_equave .. "-" .. mos_prefix .. "step (equave)'''\n"
result = result .. "| Perfect " .. steps_per_equave .. "-" .. mos_prefix .. "step'''\n"
result = result .. "| " .. p.mos_interval_to_step_count_string(darkest_mode, steps_per_equave) .. "\n"
result = result .. string.format("| %.1f¢\n", rat.cents(input_mos.equave))
result = result .. string.format("| P%ims\n", steps_per_equave)
end
end

Revision as of 23:33, 6 June 2024

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
10 _mos_intervals (main) (input_mos, mos_prefix)
84 mos_intervals (invokable) (frame)
Lua modules required (6)
Variable Module Functions used
et Module:ET dependency not used
mos Module:MOS new
as_string
mode_to_step_matrix
brightest_mode
darkest_mode
equave_step_count
period_step_count
bright_gen_step_count
dark_gen_step_count
interval_eq
interval_as_string
interval_to_cents
parse
ord Module:Ordinal dependency not used
rat Module:Rational dependency not used
tamnams Module:TAMNAMS interval_quality
lookup_prefix
utils Module:Utils dependency not used

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 utils = require('Module:Utils')
local et = require('Module:ET')
local tamnams = require('Module:TAMNAMS')
local p = {}

-- Main function; to be called by wrapper
function p._mos_intervals(input_mos, mos_prefix)
	-- Default param for input mos is 5L 2s
	local input_mos = input_mos or mos.new(5, 2, 2)
	local mos_prefix = mos_prefix or "dia"
	
	-- Get the scale sig
	local scale_sig = mos.as_string(input_mos)
	
	-- Get the brightest and darkest modes as step matrices
	local bright_step_matrix = mos.mode_to_step_matrix(mos.brightest_mode(input_mos))
	local dark_step_matrix = mos.mode_to_step_matrix(mos.darkest_mode(input_mos))
	
	-- Get the number of steps per period and equave
	local equave_step_count = mos.equave_step_count(input_mos)
	local period_step_count = mos.period_step_count(input_mos)
	
	-- Get the step counts for the bright and dark generators
	local bright_gen_step_count = mos.bright_gen_step_count(input_mos)
	local dark_gen_step_count = mos.dark_gen_step_count(input_mos)
	
	-- Create the table, starting with the headers
	local result = '{| class="wikitable"\n'
	result = result .. '|+ Intervals of ' .. scale_sig .. '\n'
	result = result .. '! colspan="2" | Intervals\n'
	result = result .. '! colspan="2" | Size\n'
	result = result .. '! rowspan="2" | Abbrev.\n'
	result = result .. '|-\n'
	result = result .. '! Generic\n'
	result = result .. '! Specific\n'
	result = result .. '! Steps\n'
	result = result .. '! Range in cents\n'
	
	-- Write each row
	for i = 1, #bright_step_matrix do

		-- Compare the bright and dark intervals. If they're the same, then the
		-- current interval class is a period interval.
		local current_bright_interval = bright_step_matrix[i]
		local current_dark_interval = dark_step_matrix[i]
		local is_period = mos.interval_eq(current_bright_interval, current_dark_interval)
		
		-- If it's a period interval, then there is only one row to write.
		-- Otherwise, there are two rows to write.
		if is_period then
			result = result .. "|-\n"
			result = result .. string.format("| '''%s-%sstep'''\n", i-1, mos_prefix)
			result = result .. string.format("| %s\n" , tamnams.interval_quality(current_bright_interval, input_mos))
			result = result .. string.format("| %s\n" , mos.interval_as_string(current_bright_interval))
			result = result .. string.format("| %d¢\n", mos.interval_to_cents(current_bright_interval, input_mos, {1,1}))
			result = result .. string.format("| %s\n" , tamnams.interval_quality(current_bright_interval, input_mos, "abbrev", "m"))
		else
			result = result .. "|-\n"
			result = result .. string.format("| rowspan=\"2\" | '''%s-%sstep'''\n", i-1, mos_prefix)
			result = result .. string.format("| %s\n" , tamnams.interval_quality(current_dark_interval, input_mos))
			result = result .. string.format("| %s\n" , mos.interval_as_string(current_dark_interval))
			result = result .. string.format("| %.3f¢ to %.3f¢\n", mos.interval_to_cents(current_dark_interval, input_mos, {1,1}), mos.interval_to_cents(current_dark_interval, input_mos, {1,0}))
			result = result .. string.format("| %s\n" , tamnams.interval_quality(current_dark_interval, input_mos, "abbrev", "m"))
			
			result = result .. "|-\n"
			result = result .. string.format("| %s\n" , tamnams.interval_quality(current_bright_interval, input_mos))
			result = result .. string.format("| %s\n" , mos.interval_as_string(current_bright_interval))
			result = result .. string.format("| %.3f¢ to %.3f¢\n", mos.interval_to_cents(current_bright_interval, input_mos, {1,1}), mos.interval_to_cents(current_dark_interval, input_mos, {1,0}))
			result = result .. string.format("| %s\n" , tamnams.interval_quality(current_bright_interval, input_mos, "abbrev", "m"))
		end

	end
	
	result = result .. "|}"	
	
	return result
	
end

-- Wrapper function; to be called by template
function p.mos_intervals(frame)
	-- Get input mos
	local input_mos = mos.parse(frame.args['Scale Signature'])
	
	-- 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 scale_sig = mos.as_string(input_mos)
	local mos_prefix = tamnams.lookup_prefix(input_mos)
	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

	local result = p._mos_intervals(input_mos, mos_prefix)

	return result
end

return p