Module:Infobox chord: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Bcmills (talk | contribs)
mNo edit summary
Bcmills (talk | contribs)
No edit summary
Line 52: Line 52:
local prime_limit = 1
local prime_limit = 1
local lcm = 1
local lcm = 1
local otonal_odd_limit = 1
local root_interval_links = {}
local root_interval_links = {}
for i, h in ipairs(harmonics) do
for i, h in ipairs(harmonics) do
-- compute LCM of all harmonics to use as the denominator in utonal form, if needed
lcm = lcm * h / (utils._gcd(lcm, h))
-- increase otonal odd limit for this harmonic, if needed
local odd = h
while odd > 0 and odd % 2 == 0 do
odd = odd / 2
end
if odd > otonal_odd_limit then
otonal_odd_limit = odd
end
-- increase prime limit for this harmonic, if needed
for prime, n in pairs(utils.prime_factorization_raw(h)) do
for prime, n in pairs(utils.prime_factorization_raw(h)) do
if prime > prime_limit then
if prime > prime_limit then
Line 60: Line 74:
end
end
lcm = lcm * h / (utils._gcd(lcm, h))
-- compute ratio of this harmonic relative to the root
 
local gcd = utils._gcd(h, root)
local gcd = utils._gcd(h, root)
local numer = h / gcd
local numer = h / gcd
Line 68: Line 81:
end
end
local utonal_intervals = nil
local utonal_odd_limit = 1
if not utils.value_provided(frame.args["Root"]) then
local utonal_intervals = {}
utonal_intervals = {}
for i, h in ipairs(harmonics) do
for i, h in ipairs(harmonics) do
local gcd = utils._gcd(lcm, h)
local gcd = utils._gcd(lcm, h)
local numer = h / gcd
local numer = h / gcd
local denom = lcm / gcd
local denom = lcm / gcd
table.insert(utonal_intervals, numer .. "/" .. denom)
table.insert(utonal_intervals, numer .. "/" .. denom)
 
local denom_odd = denom
while denom_odd > 0 and denom_odd % 2 == 0 do
denom_odd = denom_odd / 2
end
if denom_odd > utonal_odd_limit then
utonal_odd_limit = denom_odd
end
end
end
end
Line 93: Line 112:
end
end


while denom % 2 == 0 do
while denom > 0 and denom % 2 == 0 do
denom = denom / 2
denom = denom / 2
end
end
Line 104: Line 123:
table.insert(infobox_data, {"Harmonics", frame.args["Harmonics"]})
table.insert(infobox_data, {"Harmonics", frame.args["Harmonics"]})
table.insert(infobox_data, {"Intervals from root", table.concat(root_interval_links, " ‒ ")})
table.insert(infobox_data, {"Intervals from root", table.concat(root_interval_links, " ‒ ")})
if utonal_intervals ~= nil then
if (not utils.value_provided(frame.args["Root"])) and (utonal_limit < otonal_limit or utonal_limit < 1000) then
table.insert(infobox_data, {"[[Utonal]] Ratios", table.concat(utonal_intervals, " ‒ ")})
table.insert(infobox_data, {"[[Utonal]] Ratios", table.concat(utonal_intervals, " ‒ ")})
end
end
if prime_limit < 96 then
if prime_limit < 96 then
table.insert(infobox_data, {"[[Prime limit]]", "[[" .. prime_limit .. "-limit|" .. prime_limit .. "]]"})
table.insert(infobox_data, {"[[Prime limit]]", "[[" .. prime_limit .. "-limit|" .. prime_limit .. "]]"})
Line 121: Line 141:
table.insert(infobox_data, {"[[Odd limit]]", odd_limit})
table.insert(infobox_data, {"[[Odd limit]]", odd_limit})
end
end
table.insert(infobox_data, "[[Otonal]] odd limit", otonal_limit)
table.insert(infobox_data, "[[Utonal]] odd limit", utonal_limit)
end
end

Revision as of 02:21, 13 August 2024

Module documentation[view] [edit] [history] [purge]
Note: Do not invoke this module directly; use the corresponding template instead: Template:Infobox chord.

This module implements {{Infobox chord}} to generate an infobox providing information about a given chord.


local p = {}

local rat = require("Module:Rational")
local utils = require("Module:Utils")
local infobox = require("Module:Infobox")

