local ord = require('Module:Ordinal')
local utils = require('Module:Utils')
local rat = require('Module:Rational')
local p = {}
-- Notes on supported types:
-- - Supported types: edo, edt, edf, edh, edr, edc (for octave, tritave/twelfth,
-- fifth, arbitrary harmonic h, arbitrary ratio r, and arbitrary cents c).
-- - Note that EDs of irrational numbers (edir) aren't supported since some of
-- these constants have names (e, pi, phi) or are formatted in special ways
-- (sqrt(2)). These aren't very common and can be expressed as edc's if need
-- be (edsqrt(2) is ed600c), or as an ed of a non-integer number (ed1.41421).
-- - Also note that, under harmontonic tuning, edc and edr are the same as ASp
-- and APSp (ambitonal sequence and arithmetic pitch sequence); these were
-- recently reclassified as edr and edc, however.
-- Parse ed function
function p.parse_ed(unparsed)
local unparsed = unparsed or "12edo"
-- If the unparsed ed is only a numeric value, default to edo
if tonumber(unparsed) ~= nil then
unparsed = unparsed .. "edo"
end
-- Parse
local steps, suffix, equave = unparsed:match('^(%d+)([Ee][Dd](.+))$')
steps = tonumber(steps)
-- If the equave is text, then the equave is:
-- o: octave, 2/1 (2nd harmonic)
-- t: tritave or twelfth, 3/1 (3rd harmonic)
-- f: fifth, 3/2
-- Make sure these have the correct equave and suffix
local ratio = "n/a"
local cents = 0
local ed_type = ""
if equave == "o" or equave == "O" or equave == "2" or equave == "2/1" then
equave = "2"
ratio = "2/1"
cents = 1200
suffix = "edo"
ed_type = "edo"
elseif equave == "t" or equave == "T" or equave == "3" or equave == "3/1" then
equave = "3"
ratio = "3/1"
cents = math.log(3) * 1200 / math.log(2)
suffix = "edt"
ed_type = "edt"
elseif equave == "f" or equave == "F" or equave == "3/2" then
equave = "3/2"
ratio = "3/2"
cents = math.log(3/2) * 1200 / math.log(2)
suffix = "edf"
ed_type = "edf"
end
-- For equaves not 2/1, 3/1, or 3/2
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
-- Primary function
function p.ed_intro(ed)
local ed = ed or "12edo"
local parsed_ed = p.parse_ed(ed)
local ed_type = parsed_ed['type']
-- Intro formats for each possible case
-- - Common abbrevs: edo, edt, edf; these have specially written intros
-- - General harmonic: edh (h-th harmonic)
-- - Arbitrary JI ratio: edr (ratio of p/q)
-- - Arbitrary constant: edc
-- - [[equal]]-step tunings: 1edo, 1edt, 1edf, 1edh, 1edp/q, 1edc
local intro_text = ""
if parsed_ed['steps'] == 1 then
if ed_type == "edo" 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]] 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
if ed_type == "edo" then
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
-- Produce a map of text to replace
-- - ''k'': number of steps in ed
-- - ''s'': size of a step
-- - ''p''/''q'': equave as a ratio
-- - ''h'': equave as a harmonic
-- - ''c'': equave as a cent value
-- - ''hth'': harmonic as an ordinal
-- - ''kth'': step count as an ordinal
local step_size = parsed_ed['cents'] / parsed_ed['steps']
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
intro_text = string.gsub(intro_text, key, value)
end
return intro_text
end
-- Wrapper function; for use with a template
function p.ed_intro_frame(frame)
local ed = frame.args['ED']
return p.ed_intro(ed)
end
return p