Module:Mediants: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
m todo
Ganaram inukshuk (talk | contribs)
Changed filter functions to be two-param, where the first param is a table that has both the mediant and depth; may momentarily break dependent modules
Line 4: Line 4:


-- Module for finding mediants, either by search depth or by search function.
-- Module for finding mediants, either by search depth or by search function.
-- TODO
-- Make two classes of filter functions: one that returns both mediants AND
-- depths, and one that returns only mediants. Filter functions therefore come
-- in two forms: two params (mediant, args) and three params (mediant, depth,
-- args).


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Line 24: Line 18:
-- Int limit filter determines whether a ratio is within an int limit. Does not
-- Int limit filter determines whether a ratio is within an int limit. Does not
-- use depth.
-- use depth.
function p.int_limit_filter(mediant, depth, int_limit)
function p.int_limit_filter(mediant_data, int_limit)
local mediant = mediant_data["mediant"]
return math.max(mediant[1], mediant[2]) <= int_limit
return math.max(mediant[1], mediant[2]) <= int_limit
end
end
Line 30: Line 25:
-- Depth filter determines whether a ratio is within a target depth. Does not
-- Depth filter determines whether a ratio is within a target depth. Does not
-- use the mediant itself.
-- use the mediant itself.
function p.search_depth_filter(mediant, depth, search_depth)
function p.search_depth_filter(mediant_data, search_depth)
local depth = mediant_data["depth"]
return depth <= search_depth
return depth <= search_depth
end
end
Line 36: Line 32:
-- Tenney height filter determines whether a ratio is within a target Tenney
-- Tenney height filter determines whether a ratio is within a target Tenney
-- height. Does not use depth.
-- height. Does not use depth.
function p.tenney_height_filter(mediant, depth, tenney_height)
function p.tenney_height_filter(mediant_data, tenney_height)
local mediant = mediant_data["mediant"]
return math.log(mediant[1] * mediant[2]) / math.log(2) <= tenney_height
return math.log(mediant[1] * mediant[2]) / math.log(2) <= tenney_height
end
end
Line 78: Line 75:
table.insert(new_depths, depth_1)
table.insert(new_depths, depth_1)
if filter_func(mediant, new_depth, filter_args) then
local mediant_data = { ["mediant"] = mediant, ["depth"] = new_depth }
if filter_func(mediant_data, filter_args) then
table.insert(new_ratios, mediant)
table.insert(new_ratios, mediant)
table.insert(new_depths, new_depth)
table.insert(new_depths, new_depth)

Revision as of 21:45, 14 September 2024

Module documentation[view] [edit] [history] [purge]
This module primarily serves as a library for other modules and has no corresponding template.

Module:Mediants is used for finding mediants starting from a set of starting ratios (by default, 1/1 and 1/0), either by search depth, integer limit, or by a custom search function.

Introspection summary for Module:Mediants 
Functions provided (8)
Line Function Params
20 int_limit_filter (mediant_data, int_limit)
27 search_depth_filter (mediant_data, search_depth)
34 tenney_height_filter (mediant_data, tenney_height)
50 find_mediants_by_filter (init_ratios, filter_func, filter_args)
95 find_only_mediants_by_filter (init_ratios, filter_func, filter_args)
113 find_mediants (init_ratios, depth)
125 find_only_mediants (init_ratios, depth)
139 tester none
Lua modules required (2)
Variable Module Functions used
mos Module:MOS new
bright_gen_to_cents
utils Module:Utils _gcd

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


local mos = require("Module:MOS")			-- For testing
local utils = require("Module:Utils")		-- For testing
local p = {}

-- Module for finding mediants, either by search depth or by search function.

--------------------------------------------------------------------------------
------------------------------- FILTER FUNCTIONS -------------------------------
--------------------------------------------------------------------------------

-- Filter functions determine whether a mediant meets a specific criteria for
-- being added to a set of mediants, be it based on something about the mediant,
-- its search depth, or both.

-- A filter function has three params: mediant, depth, and a set of args, which
-- can be a single value or a table of values.

-- Int limit filter determines whether a ratio is within an int limit. Does not
-- use depth.
function p.int_limit_filter(mediant_data, int_limit)
	local mediant = mediant_data["mediant"]
	return math.max(mediant[1], mediant[2]) <= int_limit
end

-- Depth filter determines whether a ratio is within a target depth. Does not
-- use the mediant itself.
function p.search_depth_filter(mediant_data, search_depth)
	local depth = mediant_data["depth"]
	return depth <= search_depth
