Module:Scale tree: Difference between revisions
Jump to navigation
Jump to search
Scale tree automatically adds "(Generators smaller than this are proper)" to appropriate mosses |
All values now round to 3 decimal points no matter what; fixed issue with dark gen of a multi-period mos being reported as the equave complement, not the period complement; rearranged code |
||
| Line 51: | Line 51: | ||
end | end | ||
function p. | -- Rewrite of scale tree function (has bugfixes and new formatting) | ||
local | function p._scale_tree(input_mos, depth, comments) | ||
local equave = | local input_mos = input_mos or MOS.new(5, 2) | ||
local L = | local depth = depth or 5 | ||
local s = | local comments = comments or {} | ||
local n = utils._gcd(L, s) | |||
local equave = input_mos.equave | |||
local abstract_bright_gen = MOS.bright_gen( | 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 | |||
local abstract_bright_gen = MOS.bright_gen(input_mos) | |||
local step_ratios = sb.sb_tree_ratios(depth) | local step_ratios = sb.sb_tree_ratios(depth) | ||
-- Default comments | -- Default comments for TAMNAMS-named step ratios | ||
local default_comments = {} | local default_comments = {} | ||
default_comments["1/1"] = "Equalized " .. MOS.as_string(mos) | default_comments["1/1"] = "Equalized " .. MOS.as_string(mos) | ||
| Line 79: | Line 77: | ||
default_comments["1/0"] = "Collapsed " .. MOS.as_string(mos) | default_comments["1/0"] = "Collapsed " .. MOS.as_string(mos) | ||
-- | -- Create table | ||
local result = "" | |||
local | |||
-- Table headers | -- Table headers | ||
| Line 102: | Line 88: | ||
result = '{| class="wikitable"\n' | result = '{| class="wikitable"\n' | ||
result = result .. '|+\n' | result = result .. '|+\n' | ||
result = result .. '! rowspan="2" | | result = result .. '! rowspan="2" | Generator (in steps)\n' | ||
result = result .. '! colspan="2" |Generator in cents\n' | result = result .. '! colspan="2" | Generator in cents\n' | ||
result = result .. '! | result = result .. '! rowspan="2" | Step ratio (hardness)\n' | ||
result = result .. '! rowspan="2" |Comments\n' | result = result .. '! rowspan="2" | Comments\n' | ||
result = result .. '|-\n' | result = result .. '|-\n' | ||
result = result .. '!Bright\n' | result = result .. '! Bright generator\n' | ||
result = result .. '!Dark\n' | result = result .. '! Dark generator\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 step_ratio = step_ratios[i] | ||
local | local steps_per_equave = step_ratio[1] * L + step_ratio[2] * s | ||
local | local steps_per_period = steps_per_equave / n | ||
local et = ET.new(steps_per_equave, equave) | |||
-- 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_cents = ET.cents(et, bright_generator_steps) | |||
-- Calculate dark generator step count and cent value | -- Calculate dark generator step count and cent value | ||
local dark_generator_steps = | local dark_generator_steps = steps_per_period - bright_generator_steps | ||
local dark_generator_cents = ET.cents(et, dark_generator_steps) | local dark_generator_cents = ET.cents(et, dark_generator_steps) | ||
-- | -- New row | ||
-- | result = result .. "|-\n" | ||
-- | |||
--local | -- Cell for bright generator, as steps in et | ||
result = result .. string.format("| [[%s|%d%s]]\n", ET.as_string(et), bright_generator_steps, ET.backslash_modifier(et)) | |||
-- 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, with divide-by-zero check | |||
local hardness = -1 | |||
if step_ratio[2] == 0 then | |||
hardness = "→ ∞" | |||
else | |||
hardness = string.format("%.3f", step_ratio[1] / step_ratio[2]) | |||
end | |||
local step_ratio_as_string = string.format("%d:%d", step_ratio[1], step_ratio[2]) | |||
result = result .. string.format("| %s (%s)\n", step_ratio_as_string, hardness) | |||
-- Cell for comment | |||
local key = step_ratios[i][1] .. "/" .. step_ratios[i][2] -- The step ratio is (literally and figuratively) the key to add comments! | local key = step_ratios[i][1] .. "/" .. step_ratios[i][2] -- The step ratio is (literally and figuratively) the key to add comments! | ||
-- Check for comments | |||
local contains_custom_comment = comments[key] ~= nil | local contains_custom_comment = comments[key] ~= nil | ||
if contains_default_comment and contains_custom_comment then | if contains_default_comment and contains_custom_comment then | ||
| Line 139: | Line 146: | ||
end | end | ||
local | local comment = comments[key] or "" | ||
result = result .. string.format("| %s\n", comment) | |||
result = result .. | |||
end | end | ||
result = result .. "|}" | result = result .. "|}" | ||
return result | return result | ||
end | end | ||
function p.scale_tree(frame) | |||
local mos = MOS.parse(frame.args["tuning"]) | |||
local depth = frame.args["depth"] or 5 | |||
local comments_unparsed = frame.args["Comments"] or "" | |||
local comments = p.parse_kv_pairs(comments_unparsed) or {} | |||
local result = p._scale_tree(mos, depth, comments) | |||
return result | |||
end | |||
return p | return p | ||
Revision as of 21:01, 10 February 2024
local p = {}
local MOS = require('Module:MOS')
local ET = require('Module:ET')
local u = require('Module:Utils')
local rat = require('Module:Rational')
local sb = require('Module:SB tree')
local utils = require('Module:Utils')
-- Helper function that parses entries from a semicolon-delimited string and returns them in an array
-- TODO: Separate this and parse_pairs into its own module of helper functions, as they're included
-- in various modules at this point
function p.parse_entries(unparsed)
local parsed = {}
for entry in string.gmatch(unparsed, '([^;]+)') do
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
table.insert(parsed, trimmed) -- Add to array
end
return parsed
end
-- Helper function that parses pairs of elements separated by a colon
-- A pair must be two elements or it will be returned as an empty array
function p.parse_pair(unparsed)
local parsed = {}
for entry in string.gmatch(unparsed, '([^:]+)') do
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
table.insert(parsed, trimmed) -- Add to array
end
if #parsed == 2 then
return parsed
else
return {}
end
end
-- Function that takes a list of semicolon-delimited pairs and returns a map
-- (or dictionary or associative array) of key-value pairs
-- Each entry is colon-delimited as key : pair
function p.parse_kv_pairs(unparsed)
-- Tokenize the string of unparsed pairs
local parsed = p.parse_entries(unparsed)
-- Then tokenize the tokens into key-value pairs
local pairs_ = {}
for i = 1, #parsed do
local pair = p.parse_pair(parsed[i])
if #pair == 2 then
pairs_[pair[1]] = pair[2]
end
end
return pairs_
end
-- Rewrite of scale tree function (has bugfixes and new formatting)
function p._scale_tree(input_mos, depth, comments)
local input_mos = input_mos or MOS.new(5, 2)
local depth = depth or 5
local comments = comments or {}
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
local abstract_bright_gen = MOS.bright_gen(input_mos)
local step_ratios = sb.sb_tree_ratios(depth)
-- Default comments for TAMNAMS-named step ratios
local default_comments = {}
default_comments["1/1"] = "Equalized " .. MOS.as_string(mos)
default_comments["4/3"] = "Supersoft " .. MOS.as_string(mos)
default_comments["3/2"] = "Soft " .. MOS.as_string(mos)
default_comments["5/3"] = "Semisoft " .. MOS.as_string(mos)
default_comments["2/1"] = "Basic " .. MOS.as_string(mos)
default_comments["5/2"] = "Semihard " .. MOS.as_string(mos)
default_comments["3/1"] = "Hard " .. MOS.as_string(mos)
default_comments["4/1"] = "Superhard " .. MOS.as_string(mos)
default_comments["1/0"] = "Collapsed " .. MOS.as_string(mos)
-- Create table
local result = ""
-- Table headers
-- There are 6 columns:
-- - Steps of ED
-- - Bright and dark gens in cents
-- - Step ratio and hardness
-- - Comments
result = '{| class="wikitable"\n'
result = result .. '|+\n'
result = result .. '! rowspan="2" | Generator (in steps)\n'
result = result .. '! colspan="2" | Generator in cents\n'
result = result .. '! rowspan="2" | Step ratio (hardness)\n'
result = result .. '! rowspan="2" | Comments\n'
result = result .. '|-\n'
result = result .. '! Bright generator\n'
result = result .. '! Dark generator\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 steps_per_equave = step_ratio[1] * L + step_ratio[2] * s
local steps_per_period = steps_per_equave / n
local et = ET.new(steps_per_equave, equave)
-- 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_cents = ET.cents(et, bright_generator_steps)
-- Calculate dark generator step count and cent value
local dark_generator_steps = steps_per_period - bright_generator_steps
local dark_generator_cents = ET.cents(et, dark_generator_steps)
-- New row
result = result .. "|-\n"
-- Cell for bright generator, as steps in et
result = result .. string.format("| [[%s|%d%s]]\n", ET.as_string(et), bright_generator_steps, ET.backslash_modifier(et))
-- 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, with divide-by-zero check
local hardness = -1
if step_ratio[2] == 0 then
hardness = "→ ∞"
else
hardness = string.format("%.3f", step_ratio[1] / step_ratio[2])
end
local step_ratio_as_string = string.format("%d:%d", step_ratio[1], step_ratio[2])
result = result .. string.format("| %s (%s)\n", step_ratio_as_string, hardness)
-- Cell for comment
local key = step_ratios[i][1] .. "/" .. step_ratios[i][2] -- The step ratio is (literally and figuratively) the key to add comments!
-- Check for comments
local contains_custom_comment = comments[key] ~= nil
if contains_default_comment and contains_custom_comment then
comment = comment .. default_comments[key] .. "<br>" .. comments[key]
elseif contains_default_comment and not contains_custom_comment then
comment = comment .. default_comments[key]
elseif not contains_default_comment and contains_custom_comment then
comment = comment .. comments[key]
end
local comment = comments[key] or ""
result = result .. string.format("| %s\n", comment)
end
result = result .. "|}"
return result
end
function p.scale_tree(frame)
local mos = MOS.parse(frame.args["tuning"])
local depth = frame.args["depth"] or 5
local comments_unparsed = frame.args["Comments"] or ""
local comments = p.parse_kv_pairs(comments_unparsed) or {}
local result = p._scale_tree(mos, depth, comments)
return result
end
return p