Module:Infobox ET: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Try automating some categories and internal refactoring using octave and twelfth
Try automating the category of the main article and the equal division
Line 48: Line 48:
local tuning = frame.args['tuning']
local tuning = frame.args['tuning']
local et = ET.parse(tuning) or ET.parse('12edo')
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
local equave_text = ET.common_suffix[et.equave]
if equave_text == nil then
equave_text = et.equave
end
categories = categories .. '[[Category:Ed' .. equave_text .. '|' .. string.rep ('#', string.len (et.size)) .. ']]'
end
local prime = ""
local prime = ""

Revision as of 17:56, 29 December 2022

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
		local equave_text = ET.common_suffix[et.equave]
		if equave_text == nil then
			equave_text = et.equave
		end
		categories = categories .. '[[Category:Ed' .. equave_text .. '|' .. string.rep ('#', string.len (et.size)) .. ']]'
	end
	
	local prime = ""
	if u.is_prime(et.size) then
		prime = " (prime)"
		if rat.eq(et.equave, 2) then
			categories = categories .. '[[Category:Prime EDO]]'
		end
	end
	
	local zeta = frame.args['Zeta']
	local is_zeta = nil
	if type(zeta) == 'string' and #zeta > 0 then
		is_zeta = not zeta:match('^[Nn][Oo]$')
	end
	if not rat.eq(et.equave, 2) then
		is_zeta = false
	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
	})
	if ET.is_highly_composite(et) or (is_zeta ~= false and ET.is_zeta(et)) 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]]'
			end
		end
		if is_zeta ~= false and ET.is_zeta(et) then
			if #text > 0 then text = text .. '<br>' end
			if type(zeta) ~= 'string' or #zeta == 0 then
				text = text .. ET.why_zeta(et)
			else
				text = text .. zeta
			end
		end
		table.insert(infobox_data, {
			'Special properties',
			'<div style="max-width: 270px;">' .. text .. '</div>'
		})
	end
	table.insert(infobox_data, {
		'Step size',
		u._round(step_size, 6) .. '¢' .. note_12edo
	})
	if not rat.eq(et.equave, rat.new(3, 2)) then
		table.insert(infobox_data, {
			'Fifth',
			approximation(et, 3/2)
		})
	end
	if not rat.eq(et.equave, 2) then
		table.insert(infobox_data, {
			'Octave',
			approximation(et, 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, {
			'Sharp fifth',
			approximation(et, 3/2, 1)
		})
		table.insert(infobox_data, {
			'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, {
			'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

	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