Module:MOS tuning spectrum: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
No edit summary
Ganaram inukshuk (talk | contribs)
Ratios -> Step Ratios; adopt mos module functions
Line 1: Line 1:
local MOS = require("Module:MOS")
local mos = require("Module:MOS")
local rat = require("Module:Rational")
local ET = require("Module:ET")
local ET = require("Module:ET")
local rat = require("Module:Rational")
local mediants = require("Module:Mediants")
local mediants = require("Module:Mediants")
local tip = require("Module:Template input parse")
local utils = require("Module:Utils")
local utils = require("Module:Utils")
local yesno = require("Module:yesno")
local yesno = require("Module:yesno")
Line 14: Line 15:
default_ratios, default_depths = mediants.find_mediants({{1,1}, {1,0}}, default_depth);
default_ratios, default_depths = mediants.find_mediants({{1,1}, {1,0}}, default_depth);
local input_mos  = args["Input MOS"] or MOS.new(5, 2)
local input_mos  = args["Input MOS"] or mos.new(5, 2)
local depth      = args["Depth"] or 5
local depth      = args["Depth"] or 5
local comments    = args["Comments"] or {}
local comments    = args["Comments"] or {}
local step_ratios = args["Ratios"] or default_ratios
local step_ratios = args["Step Ratios"] or default_ratios
local depths      = args["Depths"] or default_depths
local depths      = args["Depths"] or default_depths
Line 24: Line 25:
local s = input_mos.ns -- Small steps in mos
local s = input_mos.ns -- Small steps in mos
local n = utils._gcd(L, s) -- Number of periods
local n = utils._gcd(L, s) -- Number of periods
local abstract_bright_gen = MOS.bright_gen(input_mos)
-- What is the equave suffix (edo, edt, edf, ed-p/q)
-- What is the equave suffix (edo, edt, edf, ed-p/q)
Line 40: Line 40:
-- Default comments for TAMNAMS-named step ratios
-- Default comments for TAMNAMS-named step ratios
local default_comments = {}
local default_comments = {}
local mos_as_string = MOS.as_string(input_mos)
local mos_as_string = mos.as_string(input_mos)
default_comments["1/1"] = string.format("'''Equalized %s'''", mos_as_string)
default_comments["1/1"] = string.format("'''Equalized %s'''", mos_as_string)
default_comments["4/3"] = string.format("'''Supersoft %s'''", mos_as_string)
default_comments["4/3"] = string.format("'''Supersoft %s'''", mos_as_string)
Line 63: Line 63:
comments_header_text = comments_header_text .. "<sup><abbr title=\"Every tuning produces a proper scale.\">(always proper)</abbr></sup>"
comments_header_text = comments_header_text .. "<sup><abbr title=\"Every tuning produces a proper scale.\">(always proper)</abbr></sup>"
elseif s == n and n > 1 then
elseif s == n and n > 1 then
comments_header_text = comments_header_text .. "<sup><abbr title=\"Every true-MOS tuning produces a proper scale.\">(always proper)</abbr></sup>"
comments_header_text = comments_header_text .. "<sup><abbr title=\"Every true-mos tuning produces a proper scale.\">(always proper)</abbr></sup>"
end
end
Line 90: Line 90:
for i = 1, #step_ratios do
for i = 1, #step_ratios do
local step_ratio = step_ratios[i]
local step_ratio = step_ratios[i]
local steps_per_equave = step_ratio[1] * L + step_ratio[2] * s
local et = mos.mos_to_et(mos, step_ratio)
local steps_per_period = steps_per_equave / n
local et = ET.new(steps_per_equave, equave)
-- Calculate the bright gen and cent value
-- Calculate the bright gen and cent value
local bright_generator_steps = step_ratio[1] * abstract_bright_gen["L"] + step_ratio[2] * abstract_bright_gen["s"]
local bright_generator_steps = mos.bright_gen_to_et_steps(input_mos, step_ratio)
local bright_generator_cents = ET.cents(et, bright_generator_steps)
local bright_generator_cents = mos.bright_gen_to_cents(input_mos, step_ratio)
-- Calculate dark generator step count and cent value
-- Calculate dark generator step count and cent value
local dark_generator_steps = steps_per_period - bright_generator_steps
local dark_generator_steps = mos.dark_gen_to_et_steps(input_mos, step_ratio)
local dark_generator_cents = ET.cents(et, dark_generator_steps)
local dark_generator_cents = mos.dark_gen_to_cents(input_mos, step_ratio)
-- New row
-- New row
Line 155: Line 153:
-- Parse scalesig
-- Parse scalesig
local input_mos = MOS.parse(args["Scale Signature"])
local input_mos = mos.parse(args["Scale Signature"])
args["Input MOS"] = input_mos
args["Input MOS"] = input_mos
args["Scale Signature"] = nil
args["Scale Signature"] = nil
Line 163: Line 161:
args["Depth"] = tonumber(args["Depth"])
args["Depth"] = tonumber(args["Depth"])
-- Generate mediants and depths
-- Parse initial ratios
local ratios, depths
local first_ratio = args["First Step Ratio"] or {1,1}
ratios, depths = mediants.find_mediants({{1,1}, {1,0}}, depth)
local last_ratio  = args["Last Step Ratio" ] or {1,0}
args["Ratios"] = ratios
-- Then generate mediants and depths
local step_ratios, depths
step_ratios, depths = mediants.find_mediants({first_ratio, last_ratio}, depth)
args["Step Ratios"] = step_ratios
args["Depths"] = depths
args["Depths"] = depths
-- Transfer comments from args to comments
-- Transfer comments from args to comments
local comments = {}
local comments = {}
for i = 1, #ratios do
for i = 1, #step_ratios do
local key = ratios[i][1] .. "/" .. ratios[i][2]
local key = step_ratios[i][1] .. "/" .. step_ratios[i][2]
if args[key] ~= nil then
if args[key] ~= nil then
comments[key] = args[key]
comments[key] = args[key]

