Module:JI ratios in ED: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
Denominator limit -> integer limit; tolerance will max out at +/- 40 cents
Ganaram inukshuk (talk | contribs)
Cleanup code
Line 24: Line 24:


-- Helper function
-- Helper function
-- Converts a set of primes into a subgroup
-- Converts a set of primes (typically a prime subgroup) into text
function p.primes_as_subgroup(primes)
function p.subgroup_as_text(primes)
local primes = primes or { 2, 3, 5, 7 }
local primes = primes or { 2, 3, 5, 7 }
Line 40: Line 40:


-- Helper function
-- Helper function
-- Given a set of primes, find its temperament interpretation; IE, is it a
-- Creates the non-header cells in the table
-- prime subgroup temperament (EG, 2.3.7) or a prime-limit temperament (EG,
function p.ji_ratio_in_ed_content_cells(steps, equave, candidate_ratios, primes)
-- 7-limit)?
local steps = steps or 12
-- If the set of primes is a prime limit, then it should contain all the primes
local equave = equave or rat.new(2)
-- between 2 (inclusive) and the largest prime (inclusive). If it skips any
local candidate_ratios = candidate_ratios or { rat.new(1), rat.new(2), rat.new(3, 2), rat.new(4, 3), rat.new (5, 4), rat.new (9, 7)}
-- primes, then it's a subgroup.
local primes = primes or { 2, 3, 7 }
function p.primes_as_temperament_interpretation(primes)
local primes = primes or { 2, 3, 5, 7 }
-- Calculate equave and tolerance
local primes_to_compare = p.primes_within_prime_limit(primes[#primes])
-- Tolerance is +/- 40 cents or 40% of a step, whichever is smaller.
local equave_in_cents = rat.cents(equave)
local tolerance = math.min(equave_in_cents / steps * 0.4, 40)
local temperament_interpretation = ""
-- Build the rows for each step, showing ratios by limit
if p.primes_as_subgroup(primes) == p.primes_as_subgroup(primes_to_compare) then
local result = ""
temperament_interpretation = string.format("%d-limit", primes[#primes])
for i = 1, steps + 1 do
else
local step = i - 1
temperament_interpretation = p.primes_as_subgroup(primes) .. " subgroup"
-- Table headers
local step_in_cents = (step / steps) * rat.cents(equave)
result = result .. string.format('| %d\n', step)
result = result .. string.format('| %.3f\n', step_in_cents)
-- Filter ratios by whether they're within the current step
local filtered_ratios = jiraf.filter_ratios_by_range(candidate_ratios, step_in_cents - tolerance, step_in_cents + tolerance)
-- Add ratios according to harmonic class
for j = 1, #primes do
local current_prime = primes[j]
-- For each prime, filter by harmonic class
-- For the first column, filter by prime limit instead
local prime_filtered_ratios = {}
if j == 1 then
prime_filtered_ratios = jiraf.filter_ratios_by_prime_limit(filtered_ratios, primes[j])
else
prime_filtered_ratios = jiraf.filter_ratios_by_harmonic_class(filtered_ratios, primes[j])
end
 
local ratios_as_text = jiraf.ratios_to_text_with_error(prime_filtered_ratios, step_in_cents, "<br>", true)
result = result .. string.format('| %s\n', ratios_as_text)
end
result = result .. string.format('|-\n')
end
end
return result
return temperament_interpretation
end
end


-- Primary function
-- Main function variant for prime limit
function p.find_ratios_in_ed(input_et, primes, tenney_height, int_limit)
function p.ji_ratios_in_ed_by_prime_limit(input_et, int_limit, prime_limit, tenney_height)
local input_et = input_et or et.parse("12ed5/2")
local input_et = input_et or et.parse("12edo")
local primes = primes or { 2, 3, 5, 7 }
local prime_limit = 7
local tenney_height = tenney_height or 10
local tenney_height = tenney_height or 10
local int_limit = int_limit or 99
local int_limit = int_limit or 99
Line 79: Line 106:
local equave_in_cents = rat.cents(equave)
local equave_in_cents = rat.cents(equave)
local tolerance = math.min(equave_in_cents / steps * 0.4, 40)
local tolerance = math.min(equave_in_cents / steps * 0.4, 40)
-- Calculate candidate ratios
-- Then filter based on whether their complements exceed the int limit
-- Then filter candidate ratios by tenney height (excludes equave factors)
local candidate_ratios = jiraf.find_candidate_ratios_within_prime_limit(equave_in_cents, int_limit, prime_limit)
candidate_ratios = jiraf.filter_ratios_by_equave_complement_int_limit(candidate_ratios, int_limit, equave)
candidate_ratios = jiraf.filter_ratios_by_no_equave_factors_tenney_height(candidate_ratios, tenney_height, equave)
-- Calculate temperament interpretation
-- Get the primes
local temperament_interpretation = p.primes_as_temperament_interpretation(primes)
local primes = p.primes_within_prime_limit(prime_limit)
-- Find candidate ratios; filter later
local max_prime = primes[#primes]
local candidate_ratios = jiraf.find_candidate_ratios_within_subgroup(equave_in_cents, int_limit, primes)
-- Build table headers
-- Build table headers
local result = string.format('{| class="wikitable center-all"\n')
local result = string.format('{| class="wikitable center-all"\n')
result = result .. string.format('|+ Intervals of %s (as a %s temperament)\n', et_as_string, temperament_interpretation)
result = result .. string.format('|+ Intervals of %s (as a %d-limit temperament)\n', et_as_string, prime_limit)
result = result .. string.format('|-\n')
result = result .. string.format('|-\n')
result = result .. string.format('! rowspan="2" | [[Degree]]\n')
result = result .. string.format('! rowspan="2" | [[Degree]]\n')
Line 96: Line 126:
result = result .. string.format('|-\n')
result = result .. string.format('|-\n')
-- Add table headers for prime limits (technically harmonic classes)
-- Add rest of table
for i = 1, #primes do
result = result .. p.ji_ratio_in_ed_content_cells(steps, equave, candidate_ratios, primes)
local current_prime = primes[i]
result = result .. string.format('! [[%d-limit]]\n', current_prime)
result = result .. string.format('|}\n')
return result
end
 
-- Main function variant for prime subgroup
function p.ji_ratios_in_ed_by_prime_subgroup(input_et, int_limit, primes, tenney_height)
local input_et = input_et or et.parse("12edo")
local primes = primes or { 2, 3, 5, 11 }
local tenney_height = tenney_height or 10
local int_limit = int_limit or 99
-- Get the number of divisions, equave, and et as text (eg edo, edt, etc)
local steps = input_et['size']
local equave = input_et['equave']
if tonumber(equave) ~= nil then
equave = rat.new(equave)
end
end
local et_as_string = et.as_string(input_et)
-- Calculate equave and tolerance
-- Tolerance is +/- 40 cents or 40% of a step, whichever is smaller.
local equave_in_cents = rat.cents(equave)
local tolerance = math.min(equave_in_cents / steps * 0.4, 40)
-- Calculate candidate ratios
-- Then filter based on whether their complements exceed the int limit
-- Then filter candidate ratios by tenney height (excludes equave factors)
local candidate_ratios = jiraf.find_candidate_ratios_within_subgroup(equave_in_cents, int_limit, primes)
candidate_ratios = jiraf.filter_ratios_by_equave_complement_int_limit(candidate_ratios, int_limit, equave)
candidate_ratios = jiraf.filter_ratios_by_no_equave_factors_tenney_height(candidate_ratios, tenney_height, equave)
-- Get subgroup as text
local subgroup_as_text = p.subgroup_as_text(primes)
-- Build table headers
local result = string.format('{| class="wikitable center-all"\n')
result = result .. string.format('|+ Intervals of %s (as a %s subgroup temperament)\n', et_as_string, subgroup_as_text)
result = result .. string.format('|-\n')
result = result .. string.format('! rowspan="2" | [[Degree]]\n')
result = result .. string.format('! rowspan="2" | [[Cent]]s\n')
result = result .. string.format('! colspan="%d" | Approximated [[JI]] intervals ([[error]] in cents)\n', #primes)
result = result .. string.format('|-\n')
result = result .. string.format('|-\n')
-- Build the rows for each step, showing ratios by limit
-- Add rest of table
for i = 1, steps + 1 do
result = result .. p.ji_ratio_in_ed_content_cells(steps, equave, candidate_ratios, primes)
local step = i - 1
-- Table headers
local step_in_cents = (step / steps) * rat.cents(equave)
result = result .. string.format('| %d\n', step)
result = result .. string.format('| %.3f\n', step_in_cents)
filtered_ratios = jiraf.filter_ratios_by_range(candidate_ratios, step_in_cents - tolerance, step_in_cents + tolerance)
-- Add ratios according to harmonic class
for j = 1, #primes do
local current_prime = primes[j]
local prime_filtered_ratios = {}
-- Override filtered ratios denpending on whether the ratio is the
-- unison or equave
if step == 0 and j == 1 then
prime_filtered_ratios = { rat.new(1) }
elseif step == 0 and j ~= 1 then
prime_filtered_ratios = {}
elseif step == steps then
prime_filtered_ratios = jiraf.filter_ratios_by_harmonic_class({ equave }, current_prime)
else
-- Filter ratios by harmonic class, then whether the ratios'
-- equave complement exceeds the int limit, then by
-- no-equave-factors tenney height (nefth).
prime_filtered_ratios = jiraf.filter_ratios_by_harmonic_class(filtered_ratios, current_prime)
prime_filtered_ratios = jiraf.filter_ratios_by_equave_complement_int_limit(prime_filtered_ratios, int_limit, equave)
prime_filtered_ratios = jiraf.filter_ratios_by_no_equave_factors_tenney_height(prime_filtered_ratios, tenney_height, equave)
end
-- Add ratios to cells
-- TODO: make first column go by prime limit (this way, the unison
-- is not excluded), with successive columns by harmonic class.
local ratios_as_text = jiraf.ratios_to_text_with_error(prime_filtered_ratios, step_in_cents, "<br>", true)
result = result .. string.format('| %s\n', ratios_as_text)
end
result = result .. string.format('|-\n')
end
result = result .. string.format('|}\n')
result = result .. string.format('|}\n')
Line 159: Line 188:
end
end
local input_et = et.parse(input_et_unparsed)
local input_et = et.parse(input_et_unparsed)
local tenney_height = tonumber(frame.args["Tenney Height"]) or 10
local int_limit = tonumber(frame.args["Integer Limit"]) or 99


local primes = { 2, 3, 5, 7 }
local primes = nil
local temperament_type = ""
if string.len(frame.args["Subgroup"]) > 0 then  
if string.len(frame.args["Subgroup"]) > 0 then  
primes = tip.parse_numeric_entries(frame.args["Subgroup"], '.') or tip.parse_numeric_entries(frame.args["Subgroup"], ',')
primes = tip.parse_numeric_entries(frame.args["Subgroup"], '.') or tip.parse_numeric_entries(frame.args["Subgroup"], ',')
temperament_type = "Subgroup"
end
end
if string.len(frame.args["Prime Limit"]) > 0 and string.len(frame.args["Subgroup"]) == 0 then
if string.len(frame.args["Prime Limit"]) > 0 and string.len(frame.args["Subgroup"]) == 0 then
primes = p.primes_within_prime_limit(tonumber(frame.args["Prime Limit"]))
primes = tonumber(frame.args["Prime Limit"])
temperament_type = "Prime Limit"
end
end
local tenney_height = tonumber(frame.args["Tenney Height"]) or 10
local result = ""
local int_limit = tonumber(frame.args["Integer Limit"]) or 99
if temperament_type == "Subgroup" then
result = p.find_ratios_in_ed_by_subgroup(input_et, primes, tenney_height, int_limit)
local result = p.find_ratios_in_ed(input_et, primes, tenney_height, int_limit)
elseif temperament_type == "Prime Limit" then
result = p.find_ratios_in_ed_by_subgroup(input_et, primes, tenney_height, int_limit)
end
return result
return result

Revision as of 06:32, 27 January 2024

Module documentation[view] [edit] [history] [purge]
This module should not be invoked directly; use its corresponding template instead: Template:JI ratios in ED.
Module:JI ratios in ED is deprecated and has no replacement. Further use of this module is not advised. This module is kept for historical purposes and should not be deleted.
Introspection summary for Module:JI ratios in ED 
Functions provided (6)
Line Function Params
13 primes_within_prime_limit (prime_limit)
27 subgroup_as_text (primes)
43 ji_ratio_in_ed_content_cells (steps, equave, candidate_ratios, primes)
90 ji_ratios_in_ed_by_prime_limit (input_et, int_limit, prime_limit, tenney_height)
136 ji_ratios_in_ed_by_prime_subgroup (input_et, int_limit, primes, tenney_height)
182 ji_ratios_in_ed_frame (invokable) (frame)
Lua modules required (6)
Variable Module Functions used
et Module:ET parse
as_string
interval Module:Interval dependency not used
jiraf Module:JI ratio finder filter_ratios_by_range
filter_ratios_by_prime_limit
filter_ratios_by_harmonic_class
ratios_to_text_with_error
find_candidate_ratios_within_prime_limit
filter_ratios_by_equave_complement_int_limit
filter_ratios_by_no_equave_factors_tenney_height
find_candidate_ratios_within_subgroup
rat Module:Rational new
cents
tip Module:Template input parse parse_numeric_entries
utils Module:Utils is_prime

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


local utils = require('Module:Utils')
local interval = require('Module:Interval')
local rat = require('Module:Rational')
local jiraf = require('Module:JI ratio finder')
local tip = require('Module:Template input parse')
local et = require('Module:ET')
local p = {}

-- Helper function
-- Given a prime limit, return an array of all primes between (and including) 2
-- and that limit
-- EG, 7-limit becomes { 2, 3, 5, 7 }
function p.primes_within_prime_limit(prime_limit)
	local prime_limit = prime_limit or 7
	
	local primes = {}
	for i = 2, prime_limit do
		if utils.is_prime(i) then
			table.insert(primes, i)
		end
	end
	return primes
end

-- Helper function
-- Converts a set of primes (typically a prime subgroup) into text
function p.subgroup_as_text(primes)
	local primes = primes or { 2, 3, 5, 7 }
	
	local subgroup = ""
	for i = 1, #primes do
		if i ~= #primes then
			subgroup = subgroup .. string.format("%d.", primes[i])
		else
			subgroup = subgroup .. primes[i]
		end
	end
	return subgroup
end

-- Helper function
-- Creates the non-header cells in the table
function p.ji_ratio_in_ed_content_cells(steps, equave, candidate_ratios, primes)
	local steps = steps or 12
	local equave = equave or rat.new(2)
	local candidate_ratios = candidate_ratios or { rat.new(1), rat.new(2), rat.new(3, 2), rat.new(4, 3), rat.new (5, 4), rat.new (9, 7)}
	local primes = primes or { 2, 3, 7 }
	
	-- Calculate equave and tolerance
	-- Tolerance is +/- 40 cents or 40% of a step, whichever is smaller.
	local equave_in_cents = rat.cents(equave)
	local tolerance = math.min(equave_in_cents / steps * 0.4, 40)
	
	-- Build the rows for each step, showing ratios by limit
	local result = ""
	for i = 1, steps + 1 do
		local step = i - 1
		
		-- Table headers
		local step_in_cents = (step / steps) * rat.cents(equave)
		result = result .. string.format('| %d\n', step)
		result = result .. string.format('| %.3f\n', step_in_cents)
		
		-- Filter ratios by whether they're within the current step
		local filtered_ratios = jiraf.filter_ratios_by_range(candidate_ratios, step_in_cents - tolerance, step_in_cents + tolerance)
		
		-- Add ratios according to harmonic class
		for j = 1, #primes do
			local current_prime = primes[j]
			
			-- For each prime, filter by harmonic class
			-- For the first column, filter by prime limit instead
			local prime_filtered_ratios = {}
			if j == 1 then
				prime_filtered_ratios = jiraf.filter_ratios_by_prime_limit(filtered_ratios, primes[j])
			else
				prime_filtered_ratios = jiraf.filter_ratios_by_harmonic_class(filtered_ratios, primes[j])	
			end

			local ratios_as_text = jiraf.ratios_to_text_with_error(prime_filtered_ratios, step_in_cents, "<br>", true)
			result = result .. string.format('| %s\n', ratios_as_text)
		end
		
		result = result .. string.format('|-\n')
	end
	return result	
end

-- Main function variant for prime limit
function p.ji_ratios_in_ed_by_prime_limit(input_et, int_limit, prime_limit, tenney_height)
	local input_et = input_et or et.parse("12edo")
	local prime_limit = 7
	local tenney_height = tenney_height or 10
	local int_limit = int_limit or 99
	
	-- Get the number of divisions, equave, and et as text (eg edo, edt, etc)
	local steps = input_et['size']
	local equave = input_et['equave']
	if tonumber(equave) ~= nil then
		equave = rat.new(equave)
	end
	local et_as_string = et.as_string(input_et)
	
	-- Calculate equave and tolerance
	-- Tolerance is +/- 40 cents or 40% of a step, whichever is smaller.
	local equave_in_cents = rat.cents(equave)
	local tolerance = math.min(equave_in_cents / steps * 0.4, 40)

	-- Calculate candidate ratios
	-- Then filter based on whether their complements exceed the int limit
	-- Then filter candidate ratios by tenney height (excludes equave factors)
	local candidate_ratios = jiraf.find_candidate_ratios_within_prime_limit(equave_in_cents, int_limit, prime_limit)
	candidate_ratios = jiraf.filter_ratios_by_equave_complement_int_limit(candidate_ratios, int_limit, equave)
	candidate_ratios = jiraf.filter_ratios_by_no_equave_factors_tenney_height(candidate_ratios, tenney_height, equave)
	
	-- Get the primes
	local primes = p.primes_within_prime_limit(prime_limit)
	
	-- Build table headers
	local result = string.format('{| class="wikitable center-all"\n')
	result = result .. string.format('|+ Intervals of %s (as a %d-limit temperament)\n', et_as_string, prime_limit)
	result = result .. string.format('|-\n')
	result = result .. string.format('! rowspan="2" | [[Degree]]\n')
	result = result .. string.format('! rowspan="2" | [[Cent]]s\n')
	result = result .. string.format('! colspan="%d" | Approximated [[JI]] intervals ([[error]] in cents)\n', #primes)
	result = result .. string.format('|-\n')
	
	-- Add rest of table
	result = result .. p.ji_ratio_in_ed_content_cells(steps, equave, candidate_ratios, primes)
	
	result = result .. string.format('|}\n')
	return result
end

-- Main function variant for prime subgroup
function p.ji_ratios_in_ed_by_prime_subgroup(input_et, int_limit, primes, tenney_height)
	local input_et = input_et or et.parse("12edo")
	local primes = primes or { 2, 3, 5, 11 }
	local tenney_height = tenney_height or 10
	local int_limit = int_limit or 99
	
	-- Get the number of divisions, equave, and et as text (eg edo, edt, etc)
	local steps = input_et['size']
	local equave = input_et['equave']
	if tonumber(equave) ~= nil then
		equave = rat.new(equave)
	end
	local et_as_string = et.as_string(input_et)
	
	-- Calculate equave and tolerance
	-- Tolerance is +/- 40 cents or 40% of a step, whichever is smaller.
	local equave_in_cents = rat.cents(equave)
	local tolerance = math.min(equave_in_cents / steps * 0.4, 40)
	
	-- Calculate candidate ratios
	-- Then filter based on whether their complements exceed the int limit
	-- Then filter candidate ratios by tenney height (excludes equave factors)
	local candidate_ratios = jiraf.find_candidate_ratios_within_subgroup(equave_in_cents, int_limit, primes)
	candidate_ratios = jiraf.filter_ratios_by_equave_complement_int_limit(candidate_ratios, int_limit, equave)
	candidate_ratios = jiraf.filter_ratios_by_no_equave_factors_tenney_height(candidate_ratios, tenney_height, equave)
	
	-- Get subgroup as text
	local subgroup_as_text = p.subgroup_as_text(primes)
	
	-- Build table headers
	local result = string.format('{| class="wikitable center-all"\n')
	result = result .. string.format('|+ Intervals of %s (as a %s subgroup temperament)\n', et_as_string, subgroup_as_text)
	result = result .. string.format('|-\n')
	result = result .. string.format('! rowspan="2" | [[Degree]]\n')
	result = result .. string.format('! rowspan="2" | [[Cent]]s\n')
	result = result .. string.format('! colspan="%d" | Approximated [[JI]] intervals ([[error]] in cents)\n', #primes)
	result = result .. string.format('|-\n')
	
	-- Add rest of table
	result = result .. p.ji_ratio_in_ed_content_cells(steps, equave, candidate_ratios, primes)
	
	result = result .. string.format('|}\n')
	return result
end

-- Wrapper function for primary function; to be called by template
function p.ji_ratios_in_ed_frame(frame)
	
	-- Parse the ed; if it's just a number, interpret it as an edo
	local input_et_unparsed = frame.args["ED"]
	if tonumber(input_et_unparsed) ~= nil then
		input_et_unparsed = input_et_unparsed .. "edo"
	end
	local input_et = et.parse(input_et_unparsed)
	
	local tenney_height = tonumber(frame.args["Tenney Height"]) or 10
	local int_limit = tonumber(frame.args["Integer Limit"]) or 99

	local primes = nil
	local temperament_type = ""
	if string.len(frame.args["Subgroup"]) > 0 then 
		primes = tip.parse_numeric_entries(frame.args["Subgroup"], '.') or tip.parse_numeric_entries(frame.args["Subgroup"], ',')
		temperament_type = "Subgroup"
	end
	if string.len(frame.args["Prime Limit"]) > 0 and string.len(frame.args["Subgroup"]) == 0 then
		primes = tonumber(frame.args["Prime Limit"])
		temperament_type = "Prime Limit"
	end
	
	local result = ""
	if temperament_type == "Subgroup" then
		result = p.find_ratios_in_ed_by_subgroup(input_et, primes, tenney_height, int_limit)
	elseif temperament_type == "Prime Limit" then
		result = p.find_ratios_in_ed_by_subgroup(input_et, primes, tenney_height, int_limit)
	end
	
	return result
end

return p