Module:Harmonics in equal: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Default number of columns set to 11. Cut off is now at 31 for prime, 23 for odd, and 12 for integer
Enable collapsing
Line 18: Line 18:
end
end


local function approx(steps, num, denom, intervals, title, prec, reduction)
local function approx(steps, num, denom, intervals, title, prec, reduction, collapsed)
local thead
if collapsed then
thead = '{| class="wikitable center-all mw-collapsible mw-collapsed"\n'
else
thead = '{| class="wikitable center-all"\n'
end
local tpri = {'! colspan="2" | Harmonic '}
local tpri = {'! colspan="2" | Harmonic '}
local tabs = {'! rowspan="2" | Error \n! absolute ([[cent|¢]]) '}
local tabs = {'! rowspan="2" | Error \n! absolute ([[cent|¢]]) '}
local trel = {'! [[Relative interval error|relative]] (%) '}
local trel = {'! [[Relative interval error|relative]] (%) '}
local tdeg = {'! colspan="2" | Step'}
local tdeg
if reduction then
if reduction then
tdeg = {'! colspan="2" | Steps<br>([[Octave_reduction|reduced]])'}
tdeg = {'! colspan="2" | Steps<br>([[Octave reduction|reduced]])'}
else
tdeg = {'! colspan="2" | Step'}
end
end
local fmt_abs = string.format(' %%+.%df', prec)
local fmt_abs = string.format(' %%+.%df', prec)
Line 44: Line 52:
local titleMarkup = ''
local titleMarkup = ''
if title then
if title then
titleMarkup = '|-\n|+ ' .. title .. '\n'
titleMarkup = '|-\n|+style=white-space:nowrap| ' .. title .. '\n'
end
end
return '{| class="wikitable center-all"\n' ..
return thead ..
titleMarkup ..
titleMarkup ..
'|-\n' ..
'|-\n' ..
Line 99: Line 107:
-- option intervals
-- option intervals
local select_intervals = "integer"
local select_intervals = "integer"
 


local name = steps .. 'ed' .. num .. '/' .. denom
local name = steps .. 'ed' .. num .. '/' .. denom
Line 142: Line 150:
reduction = false
reduction = false
end
end
return approx( steps, num, denom, {unpack(intervals[select_intervals], start, start+columns-1)}, title, prec, reduction)
local collapsed = frame.args['collapsed']
return approx( steps, num, denom, {unpack(intervals[select_intervals], start, start+columns-1)}, title, prec, reduction, collapsed)
end
end


return p;
return p;

Revision as of 11:01, 7 April 2023

Module documentation[view] [edit] [history] [purge]
This module should not be invoked directly; use its corresponding template instead: Template:Harmonics in equal.

Calculates approximations for harmonics in equal-step tunings and present them in form of a table.

Introspection summary for Module:Harmonics in equal 
Functions provided (1)
Line Function Params
96 harmonics_in_equal (invokable) (frame)
Lua modules required (0)
Variable Module Functions used

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


local p = {}

-- direct mapping
local function map(p, steps, num, denom)
	local s = math.log(p) / math.log(num/denom)
	return math.floor(s*steps + .5)
end
-- check consistency for 9, 15 and 21
local function check_consistency(steps, num, denom)
	local p3 = map(3, steps, num, denom)
	local p5 = map(5, steps, num, denom)
	local p7 = map(7, steps, num, denom)
	local p9 = map(9, steps, num, denom)
	local p15 = map(15, steps, num, denom)
	local p21 = map(21, steps, num, denom)
	
	return (p9 == 2*p3) and (p15 == p3+p5) and (p21 == p3+p7)
end

