Module:ED intro: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
m Removed link from "equal division(s)"
Address Template talk: ED intro #Square/cube root of 2. Now square/cube root are displayed instead of 2nd/3rd root
 
(42 intermediate revisions by 4 users not shown)
Line 1: Line 1:
local ord = require('Module:Ordinal')
local ord = require("Module:Ordinal")
local utils = require('Module:Utils')
local rat = require("Module:Rational")
local rat = require('Module:Rational')
local utils = require("Module:Utils")
local yesno = require("Module:Yesno")
local p = {}
local p = {}


-- Module for creating an intro for an equal-division tuning.
-- TODO: Add support for the following
-- Supported types: edo, edt, edf, edh, edr, edc (for octave, tritave/twelfth,
-- - Make it clear what text is being swapped in string.gsub
-- fifth, arbitrary harmonic h, arbitrary ratio r, and arbitrary cents c).


-- Notes:
-- Notes:
-- - Edo and edt are technically edh's, and edf an edr; these are their own
-- - Edo and edt are technically equal divisions of a harmonic, and edf an equal
--  types because their intros have specific wording.
--  division of a ratio; these are their own types because their intros have
-- - Edh is a special case of edr, where the ratio is h/1.
--  specific wording.
-- - Under harmonotonic tuning, edc and edr are also called ASp and APSp
-- - An equal division of a harmonic is a special case of an equal division of a
--  (ambitonal sequence and arithmetic pitch sequence, respectively). These
--  ratio, where the ratio is h/1.
--   were formerly called equal-step tunings but were reclassified as 1ed.
-- - An equal division of a ratio and an equal division of a non-integer
--  constant or cent value are also called AS (ambitonal sequence) and APS
--  (arithmetic pitch sequence), respectively.  
-- - All of these are equal-step tunings. We've adopted the 1ed-p notation where
--  possible.  
-- - Equal divisions of irrational constants (such as pi and e) are not very
-- - Equal divisions of irrational constants (such as pi and e) are not very
--  common, but can be denoted as equal divisions of arbitrary cent values if
--  common, but count as equal divisions of arbitrary cent values.
--  needed.


-- Parse ed function
local ED_TYPE_EDO = "EDO"
function p.parse_ed(unparsed)
local ED_TYPE_EDT = "EDT"
local unparsed = unparsed or "12edo"
local ED_TYPE_EDF = "EDF"
local ED_TYPE_EDH = "EDH" -- "equal division of a harmonic"
local ED_TYPE_EDR = "EDR" -- "equal division of a ratio"
local ED_TYPE_EDC = "EDC" -- "equal division of a cent value"
local ED_TYPE_DEFAULT = "UNKNOWN_TYPE"
 
-- Separate function for edo intro
function p.edo_intro(ed)
local ed = ed or 12
-- Exactly or about? Check up to three significant figures
local edstep_size = 1200 / ed
local edstep_size_rounded = utils._round(edstep_size, 3)
local is_exact = edstep_size - edstep_size_rounded == 0
local intro_text = ""
if ed == 1 then
intro_text = "'''1 equal division of the octave''' (abbreviated '''1edo''' or '''1ed2'''), also called '''1-tone equal temperament''' ('''1tet''') or '''1 equal temperament''' ('''1et''') when viewed under a [[regular temperament]] perspective, is the [[tuning system]] that uses [[equal]] steps of 2/1 (one [[octave]]), or exactly/about ''s''{{cent}}."
else
intro_text = "'''''k'' equal divisions of the octave''' (abbreviated '''''k''edo''' or '''''k''ed2'''), also called '''''k''-tone equal temperament''' ('''''k''tet''') or '''''k'' equal temperament''' ('''''k''et''') when viewed under a [[regular temperament]] perspective, is the [[tuning system]] that divides the [[octave]] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of 2<sup>1/''k''</sup>, or the ''kth'' root of 2."
end
 
-- Replace certain strings with the intended final versions
intro_text = string.gsub(intro_text, "''k''", ed)
intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
return intro_text
end
 
-- Separate function for edt intro
function p.edt_intro(ed)
local ed = ed or 13
-- Exactly or about? Check up to three significant figures
local equave_in_cents = math.log(3) * 1200
local edstep_size = equave_in_cents / math.log(2) / ed
local edstep_size_rounded = utils._round(edstep_size, 3)
local is_exact = edstep_size - edstep_size_rounded == 0
local intro_text = ""
if ed == 1 then
intro_text = "'''1 equal division of the tritave''', '''perfect twelfth''', or '''3rd harmonic''' (abbreviated '''1edt''' or '''1ed3'''), is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of [[3/1]] (one tritave), or exactly/about ''s''{{cent}}."
else
intro_text = "'''''k'' equal divisions of the tritave''', '''perfect twelfth''', or '''3rd harmonic''' (abbreviated '''''k''edt''' or '''''k''ed3'''), is a [[nonoctave]] [[tuning system]] that divides the interval of [[3/1]] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of 3<sup>1/''k''</sup>, or the ''kth'' root of 3."
end
 
-- Replace certain strings with the intended final versions
intro_text = string.gsub(intro_text, "''k''", ed)
intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
return intro_text
end
 
