Module:Infobox ET: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Move special properties to the last, move octave before fifth. Add "dual" marker to sharp and flat fifths
Category sorting of "prime edo"
Line 63: Line 63:
prime = " (prime)"
prime = " (prime)"
if rat.eq(et.equave, 2) then
if rat.eq(et.equave, 2) then
categories = categories .. '[[Category:Prime EDO]]'
categories = categories .. '[[Category:Prime EDO|' .. string.rep ('#', string.len (et.size)) .. ']]'
end
end
end
end

Revision as of 16:45, 22 January 2023

Module documentation[view] [edit] [history] [purge]
Note: Do not invoke this module directly; use the corresponding template instead: Template:Infobox ET.

This module automatically fills in information about a specified equal temperament tuning.


local p = {}
local i = require('Module:Interval')
local u = require('Module:Utils')
local rat = require('Module:Rational')
local l = require('Module:Limits')
local ET = require('Module:ET')
local infobox = require('Module:Infobox')

-- towards is one of: -1 (floor), 0 (nearest), 1 (ceil)
local function approximation(et, interval, towards, precomputed_approx)
	local approx = precomputed_approx or ET.approximate(et, interval, towards or 0)
	
	local tuning = et.size
	if not rat.eq(et.equave, 2) then
		tuning = tuning .. et.suffix
	end
	
	local ratio = rat.new(approx, et.size)
	
	local convergement_notice = ''
	local converges = rat.converges(ratio, math.log(interval) / math.log(rat.as_float(et.equave)))
	if converges then
		convergement_notice = '<br/>(' .. converges .. ')'
	end
	
	if rat.as_table(ratio)[1] ~= approx then
		convergement_notice = ''
		local link = rat.as_table(ratio)[2] .. et.suffix
		ratio = ' (→[[' .. link .. '|' .. rat.as_ratio(ratio, '\\')
		if not rat.eq(et.equave, 2) then
			ratio = ratio .. et.suffix
		end
		ratio = ratio .. ']])'
	else
		ratio = ''
	end
	
	local cents = u._round(ET.cents(et, approx), 6)
	
	return approx .. '\\' .. tuning .. ' (' .. cents .. '¢)' .. ratio .. convergement_notice
end

