|
|
| Line 1: |
Line 1: |
| local mos = require('Module:MOS') | | local mos = require('Module:MOS') |
| | local et = require('Module:ET') |
| local rat = require('Module:Rational') | | local rat = require('Module:Rational') |
| local utils = require('Module:Utils') | | local utils = require('Module:Utils') |
| local mosnot = require('Module:MOS notation') | | local mosnot = require('Module:MOS notation') -- Contains the important functions |
| local et = require('Module:ET')
| |
| local p = {} | | local p = {} |
|
| |
|
| -- Version 2 of the mos degrees moudle (by ganaraminukshuk) | | -- TODO: For preprocessing, add bools for whether a note is a nominal, the |
| -- This is based more off of the mos intervals module and older, non-tempalte tables that feature JI ratios,
| | -- unison, the period, or the equave, so the main function doesn't have to |
| -- and doesn't depend on genchains (except for note names) to calculate cent values or degree names.
| | -- calculate them |
| -- Current TODO list:
| |
| -- - Optional note names column
| |
| | |
| -- 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
| |
| | |
| -- Helper function
| |
| -- Parses pairs of elements separated by a colon
| |
| -- A pair must be two elements or it will be returned as an empty array
| |
| function p.parse_pair(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
| |
| if #parsed == 2 then
| |
| return parsed
| |
| else
| |
| return {}
| |
| end
| |
| end
| |
| | |
| -- Helper function | |
| -- Takes a list of semicolon-delimited pairs and returns a map
| |
| -- (or dictionary or associative array) of key-value pairs
| |
| -- Each entry is colon-delimited as key : pair
| |
| function p.parse_kv_pairs(unparsed)
| |
| -- Tokenize the string of unparsed pairs
| |
| local parsed = p.parse_entries(unparsed)
| |
| -- Then tokenize the tokens into key-value pairs
| |
| local pairs_ = {}
| |
| for i = 1, #parsed do
| |
| local pair = p.parse_pair(parsed[i])
| |
| if #pair == 2 then
| |
| pairs_[pair[1]] = pair[2]
| |
| end
| |
| end
| |
| return pairs_
| |
| end
| |
|
| |
|
| -- Helper function | | -- Helper function |
| Line 63: |
Line 14: |
| -- where each step ratio is separated with a slash | | -- where each step ratio is separated with a slash |
| -- EG, "2/1; 3/1; 3/2" becomes {{2, 1}, {3, 1}, {3, 2}} | | -- EG, "2/1; 3/1; 3/2" becomes {{2, 1}, {3, 1}, {3, 2}} |
| -- NOTE: module relies on mosnot (mos notation) to parse step ratios
| | function p.parse_step_ratios(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 |
| Line 88: |
Line 38: |
|
| |
|
| -- Helper function | | -- Helper function |
| -- Takes in a step pattern and a quantity of mossteps and calculates the number | | -- Parses genchain extend values, where the first value is for the ascending |
| -- of large and small steps in that interval (or substring), returned as an | | -- chain and the second value is for the descending chain |
| -- associative array containing the large and small step counts.
| | function p.parse_genchain_extend(unparsed) |
| -- It's an associative array b/c that's how the brightgen function in the mos
| | local parsed = {} |
| -- module works.
| | for entry in string.gmatch(unparsed, '([^,]+)') do |
| function p.mosstep_pattern_to_vector(mosstep_pattern, mossteps) | | local trimmed = entry:gsub("^%s*(.-)%s*$", "%1") |
| local large_step_count = 0 | | table.insert(parsed, trimmed) -- Add to array |
| local small_step_count = 0
| |
|
| |
| for i = 1, mossteps do | |
| local step = string.sub(mosstep_pattern, i, i)
| |
| if step == "L" then | |
| large_step_count = large_step_count + 1
| |
| elseif step == "s" then
| |
| small_step_count = small_step_count + 1
| |
| end | |
| end | | end |
|
| |
| local mosstep_vector = { ['L'] = large_step_count, ['s'] = small_step_count }
| |
| return mosstep_vector
| |
| end
| |
|
| |
| -- Helper function
| |
| -- Takes in a mosstep (as an assoc. array containing the number of L's and s's),
| |
| -- and a step ratio (as 2-element array containing the sizes of L and s) and
| |
| -- calculates number of et-steps.
| |
| function p.interval_to_etsteps(mosstep_vector, step_ratios)
| |
| return mosstep_vector['L'] * step_ratios[1] + mosstep_vector['s'] * step_ratios[2]
| |
| end
| |
|
| |
| -- Helper function
| |
| -- Extracts the prefix from the mos module, without the dash and without any text after that
| |
| -- May need to revisit to clean up code since this splits text at the "-".
| |
| function p.get_mos_prefix(scale_sig)
| |
| local unparsed = mos.tamnams_prefix[scale_sig]
| |
|
| |
| local parsed = {}
| |
| | | |
| if unparsed == nil then | | if #parsed == 1 then |
| return "mos" | | return { tonumber(parsed[1]), tonumber(parsed[1]) } |
| else | | else |
| for entry in string.gmatch(unparsed, '([^-]+)') do | | return { tonumber(parsed[1]), tonumber(parsed[2]) } |
| local trimmed = entry:gsub("^%s*(.-)%s*$", "%1")
| |
| table.insert(parsed, trimmed) -- Add to array
| |
| end
| |
| return parsed[1]
| |
| end | | end |
| end | | end |
|
| |
|
| -- Helper function | | -- Helper function; creates the column for the note name |
| -- Calculates note names and stores it in an associative array | | -- Decoupled degree and note name functions allow note names being omitted. |
| -- Default notation is diamond-mos, unless it's 5L 2s, then it's standard notation
| | function p.preprocess_note_names(input_mos, udp, note_symbols, sharp_symbol, flat_symbol, asc_chain_length, des_chain_length) |
| function p.get_note_names(input_mos, udp, note_symbols, chroma_plus_symbol, chroma_minus_symbol, number_of_alterations) | | -- Test parameters |
| -- 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, 2) |
| local udp = {5,1} or udp | | local udp = udp or { 5, 1 } |
| local note_symbols = note_symbols or "CDEFGAB" | | local note_symbols = note_symbols or "CDEFGAB" |
| local chroma_plus_symbol = chroma_plus_symbol or "#" | | local sharp_symbol = sharp_symbol or "#" |
| local chroma_minus_symbol = chroma_minus_symbol or "b" | | local flat_symbol = flat_symbol or "b" |
| local number_of_alterations = number_of_alterations or 1 | | local asc_chain_length = input_mos.nL * 2 + input_mos.ns |
| | local des_chain_length = input_mos.nL * 2 + input_mos.ns |
| | ]]-- |
| | | |
| -- Get the number of mossteps per period and equave | | -- Get the number of mossteps per period and equave |
| Line 154: |
Line 73: |
| local mossteps_per_period = mossteps_per_equave / periods_per_equave | | local mossteps_per_period = mossteps_per_equave / periods_per_equave |
| | | |
| -- Get the number of generators going up and down from the UDP | | -- How long are the initial genchain lengths? (These correspond to the UDP) |
| local generators_up = udp[1] | | local gens_up_per_period = udp[1] / periods_per_equave |
| local generators_down = udp[2] | | local gens_dn_per_period = udp[2] / periods_per_equave |
|
| |
| -- How long is the inital genchain for notes without accidentals?
| |
| local gens_up_per_period = generators_up / periods_per_equave
| |
| local gens_down_per_period = generators_down / periods_per_equave
| |
|
| |
| -- How long should the genchain extend after the initial genchain?
| |
| -- The initial genchain lengths are determined by the U and D in the UDP
| |
| -- The final genchain length is the following: (x + y) * (alterations + 1)
| |
| local ascending_genchain_length = (mossteps_per_period) * (number_of_alterations + 1)
| |
| local descending_genchain_length = (mossteps_per_period) * (number_of_alterations + 1)
| |
|
| |
| -- Get the ascending and descending genchains
| |
| -- The genchains are notationally agnostic so notation needs to be applied to them
| |
| local ascending_genchain = mosnot.mos_nomacc_chain(input_mos, gens_up_per_period, ascending_genchain_length, true)
| |
| local descending_genchain = mosnot.mos_nomacc_chain(input_mos, gens_down_per_period, descending_genchain_length, false)
| |
|
| |
| -- Also get the ascending and descending degreechains
| |
| -- These chains are encoded in a numeric form and must be converted into actual names
| |
| local ascending_degchain = mosnot.mos_degree_chain(input_mos, ascending_genchain_length, true)
| |
| local descending_degchain = mosnot.mos_degree_chain(input_mos, descending_genchain_length, false)
| |
| | | |
| -- Create an empty asoociative array | | -- Get the genchains |
| local note_names = {} | | local asc_genchain = mosnot.mos_nomacc_chain(input_mos, gens_up_per_period, asc_chain_length, true) |
| | local des_genchain = mosnot.mos_nomacc_chain(input_mos, gens_dn_per_period, des_chain_length, false) |
| | | |
| -- Add the notes to the array | | -- Calculate the entries for each cell |
| for j = 1, periods_per_equave do | | local column = {} |
| for i = 1, #ascending_genchain[j] do | | for i = 1, periods_per_equave do |
| -- Convert the notationally agnostic form into a form that uses given notation
| | -- Add degrees from ascending chain |
| local note = ascending_genchain[j][i] | | for j = 1, asc_chain_length do |
| local note_symbol = string.sub(note_symbols, note['mossteps'] + 1, note['mossteps'] + 1)
| | local mossteps = asc_genchain[i][j]['mossteps'] |
| local chroma_count = note['chromas'] | | local chromas = asc_genchain[i][j]['chromas'] |
| local note_name = note_symbol .. string.rep(chroma_plus_symbol, chroma_count)
| |
| | | |
| -- Convert the encoded degree into text | | -- Find the note name |
| local degree_encoded = ascending_degchain[j][i] | | local note_symbol = string.sub(note_symbols, mossteps + 1, mossteps + 1) |
| local degree_decoded = mosnot.mosstep_and_quality_to_degree(degree_encoded['mossteps'], degree_encoded['quality'], "m", "mosdegree", "abbreviated") | | local note_name = mosnot.mosstep_and_chroma_to_note_name(mossteps, chromas, note_symbol, sharp_symbol) |
|
| | |
| -- Add to note names
| | table.insert(column, note_name) |
| note_names[degree_decoded] = note_name | | end |
| | |
| | -- Calculate the stop value for the for loop as being 1 or 2, depending |
| | -- on whether this is the last period or not |
| | local stop_value = 1 |
| | if i ~= periods_per_equave then |
| | stop_value = stop_value + 1 |
| end | | end |
| | | |
| for i = 1, #descending_genchain[j] do | | -- Add degrees from descending chain |
| -- Convert the notationally agnostic form into a form that uses given notation
| | -- The descending chain differs from the ascending chain: |
| local note = descending_genchain[j][i] | | -- - The descending chain should follow after the ascending chain. |
| local note_symbol = string.sub(note_symbols, note['mossteps'] + 1, note['mossteps'] + 1)
| | -- - The descending chain's entries should be added backwards and skip |
| local chroma_count = note['chromas'] * -1 | | -- the root. |
| local note_name = note_symbol .. string.rep(chroma_minus_symbol, chroma_count)
| | -- - This way, if the mos is multi-period, the root of the next period's |
| | -- ascending chain (which is the same as the current period's descend- |
| | -- ing chain) won't be added twice. |
| | -- - If the period is the last period, add the root as the equave. |
| | for j = des_chain_length, stop_value, -1 do |
| | local mossteps = des_genchain[i][j]['mossteps'] |
| | local chromas = des_genchain[i][j]['chromas'] |
| | | |
| -- Convert the encoded degree into text | | -- Find the note name |
| local degree_encoded = descending_degchain[j][i]
| | -- If the mosstep is the root of the period, add a period to it |
|
| | local note_symbol = string.sub(note_symbols, mossteps + 1, mossteps + 1) |
| -- For the descending chain, any mossteps that correspond to the root of | | if mossteps % mossteps_per_period == 0 then |
| -- a period should correspond instead to the root one period up (EG, if
| | mossteps = mossteps + mossteps_per_period |
| -- the root refers to the unison, it should be the degree one octave up)
| |
| local mossteps_transposed = degree_encoded['mossteps'] | |
| if mossteps_transposed % mossteps_per_period == 0 then | |
| mossteps_transposed = mossteps_transposed + mossteps_per_period | |
| end | | end |
| | local note_name = mosnot.mosstep_and_chroma_to_note_name(mossteps, chromas, note_symbol, flat_symbol) |
| | | |
| local degree_decoded = mosnot.mosstep_and_quality_to_degree(mossteps_transposed, degree_encoded['quality'], "m", "mosdegree", "abbreviated") | | table.insert(column, note_name) |
|
| |
| -- Add to note names
| |
| note_names[degree_decoded] = note_name
| |
| end | | end |
| end | | end |
| | | |
| -- Last note in the gamut is the root up one equave
| | return column |
| --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 | |
| end | | end |
|
| |
|
| -- Helper function; generate the step vectors for every interval required for the table | | -- Helper function; creates the column for the degree name |
| function p.get_mosstep_vectors(input_mos, number_of_alterations) | | -- Decoupled degree and note name functions allow note names being omitted. |
| -- Default params | | function p.preprocess_degrees(input_mos, asc_chain_length, des_chain_length, prefix, notation) |
| local input_mos = input_mos or mos.new(5, 2) | | -- Test parameters |
| local number_of_alterations = number_of_alterations or 0 | | --[[ |
| | local input_mos = input_mos or mos.new(5, 2, 2) |
| | local asc_chain_length = input_mos.nL * 2 + input_mos.ns |
| | local des_chain_length = input_mos.nL * 2 + input_mos.ns |
| | local prefix = "mos" |
| | local notation = "mosstep" |
| | ]]-- |
| | | |
| -- Get the brightest mode | | -- Get the number of mossteps per period and equave |
| local brightest_mode = mos.brightest_mode(input_mos) | | local periods_per_equave = utils._gcd(input_mos.nL, input_mos.ns) |
| | | |
| -- Get the number of mossteps per period and equave | | -- Get the degrees |
| local mossteps_per_equave = (input_mos.nL + input_mos.ns) | | local asc_degrees = mosnot.mos_degree_chain(input_mos, asc_chain_length, true) |
| local mossteps_per_period = mossteps_per_equave / utils._gcd(input_mos.nL, input_mos.ns) | | local des_degrees = mosnot.mos_degree_chain(input_mos, des_chain_length, false) |
| | | |
| -- Add intervals and their alterations, using the large interval size as the zero point for alterations | | -- Calculate the entries for each cell |
| local mosstep_vectors = {} | | local column = {} |
| for i = 1, mossteps_per_equave + 1 do | | for i = 1, periods_per_equave do |
| local mossteps = i - 1
| | -- Add degrees from ascending chain |
| | for j = 1, asc_chain_length do |
| | local mossteps = asc_degrees[i][j]['mossteps'] |
| | local quality = asc_degrees[i][j]['quality'] |
| | |
| | -- Find the degree name |
| | -- If the degree is the perfect 0-mosdegree, append "unison" |
| | local degree_name = mosnot.mosstep_and_quality_to_degree(mossteps, quality, prefix, notation) |
| | if mossteps == 0 and quality == 0 then |
| | degree_name = degree_name .. " (unison)" |
| | end |
| | |
| | table.insert(column, degree_name) |
| | end |
| | | |
| -- Consecutive alterations are always one chroma apart | | -- Calculate the stop value for the for loop as being 1 or 2, depending |
| -- With a perfect non-generator interval, alterations are added by going down and up the same amount
| | -- on whether this is the last period or not |
| -- With all other intervals, since there are two sizes and the large interval size is treated as the zero point,
| | local stop_value = 1 |
| -- alterations are instead by going down n+1 chromas, then going up n chromas
| | if i ~= periods_per_equave then |
| -- With the unison, don't go down (only up), and with the equave, don't go up (only down). | | stop_value = stop_value + 1 |
| local min_alterations = 0 | |
| local max_alterations = 0
| |
|
| |
| if mossteps == 0 then | |
| -- Unison; the min number of alterations is 0 | |
| min_alterations = 0
| |
| max_alterations = number_of_alterations
| |
| elseif mossteps == mossteps_per_equave then
| |
| -- Equave; the max number of alterations is 0
| |
| min_alterations = -number_of_alterations
| |
| max_alterations = 0
| |
| elseif mossteps % mossteps_per_period == 0 then
| |
| -- Non-unison non-equave periods; the max and min have the "distance" from the zero point
| |
| min_alterations = -number_of_alterations
| |
| max_alterations = number_of_alterations
| |
| else
| |
| -- All other intervals; the min's distance is one more than the max's distance
| |
| min_alterations = -number_of_alterations - 1
| |
| max_alterations = number_of_alterations
| |
| end | | end |
| | | |
| -- Get the current mosstep vector based on the brightest mode | | -- Add degrees from descending chain |
| local current_mosstep_vector = p.mosstep_pattern_to_vector(brightest_mode, mossteps) | | -- The descending chain differs from the ascending chain: |
| for j = min_alterations, max_alterations do | | -- - The descending chain should follow after the ascending chain. |
| -- j is the number of chromas to add or subtract from the base vector
| | -- - The descending chain's entries should be added backwards and skip |
| -- Since a chroma is defined as (L-s), add j large steps and subtract j small steps from the current mosstep vector
| | -- the root. |
| local L_count = current_mosstep_vector['L'] + j | | -- - This way, if the mos is multi-period, the root of the next period's |
| local s_count = current_mosstep_vector['s'] - j | | -- ascending chain (which is the same as the current period's descend- |
| | -- ing chain) won't be added twice. |
| | -- - If the period is the last period, add the root as the equave. |
| | for j = des_chain_length, stop_value, -1 do |
| | local mossteps = des_degrees[i][j]['mossteps'] |
| | local quality = des_degrees[i][j]['quality'] |
| | | |
| local current_mosstep_vector = { ['L'] = L_count, ['s'] = s_count } | | -- Find the degree name |
| table.insert(mosstep_vectors, current_mosstep_vector) | | -- If the degree corresponds to the equave, say it's the equave |
| | local degree_name = mosnot.mosstep_and_quality_to_degree(mossteps, quality, prefix, notation) |
| | |
| | -- If j is ever 1, then the mosdegree is the equave |
| | -- This only happens if the current period is the last period |
| | -- If the equave is 2/1, that's the octave |
| | if j == 1 then |
| | if rat.eq(input_mos.equave, 2) then |
| | degree_name = degree_name .. " (octave)" |
| | else |
| | degree_name = degree_name .. " (equave)" |
| | end |
| | end |
| | |
| | table.insert(column, degree_name) |
| end | | end |
| end | | end |
| | | |
| return mosstep_vectors | | return column |
| end | | end |
|
| |
|
| -- Helper function; generate the mosdegree names and their abbreviations for the mos | | -- Helper function |
| function p.get_mosdegree_names_and_abbrevs(input_mos, mos_prefix, number_of_alterations) | | -- Creates the columns for the step and cent sizes |
| -- Default params | | -- Separating this into its own function makes it easy to add colums for |
| local input_mos = input_mos or mos.new(5, 2) | | -- different step ratios in the same table. |
| local number_of_alterations = number_of_alterations or 0 | | function p.preprocess_steps_and_cents(input_mos, step_ratio, asc_chain_length, des_chain_length) |
| local mos_prefix = mos_prefix or "mos" | | -- Test parameters |
| | --[[ |
| | local input_mos = input_mos or mos.new(5, 2, 2) |
| | local step_ratio = { 2, 1 } |
| | local asc_chain_length = input_mos.nL * 2 + input_mos.ns |
| | local des_chain_length = input_mos.nL * 2 + input_mos.ns |
| | ]]-- |
| | | |
| -- Get the number of mossteps per period and equave | | -- Get the number of mossteps per period and equave |
| local mossteps_per_equave = (input_mos.nL + input_mos.ns) | | local mossteps_per_equave = input_mos.nL + input_mos.ns |
| local mossteps_per_period = mossteps_per_equave / utils._gcd(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 |
| | |
| | -- What et is produced given the step ratio and equave? |
| | local steps_in_et = input_mos.nL * step_ratio[1] + input_mos.ns * step_ratio[2] |
| | local et_for_mos = et.new(steps_in_et, input_mos.equave) |
| | | |
| -- Get the step counts for the bright and dark generators | | -- How many esteps per period? Per bright/dark gen? |
| | local esteps_per_period = steps_in_et / periods_per_equave |
| 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 esteps_per_bright_gen = bright_gen['L'] * step_ratio[1] + bright_gen['s'] * step_ratio[2] |
| local mossteps_per_dark_gen = mossteps_per_period - mossteps_per_bright_gen | | local esteps_per_dark_gen = esteps_per_period - esteps_per_bright_gen |
| | |
| | -- How many decimal places to round to? |
| | local round = 1 |
| | | |
| -- Main loop | | -- Calculate the entries for each row |
| -- Interval qualities depend on whether the intervals are generators or if there is only one size. | | local rows = {} |
| -- Cases for which there are alterations either below or above the main interval sizes, but not both: | | for i = 1, periods_per_equave do |
| -- - If the interval class is the unison, there are no extensions before it and there is only one size (perfect).
| | -- Add step/cent values for ascending chain |
| -- - If the interval class is the equave, there are no extensions after it and there is only one size (perfect).
| | for j = 1, asc_chain_length do |
| -- Cases for which there are alterations above and below the main interval sizes:
| | |
| -- - If the interval class is a non-unison non-equave period, there are extensions before and after and there is only one size (perfect).
| | -- Find the estep count |
| -- - If the interval class is a non-generator interval, or is the generator for an nL ns mos, there are extensions before and after and the sizes are major and minor.
| | local estep_count = ((j - 1) * esteps_per_bright_gen) % esteps_per_period + (i - 1) * esteps_per_period |
| -- - If the interval class is the bright generator, there are extensions before and after and the sizes are perfect (large) and diminished (small).
| | |
| -- - If the interval class is the dark generator, there are extensions before and after and the sizes are augmented (large) and perfect (small).
| | -- Find the cent value, rounded |
| local mosdegree_names = {}
| | local cent_value = utils._round_dec(et.cents(et_for_mos, estep_count), round) |
| local mosdegree_abbrevs = {}
| | |
| for i = 1, mossteps_per_equave + 1 do
| | -- Add the row |
| -- For calculating mossteps
| | local row = { estep_count, cent_value } |
| local mossteps = i - 1 | | table.insert(rows, row) |
| | end |
| | | |
| -- For bright and dark gens | | -- Calculate the stop value for the for loop as being 1 or 2, depending |
| local is_nL_ns = input_mos.nL == input_mos.ns | | -- on whether this is the last period or not |
| local is_bright_gen = mossteps % mossteps_per_period == mossteps_per_bright_gen and not is_nL_ns | | local stop_value = 1 |
| local is_dark_gen = mossteps % mossteps_per_period == mossteps_per_dark_gen and not is_nL_ns | | if i ~= periods_per_equave then |
| | stop_value = stop_value + 1 |
| | end |
| | | |
| if mossteps % mossteps_per_period == 0 then | | -- Add step/cent values for ascending chain |
| -- For perfect intervals
| | -- The descending chain differs from the ascending chain: |
| -- Operation for pre-alterations (diminshed degrees)
| | -- - The descending chain should follow after the ascending chain. |
| if number_of_alterations > 0 and mossteps ~= 0 then
| | -- - The descending chain's entries should be added backwards and skip |
| for j = number_of_alterations, 1, -1 do
| | -- the root. |
| -- Diminished degree is formatted as "Diminished degree"; more than 1 augmentation is "2× Diminished", "3× Diminished", and so on
| | -- - This way, if the mos is multi-period, the root of the next period's |
| local dim_degree = ""
| | -- ascending chain (which is the same as the current period's descend- |
| if j == 1 then dim_degree = string.format("Diminished %d-%sstep", mossteps, mos_prefix)
| | -- ing chain) won't be added twice. |
| else dim_degree = string.format("%d× Diminished %d-%sstep", j, mossteps, mos_prefix)
| | -- - If the period is the last period, add the root as the equave. |
| end
| | for j = des_chain_length, stop_value, -1 do |
|
| |
| -- Format abbreviation as repetitions of the letter "d", followed by the mosdegree
| |
| local dim_abbrev = string.rep("d", j) .. string.format("%dmd", mossteps)
| |
|
| |
| -- Insert
| |
| table.insert(mosdegree_names, dim_degree)
| |
| table.insert(mosdegree_abbrevs, dim_abbrev)
| |
| end
| |
| end
| |
| | | |
| -- Calculate the main degree name and abbreviation | | -- Find the estep count |
| local degree_name = string.format("Perfect %d-%sstep", mossteps, mos_prefix) | | local estep_count = ((j - 1) * esteps_per_dark_gen) % esteps_per_period + (i - 1) * esteps_per_period |
| local abbrev_name = string.format("P%dmd", mossteps)
| |
| | | |
| -- Main operation | | -- Find the cent value |
| table.insert(mosdegree_names, degree_name) | | local cent_value = utils._round_dec(et.cents(et_for_mos, estep_count), round) |
| table.insert(mosdegree_abbrevs, abbrev_name)
| |
| | | |
| -- Operation for post-alterations (augmented degrees) | | -- If j is ever 1, then the cent and step values are for the equave |
| if number_of_alterations > 0 and mossteps ~= mossteps_per_equave then
| | -- This only happens if the current period is the last period |
| for j = 1, number_of_alterations do
| | if j == 1 then |
| -- Augmented degree is formatted as "Augmented degree"; more than 1 augmentation is "2× Augmented", "3× Augmented", and so on
| | cent_value = utils._round_dec(rat.cents(input_mos.equave), round) |
| local aug_degree = ""
| | estep_count = steps_in_et |
| if j == 1 then aug_degree = string.format("Augmented %d-%sstep", mossteps, mos_prefix)
| |
| else aug_degree = string.format("%d× Augmented %d-%sstep", j, mossteps, mos_prefix)
| |
| end
| |
|
| |
| -- Format abbreviation as repetitions of the letter "A", followed by the mosdegree
| |
| local aug_abbrev = string.rep("A", j) .. string.format("%dmd", mossteps)
| |
|
| |
| -- Insert
| |
| table.insert(mosdegree_names, aug_degree)
| |
| table.insert(mosdegree_abbrevs, aug_abbrev)
| |
| end
| |
| end
| |
| else
| |
| -- For intervals with two sizes
| |
| -- Operation for pre-alterations (diminshed degrees) | |
| if number_of_alterations > 0 and mossteps ~= 0 then
| |
| for j = number_of_alterations, 1, -1 do
| |
| -- The number of diminishings depends on whether the interval class is the bright gen; if so,
| |
| -- then one interval will already be diminished so intervals below that already start at 2xdim.
| |
| local dim_amount = 0
| |
| if is_bright_gen then dim_amount = 1 + j
| |
| else dim_amount = j
| |
| end
| |
|
| |
| -- Diminished degree is formatted as "Diminished degree"; more than 1 augmentation is "2× Diminished", "3× Diminished", and so on
| |
| local dim_degree = ""
| |
| if dim_amount == 1 then dim_degree = string.format("Diminished %d-%sstep", mossteps, mos_prefix)
| |
| else dim_degree = string.format("%d× Diminished %d-%sstep", dim_amount, mossteps, mos_prefix)
| |
| end
| |
|
| |
| -- Format abbreviation as repetitions of the letter "d", followed by the mosdegree
| |
| local dim_abbrev = string.rep("d", dim_amount) .. string.format("%dmd", mossteps)
| |
|
| |
| -- Insert
| |
| table.insert(mosdegree_names, dim_degree)
| |
| table.insert(mosdegree_abbrevs, dim_abbrev)
| |
| end
| |
| end
| |
|
| |
| -- Calculate the small and large names and abbreviations
| |
| local small_degree_label = "Minor"
| |
| local large_degree_label = "Major"
| |
| local small_degree_abbrev = "m"
| |
| local large_degree_abbrev = "M"
| |
| if is_bright_gen then
| |
| -- Bright gen: diminished (small) and perfect (large)
| |
| small_degree_label = "Diminished"
| |
| large_degree_label = "Perfect"
| |
| small_degree_abbrev = "d"
| |
| large_degree_abbrev = "P"
| |
| elseif is_dark_gen then
| |
| -- Dark gen: perfect (small) and augmentd (large)
| |
| small_degree_label = "Perfect" | |
| large_degree_label = "Augmented"
| |
| small_degree_abbrev = "P"
| |
| large_degree_abbrev = "A"
| |
| end | | end |
| | | |
| -- Main operation | | -- Add the row |
| local small_degree_name = string.format("%s %d-%sstep", small_degree_label, mossteps, mos_prefix) | | local row = { estep_count, cent_value } |
| local large_degree_name = string.format("%s %d-%sstep", large_degree_label, mossteps, mos_prefix) | | table.insert(rows, row) |
| local small_abbrev_name = string.format("%s%dmd", small_degree_abbrev, mossteps)
| |
| local large_abbrev_name = string.format("%s%dmd", large_degree_abbrev, mossteps)
| |
| table.insert(mosdegree_names, small_degree_name)
| |
| table.insert(mosdegree_names, large_degree_name)
| |
| table.insert(mosdegree_abbrevs, small_abbrev_name)
| |
| table.insert(mosdegree_abbrevs, large_abbrev_name)
| |
|
| |
| -- Operation for post-alterations (augmented degrees)
| |
| if number_of_alterations > 0 and mossteps ~= mossteps_per_equave then
| |
| for j = 1, number_of_alterations do
| |
| -- The number of augmentings depends on whether the interval class is the dark gen; if so,
| |
| -- then one interval will already be augmented so intervals above that already start at 2xaug.
| |
| local aug_amount = 0
| |
| if is_dark_gen then aug_amount = 1 + j
| |
| else aug_amount = j
| |
| end
| |
|
| |
| -- Augmented degree is formatted as "Augmented degree"; more than 1 augmentation is "2× Augmented", "3× Augmented", and so on
| |
| local aug_degree = ""
| |
| if aug_amount == 1 then aug_degree = string.format("Augmented %d-%sstep", mossteps, mos_prefix)
| |
| else aug_degree = string.format("%d× Augmented %d-%sstep", aug_amount, mossteps, mos_prefix)
| |
| end
| |
|
| |
| -- Format abbreviation as repetitions of the letter "A", followed by the mosdegree
| |
| local aug_abbrev = string.rep("A", aug_amount) .. string.format("%dmd", mossteps)
| |
|
| |
| -- Insert
| |
| table.insert(mosdegree_names, aug_degree)
| |
| table.insert(mosdegree_abbrevs, aug_abbrev)
| |
| end
| |
| end
| |
| end | | end |
| end | | end |
| | | |
| return mosdegree_names, mosdegree_abbrevs | | return rows |
| end | | end |
|
| |
|
| -- Separate function for testing; the main "frame" function will call this | | -- Algorithm: |
| function p.mos_degrees(input_mos, step_ratios, mos_prefix, show_abbreviations, number_of_alterations, ji_ratios, udp, notation, show_notation) | | -- Use the input mos, udp, and step ratio to find the genchains |
| -- Default params; all parameters are already parsed | | -- Using the genchains and UDP, find the mos's intervals/degrees |
| local input_mos = input_mos or mos.new(5, 2) | | -- Format the result as a table |
| local step_ratios = step_ratios or {{2,1}, {3,1}, {3,2}}
| | function p.mos_degrees_frame(frame) |
| local mos_prefix = mos_prefix or "mos"
| | -- Default parameters for input mos and step ratio (5L 2s and 2:1 step ratio) |
| local show_abbrevs = show_abbreviations == 1
| | local input_mos = mos.parse(frame.args['Scale Signature']) or mos.new(2, 5, 2) |
| local number_of_alterations = number_of_alterations or 1
| |
| local ji_ratios = ji_ratios or {["P0md"]="1/1"}
| |
| local udp = udp or {5,1}
| |
| local notation = notation or mosnot.parse_notation("CDEFGAB; #; b")
| |
| local show_notation = show_notation == 1
| |
| | | |
| -- Get the scale sig | | -- Step ratios |
| local scale_sig = mos.as_string(input_mos) | | -- Up to three step ratios can be entered; the default is only 2/1 |
| | -- Had to use parse function to make sure the default works |
| | local step_ratios = p.parse_step_ratios(frame.args['Step Ratio']) or p.parse_step_ratios("2/1") |
| | | |
| -- Get the brightest and darkest modes for the mos
| | -- Get the number of mossteps per period and equave |
| local brightest_mode = mos.brightest_mode(input_mos)
| | local mossteps_per_equave = input_mos.nL + input_mos.ns |
| local darkest_mode = string.reverse(brightest_mode)
| |
|
| |
| -- Get the number of mossteps per period and equave, and periods per equave | |
| local mossteps_per_equave = (input_mos.nL + input_mos.ns) | |
| local periods_per_equave = utils._gcd(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 | | local mossteps_per_period = mossteps_per_equave / periods_per_equave |
| | | |
| -- Get the step counts for the bright and dark generators | | -- If certain params were left blank and the scalesig is 5L 2s, the default |
| local bright_gen = mos.bright_gen(input_mos) | | -- params will be for standard notation |
| local steps_per_bright_gen = bright_gen['L'] + bright_gen['s']
| | local scale_sig = mos.as_string(input_mos) |
| local steps_per_dark_gen = mossteps_per_period - steps_per_bright_gen
| |
| | | |
| -- Get the step counts as a vector (or associative array, rather) | | -- The default UDP corresponds to the middle mode. For mosses with an even |
| local input_mos_step_vector = {['L'] = input_mos.nL, ['s'] = input_mos.ns} | | -- 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 = mosnot.parse_udp(frame.args['UDP']) or udp_default |
| | | |
| -- What's the equave in cents? | | -- Get genchain extend value |
| local equave_in_cents = rat.cents(input_mos.equave)
| | -- There are two genchain extend values for ascending and descending chains |
| | | -- The default value for both is the number of large steps per period, so |
| -- How many decimal places to round to? (hardcoded) | | -- this value is per genchain per period. |
| local round = 1
| | local genchain_extend_default = input_mos.nL / periods_per_equave |
| | | local genchain_extend = p.parse_genchain_extend(frame.args['Genchain Extend']) |
| -- Precalculate the ets for each step ratio | | local genchain_extend_up = genchain_extend[1] or genchain_extend_default |
| -- Each et is used to calculate a scale degree's cent value | | local genchain_extend_dn = genchain_extend[2] or genchain_extend_default |
| local ets_for_mos = {} | |
| for i = 1, #step_ratios do | |
| local etsteps = p.interval_to_etsteps(input_mos_step_vector, step_ratios[i])
| |
| local et_for_mos = et.new(etsteps, input_mos.equave)
| |
| table.insert(ets_for_mos, et_for_mos)
| |
| end | |
| | | |
| -- Precalculate degree names, degree abbreviations, and mosstep vectors | | -- Should a note names column be added? |
| local degree_names, degree_abbrevs = p.get_mosdegree_names_and_abbrevs(input_mos, mos_prefix, number_of_alterations) | | local add_note_names = frame.args['Notation'] ~= "NONE" |
| local mosstep_vectors = p.get_mosstep_vectors(input_mos, number_of_alterations)
| |
| | | |
| -- Precalculate default comments for JI ratios; there's only two entries here | | -- Get notation: naturals (or nominals), sharp symbol, and flat symbol |
| local default_ji_comments = {} | | local notation_default = { ['Naturals'] = string.sub("JKLMNOPQRSTUVWXYZ", 1, mossteps_per_equave), ['Sharp'] = "&", ['Flat'] = "@" } |
| default_ji_comments["P0md"] = "1/1 (exact)" | | if scale_sig == "5L 2s" then |
| default_ji_comments[string.format("P%dmd", mossteps_per_equave)] = string.format("%s (exact)", rat.as_ratio(input_mos.equave))
| | notation_default['Naturals'] = "CDEFGAB" |
| | notation_default['Sharp'] = "#" |
| | notation_default['Flat'] = "b" |
| | end |
| | local notation = mosnot.parse_notation(frame.args['Notation']) or notation_default |
| | local note_symbols = notation['Naturals'] |
| | local sharp_symbol = notation['Sharp'] |
| | local flat_symbol = notation['Flat'] |
| | | |
| -- Then, using the UDP, get the notation | | -- Get notational options |
| -- The default notation is either standard notation (for 5L 2s) or diamond-mos (most other mosses) | | local mos_prefix = "mos" -- TODO: add prefix lookup |
| -- If notation is passed in, use that instead | | if frame.args['MOS Prefix'] == "NONE" then |
| -- If no notation is passed in, notation will not be displayed | | mos_prefix = "" |
| local note_names = {}
| | elseif string.len(frame.args['MOS Prefix']) > 0 then |
| local root_note = ""
| | mos_prefix = frame.args['MOS Prefix'] |
| if show_notation then
| |
| note_names = p.get_note_names(input_mos, udp, notation['Naturals'], notation["Sharp"], notation["Flat"], number_of_alterations) | |
| --root_note = string.sub(notation['Naturals'], 1, 1) | |
| end | | end |
| | | |
| -- Create the table, starting with the headers | | -- Override values for testing |
| local result = '{| class="wikitable sortable"\n' | | --[[ |
| | local input_mos = mos.new(5, 2, 2) |
| | local step_ratios = {{ 2, 1 }, {3, 1}, {3, 2}} |
| | local udp = { 5, 1 } |
| | local note_symbols = "CDEFGAB" |
| | local sharp_symbol = "#" |
| | local flat_symbol = "b" |
| | 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 |
| | local scale_sig = mos.as_string(inpnut_mos) |
| | ]]-- |
| | |
| | -- Calculate the chain lengths |
| | local asc_chain_length = udp[1] / periods_per_equave + 1 + genchain_extend_up |
| | local des_chain_length = udp[2] / periods_per_equave + 1 + genchain_extend_dn |
| | |
| | -- Get the degrees and note names |
| | local degrees = p.preprocess_degrees (input_mos, asc_chain_length, des_chain_length, mos_prefix) |
| | local note_names = p.preprocess_note_names(input_mos, udp, note_symbols, sharp_symbol, flat_symbol, asc_chain_length, des_chain_length) |
| | | |
| -- First row | | -- Get the step and cent values |
| result = result .. string.format("|+ Scale degree of %s\n", scale_sig) | | -- Do this for each step ratio |
| result = result .. '! rowspan="2" class="unsortable" | Scale degree\n' | | local steps_and_cents = {} |
| | for i = 1, #step_ratios do |
| | local cells = p.preprocess_steps_and_cents(input_mos, step_ratios[i], asc_chain_length, des_chain_length) |
| | table.insert(steps_and_cents, cells) |
| | end |
| | | |
| -- Add column for abbreviations | | -- Pre-calculate the ets and step counts for each step ratio |
| -- Abbreviations do not use a mos-prefix or mos-name | | local steps_in_ets = {} |
| if show_abbrevs then | | local ets_for_mos = {} |
| result = result .. '! rowspan="2" class="unsortable" | Abbrev.\n' | | for i = 1, #step_ratios do |
| | local steps_in_ets_i = input_mos.nL * step_ratios[i][1] + input_mos.ns * step_ratios[i][2] |
| | local ets_for_mos_i = et.new(steps_in_ets_i, input_mos.equave) |
| | table.insert(steps_in_ets, steps_in_ets_i) |
| | table.insert(ets_for_mos, ets_for_mos_i) |
| end | | end |
| | |
| | -- Format the output as a table, starting with the header row |
| | local result = '{| class="wikitable sortable"\n' |
| | result = result .. '! rowspan="2" |Scale degree\n' |
| | | |
| -- Add column for note names
| | if add_note_names then |
| if show_notation then | | result = result .. '! rowspan="2" |On ' .. string.sub(note_symbols, 1, 1) .. "\n" |
| result = result .. string.format('! rowspan="2" class="unsortable" | On %s\n', root_note) | |
| end | | end |
| | | |
| -- Add column headers for up to 5 different step ratios | | -- Add this once for every step ratio to be represented |
| for i = 1, #step_ratios do | | for i = 1, #step_ratios do |
| -- Step ratio names, for reference | | -- Add names for specific step ratios |
| local tamnams_step_ratios = { | | local step_ratio_names = { |
| ['1:1'] = "Equalized", | | ['1:1'] = "Equalized", |
| ['4:3'] = "Supersoft", | | ['4:3'] = "Supersoft", |
| Line 559: |
Line 424: |
| ['1:0'] = "Collapsed", | | ['1:0'] = "Collapsed", |
| } | | } |
|
| |
| -- Get name for step ratio
| |
| local step_ratio_simplified = mosnot.simplify_step_ratio(step_ratios[i]) | | local step_ratio_simplified = mosnot.simplify_step_ratio(step_ratios[i]) |
| local step_ratio_key = step_ratio_simplified[1] .. ":" .. step_ratio_simplified[2] | | local step_ratio_string = step_ratio_simplified[1] .. ":" .. step_ratio_simplified[2] |
| local step_ratio_name = tamnams_step_ratios[step_ratio_key] | | local step_ratio_name = step_ratio_names[step_ratio_string] |
| | | |
| -- Calculate the et for the mos with a given step ratio; this is needed to produce
| | -- Add column header text |
| -- the name for the et/edo
| |
| local et_for_mos = et.new(ets_for_mos[i].size, input_mos.equave)
| |
| local et_as_string = et.as_string(et_for_mos)
| |
|
| |
| -- 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(ets_for_mos[i]) .. " (L:s = " .. step_ratios[i][1] .. ":" .. step_ratios[i][2] .. ")\n" |
| else | | else |
| result = result .. '! colspan="2" |' .. et_as_string .. " (" .. step_ratio_name .. ", L:s = " .. step_ratio_key .. ")\n" | | result = result .. '! colspan="2" |' .. step_ratio_name .. " " .. scale_sig .. "\n" |
| | result = result .. '(' .. et.as_string(ets_for_mos[i]) .. ", L:s = " .. step_ratios[i][1] .. ":" .. step_ratios[i][2] .. ")\n" |
| end | | end |
| end | | end |
| | | |
| -- Add JI ratio column header | | -- Next row |
| result = result .. '! Rowspan="2" class="unsortable" | Approx. JI Ratios\n' | | result = result .. "|-\n" |
| | | |
| -- Second row
| | -- Add this once for every step ratio to be represented |
| result = result .. "|-\n"
| | result = result .. string.rep("! Steps\n! Cents\n", #step_ratios) |
| -- Add headers for the steps and cents up to 5 times | |
| for i = 1, #step_ratios do | |
| result = result .. '! Steps\n'
| |
| result = result .. '! Cents\n'
| |
| end
| |
| | | |
| -- Add in successive rows, containing the degree name, abbreviation (if applicable), | | -- Add each row, containing a degree, note name, step count, and cent value |
| -- note names (if applicable), step size (in steps and cents), and JI ratio
| | for i = 1, #degrees do |
| for i = 1, #degree_names do | | |
| -- Start new row | | -- Is the row for a nominal? (Nominals have no accidentals and are |
| result = result .. "|-\n" | | -- therefore strings of size 1) |
| | -- Nominals don't depend on step ratio |
| | local is_nominal = string.len(note_names[i]) == 1 |
| | | |
| -- Add degree name | | -- Add new row, with coloring |
| -- Make the text bold if the interval is a perfect interval | | if not is_nominal then |
| local degree_name = degree_names[i]
| | result = result .. '|- bgcolor="#eaeaff"\n' |
| if string.find(degree_name, "Perfect") then
| |
| result = result .. string.format("| '''%s'''\n", degree_names[i]) | |
| else | | else |
| result = result .. string.format("| %s\n", degree_names[i]) | | result = result .. "|-\n" |
| end | | end |
| | | |
| -- Add abbrev if allowed | | -- Is the row for a period? |
| local degree_abbrev = degree_abbrevs[i] | | -- Check whether it's a period only for the using the cent value for the |
| if show_abbrevs then | | -- first step ratio, since it'll be a period for the other ratios |
| result = result .. string.format("| %s\n", degree_abbrev)
| | local cent_value = steps_and_cents[1][i][1] -- First set of cells, current row, first column is current cent value |
| end
| | local is_period = cent_value % (steps_in_ets[1] / periods_per_equave) == 0 |
| | | |
| -- Add note names if allowed | | -- Add cell for degree |
| -- Use the degree_abbrev as the key when accessing key-value pairs | | -- Make any nominals that correspond to the period bold |
| if show_notation then | | if is_period and is_nominal then |
| result = result .. string.format("| %s\n", note_names[degree_abbrev]) | | result = result .. "| '''" .. degrees[i] .. "'''\n" |
| | else |
| | result = result .. "| " .. degrees[i] .. "\n" |
| end | | end |
| | | |
| -- Add mossteps and cent values | | -- Add cell for note name, if allowed |
| -- Rounding is hardcoded to one decimal place
| | if add_note_names then |
| local round = 1
| | result = result .. "| " .. note_names[i] .. "\n" |
| 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 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", cents)
| |
| end | | end |
| | | |
| -- Add JI ratios if any | | -- Add cells for step size |
| local ji_comment_entry = "" | | for j = 1, #step_ratios do |
| local default_ji_comment = default_ji_comments[degree_abbrev]
| | result = result .. "| " .. steps_and_cents[j][i][1] .. "\n" -- Steps |
| local entered_ji_comment = ji_ratios[degree_abbrev]
| | result = result .. "| " .. steps_and_cents[j][i][2] .. "¢\n" -- Cents |
| if default_ji_comment == nil and entered_ji_comment == nil then
| |
| -- No comments
| |
| result = result .. "|\n" | |
| elseif default_ji_comment ~= nil and entered_ji_comment == nil then
| |
| -- Default comments but no entered comments
| |
| result = result .. string.format("| %s\n", default_ji_comment)
| |
| elseif default_ji_comment == nil and entered_ji_comment ~= nil then
| |
| -- Entered comments but no default comments
| |
| result = result .. string.format("| %s\n", entered_ji_comment) | |
| else
| |
| -- Both comments present; default comments take precedence
| |
| result = result .. string.format("| %s, %s\n", default_ji_comment, entered_ji_comment)
| |
| end | | end |
| end | | end |
| | | |
| -- End of table
| |
| result = result .. "|}" | | result = result .. "|}" |
| | | |
| return result | | return result |
| end | | end |
|
| |
| -- This function is to be called by a template, with parameters
| |
| function p.mos_degrees_frame(frame)
| |
| -- Default param for input mos is 5L 2s
| |
| local input_mos = mos.parse(frame.args['Scale Signature']) or mos.new(2, 5, 2)
| |
|
| |
| -- Get the scale sig; for calculating the mos prefix
| |
| local scale_sig = mos.as_string(input_mos)
| |
|
| |
| -- Get the step ratio
| |
| local step_ratios = p.parse_step_ratio(frame.args['Step Ratio']) or p.parse_step_ratio("2/1")
| |
|
| |
| -- Default param for mos prefix
| |
| -- If "NONE" is given, no prefix will be used
| |
| -- If left blank, try to find the appropriate mos prefix, or else defualt to "mos"
| |
| -- If not left blank, use the prefix passed in instead
| |
| local mos_prefix = "mos"
| |
| if frame.args['MOS Prefix'] == "NONE" then
| |
| mos_prefix = ""
| |
| elseif string.len(frame.args['MOS Prefix']) == 0 then
| |
| mos_prefix_lookup = p.get_mos_prefix(scale_sig)
| |
| if string.len(mos_prefix_lookup) ~= 0 then
| |
| mos_prefix = mos_prefix_lookup
| |
| end
| |
| else
| |
| mos_prefix = frame.args['MOS Prefix']
| |
| end
| |
|
| |
| -- Get whether to display abbreviations
| |
| local show_abbreviations = 0
| |
| if frame.args['Show Abbreviations'] == "1" or frame.args['Show Abbreviations'] == 1 then
| |
| show_abbreviations = 1
| |
| end
| |
|
| |
| -- Get the number of alterations
| |
| local number_of_alterations = 0
| |
| if string.len(frame.args['Number of Alterations']) ~= 0 then
| |
| number_of_alterations = tonumber(frame.args['Number of Alterations'])
| |
| end
| |
|
| |
| -- Get JI ratios
| |
| local ji_ratios_parsed = {}
| |
| if #frame.args['JI Ratios'] > 0 then
| |
| -- If the comments can't be parsed, default to an empty table
| |
| ji_ratios_parsed = p.parse_kv_pairs(frame.args['JI Ratios']) or {}
| |
| end
| |
|
| |
| -- 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 = { 5, 1 }
| |
| if #frame.args["UDP"] > 0 then
| |
| udp_parsed = mosnot.parse_udp(frame.args["UDP"])
| |
| else
| |
| if scale_sig ~= "5L 2s" then
| |
| udp_default = { periods_per_equave * math.ceil((mossteps_per_period - 1)/ 2), periods_per_equave * math.floor((mossteps_per_period - 1) / 2) }
| |
| end
| |
| 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" }
| |
| elseif frame.args["Notation"] == "Default" and scale_sig ~= "5L 2s" then
| |
| local default_nominals = "JKLMNOPQRSTUVWXYZ"
| |
| local note_count = input_mos.nL + input_mos.ns
| |
| notation_parsed = { ['Naturals'] = string.sub(default_nominals, 1, note_count), ['Sharp'] = "&", ['Flat'] = "@" }
| |
| else
| |
| notation_parsed = mosnot.parse_notation(frame.args["Notation"])
| |
| end
| |
| show_notation = 1
| |
| 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)
| |
|
| |
| return result
| |
| end
| |
|
| |
| return p | | return p |