local function approx(steps, num, denom, intervals, title, prec, reduction, collapsed)
	local thead
	if collapsed then
		thead = '{| class="wikitable center-all mw-collapsible mw-collapsed"\n'
	else
		thead = '{| class="wikitable center-all"\n'
	end
	local tpri = {'! colspan="2" | Harmonic '}
	local tabs = {'! rowspan="2" | Error \n! absolute ([[cent|¢]]) '}
	local trel = {'! [[Relative interval error|relative]] (%) '}
	local tdeg
	if reduction then
		tdeg = {'! colspan="2" | Steps<br>([[Octave reduction|reduced]])'}
	else
		tdeg = {'! colspan="2" | Step'}
	end
	local fmt_abs = string.format(' %%+.%df', prec)
	local fmt_rel = ' %+.0f'
	local equave = math.log(num/denom) / math.log(2)
	for _, p in pairs(intervals) do
		s = math.log(p) / math.log(num/denom)
		v = s*steps
		ev = math.floor(v + .5)
		table.insert(tpri, '' .. p)
		table.insert(tabs, string.format(fmt_abs, 1200 * equave * (ev - v ) / steps))
		table.insert(trel, string.format(fmt_rel, 100 * (ev - v)))
		if reduction then
			table.insert(tdeg, '' .. ev .. '<br>('.. ev % steps .. ')')
		else
			table.insert(tdeg, '' .. ev)
		end
	end
	local titleMarkup = ''
	if title then
		titleMarkup = '|-\n|+style=white-space:nowrap| ' .. title .. '\n'
	end
	
	return thead ..
		titleMarkup ..
		'|-\n' ..
		table.concat(tpri, '\n!') .. '\n' ..
		'|-\n' ..
		table.concat(tabs, '\n|') .. '\n' ..
		'|-\n' ..
		table.concat(trel, '\n|') .. '\n' ..
		'|-\n' ..
	table.concat(tdeg, '\n|') .. '\n' ..
		'|}'
end

local intervals = {}

intervals.prime = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 }
intervals.odd = {3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53 }
intervals.integer = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11,  12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}

-- evaluate input on error use default
local function eval_num_arg(input, def_value)
	local result = input
	if type(input) ~= 'number' then
		result = def_value
		if type(input) == 'string' then
			input = input:match("^%s*(.-)%s*$")
			if string.len(input) > 0 then
				result = tonumber(input)
			end
		end
	end
	return result
end

-- calculate default precision
local function prec_by_equal(steps, num, denom)
	return math.floor(math.log(steps*1.9*math.log(2)/math.log(num/denom))/math.log(10))
end

function p.harmonics_in_equal (frame)
	-- optional number of steps, default: 12
	local steps = eval_num_arg(frame.args['steps'], 12)
	-- numerator, default: 2
	local num = eval_num_arg(frame.args['num'], 2)
	-- denominator, default: 1
	local denom = eval_num_arg(frame.args['denom'], 1)
	-- optional number of columns, default: 11
	local columns = eval_num_arg(frame.args['columns'], 11)
	-- optional start column, default: start with prime 2
	local start = eval_num_arg(frame.args['start'], 1)
	-- option intervals
	local select_intervals = "integer"


	local name = steps .. 'ed' .. num .. '/' .. denom
	if denom == 1 then
		if num == 2 then
			name = steps .. 'edo'
			select_intervals = "odd"
			if check_consistency(steps, num, denom) then
				-- select_intervals = "prime_no2"
				select_intervals = "prime"
			end
		elseif num == 3 then
			name = steps .. 'edt'
		else
			name = steps .. 'ed' .. num
		end
	end
	if num == 3 and denom == 2 then
		name = steps .. 'edf'
	end
	
	-- override default intervals
	if frame.args['intervals'] and string.len(frame.args['intervals']) > 0 then
		select_intervals = frame.args['intervals']
	end
	
	title_intervals = "prime harmonics"
	if select_intervals == "odd" then
		title_intervals = "odd harmonics"
	elseif select_intervals == "integer" then
		title_intervals = "harmonics"
	end
	
	local title = frame.args['title']
	if title == nil or string.len(title) == 0 then
		title = "Approximation of ".. title_intervals .. " in " .. name
	end
	-- optional precision for abs error, default about 3 digits
	local prec = eval_num_arg(frame.args['prec'], prec_by_equal(steps, num, denom))
	local reduction = true
	if steps == 1 then
		reduction = false	
	end
	local collapsed = frame.args['collapsed']
	
	return approx( steps, num, denom, {unpack(intervals[select_intervals], start, start+columns-1)}, title, prec, reduction, collapsed)
end

return p;