Module:Chord consistency: Difference between revisions
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..." |
ArrowHead294 (talk | contribs) mNo edit summary |
||
(10 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
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 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 | |||
-- 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 = p.additively_consistent_int(et, all_interval, 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, "…") | |||
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 | 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 158: | ||
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 186: | ||
local root_interval_links = {} | local root_interval_links = {} | ||
local step_interval_links = {} | local step_interval_links = {} | ||
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 200: | ||
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 | end | ||
end | end | ||
cat = "(d >= " .. distance .. ") " .. p.consistent_edos(harmonics, distance, 'edo', 4) | |||
--end | |||
return cat | return cat |