Module:MOS: Difference between revisions

From Xenharmonic Wiki
Jump to navigation Jump to search
Inthar (talk | contribs)
No edit summary
Inthar (talk | contribs)
No edit summary
Line 32: Line 32:
end
end
return '' .. mos.nL .. 'L ' .. mos.ns .. 's' .. suffix
return '' .. mos.nL .. 'L ' .. mos.ns .. 's' .. suffix
end
-- Find the brightest mode of a mos.
-- We go back to the root of the scale tree, then progressively construct descendants.
function p.brightest_mode(mos)
local nL, ns, equave = mos.nL, mos.ns, mos.equave
local d = gcd(nL, ns)
if d > 1 then -- use single period mos, with period as new equave
return string.rep(p.brightest_mode(MOS.new(nL/d, ns/d, p.backslash_ratio(ET.new(d, equave, nil), 1))), d)
else
current_nL, current_ns = nL, ns
local path_to_root = {}
local i = 1 -- for indexing into path_to_root while building it
while current_nL > 1 or current_ns > 1 do -- while current mos is not 1L 1s, record current mos and go up to parent
assert(current_nL ~= current_ns, "current_nL == current_ns")
path_to_root[i] = {[1] = current_nL, [2] = current_ns}
i = i + 1
current_nL = math.min(nL, ns) -- parent's nL
current_ns = math.max(nL, ns)-math.min(nL, ns) -- parent's ns
end
local len_of_path = table.getn(path_to_root)
local result = 'Ls'
for j in len_of_path, 1, -1 do
local child_nL, child_ns = path_to_root[j][1], path_to_root[j][2]
local L_becomes = ''
local s_becomes = ''
if child_nL > child_ns then -- this will give darkest mode
L_becomes = 'sL'
s_becomes = 'L'
else
L_becomes = 'Ls'
s_becomes = 's'
end
local replace_with = {['L'] = L_becomes, ['s'] = s_becomes}
result = string.gsub(result, '[Ls]', replace_with)
if child_nL > child_ns then -- reverse for brightest mode
result = string.reverse(result)
end
end
return result
end
end
function p.bright_gen(mos) -- Compute the abstract bright generator as a "vector" of L and s steps.
local nL, ns, equave = mos.nL, mos.ns, mos.equave
local d = gcd(nL, ns)
if d > 1 then -- use single period mos, with period as new equave
return string.rep(p.bright_gen(MOS.new(nL/d, ns/d, p.backslash_ratio(ET.new(d, equave, nil), 1))), d)
else
current_nL, current_ns = nL, ns
local path_to_root = {}
local i = 1 -- for indexing into path_to_root while building it
while current_nL > 1 or current_ns > 1 do -- while current mos is not 1L 1s, record current mos and go up to parent
assert(current_nL ~= current_ns, "current_nL == current_ns")
path_to_root[i] = {[1] = current_nL, [2] = current_ns}
i = i + 1
current_nL = math.min(nL, ns) -- parent's nL
current_nL = math.max(nL, ns)-math.min(nL, ns) -- parent's ns
end
local len_of_path = table.getn(path_to_root)
local result = {['L'] = 1, ['s'] = 0, ['equave'] = equave} -- Record the previous bright gen; the bright gen of 1L 1s is 1L + 0s.
for j in len_of_path, 1, -1 do
local child_nL, child_ns = path_to_root[j][1], path_to_root[j][2]
if child_nL > child_ns then
-- The old bright gen will now be the dark gen, take equave complement for the new bright gen.
-- new L = old s.
-- The # of new L's in (now dark) generator is # of old L's + # of old s's;
-- the # of new s's in it is the # of old L's.
result = {['L'] = child_nL - (result['L'] + result['s']), ['s'] = child_ns - result['L'], equave}
else -- The old bright gen will stay bright.
-- new s = old s.
-- The # of new L's in the generator is # of old L's;
-- the # of new s's in it is # of old L's + # of new s's.
result = {['L'] = result['L'], ['s'] = result['L'] + result['s'], equave}
end
end
return result
end
end
end


return p
return p

Revision as of 00:27, 2 April 2023

Module documentation[view] [edit] [history] [purge]
This module primarily serves as a library for other modules and has no corresponding template.

This module provides functions for working with MOS scales in Lua code.


Introspection summary for Module:MOS 
Functions provided (5)
Line Function Params
7 new (nL, ns, equave)
15 parse (unparsed)
28 as_string (mos)
38 brightest_mode (mos)
77 bright_gen (mos)
Lua modules required (3)
Variable Module Functions used
et Module:ET dependency not used
rat Module:Rational parse
eq
as_ratio
seq Module:Sequence dependency not used

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


local rat = require('Module:Rational')
local seq = require('Module:Sequence')
local et = require('Module:ET')
local p = {}