-- Separate function for edf intro
function p.edf_intro(ed)
local ed = ed or 7
-- Exactly or about? Check up to three significant figures
local equave_in_cents = math.log(3/2) * 1200
local edstep_size = equave_in_cents / math.log(2) / ed
local edstep_size_rounded = utils._round(edstep_size, 3)
local is_exact = edstep_size - edstep_size_rounded == 0
local intro_text = ""
if ed == 1 then
intro_text = "'''1 equal division of the perfect fifth''' (abbreviated '''1edf''' or '''1ed3/2''') is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of [[3/2]] (one perfect fifth), or exactly/about ''s''{{cent}}."
else
intro_text = "'''''k'' equal divisions of the perfect fifth''' (abbreviated '''''k''edf''' or '''''k''ed3/2''') is a [[nonoctave]] [[tuning system]] that divides the interval of [[3/2]] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of (3/2)<sup>1/''k''</sup>, or the ''kth'' root of 3/2."
end
 
-- Replace certain strings with the intended final versions
intro_text = string.gsub(intro_text, "''k''", ed)
intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
return intro_text
end
 
-- Separate function for edh intro (arbitrary harmonic)
function p.edh_intro(ed, harmonic)
local ed = ed or 12
local harmonic = harmonic or 4
-- Exactly or about? Check up to three significant figures
local equave_in_cents = math.log(harmonic) * 1200
local edstep_size = equave_in_cents / math.log(2) / ed
local edstep_size_rounded = utils._round(edstep_size, 3)
local is_exact = edstep_size - edstep_size_rounded == 0
local intro_text = ""
if ed == 1 then
intro_text = "'''1 equal division of the hth harmonic''' (abbreviated '''1ed''h''''') is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of [[''h''/1]], or exactly/about ''s''{{cent}}."
else
intro_text = "'''''k'' equal divisions of the hth harmonic''' (abbreviated '''''k''ed''h''''') is a [[nonoctave]] [[tuning system]] that divides the interval of [[''h''/1]] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of ''h''<sup>1/''k''</sup>, or the ''kth'' root of ''h''."
end
 
-- Replace certain strings with the intended final versions
intro_text = string.gsub(intro_text, "''k''", ed)
intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
intro_text = string.gsub(intro_text, "hth", ord._ordinal(harmonic))
intro_text = string.gsub(intro_text, "''h''", harmonic)
return intro_text
end
 
-- Separate function for edr intro (arbitrary ratio)
function p.edr_intro(ed, ratio)
local ed = ed or 12
local ratio = ratio or rat.new(9,4)
-- Exactly or about? Check up to three significant figures
local equave_in_cents = rat.cents(ratio)
local edstep_size = equave_in_cents / ed
local edstep_size_rounded = utils._round(edstep_size, 3)
local is_exact = edstep_size - edstep_size_rounded == 0
local intro_text = ""
if ed == 1 then
intro_text = "'''1 equal division of ''p/q''''' (abbreviated '''1ed''p/q''''') is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of [[''p/q'']], or exactly/about ''s''{{cent}}."
else
intro_text = "'''''k'' equal divisions of ''p/q''''' (abbreviated '''''k''ed''p/q''''') is a [[nonoctave]] [[tuning system]] that divides the interval of [[''p/q'']] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of (''p/q'')<sup>1/''k''</sup>, or the ''kth'' root of ''p/q''."
end
 
-- Replace certain strings with the intended final versions
intro_text = string.gsub(intro_text, "''k''", ed)
intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
intro_text = string.gsub(intro_text, "''p/q''", rat.as_ratio(ratio))
return intro_text
end
 
-- Separate function for edc (arbitrary cent value)
function p.edc_intro(ed, cents)
local ed = ed or 1
local cents = cents or 97.5
-- Exactly or about? Check up to three significant figures
local edstep_size = cents / ed
local edstep_size_rounded = utils._round(edstep_size, 3)
local is_exact = edstep_size - edstep_size_rounded == 0
local intro_text = ""
if ed == 1 then
intro_text = "'''1 equal division of ''c''{{c}}''' (abbreviated '''1ed''c''{{c}}''') is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of ''c''{{cent}}."
else
intro_text = "'''''k'' equal divisions of ''c''{{c}}''' (abbreviated '''''k''ed''c''{{c}}''') is a [[nonoctave]] [[tuning system]] that divides the interval of ''c''{{c}} into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each."
end
 
-- Replace certain strings with the intended final versions
intro_text = string.gsub(intro_text, "''k''", ed)
intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
intro_text = string.gsub(intro_text, "''c''", cents)
return intro_text
end
 
-- ord._ordinal but for roots
function p.root_ordinal(n)
local result = ord._ordinal(n)
if n == 2 then
result = "square"
elseif n == 3 then
result = "cube"
end
return result
end
 
