Module:MOS degrees: Difference between revisions

Ganaram inukshuk (talk | contribs)
Added support for notation; notation is optional now
ArrowHead294 (talk | contribs)
mNo edit summary
 
(21 intermediate revisions by 2 users not shown)
Line 1: Line 1:
local mos = require('Module:MOS')
local rat = require('Module:Rational')
local utils = require('Module:Utils')
local mosnot = require('Module:MOS notation')
local et = require('Module:ET')
local p = {}
local p = {}


-- Version 2 of the mos degrees moudle (by ganaraminukshuk)
local et = require("Module:ET")
-- This is based more off of the mos intervals module and older, non-tempalte tables that feature JI ratios,
--local jiraf = require("Module:JI ratio finder")
-- and doesn't depend on genchains (except for note names) to calculate cent values or degree names.
local mos = require("Module:MOS")
-- Current TODO list:
local mosnot = require("Module:MOS notation")
-- - Optional note names column
local rat = require("Module:Rational")
local tamnams = require("Module:TAMNAMS")
local utils = require("Module:Utils")
local yesno = require("Module:Yesno")
 
-- TODO:
-- Rewrite "main function" into a underscore-prefixed function to be called by Lua code and a wrapper to be called by templates. (HIGH PRIORITY!!!)
-- Adopt MOS arithmetic function (MEDIUM-PRIORITY!!!)
-- Add support for double accidentals (low-priority)
-- Move certain helper functions to helper modules (low-priority)


-- Helper function
-- Helper function
-- Parses entries from a semicolon-delimited string and returns them in an array
-- 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
-- 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
-- in various modules at this point, such as: scale tree, MOS modes
function p.parse_entries(unparsed)
function p.parse_entries(unparsed)
local parsed = {}
local parsed = {}
for entry in string.gmatch(unparsed, '([^;]+)') do
for entry in string.gmatch(unparsed, "([^;]+)") do
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
table.insert(parsed, trimmed) -- Add to array
table.insert(parsed, trimmed) -- Add to array
Line 30: Line 34:
function p.parse_pair(unparsed)
function p.parse_pair(unparsed)
local parsed = {}
local parsed = {}
for entry in string.gmatch(unparsed, '([^:]+)') do
for entry in string.gmatch(unparsed, "([^:]+)") do
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
table.insert(parsed, trimmed) -- Add to array
table.insert(parsed, trimmed) -- Add to array
Line 66: Line 70:
function p.parse_step_ratio(unparsed)
function p.parse_step_ratio(unparsed)
local parsed = {}
local parsed = {}
for entry in string.gmatch(unparsed, '([^;]+)') do
for entry in string.gmatch(unparsed, "([^;]+)") do
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
table.insert(parsed, trimmed) -- Add to array
table.insert(parsed, trimmed) -- Add to array
Line 106: Line 110:
end
end


local mosstep_vector = { ['L'] = large_step_count, ['s'] = small_step_count }
local mosstep_vector = { ["L"] = large_step_count, ["s"] = small_step_count }
return mosstep_vector
return mosstep_vector
end
end
Line 115: Line 119:
-- calculates number of et-steps.
-- calculates number of et-steps.
function p.interval_to_etsteps(mosstep_vector, step_ratios)
function p.interval_to_etsteps(mosstep_vector, step_ratios)
return mosstep_vector['L'] * step_ratios[1] + mosstep_vector['s'] * step_ratios[2]
return mosstep_vector["L"] * step_ratios[1] + mosstep_vector["s"] * step_ratios[2]
end
end


-- Helper function
-- Helper function
-- Extracts the prefix from the mos module, without the dash and without any text after that
-- For producing row highlighting for the table
-- May need to revisit to clean up code since this splits text at the "-".
-- Alterations are highlighted, except for singy augmented/diminished intervals for generators
function p.get_mos_prefix(scale_sig)
function p.calculate_row_colors(input_mos, number_of_alterations)
local unparsed = mos.tamnams_prefix[scale_sig]
-- Default parameters for input mos and step ratio (5L 2s and 2:1 step ratio)
local input_mos = input_mos or mos.new(4, 4, 2)
local number_of_alterations = number_of_alterations or 1
local parsed = {}
-- Get the number of mossteps per period and equave
local mossteps_per_equave = input_mos.nL + input_mos.ns
local periods_per_equave = utils._gcd(input_mos.nL, input_mos.ns)
local mossteps_per_period = mossteps_per_equave / periods_per_equave
if unparsed == nil then
local row_colors = {}
return "mos"
for i = 1, mossteps_per_equave + 1 do
else
local mosstep = i - 1
for entry in string.gmatch(unparsed, '([^-]+)') do
local is_period = mosstep % mossteps_per_period == 0
local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
local is_root = mosstep == 0
table.insert(parsed, trimmed) -- Add to array
local is_equave = mosstep == mossteps_per_equave
-- Row colors for pre-alterations
-- If this is the root, don't add rows before it
if not is_root then
for i = 1, number_of_alterations do
table.insert(row_colors, "#eaeaff")
end
end
-- Row colors for main mossetps (default row color)
if is_period then
table.insert(row_colors, "none")
else
table.insert(row_colors, "none")
table.insert(row_colors, "none")
end
-- Row colors for post-alterations
-- If this is the equave, don't add rows after it
if not is_equave then
for i = 1, number_of_alterations do
table.insert(row_colors, "#eaeaff")
end
end
end
return parsed[1]
end
end
return row_colors
end
end


