Module:Chord consistency: Difference between revisions
Jump to navigation
Jump to search
Dummy index (talk | contribs) No edit summary |
Dummy index (talk | contribs) equave-free veresion of additively_consistent() |
||
Line 2: | Line 2: | ||
local utils = require("Module:Utils") | local utils = require("Module:Utils") | ||
local ET = require('Module:ET') | local ET = require('Module:ET') | ||
local p = {} | local p = {} | ||
-- check additive consistency for a set of ratios (equave-free version): | |||
-- approx(a*b) = approx(a) + approx(b) forall a, b: a, b, ab in ratios | |||
-- `distinct`: whether distinct ratios are required to be mapped to distinct approximations | |||
-- `previous`: already computed ratios for the previous iteraton | |||
function p.additively_consistent_int(et, ratios, distinct, previous) | |||
distinct = distinct or false | |||
previous = previous or {} | |||
if distinct then | |||
local approx_set = {} | |||
for a_key, a in pairs(previous) do | |||
local a_approx = ET.approximate(et, rat.as_float(a)) % et.size | |||
if approx_set[a_approx] then | |||
if not rat.eq(rat.div(a, approx_set[a_approx]), 1) then | |||
mw.log(a_key .. ' -> ' .. a_approx .. ': conflict!') | |||
return false | |||
end | |||
end | |||
approx_set[a_approx] = a | |||
mw.log(a_key .. ' -> ' .. a_approx) | |||
end | |||
for a_key, a in pairs(ratios) do | |||
local a_approx = ET.approximate(et, rat.as_float(a)) % et.size | |||
if approx_set[a_approx] then | |||
if not rat.eq(rat.div(a, approx_set[a_approx]), 1) then | |||
mw.log(a_key .. ' -> ' .. a_approx .. ': conflict!') | |||
return false | |||
end | |||
end | |||
approx_set[a_approx] = a | |||
mw.log(a_key .. ' -> ' .. a_approx) | |||
end | |||
end | |||
if type(distinct) == 'number' then | |||
return true | |||
end | |||
local previous_ordered = {} | |||
for a_key, a in pairs(previous) do | |||
table.insert(previous_ordered, a) | |||
end | |||
local ratios_ordered = {} | |||
for a_key, a in pairs(ratios) do | |||
table.insert(ratios_ordered, a) | |||
end | |||
for i, a in ipairs(ratios_ordered) do | |||
local a_approx = ET.approximate(et, rat.as_float(a)) | |||
for j, b in ipairs(previous_ordered) do | |||
local b_approx = ET.approximate(et, rat.as_float(b)) | |||
local c = rat.mul(a, b) | |||
local c_approx = ET.approximate(et, rat.as_float(c)) | |||
local c_key = rat.as_ratio(c) | |||
if previous[c_key] or ratios[c_key] then | |||
if c_approx ~= a_approx + b_approx then | |||
mw.log('a = ' .. rat.as_ratio(a) .. '; b = ' .. rat.as_ratio(b) .. '; ab = ' .. c_key) | |||
mw.log(a_approx .. ' + ' .. b_approx .. ' != ' .. c_approx) | |||
return false | |||
end | |||
end | |||
end | |||
for j, b in ipairs(ratios_ordered) do | |||
if i <= j then | |||
local b_approx = ET.approximate(et, rat.as_float(b)) | |||
local c = rat.mul(a, b) | |||
local c_approx = ET.approximate(et, rat.as_float(c)) | |||
local c_key = rat.as_ratio(c) | |||
if previous[c_key] or ratios[c_key] then | |||
if c_approx ~= a_approx + b_approx then | |||
mw.log('a = ' .. rat.as_ratio(a) .. '; b = ' .. rat.as_ratio(b) .. '; ab = ' .. c_key) | |||
mw.log(a_approx .. ' + ' .. b_approx .. ' != ' .. c_approx) | |||
return false | |||
end | |||
end | |||
end | |||
end | |||
end | |||
return true | |||
end | |||
function p.noinfobox_chord(frame) | function p.noinfobox_chord(frame) | ||
Line 68: | Line 148: | ||
for i = 1, 50 do | for i = 1, 50 do | ||
local et = ET.parse('' .. i .. 'edo') | local et = ET.parse('' .. i .. 'edo') | ||
local consistent = | local consistent = p.additively_consistent_int(et, all_interval, false, previous) | ||
if consistent then | if consistent then | ||
table.insert(vals, "[[" .. i .. "edo]]") | table.insert(vals, "[[" .. i .. "edo]]") |
Revision as of 14:11, 14 December 2024
This module provides some functions which enumerate consistent equal divisions relative to some chord.
Functions
additively_consistent_int
- Slightly differ version of
Module:Limits.additively_consistent
. That function must be supplied with octave-reduced intervals.
consistent_edos
- 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.
noinfobox_chord
(obsolete)- A piece of code for Module: Infobox chord.
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 rat = require('Module:Rational')
local utils = require("Module:Utils")
local ET = require('Module:ET')
local p = {}
-- check additive consistency for a set of ratios (equave-free version):
-- approx(a*b) = approx(a) + approx(b) forall a, b: a, b, ab in ratios
-- `distinct`: whether distinct ratios are required to be mapped to distinct approximations
-- `previous`: already computed ratios for the previous iteraton
function p.additively_consistent_int(et, ratios, distinct, previous)
distinct = distinct or false
previous = previous or {}
if distinct then
local approx_set = {}
for a_key, a in pairs(previous) do
local a_approx = ET.approximate(et, rat.as_float(a)) % et.size
if approx_set[a_approx] then
if not rat.eq(rat.div(a, approx_set[a_approx]), 1) then
mw.log(a_key .. ' -> ' .. a_approx .. ': conflict!')
return false
end
end
approx_set[a_approx] = a
mw.log(a_key .. ' -> ' .. a_approx)
end
for a_key, a in pairs(ratios) do
local a_approx = ET.approximate(et, rat.as_float(a)) % et.size
if approx_set[a_approx] then
if not rat.eq(rat.div(a, approx_set[a_approx]), 1) then
mw.log(a_key .. ' -> ' .. a_approx .. ': conflict!')
return false
end
end
approx_set[a_approx] = a
mw.log(a_key .. ' -> ' .. a_approx)
end
end
if type(distinct) == 'number' then
return true
end
local previous_ordered = {}
for a_key, a in pairs(previous) do
table.insert(previous_ordered, a)
end
local ratios_ordered = {}
for a_key, a in pairs(ratios) do
table.insert(ratios_ordered, a)
end
for i, a in ipairs(ratios_ordered) do
local a_approx = ET.approximate(et, rat.as_float(a))
for j, b in ipairs(previous_ordered) do
local b_approx = ET.approximate(et, rat.as_float(b))
local c = rat.mul(a, b)
local c_approx = ET.approximate(et, rat.as_float(c))
local c_key = rat.as_ratio(c)
if previous[c_key] or ratios[c_key] then
if c_approx ~= a_approx + b_approx then
mw.log('a = ' .. rat.as_ratio(a) .. '; b = ' .. rat.as_ratio(b) .. '; ab = ' .. c_key)
mw.log(a_approx .. ' + ' .. b_approx .. ' != ' .. c_approx)
return false
end
end
end
for j, b in ipairs(ratios_ordered) do
if i <= j then
local b_approx = ET.approximate(et, rat.as_float(b))
local c = rat.mul(a, b)
local c_approx = ET.approximate(et, rat.as_float(c))
local c_key = rat.as_ratio(c)
if previous[c_key] or ratios[c_key] then
if c_approx ~= a_approx + b_approx then
mw.log('a = ' .. rat.as_ratio(a) .. '; b = ' .. rat.as_ratio(b) .. '; ab = ' .. c_key)
mw.log(a_approx .. ' + ' .. b_approx .. ' != ' .. c_approx)
return false
end
end
end
end
end
return true
end
function p.noinfobox_chord(frame)
local page_name = frame:preprocess("{{PAGENAME}}")
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
-- 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 = {}
local all_interval = {}
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
-- 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 = p.additively_consistent_int(et, all_interval, false, previous)
if consistent then
table.insert(vals, "[[" .. i .. "edo]]")
end
end
--end
cat = table.concat(vals, ", ")
return cat
end
return p