Module:Chord consistency: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Dummy index (talk | contribs)
Created page with "local rat = require('Module:Rational') local utils = require("Module:Utils") local ET = require('Module:ET') local consistency = require('Module:Limits') local p = {} functio..."
 
Dummy index (talk | contribs)
delete additively_consistent_int() and use new version of Module:Limits.additively_consistent()
 
(11 intermediate revisions by 2 users not shown)
Line 1: Line 1:
local limits = require('Module:Limits')
local ET = require('Module:ET')
local rat = require('Module:Rational')
local rat = require('Module:Rational')
local utils = require("Module:Utils")
local utils = require("Module:Utils")
local ET = require('Module:ET')
local consistency = require('Module:Limits')
local p = {}
local p = {}
-- determine maximum error
function p.max_error(et, ratios)
local maxe = 0.0
for a_key, a in pairs(ratios) do
local a_approx = ET.approximate(et, rat.as_float(a))
local e = math.abs((ET.cents(et, a_approx) - rat.cents(a)) / ET.cents(et, 1))
if (e > maxe) then
maxe = e
end
end
return maxe
end
function p.consistent_edos(harmonics, distance, ed, maxlen)
distance = distance or 1.0
ed = ed or 'edo'
local max_n = 72
maxlen = maxlen or max_n
if max_n < maxlen then max_n = maxlen end
local all_interval = {}
for i, h in ipairs(harmonics) do
-- compute all ratio
for j, g in ipairs(harmonics) do
if j > i then
local a = rat.new(g, h)
all_interval[rat.as_ratio(a)] = a
end
end
end
local vals = {}
for i = 1, max_n do
local et = ET.parse('' .. i .. ed)
local consistent = limits.additively_consistent(et, all_interval, false, false, nil)
if consistent then
local maxe = p.max_error(et, all_interval)
if maxe <= 5.0e-11 then
table.insert(vals, "[[" .. i .. ed .. "]]" .. "(just)")
break
end
local dist = 0.5/maxe
local up = (dist >= distance)
local llevel = 0
while (dist >= 2) do
llevel = llevel + 1
dist = dist / 2
end
if up then
if #vals >= maxlen then
table.insert(vals, "&hellip;")
break
end
table.insert(vals, "[[" .. i .. ed .. "]]" .. string.rep("*", llevel))
end
end
end
return table.concat(vals, ", ")
end


function p.noinfobox_chord(frame)
function p.noinfobox_chord(frame)
local page_name = frame:preprocess("{{PAGENAME}}")
local distance = tonumber(frame.args["Distance"])
local debug_data = ""
local debug_data = ""
local infobox_data = {}
local infobox_data = {}
local cats = ""
local cats = ""


if utils.value_provided(frame.args["Harmonics"]) then
--if utils.value_provided(frame.args["Harmonics"]) then
local harmonics = {}
local harmonics = {}
for hs in string.gmatch(frame.args["Harmonics"], "[^:]+") do
for hs in string.gmatch(frame.args["Harmonics"], "[^:]+") do
Line 18: Line 78:
assert(h > 0, "invalid harmonic")
assert(h > 0, "invalid harmonic")
table.insert(harmonics, h)
table.insert(harmonics, h)
end
if distance == nil then
if #harmonics >= 5 then
distance = 1.5
elseif #harmonics >= 3 then
distance = 2.0
else
distance = 3.0
end
end
end


Line 36: Line 106:
local root_interval_links = {}
local root_interval_links = {}
local step_interval_links = {}
local step_interval_links = {}
local all_interval = {}
for i, h in ipairs(harmonics) do
for i, h in ipairs(harmonics) do
-- compute ratio of this harmonic relative to the root
-- compute ratio of this harmonic relative to the root
Line 51: Line 120:
local step_denom = prev / step_gcd
local step_denom = prev / step_gcd
table.insert(step_interval_links, "[[" .. step_numer .. "/" .. step_denom .. "]]")
table.insert(step_interval_links, "[[" .. step_numer .. "/" .. step_denom .. "]]")
end
-- compute all ratio
for j, g in ipairs(harmonics) do
if j > i then
local step_gcd = utils._gcd(g, h)
local step_numer = g / step_gcd
local step_denom = h / step_gcd
local a = rat.new(g, h)
all_interval[rat.as_ratio(a)] = a
end
end
end
local vals = {}
for i = 1, 50 do
local et = ET.parse('' .. i .. 'edo')
local consistent = consistency.additively_consistent(et, all_interval, false, previous)
if consistent then
table.insert(vals, "[[" .. i .. "edo]]")
end
end
end
end
end
cat = table.concat(vals, ", ")
cat = "(d >= " .. distance .. ") " .. p.consistent_edos(harmonics, distance, 'edo', 4)
--end


return cat
return cat

