Module:MOS tuning spectrum: Difference between revisions
Jump to navigation
Jump to search
todo |
Remove depth arg entirely from function, as deepest depth can be found via helper function from mediants module |
||
| Line 6: | Line 6: | ||
-- TODO: | -- TODO: | ||
-- - (Low priority): Force-add comments that lie outside searched depth. If such | -- - (Low priority): Force-add comments that lie outside searched depth. If such | ||
-- ratios are added, they're added in either the last column or their own | -- ratios are added, they're added in either the last column or their own | ||
| Line 16: | Line 12: | ||
-- Re-re-rewrite of tuning spectrum | -- Re-re-rewrite of tuning spectrum | ||
function p._mos_tuning_spectrum(args) | function p._mos_tuning_spectrum(args) | ||
-- Find default ratios, if nothing was entered | |||
local default_ratios, default_depths | local default_ratios, default_depths | ||
local default_depth = 5 | local default_depth = 5 | ||
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 comments = args["Comments" ] or {} | |||
local comments = args["Comments"] or {} | |||
local step_ratios = args["Step 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 | ||
local deepest = mediants.deepest_depth(depths) | |||
local equave = input_mos.equave | local equave = input_mos.equave | ||
local large_steps = input_mos.nL -- Large steps in mos | local large_steps = input_mos.nL -- Large steps in mos | ||
local small_steps = input_mos.ns -- Small steps in mos | local small_steps = input_mos.ns -- Small steps in mos | ||
| Line 71: | Line 68: | ||
.. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("Scale tree and tuning spectrum of %s\n", mos_as_string) | .. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("Scale tree and tuning spectrum of %s\n", mos_as_string) | ||
.. "|-\n" | .. "|-\n" | ||
.. string.format("! rowspan=\"2\" colspan=\"%d\" | Generator<sup><abbr title=\"In steps of %s.\">(%s)</abbr></sup>\n", | .. string.format("! rowspan=\"2\" colspan=\"%d\" | Generator<sup><abbr title=\"In steps of %s.\">(%s)</abbr></sup>\n", deepest + 1, et_suffix, et_suffix) | ||
.. "! colspan=\"2\" | Cents\n" | .. "! colspan=\"2\" | Cents\n" | ||
.. "! colspan=\"2\" | Step ratio\n" | .. "! colspan=\"2\" | Step ratio\n" | ||
| Line 155: | Line 152: | ||
-- Processed args: | -- Processed args: | ||
-- - Input MOS (processed from textual scalesig) | -- - Input MOS (processed from textual scalesig) | ||
-- - Step Ratios (table of step ratios found) | -- - Step Ratios (table of step ratios found) | ||
-- - Depths (table of how deep each step ratio was found at) | -- - Depths (table of how deep each step ratio was found at) | ||
| Line 163: | Line 158: | ||
local args = getArgs(frame) | local args = getArgs(frame) | ||
-- Parse scalesig | -- Parse scalesig as input mos, then remove 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 | ||
-- Parse initial ratios | -- Parse initial ratios | ||
local first_ratio = args["First Step Ratio"] or {1,1} | local first_ratio = args["First Step Ratio"] or {1,1} | ||
local last_ratio = args["Last Step Ratio" ] or {1,0} | local last_ratio = args["Last Step Ratio" ] or {1,0} | ||
-- Then generate mediants and depths | -- Then generate mediants and depths | ||
-- If using depth-based search, parse the depth, then remove it from args | |||
-- If using int-limit search, parse the int limit, then remove it from args, | |||
-- and remove depth for good measure | |||
local step_ratios, depths | local step_ratios, depths | ||
local use_int_limit = args["Int Limit"] ~= nil | |||
if use_int_limit then | if use_int_limit then | ||
-- Int-limit search; parse int-limit | |||
local int_limit = tonumber(args["Int Limit"]) | |||
args["Int Limit"] = nil | |||
-- Find ratios and depths found | |||
step_ratios, depths = mediants.find_mediants_by_int_limit({first_ratio, last_ratio}, int_limit) | step_ratios, depths = mediants.find_mediants_by_int_limit({first_ratio, last_ratio}, int_limit) | ||
else | |||
-- Depth-based search; parse depth | |||
local depth = tonumber(args["Depth"]) or 5 | |||
-- Find ratios and depths found | |||
step_ratios, depths = mediants.find_mediants({first_ratio, last_ratio}, depth) | step_ratios, depths = mediants.find_mediants({first_ratio, last_ratio}, depth) | ||
end | end | ||
args["Depth"] = nil | |||
args["Step Ratios"] = step_ratios | args["Step Ratios"] = step_ratios | ||
args["Depths"] = depths | args["Depths"] = depths | ||
Revision as of 06:55, 4 October 2025
- 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 | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||||||||||||||||||||
No function descriptions were provided. The Lua code may have further information.
local mos = require("Module:MOS")
local mediants = require("Module:Mediants")
local yesno = require("Module:yesno")
local getArgs = require("Module:Arguments").getArgs
local p = {}
-- TODO:
-- - (Low priority): Force-add comments that lie outside searched depth. If such
-- ratios are added, they're added in either the last column or their own
-- column appended after the last one.
-- Re-re-rewrite of tuning spectrum
function p._mos_tuning_spectrum(args)
-- Find default ratios, if nothing was entered
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 comments = args["Comments" ] or {}
local step_ratios = args["Step Ratios"] or default_ratios
local depths = args["Depths" ] or default_depths
local deepest = mediants.deepest_depth(depths)
local equave = input_mos.equave
local large_steps = input_mos.nL -- Large steps in mos
local small_steps = input_mos.ns -- Small steps in mos
local periods = mos.period_count(input_mos) -- Number of periods
-- What is the et suffix (edo, edt, edf, ed-p/q)?
local et_suffix = mos.et_suffix(input_mos)
-- 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 periods < small_steps 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 small_steps == 1 then
comments_header_text = comments_header_text .. "<sup><abbr title=\"Every tuning produces a proper scale.\">(always proper)</abbr></sup>"
elseif small_steps == periods and periods > 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 %s.\">(%s)</abbr></sup>\n", deepest + 1, et_suffix, et_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_as_string = mos.et_string(input_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)
local bright_generator_string = mos.bright_gen_to_et_string(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|%s]]", et_as_string, bright_generator_string)
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 = "→ ∞"
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; preprocesses args
-- Unprocessed args:
-- - Scale Signature (textual scalesig)
-- - Depth (how deep to search for mediants)
-- - Int Limit (if present, overrides depth-based search for int-limit search)
-- - X/Y (comments for individual step ratios)
-- - First/Last Step Ratio (initial ratios for mediant search; default is
-- 1/1 and 1/0 respectively)
-- Processed args:
-- - Input MOS (processed from textual scalesig)
-- - Step Ratios (table of step ratios found)
-- - Depths (table of how deep each step ratio was found at)
-- - Comments (step ratio comments placed in their own table)
function p.mos_tuning_spectrum(frame)
local args = getArgs(frame)
-- Parse scalesig as input mos, then remove scalesig
local input_mos = mos.parse(args["Scale Signature"])
args["Input MOS"] = input_mos
args["Scale Signature"] = nil
-- 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
-- If using depth-based search, parse the depth, then remove it from args
-- If using int-limit search, parse the int limit, then remove it from args,
-- and remove depth for good measure
local step_ratios, depths
local use_int_limit = args["Int Limit"] ~= nil
if use_int_limit then
-- Int-limit search; parse int-limit
local int_limit = tonumber(args["Int Limit"])
args["Int Limit"] = nil
-- Find ratios and depths found
step_ratios, depths = mediants.find_mediants_by_int_limit({first_ratio, last_ratio}, int_limit)
else
-- Depth-based search; parse depth
local depth = tonumber(args["Depth"]) or 5
-- Find ratios and depths found
step_ratios, depths = mediants.find_mediants({first_ratio, last_ratio}, depth)
end
args["Depth"] = nil
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 result = p._mos_tuning_spectrum(args)
if debugg == true then
result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>"
end
return frame:preprocess(result)
end
function p.tester()
local args = {}
return p._mos_tuning_spectrum(args)
end
return p