Module:ED intro: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
Still work-in-progress; includes todo on what to bugfix
Ganaram inukshuk (talk | contribs)
Bugfixed; rewritten to support edo, edt, edf, edh (harmonics), edr (arbitrary ratios), and edc (arbitrary cents)
Line 4: Line 4:
local p = {}
local p = {}


-- TODO:
-- Notes on supported types:
-- - Enforce ED types: edo, edt, edf, edh, edr, edc for octave, tritave/twelfth,
-- - Supported types: edo, edt, edf, edh, edr, edc (for octave, tritave/twelfth,
--  fifth, arbitrary harmonic h, arbitrary ratio r, and arbitrary cent value c.
--  fifth, arbitrary harmonic h, arbitrary ratio r, and arbitrary cents c).
--   Note that EDs of named constants, like pi, are currently not included, as
-- - Note that EDs of irrational numbers (edir) aren't supported since some of
--  they probably need their own typing (ednc) separate from edc.
--  these constants have names (e, pi, phi) or are formatted in special ways
-- - Bugfix edr not reporting the correct equave
--  (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)
function p.parse_ed(unparsed)
local unparsed = unparsed or "12edo"
local unparsed = unparsed or "12edo"
Line 20: Line 25:
-- Parse
-- Parse
local steps, equave = unparsed:match('^(%d+)[Ee][Dd](.+)$')
local steps, suffix, equave = unparsed:match('^(%d+)([Ee][Dd](.+))$')
steps = tonumber(steps)
steps = tonumber(steps)
Line 27: Line 32:
-- t: tritave or twelfth, 3/1 (3rd harmonic)
-- t: tritave or twelfth, 3/1 (3rd harmonic)
-- f: fifth, 3/2
-- f: fifth, 3/2
local ed_type = ""
-- Make sure these have the correct equave and suffix
local ratio = "n/a"
local ratio = "n/a"
local cents = 0
local cents = 0
if equave == "o" or equave == "O" or equave == "2" then
local ed_type = ""
if equave == "o" or equave == "O" or equave == "2" or equave == "2/1" then
equave = "2"
equave = "2"
ratio = "2/1"
ratio = "2/1"
cents = 1200
cents = 1200
suffix = "edo"
ed_type = "edo"
ed_type = "edo"
elseif equave == "t" or equave == "T" or equave == "3" then
elseif equave == "t" or equave == "T" or equave == "3" or equave == "3/1" then
equave = "3"
equave = "3"
ratio = "3/1"
ratio = "3/1"
cents = math.log(3) * 1200 / math.log(2)
cents = math.log(3) * 1200 / math.log(2)
suffix = "edt"
ed_type = "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
Line 44: Line 52:
ratio = "3/2"
ratio = "3/2"
cents = math.log(3/2) * 1200 / math.log(2)
cents = math.log(3/2) * 1200 / math.log(2)
suffix = "edf"
ed_type = "edf"
ed_type = "edf"
end
end
-- For equaves not 2/1, 3/1, or 3/2
-- For equaves not 2/1, 3/1, or 3/2
if ed_type == "" then
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 a number or ratio, convert it to cents
-- If the equave is already a cent value, extract it
-- If the equave is already a cent value, extract it
local is_cents = string.match(equave, '^(.+)c') ~= nil
local is_cents = string.match(equave, '^.+c$') ~= nil
local is_numeric = tonumber(equave) ~= nil
local is_harmonic = string.match(equave, '^%d+$') ~= nil
local is_ratio = string.match(equave, '^%d+/%d+$') ~= nil
local is_ratio = string.match(equave, '^%d+/%d+$') ~= nil
if is_cents then
if is_cents then
cents = tonumber(equave:match('^(.+)c'))
ed_type = "edc"
ed_type = "edc"
elseif is_numeric then
cents = tonumber(equave:match('^(.+)c$'))
cents = math.log(tonumber(equave)) * 1200 / math.log(2)
elseif is_harmonic then
ed_type = "edh"
-- Classify ed type as either ed of a constant (if not a whole number)
local harmonic = tonumber(string.match(equave, '^%d+$'))
-- or ed of a harmonic (if it's a whole number); if it is a harmonic,
cents = math.log(harmonic) * 1200 / math.log(2)
-- write it as a ratio (h/1)
ratio = string.format("%d/1", harmonic)
if math.floor(tonumber(equave)) ~= tonumber(equave) then
ed_type = "edc"
else
ed_type = "edh"
ratio = equave .. "/1"
end
elseif is_ratio then
elseif is_ratio then
local ratio = rat.parse(equave)
ed_type = "edr"
cents = rat.cents(ratio)
local num, den = equave:match('^(%d+)/(%d+)$')
equave = rat.as_ratio(ratio) -- Ensures ratio is simplified
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
ratio = equave
ed_type = "edr"
end
end
end
end


return { ['cents'] = cents, ['equave'] = equave, ['steps'] = steps, ['ratio'] = ratio, ['type'] = ed_type }
return { ['cents'] = cents, ['equave'] = equave, ['ratio'] = ratio, ['steps'] = steps, ['suffix'] = suffix, ['type'] = ed_type }
end
end


-- Primary function
function p.ed_intro(ed)
function p.ed_intro(ed)
local ed = ed or "12ed19/13"
local ed = ed or "1ed5/4"
local parsed_ed = p.parse_ed(ed)
local parsed_ed = p.parse_ed(ed)
Line 95: Line 101:
-- - Equal-step tunings: 1edo, 1edt, 1edf, 1edh, 1edp/q, 1edc
-- - Equal-step tunings: 1edo, 1edt, 1edf, 1edh, 1edp/q, 1edc
local intro_text = ""
local intro_text = ""
if is_est then
if parsed_ed['steps'] == 1 then
if ed_type == "edo" 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."
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."
Line 105: Line 111:
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."
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
elseif ed_type == "edr" then
intro_text = "'''1 equal division of ''p''/''q''''' (abbreviated '''1ed''p''/''q''''') is the [[non-octave]] [[tuning system]] where adjacent pitches are one interval of [[''p''/''q'']], or exactly/about ''s'' [[¢]], apart from each other."
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
elseif ed_type == "edc" then
intro_text = "'''1 equal division of ''c''¢''' (abbreviated '''1ed''c''''') is the [[non-octave]] [[tuning system]] where adjacent pitches are ''s'' [[¢]], apart from each other."
intro_text = "'''1 equal division of ''c''¢''' (abbreviated '''1ed''c'''''), also called '''arithmetic pitch sequence of ''c''¢''' ('''APS''c''¢'''), is the [[non-octave]] [[tuning system]] where adjacent pitches are ''s'' [[¢]] apart from each other."
end
end
else
else
Line 150: Line 156:
return intro_text
return intro_text
end
-- Wrapper function; for use with a template
function p.ed_intro_frame(frame)
local ed = frame.args['ED']
return ed_intro(ed)
end
end


return p
return p

Revision as of 19:49, 28 January 2024

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 (3)
Line Function Params
18 parse_ed (unparsed)
90 ed_intro (ed)
161 ed_intro_frame (invokable) (frame)
Lua modules required (3)
Variable Module Functions used
ord Module:Ordinal _ordinal
rat Module:Rational dependency not used
utils Module:Utils _gcd
_round

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


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 "1ed5/4"
	
	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'''''), also called '''arithmetic pitch sequence of ''c''¢''' ('''APS''c''¢'''), is the [[non-octave]] [[tuning system]] where adjacent pitches are ''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''' or '''''k''ed''c''¢''') is the [[non-octave]] [[tuning system]] that divides interval of ''c''¢ is divided 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(step_size, 3),
		["''c''"] = utils._round(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 ed_intro(ed)
end

return p