Revision as of 05:08, 28 February 2025

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

This template displays the tuning spectrum of a given mos scale, showing various step ratios between 1:1 to 1:0 (by default).

Introspection summary for Module:MOS tuning spectrum 
Functions provided (2)
Line Function Params
12 _mos_tuning_spectrum (main) (args)
151 mos_tuning_spectrum (invokable) (frame)
Lua modules required (8)
Variable Module Functions used
getArgs Module:Arguments getArgs
ET Module:ET as_string
mediants Module:Mediants find_mediants
mos Module:MOS new
as_string
mos_to_et
bright_gen_to_et_steps
bright_gen_to_cents
dark_gen_to_et_steps
dark_gen_to_cents
parse
rat Module:Rational eq
new
as_ratio
tip Module:Template input parse dependency not used
utils Module:Utils _gcd
yesno Module:yesno yesno

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


local mos = require("Module:MOS")
local rat = require("Module:Rational")
local ET = require("Module:ET")
local mediants = require("Module:Mediants")
local tip = require("Module:Template input parse")
local utils = require("Module:Utils")
local yesno = require("Module:yesno")
local getArgs = require("Module:Arguments").getArgs
local p = {}

-- Re-re-rewrite of tuning spectrum
function p._mos_tuning_spectrum(args)
	local default_ratios, default_depths
	local default_depth = 5
	default_ratios, default_depths = mediants.find_mediants({{1,1}, {1,0}}, default_depth);
	
	local input_mos   = args["Input MOS"] or mos.new(5, 2)
	local depth       = args["Depth"] or 5
	local comments    = args["Comments"] or {}
	local step_ratios = args["Step Ratios"] or default_ratios
	local depths      = args["Depths"] or default_depths
	
	local equave = input_mos.equave
	local L = input_mos.nL		-- Large steps in mos
	local s = input_mos.ns		-- Small steps in mos
	local n = utils._gcd(L, s)		-- Number of periods
	
	-- What is the equave suffix (edo, edt, edf, ed-p/q)
	local equave_suffix = ""
	if rat.eq(input_mos.equave, rat.new(2)) then
		equave_suffix = "o"
	elseif rat.eq(input_mos.equave, rat.new(3)) then
		equave_suffix = "t"
	elseif rat.eq(input_mos.equave, rat.new(3, 2)) then
		equave_suffix = "f"
	else
		equave_suffix = rat.as_ratio(input_mos.equave)
	end
	
	-- Default comments for TAMNAMS-named step ratios
	local default_comments = {}
	local mos_as_string = mos.as_string(input_mos)
	default_comments["1/1"] = string.format("'''Equalized %s'''", mos_as_string)
	default_comments["4/3"] = string.format("'''Supersoft %s'''", mos_as_string)
	default_comments["3/2"] = string.format("'''Soft %s'''", mos_as_string)
	default_comments["5/3"] = string.format("'''Semisoft %s'''", mos_as_string)
	default_comments["2/1"] = string.format("'''Basic %s'''", mos_as_string)
	default_comments["5/2"] = string.format("'''Semihard %s'''", mos_as_string)
	default_comments["3/1"] = string.format("'''Hard %s'''", mos_as_string)
	default_comments["4/1"] = string.format("'''Superhard %s'''", mos_as_string)
	default_comments["1/0"] = string.format("'''Collapsed %s'''", mos_as_string)
	
	-- Append boundary of proper scales to basic comment, if applicable
	-- Monosmall mosses and knL ns mosses are always proper, but all other mosses
	-- are proper if the step ratio is within the soft-of-basic range
	if n < s then
		default_comments["2/1"] = default_comments["2/1"] .. "<br />Scales with tunings softer than this are proper"
	end
	
	-- Produce table header for the comments
	local comments_header_text = "Comments"
	if s == 1 then
		comments_header_text = comments_header_text .. "<sup><abbr title=\"Every tuning produces a proper scale.\">(always proper)</abbr></sup>"
	elseif s == n and n > 1 then
		comments_header_text = comments_header_text .. "<sup><abbr title=\"Every true-mos tuning produces a proper scale.\">(always proper)</abbr></sup>"
	end
	
	-- Table headers
	-- There are 6 columns:
	-- - Steps of ED
	-- - Bright and dark gens in cents
	-- - Step ratio and hardness
	-- - Comments
	local result = "{| class=\"wikitable center-all\"\n"
		.. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("Scale tree and tuning spectrum of %s\n", mos_as_string)
		.. "|-\n"
		.. string.format("! rowspan=\"2\" colspan=\"%d\" | Generator<sup><abbr title=\"In steps of ed%s.\">(ed%s)</abbr></sup>\n", depth + 1, equave_suffix, equave_suffix)
		.. "! colspan=\"2\" | Cents\n"
		.. "! colspan=\"2\" | Step ratio\n"
		.. "! rowspan=\"2\" | " .. comments_header_text .. "\n"
		.. "|-\n"
		.. "! Bright\n"
		.. "! Dark\n"
		.. "! L:s\n"
		.. "! Hardness\n"

	-- Rounding is done using string.format, to 3 decimal places (%.3f)
	
	-- Create each row of the table
	for i = 1, #step_ratios do
		local step_ratio = step_ratios[i]
		local et = mos.mos_to_et(mos, step_ratio)
		
		-- Calculate the bright gen and cent value
		local bright_generator_steps = mos.bright_gen_to_et_steps(input_mos, step_ratio)
		local bright_generator_cents = mos.bright_gen_to_cents(input_mos, step_ratio)
		
		-- Calculate dark generator step count and cent value
		local dark_generator_steps = mos.dark_gen_to_et_steps(input_mos, step_ratio)
		local dark_generator_cents = mos.dark_gen_to_cents(input_mos, step_ratio)
		
		-- New row
		result = result .. "|-\n"	
		
		-- Cells for bright generator, as steps in et
		local current_depth = depths[i] + 1
		for i = 1, depth + 1 do
			result = result .. "| "
			if i == current_depth then
				result = result .. string.format("[[%s|%d\\%s]]", ET.as_string(et), bright_generator_steps, et.size)
			end
			result = result .. "\n"
		end
		
		-- Cells for generators in cents
		result = result .. string.format("| %.3f\n", bright_generator_cents)
		result = result .. string.format("| %.3f\n", dark_generator_cents)
		
		-- Cell for step ratio
		result = result .. string.format("| %d:%d\n", step_ratio[1], step_ratio[2])
		
		-- Cell for hardness, with divide-by-zero check
		local hardness = ""
		if step_ratio[2] == 0 then
			hardness = "&rarr;&nbsp;&#8734;"
		else
			hardness = string.format("%.3f", step_ratio[1] / step_ratio[2])
		end
		result = result .. string.format("| %s\n", hardness)
		
		-- Cell for comment
		-- Default comments are on their own line before custom comments
		local key = step_ratios[i][1] .. "/" .. step_ratios[i][2]		-- The step ratio is (literally and figuratively) the key to add comments!
		local comment = ""
		local default_comment = default_comments[key] or ""
		local custom_comment = comments[key] or ""
		if default_comment == "" then
			comment = custom_comment
		else
			comment = default_comment .. "<br />" .. custom_comment
		end
		result = result .. string.format("| style=\"text-align: left;\" | %s\n", comment)
		
	end
	
	result = result .. "|}"
	return result