Latest revision as of 13:46, 31 October 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 some functions which enumerate consistent equal divisions relative to some chord.


Introspection summary for Module:Chord consistency 
Functions provided (3)
Line Function Params Description
8 max_error (et, ratios)
20 consistent_edos (harmonics, distance, ed, maxlen) Output list of consistent edos relative to given chord (Lua table of harmonics) up to 72edo. Different equaves, minimum consistency distance, maximum length of list are specifiable.
68 noinfobox_chord (invokable) (frame) Obsolete. Formerly used for Module:Infobox chord.
Lua modules required (4)
Variable Module Functions used
ET Module:ET approximate
cents
parse
limits Module:Limits additively_consistent
rat Module:Rational as_float
cents
new
as_ratio
utils Module:Utils _gcd

Format of edos list

Output of consistent_edos are links to individual edos with each trailing several asterisks. These indicate consistency distace d briefly, none as 1 ≤ d < 2; * as 2 ≤ d < 4; ** as 4 ≤ d < 8; …


local limits = require('Module:Limits')
local ET = require('Module:ET')
local rat = require('Module:Rational')
local utils = require("Module:Utils")
local p = {}

-- determine maximum error
function p.max_error(et, ratios)
	local maxe = 0.0
	for a_key, a in pairs(ratios) do
		local a_approx = ET.approximate(et, rat.as_float(a))
		local e = math.abs((ET.cents(et, a_approx) - rat.cents(a)) / ET.cents(et, 1))
		if (e > maxe) then
			maxe = e
		end
	end
	return maxe
end

function p.consistent_edos(harmonics, distance, ed, maxlen)
	distance = distance or 1.0
	ed = ed or 'edo'
	local max_n = 72
	maxlen = maxlen or max_n
	if max_n < maxlen then max_n = maxlen end
	local all_interval = {}

	for i, h in ipairs(harmonics) do
		-- compute all ratio
		for j, g in ipairs(harmonics) do
			if j > i then
				local a = rat.new(g, h)
				all_interval[rat.as_ratio(a)] = a
			end
		end
	end

	local vals = {}
	for i = 1, max_n do
		local et = ET.parse('' .. i .. ed)
		local consistent = limits.additively_consistent(et, all_interval, false, false, nil)
		if consistent then
			local maxe = p.max_error(et, all_interval)
			if maxe <= 5.0e-11 then
				table.insert(vals, "[[" .. i .. ed .. "]]" .. "(just)")
				break
			end
			local dist = 0.5/maxe
			local up = (dist >= distance)
			local llevel = 0
			while (dist >= 2) do
				llevel = llevel + 1
				dist = dist / 2
			end
			if up then
				if #vals >= maxlen then
					table.insert(vals, "&hellip;")
					break
				end
				table.insert(vals, "[[" .. i .. ed .. "]]" .. string.rep("*", llevel))
			end
		end
	end

	return table.concat(vals, ", ")
end

function p.noinfobox_chord(frame)
	local distance = tonumber(frame.args["Distance"])
	local debug_data = ""
	local infobox_data = {}
	local cats = ""

	--if utils.value_provided(frame.args["Harmonics"]) then
		local harmonics = {}
		for hs in string.gmatch(frame.args["Harmonics"], "[^:]+") do
			h = tonumber(hs)  -- TODO: support rational entries?
			assert(h > 0, "invalid harmonic")
			table.insert(harmonics, h)
		end

		if distance == nil then
			if #harmonics >= 5 then
				distance = 1.5
			elseif #harmonics >= 3 then
				distance = 2.0
			else
				distance = 3.0
			end
		end

		-- reduce harmonics to simplest terms, in case the user accidentally failed to reduce them
		local gcd = harmonics[1]
		for i, h in ipairs(harmonics) do
			gcd = utils._gcd(gcd, h)
			if gcd == 1 then break end
		end
		if gcd > 1 then
			for i, h in ipairs(harmonics) do
				harmonics[i] = harmonics[i] / gcd
			end
		end

		local root = harmonics[1]

		local root_interval_links = {}
		local step_interval_links = {}
		for i, h in ipairs(harmonics) do
			-- compute ratio of this harmonic relative to the root
			local gcd = utils._gcd(h, root)
			local numer = h / gcd
			local denom = root / gcd
			table.insert(root_interval_links, "[[" .. numer .. "/" .. denom .. "]]")

			-- compute ratio of this harmonic relative to the previous
			if i > 1 then
				local prev = harmonics[i-1]
				local step_gcd = utils._gcd(h, prev)
				local step_numer = h / step_gcd
				local step_denom = prev / step_gcd
				table.insert(step_interval_links, "[[" .. step_numer .. "/" .. step_denom .. "]]")
			end
		end
		
		cat = "(d >= " .. distance .. ") " .. p.consistent_edos(harmonics, distance, 'edo', 4)
	--end

	return cat
end

return p