Module:Scale tree

From Xenharmonic Wiki
Revision as of 07:10, 28 May 2023 by Ganaram inukshuk (talk | contribs) (Simplified entry of comments so it's based on key-value pairs: step ratio followed by comment text)
Jump to navigation Jump to search

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')

-- 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
		table.insert(parsed, entry)		-- 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
		table.insert(parsed, entry)		-- 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 unparsed_pairs = p.parse_entries(unparsed_pairs)
	-- Then tokenize the tokens into key-value pairs
	local parsed_pairs = {}
	for unparsed_pair in unpared_pairs do
		local parsed_pair = p.parse_pair(unparsed_pair)
		if #parsed_pair == 2 then
			parsed_pairs[pair[1]] = pair[2]
		end
	end
	return parsed_pairs
end

function p.scale_tree(frame)
	local mos = MOS.parse(frame.args['tuning'])
	local equave = mos.equave
	local L = mos.nL
	local s = mos.ns
	local collapsed_et = ET.new(L, equave)
	local abstract_bright_gen = MOS.bright_gen(mos)
	local collapsed_bright_steps = abstract_bright_gen['L']
	local equalized_et = ET.new(L + s, equave)
	local equalized_bright_steps = abstract_bright_gen['L'] + abstract_bright_gen['s']
	local result = ""

	local depth = frame.args['depth'] or 5
	local step_ratios = sb.sb_tree_ratios(depth)
	
	-- Get comments
	-- Comments are entered as a pair consisting of a JI ratio and comment, separated by a colon
	-- p/q: comment text as a string
	-- Each entry is then entered as a semicolon-delimtied list
	local comments_unparsed = frame.args['Comments']
	local comments = p.parse_kv_pairs(comments_unparsed)
	
	result = '{|class="wikitable"\n'
	result = result .. "|-\n"
	result = result .. "!Generator (chroma-positive)\n"
	result = result .. "!Cents (chroma-positive)\n"
	result = result .. "!L\n"
	result = result .. "!s\n"
	result = result .. "!L/s\n"
	result = result .. "!Comments\n"
	local i = 1
	while i <= #step_ratios do
		local step_ratio = step_ratios[i]
		local et = ET.new(step_ratio[1] * L + step_ratio[2] * s, equave)
		local generator_steps =  step_ratio[1] * collapsed_bright_steps + step_ratio[2] * (equalized_bright_steps - collapsed_bright_steps)
		
		-- Entry of comments is done using an associative array, as entries may be sparse
		-- Old code commented out
		--local comments = frame.args[("comment_" .. step_ratio[1] .. "_" .. step_ratio[2])] or ""
		local key = step_ratios[i][1] .. "/" .. step_ratios[i][2]		-- The step ratio is (literally and figuratively) the key to add comments!
		
		local l_s = "&rarr; ∞"
		if not (step_ratio[1] == 1 and step_ratio[2] == 0)  then
			l_s =  u._round(step_ratio[1] / step_ratio[2], 4)
		end
		result = result .. "|-\n"
		result = result .. "|[[" .. ET.as_string(et) .. "|" .. generator_steps  .. ET.backslash_modifier(et) .. "]]\n"
		result = result .. "|" .. u._round(math.log(rat.as_float(et.equave)^(generator_steps / et.size))/math.log(2) * 1200, 6) .. "\n"
		result = result .. "|" .. step_ratio[1] .. "\n"
		result = result .. "|" .. step_ratio[2] .. "\n"
		result = result .. "|" .. l_s .. "\n"
		--result = result .. "|" .. comments .. "\n"
		result = result .. "|" .. comments[key] .. "\n"

		i = i + 1
	end
		
	result = result .. "|}"
	return result
end
return p