-- Parse function
function p.parse(unparsed)
local unparsed = unparsed or "12"
-- If the unparsed ed is only a numeric value, default to edo
-- If the unparsed ed is only a numeric value, default to edo
Line 28: Line 210:
end
end
-- Parse
-- Parse the step count, the suffix, and the equave
local steps, suffix, equave = unparsed:match('^(%d+)([Ee][Dd](.+))$')
local steps, equave = unparsed:match("^(%d+)[Ee][Dd](.+)$")
steps = tonumber(steps)
-- If the equave is text, then the equave is:
-- Determine if the ed is for a cent value
-- o: octave, 2/1 (2nd harmonic)
local is_cents = string.match(equave, "%d*%.?%d+[Cc¢]$") ~= nil
-- t: tritave or twelfth, 3/1 (3rd harmonic)
-- f: fifth, 3/2
-- Parse equave
-- Make sure these have the correct equave and suffix
local parsed_equave
local ratio = "n/a"
local ed_type = ED_TYPE_DEFAULT
local cents = 0
local ed_type = ""
if equave == "o" or equave == "O" or equave == "2" or equave == "2/1" then
if equave == "o" or equave == "O" or equave == "2" or equave == "2/1" then
equave = "2"
-- Equave is octave
ratio = "2/1"
parsed_equave = 2
cents = 1200
ed_type = ED_TYPE_EDO
suffix = "edo"
ed_type = "edo"
elseif equave == "t" or equave == "T" or equave == "3" or equave == "3/1" then
elseif equave == "t" or equave == "T" or equave == "3" or equave == "3/1" then
equave = "3"
-- Equave is tritave/twelfth
ratio = "3/1"
parsed_equave = 3
cents = math.log(3) * 1200 / math.log(2)
ed_type = ED_TYPE_EDT
suffix = "edt"
ed_type = "edt"
elseif equave == "f" or equave == "F" or equave == "3/2" then
elseif equave == "f" or equave == "F" or equave == "3/2" then
equave = "3/2"
-- Equave is fifth
ratio = "3/2"
parsed_equave = rat.new(3,2)
cents = math.log(3/2) * 1200 / math.log(2)
ed_type = ED_TYPE_EDF
suffix = "edf"
elseif string.match(equave, "^%d+$") ~= nil then
ed_type = "edf"
-- Equave is arbitrary harmonic (not 2/1 or 3/1)
parsed_equave = tonumber(equave)
ed_type = ED_TYPE_EDH
elseif string.match(equave, "^%d+/%d+$") ~= nil then
-- Equave is arbitrary ratio (not 3/2)
local num, den = equave:match("^(%d+)/(%d+)$")
parsed_equave = rat.new(tonumber(num), tonumber(den))
ed_type = ED_TYPE_EDR
elseif is_cents then
-- Equave is arbitrary cent value
parsed_equave = tonumber(equave:match("^(%d*%.?%d+)[Cc¢]$"))
ed_type = ED_TYPE_EDC
else
-- Equave is unsupported
parsed_equave = 0
end
end
-- For equaves not 2/1, 3/1, or 3/2
return tonumber(steps), parsed_equave, ed_type
if suffix ~= "edo" and suffix ~= "edt" and suffix ~= "edf" then
-- If the equave is a number or ratio, convert it to cents
-- If the equave is already a cent value, extract it
local is_cents = string.match(equave, '^.+c$') ~= nil
local is_harmonic = string.match(equave, '^%d+$') ~= nil
local is_ratio = string.match(equave, '^%d+/%d+$') ~= nil
if is_cents then
ed_type = "edc"
cents = tonumber(equave:match('^(.+)c$'))
elseif is_harmonic then
ed_type = "edh"
local harmonic = tonumber(string.match(equave, '^%d+$'))
cents = math.log(harmonic) * 1200 / math.log(2)
ratio = string.format("%d/1", harmonic)
elseif is_ratio then
ed_type = "edr"
local num, den = equave:match('^(%d+)/(%d+)$')
local gcd = utils._gcd(num, den)
num = num / gcd
den = den / gcd
cents = math.log(num / den) * 1200 / math.log(2)
equave = string.format("%d/%d", num, den)
ratio = equave
end
end
 
return { ['cents'] = cents, ['equave'] = equave, ['ratio'] = ratio, ['steps'] = steps, ['suffix'] = suffix, ['type'] = ed_type }
end
end


