Module:JI ratios: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| Line 14: | Line 14: | ||
-- 128 -> ~2500 ratios | -- 128 -> ~2500 ratios | ||
-- 100 -> ~1500 ratios | -- 100 -> ~1500 ratios | ||
local SEARCH_MAX = | local SEARCH_MAX = 150 | ||
local DEFAULT_INT_LIMIT = 50 | |||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
| Line 25: | Line 27: | ||
function p.search_by_int_limit(integer_limit, max_cents) | function p.search_by_int_limit(integer_limit, max_cents) | ||
local max_cents = max_cents or 1200 | local max_cents = max_cents or 1200 | ||
local integer_limit = integer_limit or | local integer_limit = integer_limit or DEFAULT_INT_LIMIT | ||
integer_limit = math.max(0, math.min(SEARCH_MAX, integer_limit)) | integer_limit = math.max(0, math.min(SEARCH_MAX, integer_limit)) | ||
| Line 89: | Line 91: | ||
end | end | ||
---- | -- Search for ratios based on params passed in. Each param is its own | ||
-- function call. Params must be parsed first. | |||
-- | function p.search_by_params(params, max_cents) | ||
local max_cents = max_cents or 1200 | |||
-- | |||
-- First get ratios up to an int limit. If no int limit was passed in, it | |||
-- will default to the hardcoded default value. | |||
local ratios = p.search_by_int_limit(params["Int Limit"], max_cents) | |||
if params["Prime Limit"] ~= nil then | |||
ratios = p.filter_by_prime_limit(ratios, params["Prime Limit"]) | |||
end | end | ||
if params["Tenney Height"] ~= nil then | |||
ratios = p.filter_by_tenney_height(ratios, params["Tenney Height"]) | |||
ratios = p.filter_by_tenney_height(ratios, | |||
end | end | ||
return ratios | |||
end | end | ||
| Line 143: | Line 114: | ||
function p.parse_search_params(search_params) | function p.parse_search_params(search_params) | ||
local parsed = tip.parse_kv_pairs(search_params) | local parsed = tip.parse_kv_pairs(search_params) | ||
if parsed["Tenney Height"] ~= nil then | |||
parsed["Tenney Height"] = tonumber(parsed["Tenney Height"]) | |||
end | |||
if parsed["Tenney Height"] ~= nil then | if parsed["Tenney Height"] ~= nil then | ||
| Line 196: | Line 171: | ||
function p.filter_by_nonprime_subgroup(ratios, subgroup) | function p.filter_by_nonprime_subgroup(ratios, subgroup) | ||
end | |||
-------------------------------------------------------------------------------- | |||
--------------------------- RATIO SORTING FUNCTIONS ---------------------------- | |||
-------------------------------------------------------------------------------- | |||
-- Sorts ratios by closeness to cent values. | |||
function p.sort_by_closeness_to_cent_values(ratios, cent_values, tolerance) | |||
local sorted_ratios = {} | |||
local curr_index = 1 -- Index of current_ratio | |||
for i = 1, #cent_values do | |||
local lower_bound = cent_values[i] - tolerance | |||
local upper_bound = cent_values[i] + tolerance | |||
local cents_within_range = true | |||
local curr_ratios = {} | |||
for j = curr_index, #ratios do | |||
local curr_ratio = ratios[j] | |||
local curr_cents = rat.cents(curr_ratio) | |||
if lower_bound < curr_cents and curr_cents < upper_bound then | |||
table.insert(curr_ratios, curr_ratio) | |||
elseif curr_cents > upper_bound then | |||
curr_index = j | |||
break | |||
end | |||
end | |||
table.insert(sorted_ratios, curr_ratios) | |||
end | |||
return sorted_ratios | |||
end | end | ||
| Line 207: | Line 215: | ||
local delimiter = delimiter or ", " | local delimiter = delimiter or ", " | ||
local text = add_links and string.format("[[%s]]", rat.as_ratio(ratios[1])) or rat.as_ratio(ratios[1]) | local text = "" | ||
if #ratios ~= 0 then | |||
text = add_links and string.format("[[%s]]", rat.as_ratio(ratios[1])) or rat.as_ratio(ratios[1]) | |||
for i = 2, #ratios do | |||
text = text .. (add_links and string.format("%s[[%s]]", delimiter, rat.as_ratio(ratios[i])) or string.format("%s%s", delimiter, rat.as_ratio(ratios[i]))) | |||
end | |||
end | end | ||
return text | return text | ||
| Line 228: | Line 239: | ||
function p.tester() | function p.tester() | ||
local | local params = p.parse_search_params("Prime Limit: 7; Tenney Height: 11") | ||
ratios = p. | ratios = p.search_by_params(params) | ||
ratios = p.sort_by_closeness_to_cent_values(ratios, {0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200}, 15) | ratios = p.sort_by_closeness_to_cent_values(ratios, {0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200}, 15) | ||
Revision as of 20:03, 29 August 2024
- This module may be invoked by templates using its corresponding template Template:JI ratios, or used directly from other modules.
| Introspection summary for Module:JI ratios | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||||||||
No function descriptions were provided. The Lua code may have further information.
-- In-progress successor to Module:JI ratio finder
-- Template for handling multiple entry of JI ratios into a template, and for
-- searching for JI ratios if automatic entry is desired.
local rat = require("Module:Rational")
local utils = require("Module:Utils")
local tip = require("Module:Template input parse")
p = {}
-- SEARCH_MAX is hardcoded to limit the size of output.
-- 400 -> ~24000 ratios
-- 300 -> ~14000 ratios
-- 200 -> ~6000 ratios
-- 150 -> ~3400 ratios
-- 128 -> ~2500 ratios
-- 100 -> ~1500 ratios
local SEARCH_MAX = 150
local DEFAULT_INT_LIMIT = 50
--------------------------------------------------------------------------------
---------------------------- RATIO SEARCH FUNCTIONS ----------------------------
--------------------------------------------------------------------------------
-- Find JI ratios up to an integer limit within the octave by finding mediants.
-- A cent value can be passed in to either exclude ratios that are above an
-- interval below the octave or include ratios above the octave.
function p.search_by_int_limit(integer_limit, max_cents)
local max_cents = max_cents or 1200
local integer_limit = integer_limit or DEFAULT_INT_LIMIT
integer_limit = math.max(0, math.min(SEARCH_MAX, integer_limit))
local ratios = {{1,1}, {2,1}}
-- Find ratios by finding the mediants between existing ratios, only adding
-- those that do not exceed the integer limit.
local new_ratios_added = true
while new_ratios_added do
new_ratios_added = false
local new_ratios = {}
for i = 1, #ratios-1 do
local ratio_1 = ratios[i]
local ratio_2 = ratios[i+1]
local mediant = {ratio_1[1]+ratio_2[1], ratio_1[2]+ratio_2[2]}
local int_max = math.max(mediant[1], mediant[2])
table.insert(new_ratios, ratios[i])
if int_max <= integer_limit then
new_ratios_added = true
table.insert(new_ratios, mediant)
end
end
table.insert(new_ratios, ratios[#ratios])
ratios = new_ratios
end
-- If the max cents is greater than the octave, duplicate all existing
-- ratios and raise them by the required number of octaves.
if max_cents > 1200 then
local new_ratios = {}
local num_octaves_up = math.ceil(max_cents / 1200)
for j = 1, num_octaves_up do
for i = 2, #ratios do
local num = ratios[i][1] * math.pow(2, j)
local den = ratios[i][2]
local gcd = utils._gcd(num, den)
num = num / gcd
den = den / gcd
if math.max(num, den) <= integer_limit then
table.insert(new_ratios, {num, den})
end
end
end
for i = 1, #new_ratios do
table.insert(ratios, new_ratios[i])
end
end
-- Remove any ratios that exceed the max cents
-- Convert to ratios that Module:Rational can work with
for i = 1, #ratios do
ratios[i] = rat.new(ratios[i][1], ratios[i][2])
end
return ratios
end
-- Search for ratios based on params passed in. Each param is its own
-- function call. Params must be parsed first.
function p.search_by_params(params, max_cents)
local max_cents = max_cents or 1200
-- First get ratios up to an int limit. If no int limit was passed in, it
-- will default to the hardcoded default value.
local ratios = p.search_by_int_limit(params["Int Limit"], max_cents)
if params["Prime Limit"] ~= nil then
ratios = p.filter_by_prime_limit(ratios, params["Prime Limit"])
end
if params["Tenney Height"] ~= nil then
ratios = p.filter_by_tenney_height(ratios, params["Tenney Height"])
end
return ratios
end
-- Parse search params.
function p.parse_search_params(search_params)
local parsed = tip.parse_kv_pairs(search_params)
if parsed["Tenney Height"] ~= nil then
parsed["Tenney Height"] = tonumber(parsed["Tenney Height"])
end
if parsed["Tenney Height"] ~= nil then
parsed["Tenney Height"] = tonumber(parsed["Tenney Height"])
end
if parsed["Prime Limit"] ~= nil then
parsed["Prime Limit"] = tonumber(parsed["Prime Limit"])
end
return parsed
end
--------------------------------------------------------------------------------
---------------------------- RATIO FILTER FUNCTIONS ----------------------------
--------------------------------------------------------------------------------
-- Filter ratios by Tenney height.
function p.filter_by_tenney_height(ratios, tenney_height)
local tenney_height = tenney_height or 8
local filtered_ratios = {}
for i = 1, #ratios do
local curr_tenney_height = rat.tenney_height(ratios[i])
if curr_tenney_height <= tenney_height then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
end
-- Filter ratios by prime limit.
function p.filter_by_prime_limit(ratios, prime_limit)
local prime_limit = prime_limit or 41
local filtered_ratios = {}
for i = 1, #ratios do
local curr_max_prime = rat.max_prime(ratios[i])
if curr_max_prime <= prime_limit then
table.insert(filtered_ratios, ratios[i])
end
end
return filtered_ratios
end
-- Filter ratios by (prime) subgroup. EG: 2.3.5.7
function p.filter_by_subgroup(ratios, subgroup)
end
-- Filter ratios by rational/nonprime subgroup. EG, 2.7/2.11/2, or 2.5.7.9
-- Does not support irrational subgroups.
function p.filter_by_nonprime_subgroup(ratios, subgroup)
end
--------------------------------------------------------------------------------
--------------------------- RATIO SORTING FUNCTIONS ----------------------------
--------------------------------------------------------------------------------
-- Sorts ratios by closeness to cent values.
function p.sort_by_closeness_to_cent_values(ratios, cent_values, tolerance)
local sorted_ratios = {}
local curr_index = 1 -- Index of current_ratio
for i = 1, #cent_values do
local lower_bound = cent_values[i] - tolerance
local upper_bound = cent_values[i] + tolerance
local cents_within_range = true
local curr_ratios = {}
for j = curr_index, #ratios do
local curr_ratio = ratios[j]
local curr_cents = rat.cents(curr_ratio)
if lower_bound < curr_cents and curr_cents < upper_bound then
table.insert(curr_ratios, curr_ratio)
elseif curr_cents > upper_bound then
curr_index = j
break
end
end
table.insert(sorted_ratios, curr_ratios)
end
return sorted_ratios
end
--------------------------------------------------------------------------------
---------------------------- RATIO STRING FUNCTIONS ----------------------------
--------------------------------------------------------------------------------
-- Convert a table of ratios into a string, with options for links and delimiter
function p.ratios_as_text(ratios, add_links, delimiter)
local add_links = add_links == true
local delimiter = delimiter or ", "
local text = ""
if #ratios ~= 0 then
text = add_links and string.format("[[%s]]", rat.as_ratio(ratios[1])) or rat.as_ratio(ratios[1])
for i = 2, #ratios do
text = text .. (add_links and string.format("%s[[%s]]", delimiter, rat.as_ratio(ratios[i])) or string.format("%s%s", delimiter, rat.as_ratio(ratios[i])))
end
end
return text
end
-- Convert a table of tables into a table of text
function p.ratios_as_texts(ratios, add_links, delimiter)
local add_links = add_links == true
local delimiter = delimiter or ", "
local texts = {}
for i = 1, #ratios do
local text = p.ratios_as_text(ratios[i], add_links, delimiter)
table.insert(texts, text)
end
return texts
end
function p.tester()
local params = p.parse_search_params("Prime Limit: 7; Tenney Height: 11")
ratios = p.search_by_params(params)
ratios = p.sort_by_closeness_to_cent_values(ratios, {0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200}, 15)
return p.ratios_as_texts(ratios)
end
return p