Module:MOS interval HE

From Xenharmonic Wiki
Jump to navigation Jump to search

Documentation transcluded from /doc
Note: Do not invoke this module directly; use the corresponding template instead: Template:MOS interval HE.
Icon-Todo.png Todo: Documentation

local mos = require("Module:MOS")
local rat = require("Module:Rational")
local et = require("Module:ET")
local tamnams = require("Module:TAMNAMS")
local interval_extension = require("Module:Interval_extension")
local yesno = require("Module:Yesno")
local p = {}

-- Main function; to be called by wrapper
function p._mos_interval_he(input_mos, mos_prefix, mos_abbrev, is_collapsed)
	-- Default param for input mos is 5L 2s
	local input_mos = input_mos or mos.new(5, 2, 2)
	local mos_prefix = mos_prefix or "mos"
	local mos_abbrev = mos_abbrev or "m"
	local is_collapsed = is_collapsed == true
	
	-- Get the scale sig
	local scale_sig = mos.as_string(input_mos)
	
	-- Get the brightest and darkest modes as step matrices
	local bright_step_matrix = mos.mode_to_step_matrix(mos.brightest_mode(input_mos))
	local dark_step_matrix = mos.mode_to_step_matrix(mos.darkest_mode(input_mos))
	
	-- Get the number of steps per period and equave
	local equave_step_count = mos.equave_step_count(input_mos)
	local period_step_count = mos.period_step_count(input_mos)
	
	-- Get the step counts for the bright and dark generators
	local bright_gen_step_count = mos.bright_gen_step_count(input_mos)
	local dark_gen_step_count = mos.dark_gen_step_count(input_mos)
	
	-- Setup for harmonic entropy
	local step_ratios = {
		{ 4, 3 },
		{ 3, 2 },
		{ 5, 3 },
		{ 2, 1 },
		{ 5, 2 },
		{ 3, 1 },
		{ 4, 1 },
	}
	
	-- Create the table
	local result = "{| class=\"wikitable mw-collapsible" .. (is_collapsed and " mw-collapsed\"\n" or "\"\n")
	
	-- Create table title
	result = result
		.. "|+ style=\"font-size: 105%; white-space: nowrap;\" | " .. string.format("Harmonic entropy info for %s\n", scale_sig)
		.. "|-\n"
		
	-- Create table headers
	result = result
		.. "! Interval"
		.. "!! Range in cents"
		.. "!! Average of [[HE]]<br/>(from [http://www.mikebattagliamusic.com/HE-JS/HE.html HE Calc])"
		.. "!! Min of [[HE]]\n"
	
	-- Write each row
	for i = 1, #bright_step_matrix do
		-- Compare the bright and dark intervals. If they're the same, then the
		-- current interval class is a period interval.
		local current_bright_interval = bright_step_matrix[i]
		local current_dark_interval = dark_step_matrix[i]
		local is_period = mos.interval_eq(current_bright_interval, current_dark_interval)
		
		-- If it's a period interval, then there is only one row to write.
		-- Otherwise, there are two rows to write, one for each size.
		if is_period then
			local cents = mos.interval_to_cents(current_bright_interval, input_mos, {1, 1})
			
			result = result .. "|-\n"
				.. string.format("|| %s " , tamnams.interval_quality(current_bright_interval, input_mos, "sentence-case", mos_prefix))
				.. string.format("|| %.1f¢ ", cents)
				.. string.format("|| ~%.4f nats ", interval_extension.harmonic_entropy_with_lookup_table(cents))
				.. string.format("|| ~%.4f nats", interval_extension.harmonic_entropy_with_lookup_table(cents))
		else
			-- Calculate the best and average harmonic entropies
			local he_dark_average = 0
			local he_bright_average = 0
			local he_dark_best = 1000
			local he_bright_best = 1000
			for i = 1, #step_ratios do
				local step_ratio = step_ratios[i]
				local he_dark = interval_extension.harmonic_entropy_with_lookup_table(mos.interval_to_cents(current_dark_interval, input_mos, step_ratio))
				local he_bright = interval_extension.harmonic_entropy_with_lookup_table(mos.interval_to_cents(current_bright_interval, input_mos, step_ratio))
				he_dark_average = he_dark_average + he_dark / #step_ratios
				he_bright_average = he_bright_average + he_bright / #step_ratios
				if he_dark_best > he_dark then
					he_dark_best = he_dark 
				end
				if he_bright_best > he_bright then
					he_bright_best = he_bright 
				end
			end
			
			-- Calculate the cent values min and max for the current intervals
			local sm_min_cents = mos.interval_to_cents(current_dark_interval, input_mos, {1,1})
			local sm_max_cents = mos.interval_to_cents(current_dark_interval, input_mos, {1,0})
			local lg_min_cents = mos.interval_to_cents(current_bright_interval, input_mos, {1,1})
			local lg_max_cents = mos.interval_to_cents(current_bright_interval, input_mos, {1,0})
			
			-- Then sort, as the min and max may be swapped 
			-- This happens if the dark interval has more small steps than large steps
			local dark_interval_range   = string.format("%.1f¢ to %.1f¢", math.min(sm_min_cents, sm_max_cents), math.max(sm_min_cents, sm_max_cents))
			local bright_interval_range = string.format("%.1f¢ to %.1f¢", math.min(lg_min_cents, lg_max_cents), math.max(lg_min_cents, lg_max_cents))
			
			result = result .. "|-\n"
				.. string.format("|| %s " , tamnams.interval_quality(current_dark_interval, input_mos, "sentence-case", mos_prefix))
				.. string.format("|| %s " , dark_interval_range)
				.. string.format("|| ~%.4f nats " , he_dark_average)
				.. string.format("|| ~%.4f nats\n" , he_dark_best)
				.. "|-\n"
				.. string.format("| %s "  , tamnams.interval_quality(current_bright_interval, input_mos, "sentence-case", mos_prefix))
				.. string.format("|| %s " , bright_interval_range)
				.. string.format("|| ~%.4f nats " , he_bright_average)
				.. string.format("|| ~%.4f nats" , he_bright_best)
		end
		result = result .. "\n"
	end
	result = result .. "|}"

	return result
end

-- Wrapper function; to be called by template
function p.mos_interval_he(frame)
	-- Get params
	local scalesig   = frame.args["Scale Signature"]
	local mos_prefix = frame.args["MOS Prefix"]
	local mos_abbrev = frame.args["MOS Abbrev"]
	local collapsed  = yesno(frame.args["Collapsed"], false)
	
	-- Parse scalesig
	local input_mos = mos.parse(scalesig)
	
	-- Verify name/prefix/abbrev
	mos_prefix = tamnams.verify_prefix(input_mos, mos_prefix)
	mos_abbrev = tamnams.verify_abbrev(input_mos, mos_abbrev)

	return p._mos_interval_he(input_mos, mos_prefix, mos_abbrev, is_collapsed)
end

return p