Module:ET: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
The "ratio" is a float...
These all looked too dumb tbh
 
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 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