Line 140: Line 173:
-- Calculates note names and stores it in an associative array
-- Calculates note names and stores it in an associative array
-- Default notation is diamond-mos, unless it's 5L 2s, then it's standard notation
-- Default notation is diamond-mos, unless it's 5L 2s, then it's standard notation
function p.get_note_names(input_mos, udp, note_symbols, chroma_plus_symbol, chroma_minus_symbol, number_of_alterations)
function p.calculate_note_names(input_mos, udp, note_symbols, chroma_plus_symbol, chroma_minus_symbol, number_of_alterations)
-- Default parameters for input mos and step ratio (5L 2s and 2:1 step ratio)
-- Default parameters for input mos and step ratio (5L 2s and 2:1 step ratio)
local input_mos = input_mos or mos.new(5, 2, 2)
local input_mos = input_mos or mos.new(5, 2)
local udp = {5,1} or udp
local udp = udp or {5,2}
local note_symbols = note_symbols or "CDEFGAB"
local note_symbols = note_symbols or "CDEFGAB"
local chroma_plus_symbol = chroma_plus_symbol or "#"
local chroma_plus_symbol = chroma_plus_symbol or "#"
local chroma_minus_symbol = chroma_minus_symbol or "b"
local chroma_minus_symbol = chroma_minus_symbol or "b"
local number_of_alterations = number_of_alterations or 1
local number_of_alterations = number_of_alterations or 0
-- Get the number of mossteps per period and equave
-- Get the number of mossteps per period and equave
Line 186: Line 219:
-- Convert the notationally agnostic form into a form that uses given notation
-- Convert the notationally agnostic form into a form that uses given notation
local note = ascending_genchain[j][i]
local note = ascending_genchain[j][i]
local note_symbol = string.sub(note_symbols, note['mossteps'] + 1, note['mossteps'] + 1)
local note_symbol = string.sub(note_symbols, note["Mossteps"] + 1, note["Mossteps"] + 1)
local chroma_count = note['chromas']
local chroma_count = note["Chromas"]
local note_name = note_symbol .. string.rep(chroma_plus_symbol, chroma_count)
local note_name = note_symbol .. string.rep(chroma_plus_symbol, chroma_count)
-- Convert the encoded degree into text
-- Convert the encoded degree into text
local degree_encoded = ascending_degchain[j][i]
local degree_encoded = ascending_degchain[j][i]
local degree_decoded = mosnot.mosstep_and_quality_to_degree(degree_encoded['mossteps'], degree_encoded['quality'], "m", "mosdegree", "abbreviated")
local degree_decoded = mosnot.decode_mosstep_quality(degree_encoded, "m", "mosdegree", "abbreviated")
-- Add to note names
-- Add to note names
Line 201: Line 234:
-- Convert the notationally agnostic form into a form that uses given notation
-- Convert the notationally agnostic form into a form that uses given notation
local note = descending_genchain[j][i]
local note = descending_genchain[j][i]
local note_symbol = string.sub(note_symbols, note['mossteps'] + 1, note['mossteps'] + 1)
local note_symbol = string.sub(note_symbols, note["Mossteps"] + 1, note["Mossteps"] + 1)
local chroma_count = note['chromas'] * -1
local chroma_count = note["Chromas"] * -1
local note_name = note_symbol .. string.rep(chroma_minus_symbol, chroma_count)
local note_name = note_symbol .. string.rep(chroma_minus_symbol, chroma_count)
Line 210: Line 243:
-- For the descending chain, any mossteps that correspond to the root of
-- For the descending chain, any mossteps that correspond to the root of
-- a period should correspond instead to the root one period up (EG, if
-- a period should correspond instead to the root one period up (EG, if
-- the root refers to the unison, it should be the degree one octave up)
-- the root refers to the unison for a single-period mos, it should be
local mossteps_transposed = degree_encoded['mossteps']
-- the degree one octave up)
if mossteps_transposed % mossteps_per_period == 0 then
if degree_encoded["Mossteps"] % mossteps_per_period == 0 then
mossteps_transposed = mossteps_transposed + mossteps_per_period
-- Transpose the mosstep by one period
degree_encoded["Mossteps"] = degree_encoded["Mossteps"] + mossteps_per_period
end
-- Correct the note name based on whether it should be a note that is
-- one period up. If the mos is single-period, then do not transpose.
if degree_encoded["Mossteps"] % mossteps_per_period == 0 and degree_encoded["Mossteps"] == 0 then
-- Correct the note name
note_symbol = string.sub(note_symbols, 1, 1)
note_name = note_symbol .. string.rep(chroma_minus_symbol, chroma_count)
elseif degree_encoded["Mossteps"] % mossteps_per_period == 0 and degree_encoded["Mossteps"] == 0 then
-- Correct the note name
note_symbol = string.sub(note_symbols, degree_encoded["Mossteps"] + 1, degree_encoded["Mossteps"] + 1)
note_name = note_symbol .. string.rep(chroma_minus_symbol, chroma_count)
end
end
local degree_decoded = mosnot.mosstep_and_quality_to_degree(mossteps_transposed, degree_encoded['quality'], "m", "mosdegree", "abbreviated")
-- Pass the encoded degree, along with the other args
local degree_decoded = mosnot.decode_mosstep_quality(degree_encoded, "m", "mosdegree", "abbreviated")
-- Add to note names
-- Add to note names
Line 222: Line 269:
end
end
end
end
-- Last note in the gamut is the root up one equave
--local degree_for_equave = string.format("P%dmd", mossteps_per_equave)
--local degree_for_root = string.format("P0md", mossteps_per_equave)
--note_names[degree_for_equave] = note_names[degree_for_root]
return note_names
return note_names
Line 232: Line 274:


-- Helper function; generate the step vectors for every interval required for the table
-- Helper function; generate the step vectors for every interval required for the table
function p.get_mosstep_vectors(input_mos, number_of_alterations)
function p.calculate_mosstep_vectors(input_mos, number_of_alterations)
-- Default params
-- Default params
local input_mos = input_mos or mos.new(5, 2)
local input_mos = input_mos or mos.new(5, 2)
Line 280: Line 322:
-- j is the number of chromas to add or subtract from the base vector
-- j is the number of chromas to add or subtract from the base vector
-- Since a chroma is defined as (L-s), add j large steps and subtract j small steps from the current mosstep vector
-- Since a chroma is defined as (L-s), add j large steps and subtract j small steps from the current mosstep vector
local L_count = current_mosstep_vector['L'] + j
local L_count = current_mosstep_vector["L"] + j
local s_count = current_mosstep_vector['s'] - j
local s_count = current_mosstep_vector["s"] - j
local current_mosstep_vector = { ['L'] = L_count, ['s'] = s_count }
local current_mosstep_vector = { ["L"] = L_count, ["s"] = s_count }
table.insert(mosstep_vectors, current_mosstep_vector)
table.insert(mosstep_vectors, current_mosstep_vector)
end
end
Line 292: Line 334:


-- Helper function; generate the mosdegree names and their abbreviations for the mos
-- Helper function; generate the mosdegree names and their abbreviations for the mos
function p.get_mosdegree_names_and_abbrevs(input_mos, mos_prefix, number_of_alterations)
function p.calculate_mosdegree_names_and_abbrevs(input_mos, mos_prefix, number_of_alterations)
-- Default params
-- Default params
local input_mos = input_mos or mos.new(5, 2)
local input_mos = input_mos or mos.new(5, 2)
Line 304: Line 346:
-- Get the step counts for the bright and dark generators
-- Get the step counts for the bright and dark generators
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
local mossteps_per_dark_gen = mossteps_per_period - mossteps_per_bright_gen
Line 335: Line 377:
-- Diminished degree is formatted as "Diminished degree"; more than 1 augmentation is "2× Diminished", "3× Diminished", and so on
-- Diminished degree is formatted as "Diminished degree"; more than 1 augmentation is "2× Diminished", "3× Diminished", and so on
local dim_degree = ""
local dim_degree = ""
if j == 1 then dim_degree = string.format("Diminished %d-%sstep", mossteps, mos_prefix)
if j == 1 then dim_degree = string.format("Diminished %d-%sdegree", mossteps, mos_prefix)
else dim_degree = string.format("%d× Diminished %d-%sstep", j, mossteps, mos_prefix)
else dim_degree = string.format("%d× Diminished %d-%sdegree", j, mossteps, mos_prefix)
end
end
Line 349: Line 391:
-- Calculate the main degree name and abbreviation
-- Calculate the main degree name and abbreviation
local degree_name = string.format("Perfect %d-%sstep", mossteps, mos_prefix)
local degree_name = string.format("Perfect %d-%sdegree", mossteps, mos_prefix)
local abbrev_name = string.format("P%dmd", mossteps)
local abbrev_name = string.format("P%dmd", mossteps)
Line 361: Line 403:
-- Augmented degree is formatted as "Augmented degree"; more than 1 augmentation is "2× Augmented", "3× Augmented", and so on
-- Augmented degree is formatted as "Augmented degree"; more than 1 augmentation is "2× Augmented", "3× Augmented", and so on
local aug_degree = ""
local aug_degree = ""
if j == 1 then aug_degree = string.format("Augmented %d-%sstep", mossteps, mos_prefix)
if j == 1 then aug_degree = string.format("Augmented %d-%sdegree", mossteps, mos_prefix)
else aug_degree = string.format("%d× Augmented %d-%sstep", j, mossteps, mos_prefix)
else aug_degree = string.format("%d× Augmented %d-%sdegree", j, mossteps, mos_prefix)
end
end
Line 387: Line 429:
-- Diminished degree is formatted as "Diminished degree"; more than 1 augmentation is "2× Diminished", "3× Diminished", and so on
-- Diminished degree is formatted as "Diminished degree"; more than 1 augmentation is "2× Diminished", "3× Diminished", and so on
local dim_degree = ""
local dim_degree = ""
if dim_amount == 1 then dim_degree = string.format("Diminished %d-%sstep", mossteps, mos_prefix)
if dim_amount == 1 then dim_degree = string.format("Diminished %d-%sdegree", mossteps, mos_prefix)
else dim_degree = string.format("%d× Diminished %d-%sstep", dim_amount, mossteps, mos_prefix)
else dim_degree = string.format("%d× Diminished %d-%sdegree", dim_amount, mossteps, mos_prefix)
end
end
Line 401: Line 443:
-- Calculate the small and large names and abbreviations
-- Calculate the small and large names and abbreviations
-- Non-generator intervals for non-nL-ns mosses are minor (small) and major (large)
local small_degree_label = "Minor"
local small_degree_label = "Minor"
local large_degree_label = "Major"
local large_degree_label = "Major"
Line 420: Line 463:
-- Main operation
-- Main operation
local small_degree_name = string.format("%s %d-%sstep", small_degree_label, mossteps, mos_prefix)
local small_degree_name = string.format("%s %d-%sdegree", small_degree_label, mossteps, mos_prefix)
local large_degree_name = string.format("%s %d-%sstep", large_degree_label, mossteps, mos_prefix)
local large_degree_name = string.format("%s %d-%sdegree", large_degree_label, mossteps, mos_prefix)
local small_abbrev_name = string.format("%s%dmd", small_degree_abbrev, mossteps)
local small_abbrev_name = string.format("%s%dmd", small_degree_abbrev, mossteps)
local large_abbrev_name = string.format("%s%dmd", large_degree_abbrev, mossteps)
local large_abbrev_name = string.format("%s%dmd", large_degree_abbrev, mossteps)
Line 441: Line 484:
-- Augmented degree is formatted as "Augmented degree"; more than 1 augmentation is "2× Augmented", "3× Augmented", and so on
-- Augmented degree is formatted as "Augmented degree"; more than 1 augmentation is "2× Augmented", "3× Augmented", and so on
local aug_degree = ""
local aug_degree = ""
if aug_amount == 1 then aug_degree = string.format("Augmented %d-%sstep", mossteps, mos_prefix)
if aug_amount == 1 then aug_degree = string.format("Augmented %d-%sdegree", mossteps, mos_prefix)
else aug_degree = string.format("%d× Augmented %d-%sstep", aug_amount, mossteps, mos_prefix)
else aug_degree = string.format("%d× Augmented %d-%sdegree", aug_amount, mossteps, mos_prefix)
end
end
Line 460: Line 503:


