Module:MOS in EDO allperiods: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Ganaram inukshuk (talk | contribs)
mNo edit summary
Ganaram inukshuk (talk | contribs)
mNo edit summary
Line 108: Line 108:
-- If the generator and its complement are the same, skip that genpair by
-- If the generator and its complement are the same, skip that genpair by
-- incrementing the starting generator by 1
-- incrementing the starting generator by 1
local period_in_edosteps = edo
local period_in_edosteps = edo / number_of_periods
local starting_generator = math.ceil(period_in_edosteps / 2)
local starting_generator = math.ceil(period_in_edosteps / 2)
local complement = period_in_edosteps - starting_generator
local complement = period_in_edosteps - starting_generator

Revision as of 05:57, 6 December 2023

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

This module calls Template:MOS in EDO allgens repeatedly to list every MOS scale in a particular equal temperament tuning over all possible periods.


local mos = require('Module:MOS')
local rat = require('Module:Rational')
local utils = require('Module:Utils')
local mosinedo = require('Module:MOS in EDO')
local p = {}

-- Helper function
-- Parses entries from a semicolon-delimited string and returns them in an array
-- TODO: Separate this and related functions (parse_pair and parse_kv_pairs) into its own module, as they're included
-- in various modules at this point, such as: scale tree, mos mdoes
function p.parse_entries(unparsed)
	local parsed = {}
	for entry in string.gmatch(unparsed, '([^;]+)') do
		local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
		table.insert(parsed, trimmed)		-- Add to array
	end
	return parsed
end

-- Main function
-- Creates an entire page section for every mos in an edo
-- Mosses with the same period are only shown; this function must be called
-- separately for each period count if it can support multi-period mosses
-- Generators start with half the period rounded down, plus 1
-- If the edo has subset edos (such as 24edo containing 12edo and 8edo), mosses
-- for those subset edos are skipped, unless show_subsets is set to 1 (true)
function p.moses_in_edo(edo, number_of_periods, show_subsets, temperaments)
	local edo = edo or 12
	local number_of_periods = number_of_periods or 1
	local show_subsets = show_subsets == 1
	local temperaments = temperaments
	
	-- Check whether the number of periods divides the edo
	-- If so, the entire page section will be for multiperiod mosses
	local period_in_edosteps = edo
	local verified_number_of_periods = 1
	if edo % number_of_periods == 0 then
		period_in_edosteps = edo / number_of_periods
		verified_number_of_periods = number_of_periods
	end
	
	-- Starting generator
	-- If the generator and its complement are the same, skip that genpair by
	-- incrementing the starting generator by 1
	local starting_generator = math.ceil(period_in_edosteps / 2)
	local complement = period_in_edosteps - starting_generator
	if starting_generator == complement then
		starting_generator = starting_generator + 1
	end
	
	-- Include temperament names?
	local show_temperament = temperaments ~= nil and #temperaments == math.floor(period_in_edosteps / verified_number_of_periods / 2)
	
	-- Temporarily disable entry of temperament names
	-- Temperament names will be entered as a set of key-value pairs instead of an array
	show_temperament = false
	
	-- Add a table for each genpair
	local result = ""
	for i = starting_generator, period_in_edosteps - 1 do
		local gen = i
		local comp = period_in_edosteps - gen
		--result = result .. string.format("MOS scales with generators %i\\%i and %i\\%i.\n", gen, edo, comp, edo)
		
		-- Skip mosses that are supported by subset edos; this happens if the
		-- gen and comp have a gcd > 1; if show_subsets is set to true, then
		-- show them anyway.
		local gcd = utils._gcd(gen, comp)
		local add_table = gcd == 1 or show_subsets
		
		-- Add table
		if add_table then
			-- TODO: Rewrite code here for entry of temperaments as a set of
			-- key-value pairs
			local temperament_index = 1 + i - starting_generator
			if show_temperament then
				result = result .. mosinedo.mos_in_edo(edo, i, verified_number_of_periods, temperaments[temperament_index])
			else
				result = result .. mosinedo.mos_in_edo(edo, i, verified_number_of_periods)
			end
		end
	end
	
	return result
	
end

-- Main function variant
-- Creates tables for every mos in an edo, for every possible period count
function p.moses_in_edo_all_periods(edo, show_subsets, temperaments)
	local edo = edo or 12
	local show_subsets = show_subsets == 1
	local temperaments = temperaments
	
	-- Include temperament names?
	local show_temperament = temperaments ~= nil --and #temperaments == math.floor(period_in_edosteps / verified_number_of_periods / 2)
	
	-- Temporarily disable entry of temperament names
	-- Temperament names will be entered as a set of key-value pairs instead of an array
	show_temperament = false
	
	-- Call a for loop that produces a set of tables for each period count
	local result = ""
	for j = 1, math.ceil(math.sqrt(edo)) do
		local number_of_periods = j
		if edo % number_of_periods == 0 then
			-- Calculate the starting genpair
			-- If the generator and its complement are the same, skip that genpair by
			-- incrementing the starting generator by 1
			local period_in_edosteps = edo / number_of_periods
			local starting_generator = math.ceil(period_in_edosteps / 2)
			local complement = period_in_edosteps - starting_generator
			if starting_generator == complement then
				starting_generator = starting_generator + 1
			end
			
			-- Add a section header
			if number_of_periods == 1 then
				result = result .. string.format("== Single-period MOS scales ==\n")
			else
				result = result .. string.format("== %i-period MOS scales ==\n", number_of_periods)
			end
			
			-- Add a table for each genpair
			for i = starting_generator, period_in_edosteps - 1 do
				-- Calculate current generators
				local gen = i
				local comp = period_in_edosteps - gen
				
				-- Skip mosses that are supported by subset edos; this happens if the
				-- gen and comp have a gcd > 1; if show_subsets is set to true, then
				-- show them anyway.
				local gcd = utils._gcd(gen, comp)
				local add_table = gcd == 1 or show_subsets
				
				-- Add table
				if add_table then
					-- TODO: Rewrite code here for entry of temperaments as a set of
					-- key-value pairs
					local temperament_index = 1 + i - starting_generator
					if show_temperament then
						result = result .. mosinedo.mos_in_edo(edo, i, verified_number_of_periods, temperaments[temperament_index])
					else
						result = result .. mosinedo.mos_in_edo(edo, i, verified_number_of_periods)
					end
				end
			end
		end
	end
	
	return result
end

function p.moses_in_edo_frame(frame)
	
	local edo = tonumber(frame.args["EDO"])
	local temperaments = p.parse_entries(frame.args["Temperaments"])
	local show_subsets = tonumber(frame.args["Show Subsets"])
	
	local result = ""
	if frame.args["Number of Periods"] == "All" then
		result = p.moses_in_edo_all_periods(edo, show_subsets, temperaments)
	else
		local number_of_periods = tonumber(frame.args["Number of Periods"])
		result = p.moses_in_edo(edo, number_of_periods, show_subsets, temperaments)
	end
	
	return result
	
end

return p