Module:MOS degrees: Difference between revisions

Ganaram inukshuk (talk | contribs)
Degreechain function outputs result in notationally agnostic form (mossteps and quality) for ease of translation to tamnams or other notation
Ganaram inukshuk (talk | contribs)
Degreechain function no longer depends on mos gamut module; interval qualities are now encoded numerically (0 = perfect root, 1 = large size, -1 = small size, 2 = augmented, -2 = diminished, etc)
Line 57: Line 57:
--[[
--[[
local input_mos = input_mos or mos.new(5, 2, 2)
local input_mos = input_mos or mos.new(5, 2, 2)
local genchain_length_per_period = genchain_length_per_period or 20
local genchain_length_per_period = genchain_length_per_period or 10
local going_up = true
local going_up = false
]]--
]]--
Line 69: Line 69:
local bright_gen = mos.bright_gen(input_mos)
local bright_gen = mos.bright_gen(input_mos)
local mossteps_per_bright_gen = bright_gen['L'] + bright_gen['s']
local mossteps_per_bright_gen = bright_gen['L'] + bright_gen['s']
local mossteps_per_dark_gen = mossteps_per_period - mossteps_per_bright_gen
-- The easiest way to get scale degrees is through the following method:
-- - Get the genchain corresponding to the second-brightest mode and
--  interpret the result as scale degrees.
-- - If this is the ascending chain, then this represents every interval
--  class' large size. For each period, the first two elements represent
--  the root (perfect) and bright generator (large size is perfect).
-- - If this is the descending chain, then find the period complement of
--  each element, which represents every interval class's small size.
--  For each period, the first two elements represent the root (perfect)
--  and dark generator (small size is perfect).
-- - Since the root and generators start as perfect, any chromas added to
--  that represent augmented/diminished degrees.
-- Note that this is agnostic of notation, so only the genchain length is
-- needed. The UDP for the second-brightest mode is auto-calculated and is
-- used regardless of whether the chain is going up or down.
local genchain = mosg.mos_genchain(input_mos, mossteps_per_period - 2, genchain_length_per_period, true)
-- Interpret as scale degrees.
-- The first two elements are always perfect.
-- 0 chromas is major (or minor if descending).
-- 1 chroma is augmented (diminished if descending).
-- n chromas is n-times augmented (n-times diminished if descending).
-- Notes and caveats:
-- - The 0-mosdegree is also called the unison.
-- - Generators for nL ns mosses aren't called perf/aug/dim, but instead
--  called major and minor. This requires offsetting the note's chroma
--  value by -1, or else the singly-aug/dim generator will be skipped.
local degreechain = {}
local degreechain = {}
for j = 1, periods_per_equave do
for j = 1, periods_per_equave do
local chain_for_period = {}
local chain_for_period = {}
for i = 1, #genchain[j] do
local note = genchain[j][i]
-- Define mossteps and quality as counters
-- Quality encodes major/minor/perfect/aug/dim numerically this way:
-- -  3 = 2x augmented
-- -  2 = 1x augmented
-- -  1 = large size (major; perfect for bright gen)
-- -  0 = perfect (reserved for root only)
-- - -1 = small size (minor; perfect for dark gen)
-- - -2 = 1x diminished
-- - -3 = 2x diminished
local mossteps = (j - 1) * mossteps_per_period
local quality = 0
for i = 1, genchain_length_per_period do
-- Put together the name
local degree = { ['mossteps'] = mossteps, ['quality'] = quality }
table.insert(chain_for_period, degree)
local mossteps = note['mossteps']
-- Increment the counters
local chromas = note['chromas']
if going_up then
-- Increment quality when mossteps is the root of the period
-- Mosses of the form nL ns have slightly different rules. Is the
if mossteps % mossteps_per_period == 0 then
-- input mos of that form?
quality = quality + 1
local is_nL_ns = input_mos.nL == input_mos.ns
if is_nL_ns and mossteps == mossteps_per_bright_gen + (j-1) * mossteps_per_period then
chromas = chromas - 1
end
-- If not going up, use the period complements instead
-- Period complements of the root will end up being the note one
-- period up, so use modular arithmetic to keep the starting point
-- constrained to the root
local aug_or_dim = "augmented"
local maj_or_min = "major"
if not going_up then
mossteps =  (j - 1) * mossteps_per_period + (mossteps_per_period - mossteps) % mossteps_per_period
aug_or_dim = "diminished"
maj_or_min = "minor"
end
-- What is the quality of the note? (major, minor, etc)
local quality = ""
if i == 1 then
quality = "perfect"
elseif i == 2 then
if is_nL_ns then
quality = maj_or_min
else
quality = "perfect"
end
end
mossteps = mossteps_per_bright_gen + mossteps -- Increment
mossteps = mossteps % mossteps_per_period -- Constrain
mossteps = mossteps + (j-1) * mossteps_per_period -- Offset by period
else
else
if chromas == 0 then
-- Increment quality when mossteps is the root of the period
quality = maj_or_min
if mossteps % mossteps_per_period == 0 then
elseif chromas == 1 then
quality = quality - 1
quality = aug_or_dim
else
quality = chromas .. "× " .. aug_or_dim
end
end
mossteps = mossteps_per_dark_gen + mossteps -- Increment
mossteps = mossteps % mossteps_per_period -- Constrain
-- Use contrained mossteps value to determine whether to
-- decrement the quality
mossteps = mossteps + (j-1) * mossteps_per_period -- Offset by period
end
end
-- Put together the name
local degree = { ['mossteps'] = mossteps, ['quality'] = quality }
table.insert(chain_for_period, degree)
end
end
table.insert(degreechain, chain_for_period)
table.insert(degreechain, chain_for_period)