-- Primary function
-- ED-EDO comparison
function p.ed_intro(ed)
-- Meant for finding what edo a nonoctave ed corresponds to
local ed = ed or "12edo"
function p.edo_compare(ed)
local ed = ed or "12edf"
local parsed_ed, parsed_equave, ed_type = p.parse(ed)
local edstep_size = 0;
if ed_type == ED_TYPE_EDC then
edstep_size = parsed_equave / parsed_ed
else
edstep_size = rat.cents(parsed_equave) / parsed_ed
end
local ed_compare = 1200 / edstep_size
local ed_compare_text = "''ed1'' roughly corresponds to ''ed2''edo, "
-- Replace certain strings with the intended final versions
ed_compare_text = string.gsub(ed_compare_text, "''ed1''", ed)
ed_compare_text = string.gsub(ed_compare_text, "''ed2''", string.format("%.3f", ed_compare))
local parsed_ed = p.parse_ed(ed)
local ed_floor = math.floor(ed_compare)
local ed_type = parsed_ed['type']
-- Intro formats for each possible case
local diff = ed_compare - ed_floor
-- - Common abbrevs: edo, edt, edf; these have specially written intros
if diff > 0.5 then
-- - General harmonic: edh (h-th harmonic)
ed_compare_text = ed_compare_text .. string.format("and can be seen as %dedo with stretched octaves", ed_floor+1)
-- - Arbitrary JI ratio: edr (ratio of p/q)
else
-- - Arbitrary constant: edc
ed_compare_text = ed_compare_text .. string.format("and can be seen as %dedo with compressed octaves", ed_floor)
-- - [[equal]]-step tunings: 1edo, 1edt, 1edf, 1edh, 1edp/q, 1edc
end
local intro_text = ""
if parsed_ed['steps'] == 1 then
if diff > 0.4 and diff < 0.6 then
if ed_type == "edo" then
ed_compare_text = ed_compare_text .. string.format(", or roughly every second step of %dedo.", 2*ed_floor+1)
intro_text = "'''1 equal division of the octave''' (abbreviated '''1edo''' or '''1ed2'''), also called '''1-tone equal temperament''' ('''1tet'''), or '''1 equal temperament''' ('''1et''') when viewed under a [[regular temperament]] perspective, is the [[tuning system]] where adjacent pitches are one [[octave]], or exactly/about ''s'' [[¢]], from each other."
elseif ed_type == "edt" then
intro_text = "'''1 equal division of the tritave''' (abbreviated '''1edt''' or '''1ed3''') is the [[non-octave]] [[tuning system]] where adjacent pitches are one tritave ([[3/1]]), or exactly/about ''s'' [[¢]], apart from each other."
elseif ed_type == "edf" then
intro_text = "'''1 equal division of the fifth''' (abbreviated '''1edf''' or '''1ed3/2''') is the [[non-octave]] [[tuning system]] where adjacent pitches are one perfect fifth ([[3/2]]), or exactly/about ''s'' [[¢]], apart from each other."
elseif ed_type == "edh" then
intro_text = "'''1 equal division of the ''hth'' harmonic''' (abbreviated '''1ed''h''''') is the [[non-octave]] [[tuning system]] where adjacent pitches are one interval of [[''h''/1]], or exactly/about ''s'' [[¢]], apart from each other."
elseif ed_type == "edr" then
intro_text = "'''1 equal division of ''p''/''q''''' (abbreviated '''1ed''p''/''q'''''), also called '''ambitonal sequence of ''p''/''q''''' ('''AS''p''/''q''''') or '''''p''/''q'' [[equal]]-step tuning''', is the [[non-octave]] [[tuning system]] where adjacent pitches are one interval of [[''p''/''q'']], or exactly/about ''s'' [[¢]], apart from each other."
elseif ed_type == "edc" then
intro_text = "'''1 equal division of ''c''¢''' (abbreviated '''1ed''c''c'''), also called '''arithmetic pitch sequence of ''c''¢''' ('''APS''c''¢'''), is the [[non-octave]] [[tuning system]] where adjacent pitches are exactly/about ''s'' [[¢]] apart from each other."
end
else
else
if ed_type == "edo" then
ed_compare_text = ed_compare_text .. "."
intro_text = "'''''k'' equal divisions of the octave''' (abbreviated '''''k''edo''' or '''''k''ed2'''), also called '''''k''-tone equal temperament''' ('''''k''tet'''), or '''''k'' equal temperament''' ('''''k''et''') when viewed under a [[regular temperament]] perspective, is the [[tuning system]] that divides the [[octave]] into ''k'' [[equal]] parts of exactly/about ''s'' [[¢]] each. Each step of ''k''edo represents a [[frequency ratio]] of 2<sup>1/''k''</sup>, or the ''kth'' root of 2."
elseif ed_type == "edt" then
intro_text = "'''''k'' equal divisions of the tritave''' (abbreviated '''''k''edt''' or '''''k''ed3''') is the [[non-octave]] [[tuning system]] that divides the interval [[3/1]] – also called the [[tritave]] or perfect twelfth – into ''k'' [[equal]] parts of exactly/about ''s'' [[¢]] each. Each step of ''k''edt represents a [[frequency ratio]] of 3<sup>1/''k''</sup>, or the ''kth'' root of 3."
elseif ed_type == "edf" then
intro_text = "'''''k'' equal divisions of the fifth''' (abbreviated '''''k''edf''' or '''''k''ed3/2''') is the [[non-octave]] [[tuning system]] that divides the interval [[3/2]], or perfect fifth, into ''k'' [[equal]] parts of exactly/about ''s'' [[¢]] each. Each step of ''k''edf represents a [[frequency ratio]] of (3/2)<sup>1/''k''</sup>, or the ''kth'' root of 3/2."
elseif ed_type == "edh" then
intro_text = "'''''k'' equal divisions of the ''hth'' harmonic''' (abbreviated '''''k''ed''h''''') is the [[non-octave]] [[tuning system]] that divides the interval [[''h''/1]], or the ''hth'' harmonic, into ''k'' [[equal]] parts of exactly/about ''s'' [[¢]] each. Each step of ''k''ed''h'' represents a [[frequency ratio]] of ''h''<sup>1/''k''</sup>, or the ''kth'' root of ''h''."
elseif ed_type == "edr" then
intro_text = "'''''k'' equal divisions of ''p''/''q''''' (abbreviated '''''k''ed''p''/''q''''') is the [[non-octave]] [[tuning system]] that divides the interval [[''p''/''q'']] into ''k'' [[equal]] pieces of exactly/about ''s'' [[¢]] each. Each step of ''k''ed''p''/''q'' represents the [[frequency ratio]] of (''p''/''q'')<sup>1/''k''</sup>, or the ''kth'' root of ''p''/''q''."
elseif ed_type == "edc" then
intro_text = "'''''k'' equal divisions of ''c''¢''' (abbreviated '''''k''ed''c''c''') is the [[non-octave]] [[tuning system]] that divides the interval of ''c'' [[¢]] into ''k'' [[equal]] pieces of exactly/about ''s'' [[¢]] each."
end
end
end
-- Produce a table of key-value pairs of text to replace
return ed_compare_text
-- - ''k'': number of steps in ed
-- - ''s'': size of a step
end
-- - ''p''/''q'': equave as a ratio
 
