Module:JI ratios

Revision as of 00:44, 29 August 2024 by Ganaram inukshuk (talk | contribs) (Created page with "-- In-progress successor to Module:JI ratio finder local rat = require("Module:Rational") local utils = require("Module:Utils") p = {} -- SEARCH_MAX is hardcoded to be 120 to...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
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 (2)
Variable Module Functions used
rat Module:Rational dependency not used
utils Module:Utils _gcd
log2

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


-- In-progress successor to Module:JI ratio finder
local rat = require("Module:Rational")
local utils = require("Module:Utils")
p = {}

-- SEARCH_MAX is hardcoded to be 120 to limit the size of output.
-- 400 -> ~24000 ratios
-- 300 -> ~14000 ratios
-- 200 -> ~6000 ratios
-- 150 -> ~3400 ratios
-- 100 -> ~1500 ratios
local SEARCH_MAX = 150

--------------------------------------------------------------------------------
---------------------------- 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 30
	
	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 METRIC FUNCTIONS ----------------------------
--------------------------------------------------------------------------------

function p.tenney_height(ratio)
	return utils.log2(ratio[1] * ratio[2])
end

--------------------------------------------------------------------------------
---------------------------- RATIO FILTER FUNCTIONS ----------------------------
--------------------------------------------------------------------------------

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 = p.tenney_height(ratios[i])
		if curr_tenney_height <= tenney_height then
			table.insert(filtered_ratios, ratios[i])
		end
	end
	return filtered_ratios
end

function p.tester()
	local ratios = p.search_by_int_limit(400)
	ratios = p.filter_by_tenney_height(ratios)
	return ratios
end

return p