function p.infobox_chord(frame)
	local debug_mode = frame.args["debug"]

	local page_name = frame:preprocess("{{PAGENAME}}")
	
	local debug_data = ""
	local infobox_data = {}
	local cats = ""

	local name = frame.args["Name"]
	if utils.value_provided(name) then
		local caption = "Name"
		if name:match(",") then
			caption = "Names"
			-- removing manual line breaks
			local matches
			name, matches = name:gsub("<br/?>", "")
			if matches > 0 then
				cats = cats .. "[[Category:Todo:remove manual line breaks]]"
			end
			-- removing whitespaces after commas
			name = name:gsub(",%s+", ",")
			-- placing line breaks after commas
			name = name:gsub(",", ",<br/>")
		end
		table.insert(infobox_data, {
			caption,
			name,
		})
	end

	if utils.value_provided(frame.args["Harmonics"]) then
		local harmonics = {}
		for hs in string.gmatch(frame.args["Harmonics"], "[^:]+") do
			h = tonumber(hs)  -- TODO: support rational entries?
			assert(h > 0, "invalid harmonic")
			table.insert(harmonics, h)
		end
		local root = harmonics[1]
		if utils.value_provided(frame.args["Root"]) then
			root = tonumber(frame.args["Root"])
			assert(root > 0, "invalid root")
		end
		assert(root > 0, "no harmonics given")

		local prime_limit = 1
		local lcm = 1
		local otonal_odd_limit = 1
		local root_interval_links = {}
		for i, h in ipairs(harmonics) do
			-- compute LCM of all harmonics to use as the denominator in utonal form, if needed
			lcm = lcm * h / (utils._gcd(lcm, h))

			-- increase otonal odd limit for this harmonic, if needed
			local odd = h
			while odd > 0 and odd % 2 == 0 do
				odd = odd / 2
			end
			if odd > otonal_odd_limit then
				otonal_odd_limit = odd
			end

			-- increase prime limit for this harmonic, if needed
			for prime, n in pairs(utils.prime_factorization_raw(h)) do
				if prime > prime_limit then
					prime_limit = prime
				end
			end
			
			-- compute ratio of this harmonic relative to the root
			local gcd = utils._gcd(h, root)
			local numer = h / gcd
			local denom = root / gcd
			table.insert(root_interval_links, "[[" .. numer .. "/" .. denom .. "]]")
		end
		
		local utonal_odd_limit = 1
		local utonal_intervals = {}
		for i, h in ipairs(harmonics) do
			local gcd = utils._gcd(lcm, h)
			local numer = h / gcd
			local denom = lcm / gcd
			table.insert(utonal_intervals, numer .. "/" .. denom)

			local denom_odd = denom
			while denom_odd > 0 and denom_odd % 2 == 0 do
				denom_odd = denom_odd / 2
			end
			if denom_odd > utonal_odd_limit then
				utonal_odd_limit = denom_odd
			end
		end

		local odd_limit = 1
		for j, b in ipairs(harmonics) do
			for i, a in ipairs(harmonics) do
				local gcd = utils._gcd(a, b)
				local numer = b / gcd
				local denom = a / gcd

				while numer % 2 == 0 do
					numer = numer / 2
				end
				if numer > odd_limit then
					odd_limit = numer
				end

				while denom > 0 and denom % 2 == 0 do
					denom = denom / 2
				end
				if denom > odd_limit then
					odd_limit = denom
				end
			end
		end

		table.insert(infobox_data, {"Harmonics", frame.args["Harmonics"]})
		table.insert(infobox_data, {"Intervals from root", table.concat(root_interval_links, " ‒ ")})
		if (not utils.value_provided(frame.args["Root"])) and (utonal_limit < otonal_limit or utonal_limit < 1000) then
			table.insert(infobox_data, {"[[Utonal]] Ratios", table.concat(utonal_intervals, " ‒ ")})
		end

		if prime_limit < 96 then
			table.insert(infobox_data, {"[[Prime limit]]", "[[" .. prime_limit .. "-limit|" .. prime_limit .. "]]"})
			cats = cats .. "[[Category:" .. prime_limit .. "-limit chords]]"
		else
			table.insert(infobox_data, {"[[Prime limit]]", prime_limit})
			cats = cats .. "[[Category:Just intonation chords]]"
		end

		if odd_limit < 32 then
			table.insert(infobox_data, {"[[Odd limit]]", "[[" .. odd_limit .. "-odd-limit|" .. odd_limit .. "]]"})
			cats = cats .. "[[Category:" .. odd_limit .. "-odd-limit chords]]"
		else 
			table.insert(infobox_data, {"[[Odd limit]]", odd_limit})
		end
		table.insert(infobox_data, "[[Otonal]] odd limit", otonal_limit)
		table.insert(infobox_data, "[[Utonal]] odd limit", utonal_limit)
	end
	
	if debug_data ~= "" then
		table.insert(infobox_data, {
			"Debug",
			debug_data,
		})
	end

	local s = infobox.build("<u>Chord information</u>", infobox_data)
	if not debug_mode then
		s = s .. cats
	end
	return s
end

return p