-- - ''h'': equave as a harmonic
-- Primary function
-- - ''c'': equave as a cent value
function p._ed_intro(ed)
-- - ''hth'': harmonic as an ordinal
local ed = ed or "12"
-- - ''kth'': step count as an ordinal
local step_size = parsed_ed['cents'] / parsed_ed['steps']
local parsed_ed, parsed_equave, ed_type = p.parse(ed)
local text_replace_map = {
["''k''"] = parsed_ed['steps'],
["''s''"] = utils._round_dec(step_size, 3),
["''c''"] = utils._round_dec(parsed_ed['cents'], 3),
["''p''/''q''"] = parsed_ed['ratio'],
["''h''"] = parsed_ed['equave'],
["''hth''"] = ord._ordinal(parsed_ed['equave']),
["''kth''"] = ord._ordinal(parsed_ed['steps']),
["exactly/about"] = (utils._round(step_size, 3) == step_size and "exactly" or "about")
}
for key, value in pairs(text_replace_map) do
if ed_type == ED_TYPE_DEFAULT then
intro_text = string.gsub(intro_text, key, value)
return "{{error|Error: Equave type not supported or entered incorrectly.}}"
elseif ed_type == ED_TYPE_EDO then
return p.edo_intro(parsed_ed)
elseif ed_type == ED_TYPE_EDT then
return p.edt_intro(parsed_ed)
elseif ed_type == ED_TYPE_EDF then
return p.edf_intro(parsed_ed)
elseif ed_type == ED_TYPE_EDH then
return p.edh_intro(parsed_ed, parsed_equave)
elseif ed_type == ED_TYPE_EDR then
return p.edr_intro(parsed_ed, parsed_equave)
elseif ed_type == ED_TYPE_EDC then
return p.edc_intro(parsed_ed, parsed_equave)
end
end
end
return intro_text
 
-- Tester function
function p.ed_intro_tester()
return
p._ed_intro("12") .. "\n" ..
p._ed_intro("12edt") .. "\n" ..
p._ed_intro("12edf") .. "\n" ..
p._ed_intro("12ed4") .. "\n" ..
p._ed_intro("12ed9/8") .. "\n" ..
p._ed_intro("12ed666c") .. "\n" .. "\n" ..
p._ed_intro("1") .. "\n" ..
p._ed_intro("1ed3") .. "\n" ..
p._ed_intro("1ed3/2") .. "\n" ..
p._ed_intro("1ed4") .. "\n" ..
p._ed_intro("1ed9/8") .. "\n" ..
p._ed_intro("1ed666c") .. "\n"
end
end


-- Wrapper function; for use with a template
-- Wrapper function; for use with a template
function p.ed_intro_frame(frame)
function p.ed_intro(frame)
local ed = frame.args['ED']
local ed = frame.args["ED"]
 
local edo = frame.args["EDO"] -- For backwards compatibility with edo intro
return p.ed_intro(ed)
local debugg = yesno(frame.args["debug"])
local result = p._ed_intro((edo == nil) and ed or edo)
if debugg == true then
result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>"
end
return frame:preprocess(result)
end
end


return p
return p

Latest revision as of 13:34, 15 March 2026

Module documentation[view] [edit] [history] [purge]
This module should not be invoked directly; use its corresponding template instead: Template:ED intro.

This module automatically fills in an introduction for an equal-step tuning. It has presets for the most common equivalences (octave, tritave, and perfect fifth) and also supports arbitrary equivalences and arithmetic pitch sequences such as 88cET.

Introspection summary for Module:ED intro 
Functions provided (12)
Line Function Params
33 edo_intro (ed)
58 edt_intro (ed)
84 edf_intro (ed)
110 edh_intro (ed, harmonic)
139 edr_intro (ed, ratio)
167 edc_intro (ed, cents)
193 root_ordinal (n)
204 parse (unparsed)
256 edo_compare (ed)
294 _ed_intro (main) (ed)
317 ed_intro_tester none
334 ed_intro (invokable) (frame)
Lua modules required (4)
Variable Module Functions used
ord Module:Ordinal _ordinal
rat Module:Rational new
cents
as_ratio
utils Module:Utils _round
yesno Module:Yesno yesno