-- Separate function for testing; the main "frame" function will call this
-- Separate function for testing; the main "frame" function will call this
function p.mos_degrees(input_mos, step_ratios, mos_prefix, show_abbreviations, number_of_alterations, ji_ratios, udp, notation)
function p.mos_degrees(input_mos, step_ratios, mos_prefix, show_abbreviations, number_of_alterations, ji_ratios, udp, notation, show_notation)
-- Default params
-- Default params; all parameters are already parsed
local input_mos = input_mos or mos.new(5, 2)
local input_mos = input_mos or mos.new(5, 2)
local step_ratios = step_ratios or {{2,1}, {3,1}, {3,2}}
local step_ratios = step_ratios or {{2,1}, {3,1}, {3,2}}
Line 468: Line 511:
local number_of_alterations = number_of_alterations or 1
local number_of_alterations = number_of_alterations or 1
local ji_ratios = ji_ratios or {["P0md"]="1/1"}
local ji_ratios = ji_ratios or {["P0md"]="1/1"}
--local udp = udp or {5,1} -- This is calculated later in the code
local udp = udp or {5,1}
local notation = "Default"
local notation = notation or mosnot.parse_notation("CDEFGAB; #; b")
local show_notation = show_notation == 1
-- Get the scale sig
-- Get the scale sig
Line 485: Line 529:
-- Get the step counts for the bright and dark generators
-- Get the step counts for the bright and dark generators
local bright_gen = mos.bright_gen(input_mos)
local bright_gen = mos.bright_gen(input_mos)
local steps_per_bright_gen = bright_gen['L'] + bright_gen['s']
local steps_per_bright_gen = bright_gen["L"] + bright_gen["s"]
local steps_per_dark_gen = mossteps_per_period - steps_per_bright_gen
local steps_per_dark_gen = mossteps_per_period - steps_per_bright_gen
-- Get the step counts as a vector (or associative array, rather)
-- Get the step counts as a vector (or associative array, rather)
local input_mos_step_vector = {['L'] = input_mos.nL, ['s'] = input_mos.ns}
local input_mos_step_vector = {["L"] = input_mos.nL, ["s"] = input_mos.ns}
-- What's the equave in cents?
-- What's the equave in cents?
Line 496: Line 540:
-- How many decimal places to round to? (hardcoded)
-- How many decimal places to round to? (hardcoded)
local round = 1
local round = 1
-- Precalculate row colors
local row_colors = p.calculate_row_colors(input_mos, number_of_alterations)