end

-- Wrapper function; to be called by template
function p.mos_tuning_spectrum(frame)
	local args = getArgs(frame)
	
	-- Parse scalesig
	local input_mos = mos.parse(args["Scale Signature"])
	args["Input MOS"] = input_mos
	args["Scale Signature"] = nil
	
	-- Parse depth
	local depth = tonumber(args["Depth"])
	args["Depth"] = tonumber(args["Depth"])
	
	-- Parse initial ratios
	local first_ratio = args["First Step Ratio"] or {1,1}
	local last_ratio  = args["Last Step Ratio" ] or {1,0}
	
	-- Then generate mediants and depths
	local step_ratios, depths
	step_ratios, depths = mediants.find_mediants({first_ratio, last_ratio}, depth)
	args["Step Ratios"] = step_ratios
	args["Depths"] = depths
	
	-- Transfer comments from args to comments
	local comments = {}
	for i = 1, #step_ratios do
		local key = step_ratios[i][1] .. "/" .. step_ratios[i][2]
		if args[key] ~= nil then
			comments[key] = args[key]
			args[key] = nil
		end
	end
	args["Comments"] = comments
	
	-- Parse debug option
	local debugg = yesno(args["debug"])
	
	-- Output
	local out_str = p._mos_tuning_spectrum(args)
	return frame:preprocess(debugg == true and "<pre>" .. out_str .. "</pre>" or out_str)
end

return p