No function descriptions were provided. The Lua code may have further information.


local ord = require("Module:Ordinal")
local rat = require("Module:Rational")
local utils = require("Module:Utils")
local yesno = require("Module:Yesno")
local p = {}

-- TODO: Add support for the following
-- - Make it clear what text is being swapped in string.gsub

-- Notes:
-- - Edo and edt are technically equal divisions of a harmonic, and edf an equal
--   division of a ratio; these are their own types because their intros have
--   specific wording.
-- - An equal division of a harmonic is a special case of an equal division of a
--   ratio, where the ratio is h/1.
-- - An equal division of a ratio and an equal division of a non-integer 
--   constant or cent value are also called AS (ambitonal sequence) and APS 
--   (arithmetic pitch sequence), respectively. 
-- - All of these are equal-step tunings. We've adopted the 1ed-p notation where
--   possible. 
-- - Equal divisions of irrational constants (such as pi and e) are not very
--   common, but count as equal divisions of arbitrary cent values.

local ED_TYPE_EDO = "EDO"
local ED_TYPE_EDT = "EDT"
local ED_TYPE_EDF = "EDF"
local ED_TYPE_EDH = "EDH" -- "equal division of a harmonic"
local ED_TYPE_EDR = "EDR" -- "equal division of a ratio"
local ED_TYPE_EDC = "EDC" -- "equal division of a cent value"
local ED_TYPE_DEFAULT = "UNKNOWN_TYPE"

-- Separate function for edo intro
function p.edo_intro(ed)
	local ed = ed or 12
	
	-- Exactly or about? Check up to three significant figures
	local edstep_size = 1200 / ed
	local edstep_size_rounded = utils._round(edstep_size, 3)
	local is_exact = edstep_size - edstep_size_rounded == 0
	
	local intro_text = ""
	if ed == 1 then
		intro_text = "'''1 equal division of the octave''' (abbreviated '''1edo''' or '''1ed2'''), also called '''1-tone equal temperament''' ('''1tet''') or '''1 equal temperament''' ('''1et''') when viewed under a [[regular temperament]] perspective, is the [[tuning system]] that uses [[equal]] steps of 2/1 (one [[octave]]), or exactly/about ''s''{{cent}}."	
	else
		intro_text = "'''''k'' equal divisions of the octave''' (abbreviated '''''k''edo''' or '''''k''ed2'''), also called '''''k''-tone equal temperament''' ('''''k''tet''') or '''''k'' equal temperament''' ('''''k''et''') when viewed under a [[regular temperament]] perspective, is the [[tuning system]] that divides the [[octave]] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of 2<sup>1/''k''</sup>, or the ''kth'' root of 2."	
	end

	-- Replace certain strings with the intended final versions
	intro_text = string.gsub(intro_text, "''k''", ed)
	intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
	intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
	intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
	
	return intro_text
end

-- Separate function for edt intro
function p.edt_intro(ed)
	local ed = ed or 13
	
	-- Exactly or about? Check up to three significant figures
	local equave_in_cents = math.log(3) * 1200
	local edstep_size = equave_in_cents / math.log(2) / ed
	local edstep_size_rounded = utils._round(edstep_size, 3)
	local is_exact = edstep_size - edstep_size_rounded == 0
	
	local intro_text = ""
	if ed == 1 then
		intro_text = "'''1 equal division of the tritave''', '''perfect twelfth''', or '''3rd harmonic''' (abbreviated '''1edt''' or '''1ed3'''), is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of [[3/1]] (one tritave), or exactly/about ''s''{{cent}}."	
	else
		intro_text = "'''''k'' equal divisions of the tritave''', '''perfect twelfth''', or '''3rd harmonic''' (abbreviated '''''k''edt''' or '''''k''ed3'''), is a [[nonoctave]] [[tuning system]] that divides the interval of [[3/1]] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of 3<sup>1/''k''</sup>, or the ''kth'' root of 3."	
	end

	-- Replace certain strings with the intended final versions
	intro_text = string.gsub(intro_text, "''k''", ed)
	intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
	intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
	intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
	
	return intro_text
end

-- Separate function for edf intro
function p.edf_intro(ed)
	local ed = ed or 7
	
	-- Exactly or about? Check up to three significant figures
	local equave_in_cents = math.log(3/2) * 1200
	local edstep_size = equave_in_cents / math.log(2) / ed
	local edstep_size_rounded = utils._round(edstep_size, 3)
	local is_exact = edstep_size - edstep_size_rounded == 0
	
	local intro_text = ""
	if ed == 1 then
		intro_text = "'''1 equal division of the perfect fifth''' (abbreviated '''1edf''' or '''1ed3/2''') is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of [[3/2]] (one perfect fifth), or exactly/about ''s''{{cent}}."	
	else
		intro_text = "'''''k'' equal divisions of the perfect fifth''' (abbreviated '''''k''edf''' or '''''k''ed3/2''') is a [[nonoctave]] [[tuning system]] that divides the interval of [[3/2]] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of (3/2)<sup>1/''k''</sup>, or the ''kth'' root of 3/2."	
	end

	-- Replace certain strings with the intended final versions
	intro_text = string.gsub(intro_text, "''k''", ed)
	intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
	intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
	intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
	
	return intro_text