-- Precalculate the ets for each step ratio
-- Precalculate the ets for each step ratio
Line 507: Line 554:
-- Precalculate degree names, degree abbreviations, and mosstep vectors
-- Precalculate degree names, degree abbreviations, and mosstep vectors
local degree_names, degree_abbrevs = p.get_mosdegree_names_and_abbrevs(input_mos, mos_prefix, number_of_alterations)
local degree_names, degree_abbrevs = p.calculate_mosdegree_names_and_abbrevs(input_mos, mos_prefix, number_of_alterations)
local mosstep_vectors = p.get_mosstep_vectors(input_mos, number_of_alterations)
local mosstep_vectors = p.calculate_mosstep_vectors(input_mos, number_of_alterations)
-- Precalculate default comments for JI ratios; there's only two entries here
-- Precalculate default comments for JI ratios; there's only two entries here
Line 514: Line 561:
default_ji_comments["P0md"] = "1/1 (exact)"
default_ji_comments["P0md"] = "1/1 (exact)"
default_ji_comments[string.format("P%dmd", mossteps_per_equave)] = string.format("%s (exact)", rat.as_ratio(input_mos.equave))
default_ji_comments[string.format("P%dmd", mossteps_per_equave)] = string.format("%s (exact)", rat.as_ratio(input_mos.equave))
-- Precalculate notation in two steps, starting with the UDP
-- The default UDP corresponds to the middle mode. For mosses with an even
-- number of modes, there are two middle modes, so use the brighter of the
-- two instead.
-- If it's 5L 2s, default to the second-brightest mode.
local udp_default = { periods_per_equave * math.ceil((mossteps_per_period - 1)/ 2), periods_per_equave * math.floor((mossteps_per_period - 1) / 2) }
if scale_sig == "5L 2s" then
udp_default = { 5, 1 }
end
local udp = udp or udp_default
-- Then, using the UDP, get the notation
-- Then, using the UDP, get the notation
Line 531: Line 567:
-- If no notation is passed in, notation will not be displayed
-- If no notation is passed in, notation will not be displayed
local note_names = {}
local note_names = {}
local show_note_names = false
local root_note = ""
local root_note = ""
if notation == "Default" then
if show_notation then
if scale_sig == "5L 2s" then
note_names = p.calculate_note_names(input_mos, udp, notation["Naturals"], notation["Sharp"], notation["Flat"], number_of_alterations)
note_names = p.get_note_names(input_mos, udp, "CDEFGAB", "#", "b", number_of_alterations)
root_note = string.sub(notation["Naturals"], 1, 1)
root_note = "C"
show_note_names = true
else
local default_nominals = string.sub("JKLMNOPQRSTUVWXYZ", 1, mossteps_per_equave)
note_names = p.get_note_names(input_mos, udp, default_nominals, "&", "@", number_of_alterations)
root_note = "J"
show_note_names = true
end
elseif string.len(notation) > 0 then
local notation_parsed = mosnot.parse_notation(notation)
note_names = p.get_note_names(input_mos, udp, notation_parsed['Naturals'], notation_parsed["Sharp"], notation_parsed["Flat"], number_of_alterations)
root_note = string.sub(notation_parsed['Naturals'], 1, 1)
show_note_names = true
end
end
-- Create the table, starting with the headers
-- Create the table, starting with the headers
local result = '{| class="wikitable sortable"\n'
local result = "{| class=\"wikitable sortable mw-collapsible mw-collapsed\"\n"
-- First row
-- First row
result = result .. string.format("|+ Scale degree of %s\n", scale_sig)
result = result
result = result .. '! rowspan="2" class="unsortable" | Scale degree\n'
.. "|+ style=\"font-size: 105%%; white-space: nowrap;\" | " .. string.format("Scale degree of %s\n", scale_sig)
.. "|-\n"
.. "! rowspan=\"2\" class=\"unsortable\" | Scale degree\n"
-- Add column for abbreviations
-- Add column for abbreviations
-- Abbreviations do not use a mos-prefix or mos-name
-- Abbreviations do not use a mos-prefix or mos-name
if show_abbrevs then
if show_abbrevs then
result = result .. '! rowspan="2" class="unsortable" | Abbrev.\n'
result = result .. "! rowspan=\"2\" class=\"unsortable\" | Abbrev.\n"
end
end
-- Add column for note names
-- Add column for note names
if show_note_names then
if show_notation then
result = result .. string.format('! rowspan="2" class="unsortable" | On %s\n', root_note)
result = result .. string.format("! rowspan=\"2\" class=\"unsortable\" | On %s\n", root_note)
end
end
Line 573: Line 597:
-- Step ratio names, for reference
-- Step ratio names, for reference
local tamnams_step_ratios = {
local tamnams_step_ratios = {
['1:1'] = "Equalized",
["1:1"] = "Equalized",
['4:3'] = "Supersoft",
["4:3"] = "Supersoft",
['3:2'] = "Soft",
["3:2"] = "Soft",
['5:3'] = "Semisoft",
["5:3"] = "Semisoft",
['2:1'] = "Basic",
["2:1"] = "Basic",
['5:2'] = "Semihard",
["5:2"] = "Semihard",
['3:1'] = "Hard",
["3:1"] = "Hard",
['4:1'] = "Superhard",
["4:1"] = "Superhard",
['1:0'] = "Collapsed",
["1:0"] = "Collapsed",
}
}
Line 596: Line 620:
-- Add the step ratio name if there is one
-- Add the step ratio name if there is one
if step_ratio_name == nil then
if step_ratio_name == nil then
result = result .. '! colspan="2" |' .. et_as_string .. " (L:s = " .. step_ratio_key .. ")\n"
result = result .. "! colspan=\"2\" | " .. et_as_string .. " (L:s = " .. step_ratio_key .. ")\n"
else
else
result = result .. '! colspan="2" |' .. et_as_string .. " (" .. step_ratio_name .. ", L:s = " .. step_ratio_key .. ")\n"
result = result .. "! colspan=\"2\" | " .. et_as_string .. " (" .. step_ratio_name .. ", L:s = " .. step_ratio_key .. ")\n"
end
end
end
end
-- Add JI ratio column header
-- Add JI ratio column header
result = result .. '! Rowspan="2" class="unsortable" | Approx. JI Ratios\n'
result = result .. "! rowspan=\"2\" class=\"unsortable\" | Approx. JI Ratios\n"
-- Second row
-- Second row
Line 609: Line 633:
-- Add headers for the steps and cents up to 5 times
-- Add headers for the steps and cents up to 5 times
for i = 1, #step_ratios do
for i = 1, #step_ratios do
result = result .. '! Steps\n'
result = result .. "! Steps\n"
result = result .. '! Cents\n'
result = result .. "! Cents\n"
end
end
Line 617: Line 641:
for i = 1, #degree_names do
for i = 1, #degree_names do
-- Start new row
-- Start new row
result = result .. "|-\n"
-- Add row highlighting if provided
local row_color = row_colors[i]
if row_color == "" then
result = result .. "|-\n"
else
result = result .. string.format("|- style=\"background: %s\"\n", row_color)
end
-- Add degree name
-- Add degree name
Line 623: Line 653:
local degree_name = degree_names[i]
local degree_name = degree_names[i]
if string.find(degree_name, "Perfect") then
if string.find(degree_name, "Perfect") then
result = result .. string.format("| '''%s'''\n", degree_names[i])
if i == 1 then
result = result .. string.format("| '''%s (unison)'''\n", degree_names[i])
elseif i == #degree_names and equave_in_cents == 1200 then
result = result .. string.format("| '''%s (octave)'''\n", degree_names[i])
elseif i == #degree_names and equave_in_cents ~= 1200 then
result = result .. string.format("| '''%s (equave)'''\n", degree_names[i])
else
result = result .. string.format("| '''%s'''\n", degree_names[i])
end
else
else
result = result .. string.format("| %s\n", degree_names[i])
result = result .. string.format("| %s\n", degree_names[i])
Line 636: Line 674:
-- Add note names if allowed
-- Add note names if allowed
-- Use the degree_abbrev as the key when accessing key-value pairs
-- Use the degree_abbrev as the key when accessing key-value pairs
if show_note_names then
if show_notation then
result = result .. string.format("| %s\n", note_names[degree_abbrev])
result = result .. string.format("| %s\n", note_names[degree_abbrev])
end
end
Line 642: Line 680:
-- Add mossteps and cent values
-- Add mossteps and cent values
-- Rounding is hardcoded to one decimal place
-- Rounding is hardcoded to one decimal place
-- Also record the cent value for JI ratio search
local round = 1
local round = 1
local average_cents = 0
for j = 1, #ets_for_mos do
for j = 1, #ets_for_mos do
local etsteps = mosstep_vectors[i]['L'] * step_ratios[j][1] + mosstep_vectors[i]['s'] * step_ratios[j][2]
local etsteps = mosstep_vectors[i]["L"] * step_ratios[j][1] + mosstep_vectors[i]["s"] * step_ratios[j][2]
local cents = utils._round_dec(et.cents(ets_for_mos[j], etsteps), round)
local cents = utils._round_dec(et.cents(ets_for_mos[j], etsteps), round)
result = result .. string.format("| %s\n", etsteps)
result = result .. string.format("| %s\n", etsteps)
result = result .. string.format("| %s\n", cents)
result = result .. string.format("| %s\n", cents)
average_cents = average_cents + cents / #ets_for_mos
end
end
-- Calculate JI ratio approximations using jiraf module
-- For now:
-- - Cent value is the average of the sizes given the step ratios
-- - Tolerance is hardcoded to +/-15 cents
-- - Prime limit is hardocoded to 19
-- - Odd limit hardcoded to 49
--local approx_ratios = jiraf.find_ratios_for_cents(average_cents, 15, 19, 39)
--local ratios_as_text = jiraf.ratios_to_text(approx_ratios);
-- Add JI ratios if any
-- Add JI ratios if any
local ji_comment_entry = ""
local ji_comment_entry = ""
local default_ji_comment = default_ji_comments[degree_abbrev]
local default_ji_comment = default_ji_comments[degree_abbrev]
-- Add ratios found using jiraf
local entered_ji_comment = ji_ratios[degree_abbrev]
local entered_ji_comment = ji_ratios[degree_abbrev]
--local default_ji_comment = nil
--local entered_ji_comment = ratios_as_text
if default_ji_comment == nil and entered_ji_comment == nil then
if default_ji_comment == nil and entered_ji_comment == nil then
-- No comments
-- No comments
Line 679: Line 735:
function p.mos_degrees_frame(frame)
function p.mos_degrees_frame(frame)
-- Default param for input mos is 5L 2s
-- Default param for input mos is 5L 2s
local input_mos = mos.parse(frame.args['Scale Signature']) or mos.new(2, 5, 2)
local input_mos = mos.parse(frame.args["Scale Signature"]) or mos.new(2, 5, 2)
-- Get the scale sig; for calculating the mos prefix
-- Get the scale sig; for calculating the mos prefix
Line 685: Line 741:
-- Get the step ratio
-- Get the step ratio
local step_ratios = p.parse_step_ratio(frame.args['Step Ratio']) or p.parse_step_ratio("2/1")
local step_ratios = p.parse_step_ratio(frame.args["Step Ratio"]) or p.parse_step_ratio("2/1")
-- Default param for mos prefix
-- Default param for mos prefix
Line 692: Line 748:
-- If not left blank, use the prefix passed in instead
-- If not left blank, use the prefix passed in instead
local mos_prefix = "mos"
local mos_prefix = "mos"
if frame.args['MOS Prefix'] == "NONE" then
if frame.args["MOS Prefix"] == "NONE" then
mos_prefix = ""
mos_prefix = ""
elseif string.len(frame.args['MOS Prefix']) == 0 then
elseif string.len(frame.args["MOS Prefix"]) == 0 then
mos_prefix_lookup = p.get_mos_prefix(scale_sig)
mos_prefix_lookup = tamnams.lookup_prefix(input_mos) or ""
if string.len(mos_prefix_lookup) ~= 0 then
if string.len(mos_prefix_lookup) ~= 0 then
mos_prefix = mos_prefix_lookup
mos_prefix = mos_prefix_lookup
end
end
else
else
mos_prefix = frame.args['MOS Prefix']
mos_prefix = frame.args["MOS Prefix"]
end
end
-- Get whether to display abbreviations
-- Get whether to display abbreviations
local show_abbreviations = 0
local show_abbreviations = 0
if frame.args['Show Abbreviations'] == "1" or frame.args['Show Abbreviations'] == 1 then
if frame.args["Show Abbreviations"] == "1" or frame.args["Show Abbreviations"] == 1 then
show_abbreviations = 1
show_abbreviations = 1
end
end
Line 711: Line 767:
-- Get the number of alterations
-- Get the number of alterations
local number_of_alterations = 0
local number_of_alterations = 0
if string.len(frame.args['Number of Alterations']) ~= 0 then
if string.len(frame.args["Number of Alterations"]) ~= 0 then
number_of_alterations = tonumber(frame.args['Number of Alterations'])
number_of_alterations = tonumber(frame.args["Number of Alterations"])
end
end
-- Get JI ratios
-- Get JI ratios
local ji_ratios_parsed = {}
local ji_ratios_parsed = {}
if #frame.args['JI Ratios'] > 0 then
if #frame.args["JI Ratios"] > 0 then
-- If the comments can't be parsed, default to an empty table
-- If the comments can't be parsed, default to an empty table
ji_ratios_parsed = p.parse_kv_pairs(frame.args['JI Ratios']) or {}
ji_ratios_parsed = p.parse_kv_pairs(frame.args["JI Ratios"]) or {}
end
end
result = p.mos_degrees(input_mos, step_ratios, mos_prefix, show_abbreviations, number_of_alterations, ji_ratios_parsed)
-- Get the number of mossteps per period and equave, and periods per equave
 
