Module:Interval edo approximation

Revision as of 05:13, 3 November 2025 by Pailiaq (talk | contribs) (trying to automate table generation (im learning how to wiki dont yell at me))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Module documentation[view] [edit] [history] [purge]
This module should not be invoked directly; use its corresponding template instead: Template:Interval edo approximation.


Introspection summary for Module:Interval edo approximation 
Functions provided (1)
Line Function Params
64 main (invokable) (frame)
Lua modules required (0)
Variable Module Functions used

No function descriptions were provided. The Lua code may have further information.


-- 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