Module:ET: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Approximating with 0edo
These all looked too dumb tbh
 
(One intermediate revision by the same user not shown)
Line 64: Line 64:
function p.backslash_ratio(et, steps)
function p.backslash_ratio(et, steps)
if et.size == 0 then
if et.size == 0 then
return nil
return 1
end
end
return rat.as_float(et.equave) ^ (steps / et.size)
return rat.as_float(et.equave) ^ (steps / et.size)
Line 71: Line 71:
function p.backslash_display(et, steps)
function p.backslash_display(et, steps)
if et.size == 0 then
if et.size == 0 then
return nil
return 1
end
end
return steps .. p.backslash_modifier(et)
return steps .. p.backslash_modifier(et)
Line 86: Line 86:
function p.cents(et, steps)
function p.cents(et, steps)
if et.size == 0 then
if et.size == 0 then
return nil
return 0
end
end
steps = steps or 1
steps = steps or 1
Line 95: Line 95:
function p.hekts(et, steps)
function p.hekts(et, steps)
if et.size == 0 then
if et.size == 0 then
return nil
return 0
end
end
steps = steps or 1
steps = steps or 1
Line 107: Line 107:
towards = towards or 0
towards = towards or 0
if et.size == 0 then
if et.size == 0 then
return ratio.inf and nil or 0
return 0
end
end
local exact = math.log(ratio) / math.log(rat.as_float(et.equave)) * et.size
local exact = math.log(ratio) / math.log(rat.as_float(et.equave)) * et.size
Line 150: Line 150:
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 z = "The Riemann zeta function and tuning #Zeta EDO lists"
local z = "The Riemann zeta function and tuning#Zeta EDO lists"


local markers = {}
local markers = {}

Latest revision as of 13:21, 19 July 2025

Module documentation[view] [edit] [history] [purge]
This module primarily serves as a library for other modules and has no corresponding template.

This module provides helper functions for equal-step tunings.

Introspection summary for Module:ET 
Functions provided (13)
Line Function Params Description
23 new (size, equave, suffix) Returns an array consisting of the components of an equal-step tuning.
43 parse (unparsed) Converts an equal tuning as a string into a Lua table.
59 as_string (et) Returns the ET as a string.
64 backslash_ratio (et, steps) Converts steps to a proper ratio as a floating-point number.
71 backslash_display (et, steps) Displays an ET as steps\number-of-divisions.
78 backslash_modifier (et)
86 cents (et, steps) Converts an interval of the ET into cents,  ¢.
95 hekts (et, steps) Converts an interval of the ET into hekts, the relative cents of 13edt.
106 approximate (et, ratio, towards) Returns the floor, round, or ceiling of a particular ratio.
122 tempers_out (et, ratio) Determines whether an ET tempers out a provided rational number.
133 is_highly_composite (et) Determines whether an ET is highly composite.
139 is_zeta (et) Determines if an ET holds any zeta records.
147 why_zeta (et) Describes what specific properties an ET has if it is a zeta record ET.
Lua modules required (2)
Variable Module Functions used
rat Module:Rational new
as_pair
as_ratio
parse
as_float
eq
is_highly_composite
seq Module:Sequence contains

local rat = require("Module:Rational")
local seq = require("Module:Sequence")
local p = {}

local common_suffix = {
	["3/2"] = "f",
	["2"] = "o",
	["2/1"] = "o",
	["3"] = "t",
	["3/1"] = "t",
}

local common_ratio = {
	["f"] = rat.new(3, 2),
	["o"] = 2,
	["t"] = 3,
	["ϕ"] = (1 + math.sqrt(5)) / 2,
	["n"] = math.exp(1),
	["π"] = math.pi
}

-- 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+%.*%d*)([Ee][Dd](.+))$")
	-- local size, suffix, equave = unparsed:match("^(%d+%.*%d*)([Cc]?[Ee][Dd]?[Tt]?(.*))$")
	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 steps to hekts
function p.hekts(et, steps)
	if et.size == 0 then
		return 0
	end
	steps = steps or 1
	return 1300 * steps / et.size * math.log(rat.as_float(et.equave)) / math.log(3)
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 z = "The Riemann zeta function and tuning#Zeta EDO lists"

	local markers = {}
	if zeta_peak then
		table.insert(markers, string.format("[[%s|Zeta peak]]", z))
	elseif zeta_peak == nil then
		table.insert(markers, string.format("[[%s|Zeta peak?]]", z))
	end

	if zeta_peak_integer then
		table.insert(markers, string.format("[[%s|Zeta peak integer]]", z))
	elseif zeta_peak_integer == nil then
		table.insert(markers, string.format("[[%s|Zeta peak integer?]]", z))
	end

	if zeta_integral then
		table.insert(markers, string.format("[[%s|Zeta integral]]", z))
	elseif zeta_integral == nil then
		table.insert(markers, string.format("[[%s|Zeta integral?]]", z))
	end

	if zeta_gap then
		table.insert(markers, string.format("[[%s|Zeta gap]]", z))
	elseif zeta_gap == nil then
		table.insert(markers, string.format("[[%s|Zeta gap?]]", z))
	end

	return table.concat(markers, "<br />")
end

return p