-- create a MOS structure (nL)L (ns)s <equave>
function p.new(nL, ns, equave)
	local nL = nL or 5
	local ns = ns or 2
	local equave = equave or 2
	return { nL = nL, ns = ns, equave = equave }
end

-- parse a MOS structure
function p.parse(unparsed)
	local nL, ns, equave = unparsed:match('^(%d+)[Ll]%s*(%d+)[Ss]%s*(.*)$')
	nL = tonumber(nL)
	ns = tonumber(ns)
	equave = equave:match('^%((.*)-equivalent%)$') or '2/1' -- Assumes this is a rational ratio written a/b
	equave = rat.parse(equave)
	if nL == nil or ns == nil or equave == nil then
		return nil
	end
	return p.new(nL, ns, equave)
end

-- construct a string representation for a MOS structure
function p.as_string(mos)
	local suffix = ''
	if not rat.eq(mos.equave, 2) then
		suffix = '<' .. rat.as_ratio(mos.equave):lower() .. '>'
	end
	return '' .. mos.nL .. 'L ' .. mos.ns .. 's' .. suffix
end

-- Find the brightest mode of a mos.
-- We go back to the root of the scale tree, then progressively construct descendants.
function p.brightest_mode(mos)
	local nL, ns, equave = mos.nL, mos.ns, mos.equave
	local d = gcd(nL, ns)
	if d > 1 then -- use single period mos, with period as new equave
		return string.rep(p.brightest_mode(MOS.new(nL/d, ns/d, p.backslash_ratio(ET.new(d, equave, nil), 1))), d)
	else
		current_nL, current_ns = nL, ns
		local path_to_root = {}
		local i = 1 -- for indexing into path_to_root while building it
		while current_nL > 1 or current_ns > 1 do -- while current mos is not 1L 1s, record current mos and go up to parent
			assert(current_nL ~= current_ns, "current_nL == current_ns")
			path_to_root[i] = {[1] = current_nL, [2] = current_ns}
			i = i + 1
			current_nL = math.min(nL, ns) -- parent's nL
			current_ns = math.max(nL, ns)-math.min(nL, ns) -- parent's ns
		end
		local len_of_path = table.getn(path_to_root)
		local result = 'Ls'
		for j in len_of_path, 1, -1 do
			local child_nL, child_ns = path_to_root[j][1], path_to_root[j][2]
			local L_becomes = ''
			local s_becomes = ''
			if child_nL > child_ns then -- this will give darkest mode
				L_becomes = 'sL'
				s_becomes = 'L'
			else
				L_becomes = 'Ls'
				s_becomes = 's'
			end
			local replace_with = {['L'] = L_becomes, ['s'] = s_becomes}
			result = string.gsub(result, '[Ls]', replace_with)
			if child_nL > child_ns then -- reverse for brightest mode
				result = string.reverse(result)
			end
		end
		return result
	end
end

function p.bright_gen(mos) -- Compute the abstract bright generator as a "vector" of L and s steps.
	local nL, ns, equave = mos.nL, mos.ns, mos.equave
	local d = gcd(nL, ns)
	if d > 1 then -- use single period mos, with period as new equave
		return string.rep(p.bright_gen(MOS.new(nL/d, ns/d, p.backslash_ratio(ET.new(d, equave, nil), 1))), d)
	else
		current_nL, current_ns = nL, ns
		local path_to_root = {}
		local i = 1 -- for indexing into path_to_root while building it
		while current_nL > 1 or current_ns > 1 do -- while current mos is not 1L 1s, record current mos and go up to parent
			assert(current_nL ~= current_ns, "current_nL == current_ns")
			path_to_root[i] = {[1] = current_nL, [2] = current_ns}
			i = i + 1
			current_nL = math.min(nL, ns) -- parent's nL
			current_nL = math.max(nL, ns)-math.min(nL, ns) -- parent's ns
		end
		local len_of_path = table.getn(path_to_root)
		local result = {['L'] = 1, ['s'] = 0, ['equave'] = equave} -- Record the previous bright gen; the bright gen of 1L 1s is 1L + 0s.
		for j in len_of_path, 1, -1 do
			local child_nL, child_ns = path_to_root[j][1], path_to_root[j][2]
			if child_nL > child_ns then 
				-- The old bright gen will now be the dark gen, take equave complement for the new bright gen.
				-- new L = old s.
				-- The # of new L's in (now dark) generator is # of old L's + # of old s's;
				-- the # of new s's in it is the # of old L's.
				result = {['L'] = child_nL - (result['L'] + result['s']), ['s'] = child_ns - result['L'], equave}
			else -- The old bright gen will stay bright.
				-- new s = old s.
				-- The # of new L's in the generator is # of old L's;
				-- the # of new s's in it is # of old L's + # of new s's.
				result = {['L'] = result['L'], ['s'] = result['L'] + result['s'], equave}
			end
		end
		return result
	end
end

return p