end

-- Tenney height filter determines whether a ratio is within a target Tenney
-- height. Does not use depth.
function p.tenney_height_filter(mediant_data, tenney_height)
	local mediant = mediant_data["mediant"]
	return math.log(mediant[1] * mediant[2]) / math.log(2) <= tenney_height
end

--------------------------------------------------------------------------------
---------------------------- GENERAL SEARCH FUNCTION ---------------------------
--------------------------------------------------------------------------------

-- General search function searches for mediants using a filter function. A
-- custom filter function can be passed in to "filter" out mediants. Ratios
-- are added using a while loop, which exits if a loop iteration adds no new
-- ratios.

-- Find mediants by filter, where the filter function and its args are passed in
-- as part of the function call.
function p.find_mediants_by_filter(init_ratios, filter_func, filter_args)
	local init_ratios = init_ratios or {{1,1}, {1,0}}
	
	local ratios = {}
	local depths = {}
	for i = 1, #init_ratios do
		table.insert(ratios, init_ratios[i])
		table.insert(depths, 0)
	end
	
	local new_ratios_added = true
	while new_ratios_added do
		new_ratios_added = false
		local new_ratios = {}
		local new_depths = {}
		
		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] }
			table.insert(new_ratios, ratio_1)
			
			local depth_1 = depths[i]
			local depth_2 = depths[i+1]
			local new_depth = math.max(depth_1, depth_2) + 1
			table.insert(new_depths, depth_1)
			
			local mediant_data = { ["mediant"] = mediant, ["depth"] = new_depth }
			if filter_func(mediant_data, filter_args) then
				table.insert(new_ratios, mediant)
				table.insert(new_depths, new_depth)
				new_ratios_added = true
			end
		end
		table.insert(new_ratios, ratios[#ratios])
		table.insert(new_depths, depths[#depths])
		
		ratios = new_ratios
		depths = new_depths
	end
	return ratios, depths
end

-- Find mediants by filter, where the filter function and its args are passed in
-- as part of the function call. Only returns mediants, not depths.
function p.find_only_mediants_by_filter(init_ratios, filter_func, filter_args)
	local init_ratios = init_ratios or {{1,1}, {1,0}}
	
	local ratios, depths
	ratios, depths = p.find_mediants_by_filter(init_ratios, filter_func, filter_args)
	return ratios
end

--------------------------------------------------------------------------------
------------------------- DEPTH-BASED SEARCH FUNCTION --------------------------
--------------------------------------------------------------------------------

-- Depth-based search finds mediants by building a tree of mediants up to a
-- specified depth. This is made a standalone function under the reasoning that
-- depth-based search is a common enough operation (EG, JI ratio search, tuning
-- spectrum step ratio search).

-- Find mediants by depth, how many times mediants are found in a set of ratios.
function p.find_mediants(init_ratios, depth)
	local init_ratios = init_ratios or {{1,1}, {1,0}}
	local depth = depth or 5

	local ratios, depths
	ratios, depths = p.find_mediants_by_filter(init_ratios, p.search_depth_filter, depth)
	
	return ratios, depths
end

-- Find mediants by depth, how many times mediants are found in a set of ratios.
-- Does not return depths.
function p.find_only_mediants(init_ratios, depth)
	local init_ratios = init_ratios or {{1,1}, {1,0}}
	local depth = depth or 5

	local ratios, depths
	ratios, depths = p.find_mediants_by_filter(init_ratios, p.search_depth_filter, depth)
	
	return ratios
end

--------------------------------------------------------------------------------
----------------------------------- TESTER -------------------------------------
--------------------------------------------------------------------------------

function p.tester()
	local func = p.int_limit_filter
	
	local ratios, depths = p.find_mediants_by_filter({{1,1}, {1,0}}, func, 12)
	ratios = p.find_mediants({{1,1}, {1,0}}, 6)
	local generators = {}
	for i = 1, #ratios do
		local input_mos = mos.new(5,2)
		local gen = mos.bright_gen_to_cents(input_mos, ratios[i])
		local gcd = utils._gcd(ratios[i][1], ratios[i][2])
		local edo = (ratios[i][1] * 5 + ratios[i][2] * 2)/gcd
		local new_string = string.format("%s:%s\t%sedo\t%.3f", ratios[i][1]/gcd, ratios[i][2]/gcd, edo, gen)
		table.insert(generators, new_string)
	end
	
	return generators
end

return p