Module:JI ratios: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
No edit summary
Ganaram inukshuk (talk | contribs)
No edit summary
Line 1: Line 1:
-- In-progress successor to Module:JI ratio finder
-- In-progress successor to Module:JI ratio finder
-- Template for handling multiple entry of JI ratios into a template.
-- 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 rat = require("Module:Rational")
local utils = require("Module:Utils")
local utils = require("Module:Utils")
local tip = require("Module:Template input parse")
p = {}
p = {}


Line 91: Line 93:
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- Sorts ratios by closeness to cent values.
function p.sort_by_closeness_to_cent_values(ratios, cent_values, tolerance)
function p.sort_by_closeness_to_cent_values(ratios, cent_values, tolerance)
local sorted_ratios = {}
local sorted_ratios = {}
Line 120: Line 123:


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
---------------------------- RATIO FILTER FUNCTIONS ----------------------------
------------------------- RATIO MULTI-SEARCH FUNCTIONS -------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- Filter ratios based on search params entered in. Each param is its own
-- function call.
function p.filter_by_search_params(ratios, search_params)
function p.filter_by_search_params(ratios, search_params)
local parsed = p.parse_search_params(search_params)
if parsed["Tenney Height"] ~= nil then
ratios = p.filter_by_tenney_height(ratios, parsed["Tenney Height"])
end
if parsed["Prime Limit"] ~= nil then
ratios = p.filter_by_prime_limit(ratios, parsed["Prime Limit"])
end
end
end


-- Parse search params.
function p.parse_search_params(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["Prime Limit"] ~= nil then
parsed["Prime Limit"] = tonumber(parsed["Prime Limit"])
end
return parsed
end
end


--------------------------------------------------------------------------------
---------------------------- RATIO FILTER FUNCTIONS ----------------------------
--------------------------------------------------------------------------------
-- Filter ratios by Tenney height.
function p.filter_by_tenney_height(ratios, tenney_height)
function p.filter_by_tenney_height(ratios, tenney_height)
local tenney_height = tenney_height or 8
local tenney_height = tenney_height or 8
Line 144: Line 173:
end
end


-- Filter ratios by prime limit.
function p.filter_by_prime_limit(ratios, prime_limit)
function p.filter_by_prime_limit(ratios, prime_limit)
local prime_limit = prime_limit or 41
local prime_limit = prime_limit or 41
Line 155: Line 185:
end
end
return filtered_ratios
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
end


Line 165: Line 206:
local add_links = add_links == true
local add_links = add_links == true
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 = add_links and string.format("[[%s]]", rat.as_ratio(ratios[1])) or rat.as_ratio(ratios[1])
for i = 2, #ratios do
for i = 2, #ratios do
Line 172: Line 214:
end
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()
function p.tester()
Line 177: Line 231:
ratios = p.filter_by_prime_limit(ratios, 5)
ratios = p.filter_by_prime_limit(ratios, 5)
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)
result = ""
for i = 1, #ratios do
result = result .. p.ratios_as_text(ratios[i]) .. "\n"
end
return result
return p.ratios_as_texts(ratios)
end
end


return p
return p

Revision as of 19:14, 29 August 2024

Module documentation[view] [edit] [history] [purge]
This module may be invoked by templates using its corresponding template Template:JI ratios, or used directly from other modules.
Module:JI ratios is a draft module. It is incomplete and may not be in active development. If possible, editors are encouraged to help with its development. In the meantime, editors should avoid using this module across the Xenharmonic Wiki, except for testing.
Introspection summary for Module:JI ratios 
Functions provided (0)
Line Function Params
Lua modules required (3)
Variable Module Functions used
rat Module:Rational new
cents
tenney_height
max_prime
as_ratio
tip Module:Template input parse parse_kv_pairs
utils Module:Utils _gcd

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

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

--------------------------------------------------------------------------------
--------------------------- 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 MULTI-SEARCH FUNCTIONS -------------------------
--------------------------------------------------------------------------------

-- Filter ratios based on search params entered in. Each param is its own
-- function call.
function p.filter_by_search_params(ratios, search_params)
	local parsed = p.parse_search_params(search_params)
	
	if parsed["Tenney Height"] ~= nil then
		ratios = p.filter_by_tenney_height(ratios, parsed["Tenney Height"])
	end
	
	if parsed["Prime Limit"] ~= nil then
		ratios = p.filter_by_prime_limit(ratios, parsed["Prime Limit"])
	end
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["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 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 = 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
	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 ratios = p.search_by_int_limit(50)
	ratios = p.filter_by_prime_limit(ratios, 5)
	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