Module:Harmonics infobox
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Harmonics infobox/doc
-- Module: Harmonics infobox
-- Infobox for "Harmonics n–2n" and "Subharmonics n–2n" pages.
-- Called through Template: Infobox harmonics and Template: Infobox subharmonics.
local p = {}
local getArgs = require("Module:Arguments").getArgs
local infobox = require("Module:Infobox")
local u = require("Module:Utils")
local yesno = require("Module:Yesno")
local EN_DASH = "–"
-- category names, in one place
local CATEGORY = { harmonics = "Overtone scales", subharmonics = "Undertone scales" }
local CATEGORY_PRIME = { harmonics = "Prime overtone scales", subharmonics = "Prime undertone scales" }
local function cents(num, den)
return math.floor(math.log(num / den) / math.log(2) * 1200 * 1000 + 0.5) / 1000
end
local function fifth_row(label, num, den)
return { label, string.format("%d/%d (%sc)", num, den, cents(num, den)) }
end
local function build(frame, kind)
local args = getArgs(frame)
local n = tonumber(args[1] or args.n or args.steps)
if not n or n < 1 or n ~= math.floor(n) then
return '<strong class="error">Template:Infobox harmonics requires a positive integer, e.g. <code>{{Infobox harmonics|12}}</code>.</strong>'
end
local sub = kind == "subharmonics"
local debug_mode = yesno(args.debug, false)
local wtext = yesno(args.wtext)
local sortkey = string.rep("#", string.len(tostring(n)))
local categories = string.format("[[Category:%s|%s]]", CATEGORY[kind], sortkey)
local prime = ""
if u.is_prime(n) then
prime = " (prime)"
categories = categories .. string.format("[[Category:%s|%s]]", CATEGORY_PRIME[kind], sortkey)
end
local data = {}
data[#data + 1] = { "Prime factorization", u._prime_factorization(n) .. prime }
-- the two steps nearest 3/2
local sharp, flat, exact
if sub then
-- degree k has ratio 2n/(2n - k); a fifth needs 2n - k = 4n/3
local m = math.floor(4 * n / 3)
if (4 * n) % 3 == 0 then
exact = { 2 * n, m }
else
sharp, flat = { 2 * n, m }, { 2 * n, m + 1 }
end
else
-- degree k has ratio (n + k)/n; a fifth needs n + k = 3n/2
local k = n + math.ceil(n / 2)
if n % 2 == 0 then
exact = { k, n }
else
sharp, flat = { k, n }, { k - 1, n }
end
end
if exact then
data[#data + 1] = fifth_row("Fifth", exact[1], exact[2])
else
data[#data + 1] = fifth_row("Dual sharp fifth", sharp[1], sharp[2])
data[#data + 1] = fifth_row("Dual flat fifth", flat[1], flat[2])
end
-- no-wrap: otherwise the caption can break after the en dash
local title = string.format(
'<span style="white-space: nowrap;">%s %d%s%d</span>',
sub and "Subharmonics" or "Harmonics",
n,
EN_DASH,
2 * n
)
local result = infobox._infobox({ ["Title"] = title, ["Rows"] = data })
if not debug_mode then
result = result .. categories
end
if wtext then
result = '<syntaxhighlight lang="wikitext">' .. result .. "</syntaxhighlight>"
end
return frame:preprocess(result)
end
function p.harmonics(frame)
return build(frame, "harmonics")
end
function p.subharmonics(frame)
return build(frame, "subharmonics")
end
return p