end

-- Separate function for edh intro (arbitrary harmonic)
function p.edh_intro(ed, harmonic)
	local ed = ed or 12
	local harmonic = harmonic or 4
	
	-- Exactly or about? Check up to three significant figures
	local equave_in_cents = math.log(harmonic) * 1200 
	local edstep_size = equave_in_cents / math.log(2) / ed
	local edstep_size_rounded = utils._round(edstep_size, 3)
	local is_exact = edstep_size - edstep_size_rounded == 0
	
	local intro_text = ""
	if ed == 1 then
		intro_text = "'''1 equal division of the hth harmonic''' (abbreviated '''1ed''h''''') is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of [[''h''/1]], or exactly/about ''s''{{cent}}."	
	else
		intro_text = "'''''k'' equal divisions of the hth harmonic''' (abbreviated '''''k''ed''h''''') is a [[nonoctave]] [[tuning system]] that divides the interval of [[''h''/1]] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of ''h''<sup>1/''k''</sup>, or the ''kth'' root of ''h''."	
	end

	-- Replace certain strings with the intended final versions
	intro_text = string.gsub(intro_text, "''k''", ed)
	intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
	intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
	intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
	intro_text = string.gsub(intro_text, "hth", ord._ordinal(harmonic))
	intro_text = string.gsub(intro_text, "''h''", harmonic)
	
	return intro_text
end

-- Separate function for edr intro (arbitrary ratio)
function p.edr_intro(ed, ratio)
	local ed = ed or 12
	local ratio = ratio or rat.new(9,4)
	
	-- Exactly or about? Check up to three significant figures
	local equave_in_cents = rat.cents(ratio)
	local edstep_size = equave_in_cents / ed
	local edstep_size_rounded = utils._round(edstep_size, 3)
	local is_exact = edstep_size - edstep_size_rounded == 0
	
	local intro_text = ""
	if ed == 1 then
		intro_text = "'''1 equal division of ''p/q''''' (abbreviated '''1ed''p/q''''') is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of [[''p/q'']], or exactly/about ''s''{{cent}}."	
	else
		intro_text = "'''''k'' equal divisions of ''p/q''''' (abbreviated '''''k''ed''p/q''''') is a [[nonoctave]] [[tuning system]] that divides the interval of [[''p/q'']] into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each. Each step represents a [[frequency ratio]] of (''p/q'')<sup>1/''k''</sup>, or the ''kth'' root of ''p/q''."	
	end

	-- Replace certain strings with the intended final versions
	intro_text = string.gsub(intro_text, "''k''", ed)
	intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
	intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
	intro_text = string.gsub(intro_text, "''kth''", p.root_ordinal(ed))
	intro_text = string.gsub(intro_text, "''p/q''", rat.as_ratio(ratio))
	
	return intro_text
end

-- Separate function for edc (arbitrary cent value)
function p.edc_intro(ed, cents)
	local ed = ed or 1
	local cents = cents or 97.5
	
	-- Exactly or about? Check up to three significant figures
	local edstep_size = cents / ed
	local edstep_size_rounded = utils._round(edstep_size, 3)
	local is_exact = edstep_size - edstep_size_rounded == 0
	
	local intro_text = ""
	if ed == 1 then
		intro_text = "'''1 equal division of ''c''{{c}}''' (abbreviated '''1ed''c''{{c}}''') is a [[nonoctave]] [[tuning system]] that uses [[equal]] steps of ''c''{{cent}}."	
	else
		intro_text = "'''''k'' equal divisions of ''c''{{c}}''' (abbreviated '''''k''ed''c''{{c}}''') is a [[nonoctave]] [[tuning system]] that divides the interval of ''c''{{c}} into ''k'' [[equal]] parts of exactly/about ''s''{{cent}} each."	
	end

	-- Replace certain strings with the intended final versions
	intro_text = string.gsub(intro_text, "''k''", ed)
	intro_text = string.gsub(intro_text, "exactly/about", (is_exact and "exactly" or "about"))
	intro_text = string.gsub(intro_text, "''s''", utils._round (edstep_size, 3))
	intro_text = string.gsub(intro_text, "''c''", cents)
	
	return intro_text
end

-- ord._ordinal but for roots
function p.root_ordinal(n)
	local result = ord._ordinal(n)
	if n == 2 then
		result = "square"
	elseif n == 3 then
		result = "cube"
	end
	return result
end

