Pailiaq
Joined 24 September 2024
m Pailiaq moved page User:Pailiaq to Module:Interval Edo Approximations |
m shh dont pay attention to me im tryin to figure out modules |
||
| Line 1: | Line 1: | ||
-- EDO Approximations Module | |||
-- Calculates EDO approximations for just intervals | |||
-- Usage: {{#invoke:EDO_Approximations|main|interval=3/2|tolerance=9|min_edo=10|max_edo=60}} | |||
local p = {} | |||
-- Convert a frequency ratio to cents | |||
local function cents(ratio) | |||
return 1200 * math.log(ratio, 2) | |||
end | |||
-- Parse a ratio string like '3/2' into a number | |||
local function parse_ratio(ratio_str) | |||
local num, denom = ratio_str:match("^(%d+)/(%d+)$") | |||
if not num or not denom then | |||
return nil | |||
end | |||
return tonumber(num) / tonumber(denom) | |||
end | |||
-- Find the best approximation of an interval in a given EDO | |||
local function find_best_approximation(ratio_cents, edo) | |||
local edostep = 1200 / edo | |||
-- Find the nearest step | |||
local best_step = math.floor(ratio_cents / edostep + 0.5) | |||
local approximation_cents = best_step * edostep | |||
local absolute_error = approximation_cents - ratio_cents | |||
local relative_error = (absolute_error / edostep) * 100 | |||
return best_step, absolute_error, relative_error | |||
end | |||
-- Calculate all EDO approximations within tolerance for a given ratio | |||
local function calculate_edo_approximations(ratio, tolerance, min_edo, max_edo) | |||
local ratio_cents = cents(ratio) | |||
local results = {} | |||
for edo = min_edo, max_edo do | |||
local steps, abs_error, rel_error = find_best_approximation(ratio_cents, edo) | |||
if math.abs(rel_error) <= tolerance then | |||
table.insert(results, { | |||
edo = edo, | |||
steps = steps, | |||
abs_error = abs_error, | |||
rel_error = rel_error | |||
}) | |||
end | |||
end | |||
return results | |||
end | |||
-- Format a number with sign and 2 decimal places | |||
local function format_error(value) | |||
if value >= 0 then | |||
return string.format("+%.2f", value) | |||
else | |||
return string.format("%.2f", value) | |||
end | |||
end | |||
-- Main function to generate the wikitable | |||
function p.main(frame) | |||
-- Get parameters from template invocation | |||
local args = frame.args | |||
local interval_str = args.interval or args[1] | |||
local tolerance = tonumber(args.tolerance) or 9.0 | |||
local min_edo = tonumber(args.min_edo) or 10 | |||
local max_edo = tonumber(args.max_edo) or 60 | |||
-- Validate and parse interval | |||
if not interval_str then | |||
return "Error: No interval specified" | |||
end | |||
local ratio = parse_ratio(interval_str) | |||
if not ratio then | |||
return "Error: Invalid interval format (use format like '3/2')" | |||
end | |||
-- Calculate approximations | |||
local results = calculate_edo_approximations(ratio, tolerance, min_edo, max_edo) | |||
if #results == 0 then | |||
return "No EDOs found within tolerance of " .. tolerance .. "%" | |||
end | |||
-- Build the wikitable | |||
local output = {} | |||
table.insert(output, '{| class="wikitable"') | |||
table.insert(output, '|+ EDO Approximations for ' .. interval_str) | |||
table.insert(output, '|-') | |||
table.insert(output, '! EDO !! Step size !! Absolute Error ([[Cent|¢]]) !! [[Relative_interval_error|Relative Error]] ([[Relative_cent|%]])') | |||
for _, result in ipairs(results) do | |||
local edo_link = string.format("[[%dedo|%d]]", result.edo, result.edo) | |||
local step_size = string.format("%d\\%d", result.steps, result.edo) | |||
local abs_err = format_error(result.abs_error) | |||
local rel_err = format_error(result.rel_error) | |||
table.insert(output, '|-') | |||
table.insert(output, string.format('| %s || %s || %s || %s', edo_link, step_size, abs_err, rel_err)) | |||
end | |||
table.insert(output, '|}') | |||
return table.concat(output, '\n') | |||
end | |||
return p | |||