Module:ET: Difference between revisions
Jump to navigation
Jump to search
Undo. These aren't more accurate Tag: Undo |
merge changes from dev |
||
Line 1: | Line 1: | ||
local rat = require( | local rat = require("Module:Rational") | ||
local seq = require( | local seq = require("Module:Sequence") | ||
local p = {} | local p = {} | ||
-- TODO: we should not represent the equave as a rational number at all | |||
local common_suffix = { | local common_suffix = { | ||
[ | ["3/2"] = "f", | ||
[ | ["2"] = "o", | ||
["2/1"] = "o", | |||
[ | ["3"] = "t", | ||
[ | ["3/1"] = "t", | ||
[ | |||
[ | -- these should not be here | ||
[ | -- ["987/610"] = "ϕ", | ||
-- ["1264/465"] = "n", | |||
-- ["355/113"] = "π", | |||
} | } | ||
local common_ratio = { | local common_ratio = { | ||
[ | ["f"] = rat.new(3, 2), | ||
[ | ["o"] = 2, | ||
[ | ["t"] = 3, | ||
[ | |||
[ | -- these should not be here | ||
[ | -- ["ϕ"] = rat.new(987, 610), | ||
-- ["n"] = rat.new(1264, 465), | |||
-- ["π"] = rat.new(355, 113), | |||
} | } | ||
Line 31: | Line 37: | ||
local equave_ratio = rat.as_ratio(equave) | local equave_ratio = rat.as_ratio(equave) | ||
equave_ratio = equave_ratio:lower() | equave_ratio = equave_ratio:lower() | ||
suffix = | suffix = "ed" | ||
if common_suffix[equave_ratio] then | if common_suffix[equave_ratio] then | ||
suffix = suffix .. common_suffix[equave_ratio] | suffix = suffix .. common_suffix[equave_ratio] | ||
Line 45: | Line 51: | ||
-- parse a ET structure | -- parse a ET structure | ||
function p.parse(unparsed) | function p.parse(unparsed) | ||
local size, suffix, equave = unparsed:match( | local size, suffix, equave = unparsed:match("^(%d+)([Ee][Dd](.+))$") | ||
if equave == nil then | if equave == nil then | ||
return nil | return nil | ||
Line 80: | Line 86: | ||
function p.backslash_modifier(et) | function p.backslash_modifier(et) | ||
if not rat.eq(et.equave, 2) then | if not rat.eq(et.equave, 2) then | ||
return | return "\\" .. et.size .. et.suffix | ||
end | end | ||
return | return "\\" .. et.size | ||
end | end | ||
Line 116: | Line 122: | ||
local t = 0 | local t = 0 | ||
for factor, power in pairs(ratio) do | for factor, power in pairs(ratio) do | ||
if type(factor) == | if type(factor) == "number" then | ||
t = t + power * p.approximate(et, factor) | t = t + power * p.approximate(et, factor) | ||
end | end | ||
Line 143: | Line 149: | ||
local zeta_integral = seq.contains(seq.zeta_integral, et.size) | local zeta_integral = seq.contains(seq.zeta_integral, et.size) | ||
local zeta_gap = seq.contains(seq.zeta_gap, et.size) | local zeta_gap = seq.contains(seq.zeta_gap, et.size) | ||
local markers = {} | local markers = {} | ||
if zeta_peak then | if zeta_peak then | ||
table.insert(markers, | table.insert(markers, "[[The Riemann zeta function and tuning #Peak EDOs|zeta peak]]") | ||
elseif zeta_peak == nil then | elseif zeta_peak == nil then | ||
table.insert(markers, | table.insert(markers, "[[The Riemann zeta function and tuning #Peak EDOs|zeta peak?]]") | ||
end | end | ||
if zeta_peak_integer then | if zeta_peak_integer then | ||
table.insert(markers, | table.insert(markers, "[[The Riemann zeta function and tuning #Peak EDOs|zeta peak integer]]") | ||
elseif zeta_peak_integer == nil then | elseif zeta_peak_integer == nil then | ||
table.insert(markers, | table.insert(markers, "[[The Riemann zeta function and tuning #Peak EDOs|zeta peak integer?]]") | ||
end | end | ||
if zeta_integral then | if zeta_integral then | ||
table.insert(markers, | table.insert(markers, "[[The Riemann zeta function and tuning #Integral of Zeta EDOs|zeta integral]]") | ||
elseif zeta_integral == nil then | elseif zeta_integral == nil then | ||
table.insert(markers, | table.insert(markers, "[[The Riemann zeta function and tuning #Integral of Zeta EDOs|zeta integral?]]") | ||
end | end | ||
if zeta_gap then | if zeta_gap then | ||
table.insert(markers, | table.insert(markers, "[[The Riemann zeta function and tuning #Zeta Gap EDOs|zeta gap]]") | ||
elseif zeta_gap == nil then | elseif zeta_gap == nil then | ||
table.insert(markers, | table.insert(markers, "[[The Riemann zeta function and tuning #Zeta Gap EDOs|zeta gap?]]") | ||
end | end | ||
return table.concat(markers, | return table.concat(markers, "<br>") | ||
end | end | ||
return p | return p |
Revision as of 20:22, 1 April 2024
This module provides helper functions for equal-step tunings.
Functions
new
- Returns an array consisting of the components of an equal-step tuning.
parse
- Designed to convert strings in the format
[number of steps]ed[equave]
into an ET structure, and returns it via thenew
function. For example,ET.parse("12edo")
returns an array containing{12, 2, "edo"}
as_string
- Returns the string representation for an ET structure.
backslash_ratio
- Converts steps to a proper ratio as a floating-point number.
backslash_display
- Displays an ET structure in backslash form (
[steps]\[number of divisions]
).
cents
- Converts the interval an ET structure represents to cents.
hekts
- Converts the interval an ET structure represents to hekts (relative cent of 13edt).
approximate
- Returns the floor, round, or ceiling of a particular ratio.
tempers_out
- Determines if an ET tempers out a provided rational number.
is_highly_composite
- Determines if an ET is highly composite.
is_zeta
- Determines if an ET holds any zeta records.
why_zeta
- Describes what specific properties an ET has if it is a zeta record ET.
local rat = require("Module:Rational")
local seq = require("Module:Sequence")
local p = {}
-- TODO: we should not represent the equave as a rational number at all
local common_suffix = {
["3/2"] = "f",
["2"] = "o",
["2/1"] = "o",
["3"] = "t",
["3/1"] = "t",
-- these should not be here
-- ["987/610"] = "ϕ",
-- ["1264/465"] = "n",
-- ["355/113"] = "π",
}
local common_ratio = {
["f"] = rat.new(3, 2),
["o"] = 2,
["t"] = 3,
-- these should not be here
-- ["ϕ"] = rat.new(987, 610),
-- ["n"] = rat.new(1264, 465),
-- ["π"] = rat.new(355, 113),
}
-- create a ET structure <size>ed<equave>
function p.new(size, equave, suffix)
size = size or 12
equave = equave or 2
if suffix == nil then
local equave_n, equave_m = rat.as_pair(equave)
local equave_ratio = rat.as_ratio(equave)
equave_ratio = equave_ratio:lower()
suffix = "ed"
if common_suffix[equave_ratio] then
suffix = suffix .. common_suffix[equave_ratio]
elseif equave_m == 1 then
suffix = suffix .. equave_n
else
suffix = suffix .. equave_ratio
end
end
return { size = size, equave = equave, suffix = suffix }
end
-- parse a ET structure
function p.parse(unparsed)
local size, suffix, equave = unparsed:match("^(%d+)([Ee][Dd](.+))$")
if equave == nil then
return nil
end
suffix = suffix:lower()
size = tonumber(size)
equave = common_ratio[equave] or rat.parse(equave)
if size == nil or equave == nil then
return nil
end
return p.new(size, equave, suffix)
end
-- construct a string representation for a ET structure
function p.as_string(et)
return et.size .. et.suffix
end
-- convert steps to a proper ratio (except that it is a float approximation)
function p.backslash_ratio(et, steps)
if et.size == 0 then
return 1
end
return rat.as_float(et.equave) ^ (steps / et.size)
end
function p.backslash_display(et, steps)
if et.size == 0 then
return 1
end
return steps .. p.backslash_modifier(et)
end
function p.backslash_modifier(et)
if not rat.eq(et.equave, 2) then
return "\\" .. et.size .. et.suffix
end
return "\\" .. et.size
end
-- convert steps to cents
function p.cents(et, steps)
if et.size == 0 then
return 0
end
steps = steps or 1
return 1200 * steps / et.size * math.log(rat.as_float(et.equave)) / math.log(2)
end
-- convert ratio to steps
-- ratio is a float!
-- towards is one of: -1 (floor), 0 (nearest), 1 (ceil)
function p.approximate(et, ratio, towards)
towards = towards or 0
if et.size == 0 then
return 0
end
local exact = math.log(ratio) / math.log(rat.as_float(et.equave)) * et.size
if towards < 0 then
return math.floor(exact)
elseif towards > 0 then
return math.ceil(exact)
else
return math.floor(exact + 0.5)
end
end
-- whether this ET tempers out the provided rational number
function p.tempers_out(et, ratio)
local t = 0
for factor, power in pairs(ratio) do
if type(factor) == "number" then
t = t + power * p.approximate(et, factor)
end
end
return t == 0
end
-- determine whether ET is highly composite
function p.is_highly_composite(et)
et.highly_composite = et.highly_composite or rat.is_highly_composite(et.size)
return et.highly_composite
end
-- determine whether ET's size could be within one of zeta function-related sequences
function p.is_zeta(et)
return seq.contains(seq.zeta_peak, et.size)
or seq.contains(seq.zeta_peak_integer, et.size)
or seq.contains(seq.zeta_integral, et.size)
or seq.contains(seq.zeta_gap, et.size)
end
-- describe why
function p.why_zeta(et)
local zeta_peak = seq.contains(seq.zeta_peak, et.size)
local zeta_peak_integer = seq.contains(seq.zeta_peak_integer, et.size)
local zeta_integral = seq.contains(seq.zeta_integral, et.size)
local zeta_gap = seq.contains(seq.zeta_gap, et.size)
local markers = {}
if zeta_peak then
table.insert(markers, "[[The Riemann zeta function and tuning #Peak EDOs|zeta peak]]")
elseif zeta_peak == nil then
table.insert(markers, "[[The Riemann zeta function and tuning #Peak EDOs|zeta peak?]]")
end
if zeta_peak_integer then
table.insert(markers, "[[The Riemann zeta function and tuning #Peak EDOs|zeta peak integer]]")
elseif zeta_peak_integer == nil then
table.insert(markers, "[[The Riemann zeta function and tuning #Peak EDOs|zeta peak integer?]]")
end
if zeta_integral then
table.insert(markers, "[[The Riemann zeta function and tuning #Integral of Zeta EDOs|zeta integral]]")
elseif zeta_integral == nil then
table.insert(markers, "[[The Riemann zeta function and tuning #Integral of Zeta EDOs|zeta integral?]]")
end
if zeta_gap then
table.insert(markers, "[[The Riemann zeta function and tuning #Zeta Gap EDOs|zeta gap]]")
elseif zeta_gap == nil then
table.insert(markers, "[[The Riemann zeta function and tuning #Zeta Gap EDOs|zeta gap?]]")
end
return table.concat(markers, "<br>")
end
return p