-- Parse function
function p.parse(unparsed)
	local unparsed = unparsed or "12"
	
	-- If the unparsed ed is only a numeric value, default to edo
	if tonumber(unparsed) ~= nil then
		unparsed = unparsed .. "edo"
	end
	
	-- Parse the step count, the suffix, and the equave
	local steps, equave = unparsed:match("^(%d+)[Ee][Dd](.+)$")
	
	-- Determine if the ed is for a cent value
	local is_cents = string.match(equave, "%d*%.?%d+[Cc¢]$") ~= nil
	
	-- Parse equave
	local parsed_equave
	local ed_type = ED_TYPE_DEFAULT
	if equave == "o" or equave == "O" or equave == "2" or equave == "2/1" then
		-- Equave is octave
		parsed_equave = 2
		ed_type = ED_TYPE_EDO
	elseif equave == "t" or equave == "T" or equave == "3" or equave == "3/1" then
		-- Equave is tritave/twelfth
		parsed_equave = 3
		ed_type = ED_TYPE_EDT
	elseif equave == "f" or equave == "F" or equave == "3/2" then
		-- Equave is fifth
		parsed_equave = rat.new(3,2)
		ed_type = ED_TYPE_EDF
	elseif string.match(equave, "^%d+$") ~= nil then
		-- Equave is arbitrary harmonic (not 2/1 or 3/1)
		parsed_equave = tonumber(equave)
		ed_type = ED_TYPE_EDH
	elseif string.match(equave, "^%d+/%d+$") ~= nil then
		-- Equave is arbitrary ratio (not 3/2)
		local num, den = equave:match("^(%d+)/(%d+)$")
		parsed_equave = rat.new(tonumber(num), tonumber(den))
		ed_type = ED_TYPE_EDR
	elseif is_cents then
		-- Equave is arbitrary cent value
		parsed_equave = tonumber(equave:match("^(%d*%.?%d+)[Cc¢]$"))
		ed_type = ED_TYPE_EDC
	else
		-- Equave is unsupported
		parsed_equave = 0
	end
	
	return tonumber(steps), parsed_equave, ed_type
end

-- ED-EDO comparison
-- Meant for finding what edo a nonoctave ed corresponds to
function p.edo_compare(ed)
	local ed = ed or "12edf"
	
	local parsed_ed, parsed_equave, ed_type = p.parse(ed)
	local edstep_size = 0;
	if ed_type == ED_TYPE_EDC then
		edstep_size = parsed_equave / parsed_ed
	else
		edstep_size = rat.cents(parsed_equave) / parsed_ed
	end
	local ed_compare = 1200 / edstep_size
	
	local ed_compare_text = "''ed1'' roughly corresponds to ''ed2''edo, "
	
	-- Replace certain strings with the intended final versions
	ed_compare_text = string.gsub(ed_compare_text, "''ed1''", ed)
	ed_compare_text = string.gsub(ed_compare_text, "''ed2''", string.format("%.3f", ed_compare))
	
	local ed_floor = math.floor(ed_compare)
	
	local diff = ed_compare - ed_floor
	if diff > 0.5 then
		ed_compare_text = ed_compare_text .. string.format("and can be seen as %dedo with stretched octaves", ed_floor+1)
	else
		ed_compare_text = ed_compare_text .. string.format("and can be seen as %dedo with compressed octaves", ed_floor)
	end
	
	if diff > 0.4 and diff < 0.6 then
		ed_compare_text = ed_compare_text .. string.format(", or roughly every second step of %dedo.", 2*ed_floor+1)
	else
		ed_compare_text = ed_compare_text .. "."
	end
	
	return ed_compare_text
	
end

-- Primary function
function p._ed_intro(ed)
	local ed = ed or "12"
	
	local parsed_ed, parsed_equave, ed_type = p.parse(ed)
	
	if ed_type == ED_TYPE_DEFAULT then
		return "{{error|Error: Equave type not supported or entered incorrectly.}}"
	elseif ed_type == ED_TYPE_EDO then
		return p.edo_intro(parsed_ed)
	elseif ed_type == ED_TYPE_EDT then
		return p.edt_intro(parsed_ed)
	elseif ed_type == ED_TYPE_EDF then
		return p.edf_intro(parsed_ed)
	elseif ed_type == ED_TYPE_EDH then
		return p.edh_intro(parsed_ed, parsed_equave)
	elseif ed_type == ED_TYPE_EDR then
		return p.edr_intro(parsed_ed, parsed_equave)
	elseif ed_type == ED_TYPE_EDC then
		return p.edc_intro(parsed_ed, parsed_equave)
	end
end

-- Tester function
function p.ed_intro_tester()
	return
		p._ed_intro("12") .. "\n" ..
		p._ed_intro("12edt") .. "\n" ..
		p._ed_intro("12edf") .. "\n" ..
		p._ed_intro("12ed4") .. "\n" ..
		p._ed_intro("12ed9/8") .. "\n" ..
		p._ed_intro("12ed666c") .. "\n" .. "\n" ..
		p._ed_intro("1") .. "\n" ..
		p._ed_intro("1ed3") .. "\n" ..
		p._ed_intro("1ed3/2") .. "\n" ..
		p._ed_intro("1ed4") .. "\n" ..
		p._ed_intro("1ed9/8") .. "\n" ..
		p._ed_intro("1ed666c") .. "\n"
end

-- Wrapper function; for use with a template
function p.ed_intro(frame)
	local ed = frame.args["ED"]
	local edo = frame.args["EDO"]		-- For backwards compatibility with edo intro
	local debugg = yesno(frame.args["debug"])
	local result = p._ed_intro((edo == nil) and ed or edo)
	
	if debugg == true then
		result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>"
	end
	
	return frame:preprocess(result)
end

return p