Module:MOS intro: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
Changed to revamped function (now includes rothenberg propriety), while maintaining the same wording as before |
||
| Line 3: | Line 3: | ||
local utils = require('Module:Utils') | local utils = require('Module:Utils') | ||
local et = require('Module:ET') | local et = require('Module:ET') | ||
local tip = require('Module:Template input parse') | |||
local p = {} | local p = {} | ||
| Line 45: | Line 46: | ||
-- Introduces the mos by its scale sig and names | -- Introduces the mos by its scale sig and names | ||
-- Names must be entered as an array | -- Names must be entered as an array | ||
function p.mos_intro_names( | function p.mos_intro_names(scale_sig, tamnams_name, other_names) | ||
local | local scale_sig = scale_sig or "5L 2s" | ||
local tamnams_name = tamnams_name or { "diatonic" } | local tamnams_name = tamnams_name or { "diatonic" } | ||
local other_names = other_names or { "other-name" } | local other_names = other_names or { "other-name" } | ||
-- Get all the mos's names, starting with tamnams names if applicable | -- Get all the mos's names, starting with tamnams names if applicable | ||
| Line 220: | Line 218: | ||
-- Construct the sentence | -- Construct the sentence | ||
local scalesig_and_names = p.mos_intro_names( | local scalesig_and_names = p.mos_intro_names(scale_sig, tamnams_parsed, other_parsed) | ||
local steps_and_periods = p.mos_intro_steps_and_periods(input_mos) | local steps_and_periods = p.mos_intro_steps_and_periods(input_mos) | ||
local step_pattern = p.mos_intro_step_pattern(input_mos) | local step_pattern = p.mos_intro_step_pattern(input_mos) | ||
| Line 226: | Line 224: | ||
return string.format("%s %s %s %s", scalesig_and_names, steps_and_periods, step_pattern, gens) | return string.format("%s %s %s %s", scalesig_and_names, steps_and_periods, step_pattern, gens) | ||
end | |||
-- Main function (updated) | |||
function p._mos_intro(input_mos, other_names) | |||
local input_mos = input_mos or mos.new(10, 2) | |||
local other_names = other_names or "hemifourths" | |||
-- Scale sig | |||
local scale_sig = mos.as_string(input_mos) | |||
-- Tamnams names, if any | |||
local tamnams_name = mos.tamnams_name[scale_sig] or "" | |||
-- Parsed names | |||
local tamnams_pasred = tip.parse_entries(tamnams_name) | |||
local other_parsed = tip.parse_entries(other_names) | |||
-- Step counts | |||
local nL = input_mos.nL | |||
local ns = input_mos.ns | |||
local n = utils._gcd(nL, ns) | |||
-- Equave as ratio and cents | |||
local equave_as_ratio = rat.as_ratio(input_mos.equave) | |||
local equave_in_cents = rat.cents(input_mos.equave) | |||
-- How many decimal places to round to? | |||
local round = 3 | |||
-- Build up intro text, starting with the scale sig and scale names | |||
-- This is done through the aid of a helper function | |||
local intro = p.mos_intro_names(scale_sig, tamnams_pasred, other_parsed) | |||
-- Add equave equivalence | |||
intro = intro .. (equave_in_cents == 1200 and " is an [[octave equivalence|octave-equivalent]] [[moment of symmetry]] scale" or "is a [[nonoctave|non-octave]] [[moment of symmetry scale]]") | |||
-- Add step counts | |||
intro = intro .. string.format(" containing %d large %s", nL, (nL == 1 and "step" or "steps")) | |||
intro = intro .. string.format(" and %d small %s", ns, (ns == 1 and "step" or "steps")) | |||
-- Add repetition | |||
if n == 1 then | |||
intro = intro .. ", repeating every " .. (equave_in_cents == 1200 and "[[octave]]." or string.format(" interval of [[%s]] (%.3f).", equave_as_ratio, utils._round_dec(equave_in_cents, round))) | |||
else | |||
intro = intro .. string.format(", with a [[period]] of %d large %s", nL/n, (nL/n == 1 and "step" or "steps")) | |||
intro = intro .. string.format(" and %d small %s", ns/n, (ns/n == 1 and "step" or "steps")) | |||
intro = intro .. string.format(" that repeats every %.3f¢", equave_in_cents / n) | |||
intro = intro .. (n == 2 and " or twice every" or string.format(" or %d times every", n)) .. (equave_in_cents == 1200 and " octave." or string.format(" every interval of [[%s]] (%.d¢).", equave_as_ratio, utils._round(equave_in_cents, round))) | |||
end | |||
-- TODO: add descendant info | |||
-- Add generator ranges | |||
-- Get the eds (ets) corresponding to the collapsed and equalized mosses | |||
local collapsed_et = et.new(nL, input_mos.equave) | |||
local equalized_et = et.new(nL + ns, input_mos.equave) | |||
-- Get the sizes of the bright generator for the collapsed and equalized et in steps | |||
-- These are used to calculate cent values for the generators | |||
-- The values for the dark generator are the period complements | |||
local generator = mos.bright_gen(input_mos) | |||
local gen_collapsed_in_steps = generator["L"] | |||
local gen_equalized_in_steps = generator["L"] + generator["s"] | |||
local bright_gen_max = et.cents(collapsed_et, gen_collapsed_in_steps) | |||
local bright_gen_min = et.cents(equalized_et, gen_equalized_in_steps) | |||
local dark_gen_min = equave_in_cents / n - bright_gen_max | |||
local dark_gen_max = equave_in_cents / n - bright_gen_min | |||
local bright_gen_min_r = tostring(utils._round_dec(bright_gen_min, round)) | |||
local bright_gen_max_r = tostring(utils._round_dec(bright_gen_max, round)) | |||
local dark_gen_min_r = tostring(utils._round_dec(dark_gen_min, round)) | |||
local dark_gen_max_r = tostring(utils._round_dec(dark_gen_max, round)) | |||
intro = intro .. string.format(" [[generator|Generators]] that produce this scale range from %s¢ to %s¢, or from %s¢ to %s¢.", bright_gen_min_r, bright_gen_max_r, dark_gen_min_r, dark_gen_max_r) | |||
-- Rothenberg propriety (rothenprop) info | |||
if ns == 1 then | |||
intro = intro .. "Scales of this form always exhibit [[proper|Rothenberg propriety]] because there is only one small step." | |||
elseif ns / n == 1 then | |||
intro = intro .. "Scales in which every period is the same sequence of steps always exhibit [[proper|Rothenberg propriety]] because there is only one small step per period." | |||
end | |||
return intro | |||
end | end | ||
| Line 234: | Line 317: | ||
local other_names = frame.args['Other Names'] or "" | local other_names = frame.args['Other Names'] or "" | ||
return p. | return p._mos_intro(input_mos, other_names) | ||
end | end | ||
return p | return p | ||
Revision as of 07:41, 3 February 2024
- This module should not be invoked directly; use its corresponding template instead: Template:MOS intro.
This module automatically fills in an introduction for MOS scales. It clarifies the equave, numbers of long and short steps, and range of generators that produce it.
| Introspection summary for Module:MOS intro | |||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
| ||||||||||||||||||||||||||||||||||||||||||||||||
No function descriptions were provided. The Lua code may have further information.
local mos = require('Module:MOS')
local rat = require('Module:Rational')
local utils = require('Module:Utils')
local et = require('Module:ET')
local tip = require('Module:Template input parse')
local p = {}
-- Helper function that parses entries from a semicolon-delimited string and returns them in an array
function p.parse_entries(unparsed)
local parsed = {}
for entry in string.gmatch(unparsed, '([^;]+)') do
table.insert(parsed, entry) -- Add to array
end
return parsed
end
-- Helper function
-- Lists out names, with each name being bold
function p.mos_intro_list_names(mos_names, conjunction)
local mos_names = mos_names or { "name1", "name2", "name3" }
local conjunction = conjunction or "and"
-- List the names
local names_list = ""
if #mos_names == 1 then
-- Only one mos name
names_list = string.format("'''%s'''", mos_names[1])
elseif #mos_names == 2 then
-- Two mos names (name and alternate-name)
names_list = string.format("'''%s''' %s '''%s'''", mos_names[1], conjunction, mos_names[2])
elseif #mos_names > 2 then
-- Three or more mos names
for i = 1, #mos_names - 1 do
names_list = names_list .. string.format("'''%s''', ", mos_names[i])
end
names_list = names_list .. string.format("%s '''%s'''", conjunction, mos_names[#mos_names])
else
-- No names
names_list = ""
end
return names_list
end
-- Helper function
-- Introduces the mos by its scale sig and names
-- Names must be entered as an array
function p.mos_intro_names(scale_sig, tamnams_name, other_names)
local scale_sig = scale_sig or "5L 2s"
local tamnams_name = tamnams_name or { "diatonic" }
local other_names = other_names or { "other-name" }
-- Get all the mos's names, starting with tamnams names if applicable
local tamnams_names_list = p.mos_intro_list_names(tamnams_name, "and")
local other_names_list = p.mos_intro_list_names(other_names, "or")
-- Construct the sentence
local sentence = string.format("'''%s'''", scale_sig)
-- Add names
if tamnams_names_list ~= "" and other_names_list ~= "" then
-- There are both tamnams names and alternate names
sentence = sentence .. string.format(", named %s in [[TAMNAMS]] (also known as %s),", tamnams_names_list, other_names_list)
elseif tamnams_names_list ~= "" and other_names_list == "" then
-- There are only tamnams names
sentence = sentence .. string.format(", named %s in [[TAMNAMS]],", tamnams_names_list)
elseif tamnams_names_list == "" and other_names_list ~= "" then
-- There are no tamnams names but there are alternate names
sentence = sentence .. string.format(", also called %s,", other_names_list)
end
return sentence
end
-- Helper function
-- Introduces the mos by its step counts and number of periods
-- This is meant to immediately follow the sentence provided by:
-- - mos_intro_tamnams_names()
function p.mos_intro_steps_and_periods(input_mos)
local input_mos = input_mos or mos.new(3, 6, 3)
-- Get the equave and the equave in cents
local equave = input_mos.equave
local equave_in_cents = rat.cents(equave)
local equave_as_ratio = rat.as_ratio(equave)
-- Get the step counts and number of periods
local nL = input_mos.nL -- Number of large steps per equave
local ns = input_mos.ns -- Number of small steps per equave
local n = utils._gcd(nL, ns) -- Number of periods
local x = round(nL / n) -- Number of large steps per period
local y = round(ns / n) -- Number of small steps per period
-- Construct the sentence
sentence = " is "
-- Is the scale nonoctave?
if equave_in_cents == 1200 then
sentence = sentence .. string.format("an [[octave equivalence|octave-equivalent]] [[moment of symmetry]] scale")
else
sentence = sentence .. string.format("a [[non-octave]] [[moment of symmetry]] scale", equave_as_ratio)
end
-- What are the step counts? Should the word "step" contain an s?
local s_nl = ""
local s_ns = ""
if nL ~= 1 then
s_nl = "s"
end
if ns ~= 1 then
s_ns = "s"
end
sentence = sentence .. string.format(" containing %d large step%s and %d small step%s", nL, s_nl, ns, s_ns)
-- How large is the period in cents? Round to 3 decimal places
local round = 3
local equave_rounded = tostring(utils._round_dec(equave_in_cents, round))
local period_rounded = tostring(utils._round_dec(equave_in_cents / n, round))
-- Should step be singular or plural?
local s_x = ""
local s_y = ""
local repetition = ""
if x ~= 1 then
s_x = "s"
end
if y ~= 1 then
s_y = "s"
end
if n == 2 then
repetition = "twice"
elseif n > 2 then
repetition = string.format("%d times", n)
end
if n == 1 and equave_in_cents == 1200 then
sentence = sentence .. string.format(", repeating every [[octave]].")
elseif n == 1 and equave_in_cents ~= 1200 then
sentence = sentence .. string.format(", repeating every interval of [[%s]] (%s¢).", equave_as_ratio, equave_rounded)
elseif n ~= 1 and equave_in_cents == 1200 then
sentence = sentence .. string.format(", with a [[period]] of %d large step%s and %d small step%s", x, s_x, y, s_y)
sentence = sentence .. string.format(" that repeats every %s¢, or %s every [[octave]].", period_rounded, repetition)
elseif n ~= 1 and equave_in_cents ~= 1200 then
sentence = sentence .. string.format(", with a [[period]] of %d large step%s and %d small step%s", x, s_x, y, s_y)
sentence = sentence .. string.format(" that repeats every %s¢, or %s every interval of %s (%s¢).", period_rounded, repetition, equave_as_ratio, equave_rounded)
end
return sentence
end
-- Helper function
-- Calculates the brightest mode of the true mos
function p.mos_intro_step_pattern(input_mos)
local input_mos = input_mos or mos.new(5, 2)
local brightest_mode = mos.brightest_mode(input_mos)
local sentence = string.format("[[mode|Modes]] of this scale are rotations of the step pattern '''%s'''.", brightest_mode)
return sentence
end
-- Helper function
-- Calculates the generator ranges of the mos
function p.mos_intro_generator_ranges(input_mos)
local input_mos = input_mos or mos.new(5, 2)
-- Get the step counts and number of periods
local nL = input_mos.nL -- Number of large steps per equave
local ns = input_mos.ns -- Number of small steps per equave
local n = utils._gcd(nL, ns) -- Number of periods
-- Get the equave as a ratio and in cents
local equave = input_mos.equave
local equave_in_cents = rat.cents(equave)
-- Get the eds (ets) corresponding to the collapsed and equalized mosses
local collapsed_et = et.new(nL, input_mos.equave)
local equalized_et = et.new(nL + ns, input_mos.equave)
-- Get the sizes of the bright generator for the collapsed and equalized et in steps
-- These are used to calculate cent values for the generators
-- The values for the dark generator are the period complements
local generator = mos.bright_gen(input_mos)
local gen_collapsed_in_steps = generator["L"]
local gen_equalized_in_steps = generator["L"] + generator["s"]
local bright_gen_max = et.cents(collapsed_et, gen_collapsed_in_steps)
local bright_gen_min = et.cents(equalized_et, gen_equalized_in_steps)
local dark_gen_min = equave_in_cents / n - bright_gen_max
local dark_gen_max = equave_in_cents / n - bright_gen_min
-- Round values
local round = 3
local bright_gen_min_r = tostring(utils._round_dec(bright_gen_min, round))
local bright_gen_max_r = tostring(utils._round_dec(bright_gen_max, round))
local dark_gen_min_r = tostring(utils._round_dec(dark_gen_min, round))
local dark_gen_max_r = tostring(utils._round_dec(dark_gen_max, round))
local sentence = string.format("[[generator|Generators]] that produce this scale range from %s¢ to %s¢, or from %s¢ to %s¢.", bright_gen_min_r, bright_gen_max_r, dark_gen_min_r, dark_gen_max_r)
return sentence
end
-- Function that creates a mos intro, given a mos and any other names
-- Intro (or lead section) consists of multiple sentences, and each is called individually
function p.mos_intro(input_mos, other_names)
local input_mos = input_mos or mos.new(5, 2)
local other_names = other_names or "name1; name2; name3"
-- Get the scale sig
local scale_sig = mos.as_string(input_mos)
-- Get tamnams names
local tamnams_name = mos.tamnams_name[scale_sig] or ""
-- Parse names
local tamnams_parsed = p.parse_entries(tamnams_name)
local other_parsed = p.parse_entries(other_names)
-- Construct the sentence
local scalesig_and_names = p.mos_intro_names(scale_sig, tamnams_parsed, other_parsed)
local steps_and_periods = p.mos_intro_steps_and_periods(input_mos)
local step_pattern = p.mos_intro_step_pattern(input_mos)
local gens = p.mos_intro_generator_ranges(input_mos)
return string.format("%s %s %s %s", scalesig_and_names, steps_and_periods, step_pattern, gens)
end
-- Main function (updated)
function p._mos_intro(input_mos, other_names)
local input_mos = input_mos or mos.new(10, 2)
local other_names = other_names or "hemifourths"
-- Scale sig
local scale_sig = mos.as_string(input_mos)
-- Tamnams names, if any
local tamnams_name = mos.tamnams_name[scale_sig] or ""
-- Parsed names
local tamnams_pasred = tip.parse_entries(tamnams_name)
local other_parsed = tip.parse_entries(other_names)
-- Step counts
local nL = input_mos.nL
local ns = input_mos.ns
local n = utils._gcd(nL, ns)
-- Equave as ratio and cents
local equave_as_ratio = rat.as_ratio(input_mos.equave)
local equave_in_cents = rat.cents(input_mos.equave)
-- How many decimal places to round to?
local round = 3
-- Build up intro text, starting with the scale sig and scale names
-- This is done through the aid of a helper function
local intro = p.mos_intro_names(scale_sig, tamnams_pasred, other_parsed)
-- Add equave equivalence
intro = intro .. (equave_in_cents == 1200 and " is an [[octave equivalence|octave-equivalent]] [[moment of symmetry]] scale" or "is a [[nonoctave|non-octave]] [[moment of symmetry scale]]")
-- Add step counts
intro = intro .. string.format(" containing %d large %s", nL, (nL == 1 and "step" or "steps"))
intro = intro .. string.format(" and %d small %s", ns, (ns == 1 and "step" or "steps"))
-- Add repetition
if n == 1 then
intro = intro .. ", repeating every " .. (equave_in_cents == 1200 and "[[octave]]." or string.format(" interval of [[%s]] (%.3f).", equave_as_ratio, utils._round_dec(equave_in_cents, round)))
else
intro = intro .. string.format(", with a [[period]] of %d large %s", nL/n, (nL/n == 1 and "step" or "steps"))
intro = intro .. string.format(" and %d small %s", ns/n, (ns/n == 1 and "step" or "steps"))
intro = intro .. string.format(" that repeats every %.3f¢", equave_in_cents / n)
intro = intro .. (n == 2 and " or twice every" or string.format(" or %d times every", n)) .. (equave_in_cents == 1200 and " octave." or string.format(" every interval of [[%s]] (%.d¢).", equave_as_ratio, utils._round(equave_in_cents, round)))
end
-- TODO: add descendant info
-- Add generator ranges
-- Get the eds (ets) corresponding to the collapsed and equalized mosses
local collapsed_et = et.new(nL, input_mos.equave)
local equalized_et = et.new(nL + ns, input_mos.equave)
-- Get the sizes of the bright generator for the collapsed and equalized et in steps
-- These are used to calculate cent values for the generators
-- The values for the dark generator are the period complements
local generator = mos.bright_gen(input_mos)
local gen_collapsed_in_steps = generator["L"]
local gen_equalized_in_steps = generator["L"] + generator["s"]
local bright_gen_max = et.cents(collapsed_et, gen_collapsed_in_steps)
local bright_gen_min = et.cents(equalized_et, gen_equalized_in_steps)
local dark_gen_min = equave_in_cents / n - bright_gen_max
local dark_gen_max = equave_in_cents / n - bright_gen_min
local bright_gen_min_r = tostring(utils._round_dec(bright_gen_min, round))
local bright_gen_max_r = tostring(utils._round_dec(bright_gen_max, round))
local dark_gen_min_r = tostring(utils._round_dec(dark_gen_min, round))
local dark_gen_max_r = tostring(utils._round_dec(dark_gen_max, round))
intro = intro .. string.format(" [[generator|Generators]] that produce this scale range from %s¢ to %s¢, or from %s¢ to %s¢.", bright_gen_min_r, bright_gen_max_r, dark_gen_min_r, dark_gen_max_r)
-- Rothenberg propriety (rothenprop) info
if ns == 1 then
intro = intro .. "Scales of this form always exhibit [[proper|Rothenberg propriety]] because there is only one small step."
elseif ns / n == 1 then
intro = intro .. "Scales in which every period is the same sequence of steps always exhibit [[proper|Rothenberg propriety]] because there is only one small step per period."
end
return intro
end
-- Function for use with a template
function p.mos_intro_frame(frame)
-- Get and parse the the mos's scale signature, in the form xL ys or xL ys <p/q>
local input_mos = mos.parse(frame.args['Scale Signature']) or mos.new(5, 2, 2)
local other_names = frame.args['Other Names'] or ""
return p._mos_intro(input_mos, other_names)
end
return p