-- Needed for calculating default UDP and notation
return result
local mossteps_per_equave = (input_mos.nL + input_mos.ns)
local periods_per_equave = utils._gcd(input_mos.nL, input_mos.ns)
local mossteps_per_period = mossteps_per_equave / periods_per_equave
-- Get UDP
-- If no UDP is found, a default will be calculated as the middle mode, or the
-- brighter of two middle modes (as with an even number of modes in a mos)
local udp_parsed = { periods_per_equave * math.ceil((mossteps_per_period - 1)/ 2), periods_per_equave * math.floor((mossteps_per_period - 1) / 2) }
if scale_sig == "5L 2s" then
udp_parsed = { 5, 1 }
end
if #frame.args["UDP"] > 0 then
udp_parsed = mosnot.parse_udp(frame.args["UDP"])
end
-- Get notation
-- This also determines whether to show notation
-- Typing in "Default" is a shortcut to default notation, wherein standard notation (for 5L 2s) or diamond-mos (for other mosses) is used
local notation_parsed = {}
local show_notation = 0
if #frame.args["Notation"] > 0 then
if frame.args["Notation"] == "Default" and scale_sig == "5L 2s" then
notation_parsed = { ["Naturals"] = "CDEFGAB", ["Sharp"] = "#", ["Flat"] = "b" }
show_notation = 1
elseif frame.args["Notation"] == "Default" and scale_sig ~= "5L 2s" then
local default_nominals = "JKLMNOPQRSTUVWXYZ"
notation_parsed = { ["Naturals"] = string.sub(default_nominals, 1, mossteps_per_equave), ["Sharp"] = "&", ["Flat"] = "@" }
show_notation = 1
else
notation_parsed = mosnot.parse_notation(frame.args["Notation"])
if notation_parsed ~= nil then
show_notation = 1
end
end
end
result = p.mos_degrees(input_mos, step_ratios, mos_prefix, show_abbreviations, number_of_alterations, ji_ratios_parsed, udp_parsed, notation_parsed, show_notation)
-- Debugger
local debugg = yesno(frame.args["debug"])
if debugg == true then
result = "<syntaxhighlight lang=\"wikitext\">" .. result .. "</syntaxhighlight>"
end
return frame:preprocess(result)
end
end


return p
return p