function p.infobox_ET(frame)
	-- debug mode
	local debug_mode = frame.args['debug'] ~= nil
	local categories = ''
	
	local tuning = frame.args['tuning']
	local et = ET.parse(tuning) or ET.parse('12edo')
	
	-- category of the main article
	categories = categories .. '[[Category:' .. tuning .. '| ]]'
	-- category of the equal division
	if rat.eq(et.equave, 2) then
		categories = categories .. '[[Category:Equal divisions of the octave|' .. string.rep ('#', string.len (et.size)) .. ']]'
	else
		categories = categories .. '[[Category:' .. et.suffix .. '|' .. string.rep ('#', string.len (et.size)) .. ']]'
	end
	
	-- prime test
	local prime = ""
	if u.is_prime(et.size) then
		prime = " (prime)"
		if rat.eq(et.equave, 2) then
			categories = categories .. '[[Category:Prime EDO|' .. string.rep ('#', string.len (et.size)) .. ']]'
		end
	end
	
	-- zeta test
	local zeta_override = frame.args['Zeta']
	local zeta_switch = nil
	if type(zeta_override) == 'string' and #zeta_override > 0 then
		zeta_switch = not zeta_override:match('^[Nn][Oo]$')
	else
		zeta_switch = rat.eq(et.equave, 2) and ET.is_zeta(et)
	end
	
	local prev_one = ''
	if et.size > 0 then
		prev_one = '[[' .. (et.size - 1) .. et.suffix .. '|← ' .. (et.size - 1) .. et.suffix .. ']]'
	end
	local next_one = '[[' .. (et.size + 1) .. et.suffix .. '|' .. (et.size + 1) .. et.suffix .. ' →]]'
	
	local step_size = ET.cents(et, 1)
	local note_12edo = ''
	if rat.eq(et.equave, 2) and et.size == 12 then
		note_12edo = '<sup>by definition</sup>'
	end
	
	local octave = ET.approximate(et, 2)
	local twelfth = ET.approximate(et, 3)
	
	local fifth = -octave + twelfth -- 3/2 = [-1 1>
	local fifth_error = ET.cents(et, fifth) - i._to_cents(3/2)
	local dual_fifth = math.abs(fifth_error) > step_size / 3

	local A1 = -11 * octave + 7 * twelfth -- 2187/2048 = [-11 7>
	local m2 = 8 * octave - 5 * twelfth -- 256/243 = [8 -5>
	local A1_cents = u._round(ET.cents(et, A1), 4)
	local m2_cents = u._round(ET.cents(et, m2), 4)

	local infobox_data = {}
	table.insert(infobox_data, {
		'Prime factorization',
		u._prime_factorization(et.size) .. prime
	})

	table.insert(infobox_data, {
		'Step size',
		u._round(step_size, 6) .. '¢' .. note_12edo
	})

	if not rat.eq(et.equave, 2) then
		table.insert(infobox_data, {
			'Octave',
			approximation(et, 2)
		})
	end
	
	if not rat.eq(et.equave, rat.new(3, 2)) then
		table.insert(infobox_data, {
			'Fifth',
			approximation(et, 3/2)
		})
	end

	table.insert(infobox_data, {
		'Semitones (A1:m2)',
		A1 .. ':' .. m2 .. ' (' .. A1_cents .. '¢ : ' .. m2_cents .. '¢)'
	})

	if dual_fifth and et.size > 0 then
		table.insert(infobox_data, {
			'Dual sharp fifth',
			approximation(et, 3/2, 1)
		})
		table.insert(infobox_data, {
			'Dual flat fifth',
			approximation(et, 3/2, -1)
		})
		local sharp = ET.approximate(et, 3/2, 1)
		local flat = ET.approximate(et, 3/2, -1)
		table.insert(infobox_data, {
			'Dual major 2nd',
			approximation(et, 9/8, 0, sharp + flat - octave)
		})
	end
	
	local consistency = tonumber(frame.args['Consistency'])
	if consistency == nil then
		consistency = l.consistency_limit(et, false, 43)
	end
	if consistency == nil then
		consistency = 'at least 43'
	end
	if consistency ~= nil then
		table.insert(infobox_data, {
			'Consistency limit',
			consistency
		})
	end
	local distinct_consistency = tonumber(frame.args['Distinct consistency'])
	if distinct_consistency == nil then
		distinct_consistency = l.consistency_limit(et, consistency or true, 43)
	end
	if distinct_consistency == nil then
		distinct_consistency = 'at least 43'
	end
	if distinct_consistency ~= nil then
		table.insert(infobox_data, {
			'Distinct consistency limit',
			distinct_consistency
		})
	end

	-- special properties
	if ET.is_highly_composite(et) or zeta_switch then
		local text = ''
		if ET.is_highly_composite(et) then
			text = text .. '[[Highly composite equal division|highly composite]]'
			if rat.eq(et.equave, 2) then
				categories = categories .. '[[Category:Highly composite EDO|' .. string.rep ('#', string.len (et.size)) .. ']]'
			end
		end
		if zeta_switch then
			if #text > 0 then text = text .. '<br>' end
			if not (type(zeta_override) == 'string' and #zeta_override > 0) then
				text = text .. ET.why_zeta(et)
			else
				text = text .. zeta_override
			end
			categories = categories .. '[[Category:Zeta|' .. string.rep ('#', string.len (et.size)) .. ']]'
		end
		table.insert(infobox_data, {
			'Special properties',
			'<div style="max-width: 270px;">' .. text .. '</div>'
		})
	end

	result = infobox.build(
		'[[' .. et.suffix .. '|' .. tuning .. ']]',
		infobox_data,
		prev_one,
		next_one
	)
	if not debug_mode then
		result = result .. categories
	end
	return result
end

return p