ホーム
おまかせ表示
ログイン
設定
Xenharmonic Wikiについて
免責事項
Xenharmonic Wiki
検索
モジュール:MOSのソースを表示
←
モジュール:MOS
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループに属する利用者のみが実行できます:
登録利用者
。
このページのソースの閲覧やコピーができます。
-- This module follows [[User:Ganaram inukshuk/Provisional style guide for Lua]] local et = require("Module:ET") local rat = require("Module:Rational") local utils = require("Module:Utils") local p = {} -------------------------------------------------------------------------------- ----------------------------- MOS-CREATING FUNCTIONS --------------------------- -------------------------------------------------------------------------------- -- Create a new mos as a table containing the counts for large and small steps, -- plus the equave. function p.new(nL, ns, equave) local nL = nL or 5 local ns = ns or 2 local equave = equave or 2 return { nL = nL, ns = ns, equave = equave } end -- Parse a mos from its scalesig "xL ys<p/q>" or "xL ys (p/q-equivalent)". -- If no equave "p/q" is provided, it's assumed to be 2/1-equivalent. function p.parse(unparsed) local nL, ns, equave = unparsed:match("^(%d+)[Ll].-(%d+)[Ss]%s*(.*)$") nL = tonumber(nL) ns = tonumber(ns) equave = equave:match("^%((.*)-equivalent%)$") or equave:match("^⟨(.*)⟩$") or equave:match("^<(.*)>$") or "2/1" -- Assumes this is a rational ratio written a/b equave = rat.parse(equave) if nL == nil or ns == nil or equave == nil then return nil end return p.new(nL, ns, equave) end -------------------------------------------------------------------------------- ---------------------- VALIDATION AND CHECKING FUNCTIONS ----------------------- -------------------------------------------------------------------------------- -- Is the mos xL ys valid (x and y are greater than 0)? function p.is_valid(mos) return mos.nL > 0 and mos.ns > 0 end -- Is the mos xL ys octave-equivalent? function p.is_octave_equivalent(mos) return rat.eq(mos.equave, rat.new(2)) end -- Is the mos nL ns? (Root mos, with root in the sense of being the root of -- the scale tree.) function p.is_root_mos(mos) return mos.nL == mos.ns end -------------------------------------------------------------------------------- ---------------------------- STRING/LINK FUNCTIONS ----------------------------- -------------------------------------------------------------------------------- -- Construct a string representation (scalesig) for a MOS structure. -- Scalesig is "xL ys <p/q>" for valid mosses, omitting <p/q> for 2/1 scales. -- Degenerate mosses (nL 0s or 0L ns) produce a string for its corresponding -- et (n-ed-p/q). -- Option to use nbsp is provided using the second param; default is nbsp. function p.as_string(mos, use_nbsp) if p.is_valid(mos) then local use_nbsp = (use_nbsp == nil and true or use_nbsp) local suffix = "" if not rat.eq(mos.equave, 2) then suffix = "⟨" .. rat.as_ratio(mos.equave):lower() .. "⟩" end return mos.nL .. "L" .. (use_nbsp and " " or " ") .. mos.ns .. "s" .. suffix else return math.max(mos.nL, mos.ns) .. p.et_suffix(mos) end end -- Construct a longer string representation for a MOS structure. -- Scalesig is "xL ys", or "xL ys (p/q-equivalent)" for nonoctave scales. -- Degenerate mosses (nL 0s or 0L ns) produce a string for its corresponding -- et (n-ed-p/q). -- Option to use nbsp is provided using the second param; default is nbsp. function p.as_long_string(mos, use_nbsp) if p.is_valid(mos) then local use_nbsp = (use_nbsp ~= nil and use_nbsp or true) local suffix = "" if not rat.eq(mos.equave, 2) then suffix = (use_nbsp and " " or " ") .. string.format("(%s-equivalent)", rat.as_ratio(mos.equave):lower()) end return mos.nL .. "L" .. (use_nbsp and " " or " ") .. mos.ns .. "s" .. suffix else return math.max(mos.nL, mos.ns) .. p.et_suffix(mos) end end -- Construct the link to a mos. If the mos is a degenerate (nL 0s) mos, then it -- will link to the corresponding equal-division page n-ed-p/q and display the -- link text as an ed, rather than a mos. function p.as_link(mos) local link = p.as_long_string(mos) local text = p.as_string(mos) if link == text then return string.format("[[%s]]", link) else return string.format("[[%s|%s]]", link, text) end end -- Construct the link to a mos, where the displayed text is the long string -- instead. Degenerate mosses link to the corresponding equal-division page. function p.as_long_link(mos) local link = p.as_long_string(mos) return string.format("[[%s]]", link) end -- Given an interval as a vector of L's and s's, produce a string "iL + js", -- where i and j are the quantities for L and s. function p.interval_as_string(interval) -- Quantity of L's as a string local L_string = "" if interval["L"] == 0 then L_string = "" elseif interval["L"] == 1 then L_string = "L" else L_string = string.format("%dL", interval["L"]) end -- Quantity of s's as a string local s_string = "" if math.abs(interval["s"]) == 0 then s_string = "" elseif math.abs(interval["s"]) == 1 then s_string = "s" else s_string = string.format("%ds", math.abs(interval["s"])) end if interval["L"] == 0 and interval["s"] == 0 then return "0" elseif interval["L"] == 0 and interval["s"] ~= 0 then return s_string elseif interval["L"] ~= 0 and interval["s"] == 0 then return L_string else return L_string .. (interval["s"] > 0 and " + " or " - ") .. s_string end end -- Return the equave by itself as a string. function p.equave_as_string(mos) return rat.as_ratio(mos.equave) end -- Return the equave enclosed in brackets. function p.equave_as_enclosed_string(mos) return "⟨" .. rat.as_ratio(mos.equave) .. "⟩" end -------------------------------------------------------------------------------- ----------------------- MOS RELATIVE/OPERATION FUNCTIONS ----------------------- -------------------------------------------------------------------------------- -- Find the parent mos of a mos. May return invalid mosses (nL 0s), meant to -- represent equal divisions of the octave (or arbitrary equave). function p.parent(mos) return p.new(math.min(mos.nL, mos.ns), math.abs(mos.nL-mos.ns), mos.equave) end -- Find the root of a mos nxL nys as nL ns. function p.root(mos) local num_periods = p.period_count(mos) return p.new(num_periods, num_periods, mos.equave) end -- Find the two child mosses of a mos xL ys as (x+y)L xs and xL x+ys. function p.children(mos) return p.new(mos.nL+mos.ns, mos.nL, mos.equave), p.new(mos.nL, mos.nL+mos.ns, mos.equave) end -- Find the sister of a mos xL ys as yL xs. function p.sister(mos) return p.new(mos.ns, mos.nL, mos.equave) end -- Find the neutralized form of a mos. May return invalid mosses (nL 0s), meant -- to represent equal divisions of the octave (or arbitrary equave). function p.neutralized(mos) if mos.nL > mos.ns then return p.new(mos.nL-mos.ns, 2*mos.ns, mos.equave) else return p.new(2*mos.nL, mos.ns-mos.nL, mos.equave) end end -- Find the two interleaved mosses of a mos xL ys as (2x+y)L ys and xL (x+2y)s. function p.interleaved(mos) return p.new(mos.nL*2+mos.ns, mos.ns, mos.equave), p.new(mos.nL, mos.ns*2+mos.nL, mos.equave) end -------------------------------------------------------------------------------- ------------------------------- MODE FUNCTIONS --------------------------------- -------------------------------------------------------------------------------- -- Find the brightest (true-mos) mode of a mos, as a string of L's and s's. -- Calculation is based on the definition of a Christoffel word, as the closest -- integer approximation to line y = #s/#L*x. function p.brightest_mode(mos) local nL = mos.nL local ns = mos.ns local d = utils._gcd(nL, ns) if d > 1 then -- use single period mos, with period as new equave nL = utils._round_dec(nL / d) ns = utils._round_dec(ns / d) end local current_L, current_s = 0, 0 local result = "" while current_L < nL or current_s < ns do if (current_s + 1) * nL <= ns * (current_L) then current_s = current_s + 1 result = result .. "s" else current_L = current_L + 1 result = result .. "L" end end return string.rep(result, d) end -- Find the darkest true-mos mode of a mos. It's the reverse of the brightest mode. function p.darkest_mode(mos) local nL = mos.nL local ns = mos.ns local d = utils._gcd(nL, ns) if d > 1 then -- use single period mos, with period as new equave nL = utils._round_dec(nL / d) ns = utils._round_dec(ns / d) end local current_L, current_s = 0, 0 local result = "" while current_L < nL or current_s < ns do if (current_s + 1) * nL <= ns * (current_L) then current_s = current_s + 1 result = "s" .. result -- !esreveR else current_L = current_L + 1 result = "L" .. result -- !esreveR end end return string.rep(result, d) end -- Given a mos, return a mode based on how it's ranked by modal brightness. -- Ordering here is based on the number of BRIGHT GENS DOWN PER PERIOD: -- 0 is the brightest mode, 1 is 2nd brightest, etc... -- To go by darkness, pass in p-d-1 for the 2nd arg, where p is the period count -- and d is the number of DARK GENS UP PER PERIOD. function p.mode_by_brightness(mos, bright_gens_down) return p.rotate_mode(p.brightest_mode(mos), bright_gens_down * p.bright_gen_step_count(mos)) end -- Given a mos, list all modes in descending order of brightness. function p.modes_by_brightness(mos) local bright_gen_step_count = p.bright_gen_step_count(mos) local period_step_count = p.period_step_count(mos) local modes = {} local current_mode = p.brightest_mode(mos) for i = 1, period_step_count do table.insert(modes, current_mode) current_mode = p.rotate_mode(current_mode, bright_gen_step_count) end return modes end -- List all unique rotations for a mode, by order of leftward shifts. Order by -- rotation will usually give a different order compared to order by brightness, -- but this is expected if the order isn't by brightness (EG, modmosses). -- Note: there will always be s/p modes, where s is the number of steps in the -- entered mode, and p is the period of repetition. At most, there will be s -- modes, but if there is a substring of length p that repeats within the mode -- (where s mod p = 0), then there will be p modes. If the mode has one step -- type, then there is only one mode. function p.mode_rotations(mode_string) local rotations = {} local current_mode = mode_string for i = 1, #mode_string do if not utils.table_contains(rotations, current_mode) then table.insert(rotations, current_mode) end current_mode = p.rotate_mode(current_mode) end return rotations end -- Rotate a mode by shifting the step sequence to the left. Negative values -- shift it to the right. Helper function for mode_by_brightness(). function p.rotate_mode(mode_string, shift_amt) local shift_amt = shift_amt == nil and 1 or shift_amt % #mode_string -- Default is 1 local first = string.sub(mode_string, 1, shift_amt) local second = string.sub(mode_string, shift_amt + 1, #mode_string) return second .. first end -------------------------------------------------------------------------------- ---------------------------- STEP MATRIX FUNCTIONS ----------------------------- -------------------------------------------------------------------------------- -- Convert a single mode (as a string) into a step matrix. This is a listing of -- every interval's step vector in the mode. function p.mode_to_step_matrix(mode_string) local matrix = {} for i = 0, #mode_string do local interval = p.interval_from_step_sequence(string.sub(mode_string, 0, i)) table.insert(matrix, interval) end return matrix end -- TODO?: replaces mode_to_step_matrices/mode_rotations_to_step_matrices with -- one function called modes_to_step_matrices? Encompasses functionality of both -- functions, but step patterns for either are generated into the same function, -- where the modes as strings are passed in. -- Given a mos, produce every step matrix for every mode. Modes are listed in -- order of brightness. function p.modes_to_step_matrices(mos) local modes = p.modes_by_brightness(mos) local matrices = {} for i = 1, #modes do table.insert(matrices, p.mode_to_step_matrix(modes[i])) end return matrices end -- Given a single mode (as a string), produce the step matrices for each -- rotation of that mode. Modes are listed in order of rotation. function p.mode_rotations_to_step_matrices(mode_string) local modes = p.mode_rotations(mode_string) local matrices = {} for i = 1, #modes do table.insert(matrices, p.mode_to_step_matrix(modes[i])) end return matrices end -- Given an input mos, produce its modal union. -- This is a listing of every interval's large and small sizes. function p.modal_union(input_mos) local brightest_mode = p.brightest_mode(input_mos) local darkest_mode = p.darkest_mode (input_mos) local interval_count = p.equave_step_count(input_mos) + 1 local modal_union = {} for i = 1, interval_count do local bright_step_seq = string.sub(brightest_mode, 1, i-1) local dark_step_seq = string.sub(darkest_mode , 1, i-1) local bright_interval = p.interval_from_step_sequence(bright_step_seq) local dark_interval = p.interval_from_step_sequence(dark_step_seq ) if p.interval_eq(bright_interval, dark_interval) then table.insert(modal_union, bright_interval) else table.insert(modal_union, dark_interval ) table.insert(modal_union, bright_interval) end end return modal_union end -------------------------------------------------------------------------------- --------------- FUNCTIONS FOR GENERATOR AND PERIOD INTERVALS ------------------- -------------------------------------------------------------------------------- -- Compute the bright gen as a vector of L's and s's. Since all mosstep -- intervals (excluding the root and period) have two sizes, this returns the -- large/perfect size. function p.bright_gen(mos) local nL = mos.nL local ns = mos.ns local d = utils._gcd(nL, ns) if d > 1 then -- use single period mos, with period as new equave nL = utils._round_dec(nL / d) ns = utils._round_dec(ns / d) end local min_dist = 2; -- the distance we get will always be <= sqrt(2) local current_L, current_s = 0, 0 local result = {["L"] = 0, ["s"] = 0} while current_L < nL or current_s < ns do if (current_s + 1) * nL <= ns * (current_L) then current_s = current_s + 1 else current_L = current_L + 1 end if current_L < nL or current_s < ns then -- check to exclude (current_L, current_s) = (nL, ns) local distance_here = math.abs(nL * current_s - ns * current_L) / math.sqrt(nL^2 + ns^2) if distance_here < min_dist then min_dist = distance_here result["L"] = current_L result["s"] = current_s end end end return result end -- Compute the dark gen as a vector of L's and s's. Since all mosstep -- intervals (excluding the root and period) have two sizes, this returns the -- small/perfect size. function p.dark_gen(mos) local bright_gen = p.bright_gen(mos) return p.period_complement(bright_gen, mos) end -- Compute the period as a vector of L's and s's. -- Period intervals as mossteps only appear as one size. function p.period(mos) local gcd = utils._gcd(mos.nL, mos.ns) return { ["L"] = mos.nL / gcd, ["s"] = mos.ns / gcd } end -- Compute the equave as a vector of L's and s's. -- Equaves as mossteps only appear as one size. For a single-period mos, this -- is the same as p.period(). function p.equave(mos) return { ["L"] = mos.nL, ["s"] = mos.ns } end -------------------------------------------------------------------------------- ------------------- FUNCTIONS FOR SINGLE-STEP INTERVALS ------------------------ -------------------------------------------------------------------------------- -- Return the unison as a vector of L's and s's. -- The unison is denoted by moving up from the root by zero steps, and thus does -- not need a mos as input. It's basically a zero vector. -- The unison only has one size: perfect. function p.unison() return { ["L"] = 0, ["s"] = 0 } end -- Return the vector for a single chroma. It's a large step minus a small step. -- Adding or subtracting any interval by this interval changes its "size". function p.chroma() return { ["L"] = 1, ["s"] = -1 } end -- Return the vector for an augmented step. It's a large step plus a chroma. function p.augmented_step() return { ["L"] = 2, ["s"] = -1 } end -- Return the vector for a single large step. function p.large_step() return { ["L"] = 1, ["s"] = 0 } end -- Return the vector for a single small step. function p.small_step() return { ["L"] = 0, ["s"] = 1 } end -- Return the vector for a diminished step. It's a small step minus a chroma. function p.diminished_step() return { ["L"] = -1, ["s"] = 2 } end -------------------------------------------------------------------------------- ---------------- INTERVAL FUNCTIONS FOR ARBITRARY INTERVALS -------------------- -------------------------------------------------------------------------------- -- Create a new interval using step counts (the quantities of L's and s's). function p.interval_from_step_counts(i, j) return { ["L"] = i, ["s"] = j } end -- Compute an arbitrary mos interval as a vector of L's and s's. Params: -- - step_count: the number of steps subtended by the mosstep. -- - size_offset: denotes whether to return the large size (0) or the small -- size (-1) (or if this is a period interval, the diminished size). Values -- other than 0 or 1 represent alterations by multiple chromas, such as -- augmented (1) or diminished (-2). function p.interval_from_mos(mos, step_count, size_offset) local size_offset = size_offset or 0 -- Optional param; defaults to large size local step_sequence = p.brightest_mode(mos) step_sequence = string.rep(step_sequence, math.ceil(step_count/(mos.nL + mos.ns))) step_sequence = string.sub(step_sequence, 1, step_count) local interval_vector = p.interval_from_step_sequence(step_sequence) local chromas = p.interval_mul(p.chroma(), size_offset) interval_vector = p.interval_add(interval_vector, chromas) return interval_vector end -- Compute an arbitrary mos interval (as a string of steps) as a vector of L's -- and s's. This also serves as a helper function for p.interval_from_mos(). -- Sequences of steps can be entered, where each step is one of five sizes: -- - L: large step. -- - s: small step. -- - c: a chroma; the difference between a large and small step. -- - A: an augmented step; a large step plus a chroma. -- - d: a diminished step, or diesis; a small step minus a chroma. function p.interval_from_step_sequence(step_sequence) local mossteps = #step_sequence local interval_vector = p.unison() for i = 1, mossteps do local step = string.sub(step_sequence, i, i) if step == "L" then interval_vector = p.interval_add(interval_vector, p.large_step()) elseif step == "s" or step == "S" then interval_vector = p.interval_add(interval_vector, p.small_step()) elseif step == "c" then interval_vector = p.interval_add(interval_vector, p.chroma()) elseif step == "A" then interval_vector = p.interval_add(interval_vector, p.augmented_step()) elseif step == "d" then interval_vector = p.interval_add(interval_vector, p.diminished_step()) end end return interval_vector end -------------------------------------------------------------------------------- ------------------------------- COUNT FUNCTIONS -------------------------------- -------------------------------------------------------------------------------- -- Given a mos, return the number of steps. function p.step_count(mos) return mos.nL + mos.ns end -- Given a mos, compute the number of steps in its bright gen (L's plus s's). function p.bright_gen_step_count(mos) local interval = p.bright_gen(mos) return interval["L"] + interval["s"] end -- Given a mos, compute the number of steps in its dark gen (L's plus s's). function p.dark_gen_step_count(mos) return p.period_step_count(mos) - p.bright_gen_step_count(mos) end -- Given a mos, compute the number of steps in its period (L's plus s's). function p.period_step_count(mos) return (mos.nL + mos.ns) / utils._gcd(mos.nL, mos.ns) end -- TODO: deprecate this since "equave_step_count" is redundant and longer than -- "step count". function p.equave_step_count(mos) return mos.nL + mos.ns end -- Given a mos, compute the number of periods it has. function p.period_count(mos) return utils._gcd(mos.nL, mos.ns) end -- Given a vector representing an interval, compute the number of mossteps it -- corresponds to. Knowledge of the corresponding mos is not needed. Intervals -- can be negative, resulting in a negative output. function p.interval_step_count(interval) return interval["L"] + interval["s"] end -- Given a vector representing an interval, compute the number of chromas it was -- raised or lowered by from its large size (for non-period intervals) or its -- perfect size (for period/root/equave intervals). This requires the mos as -- input. -- size_offset denotes whether to count chromas from the large size; changing -- this to -1 counts chromas from the small size. Like size_offset for -- interval_from_mos, this can be used to denote altered mossteps (augmented, -- diminished, etc). function p.interval_chroma_count(interval, mos, size_offset) local size_offset = size_offset or 0 -- Default of 0. local step_count = p.interval_step_count(interval) local base_interval = p.interval_from_mos(mos, step_count, 0) return interval["L"] - base_interval["L"] - size_offset end -------------------------------------------------------------------------------- --------------- INTERVAL ARITHMETIC AND MANIPULATION FUNCTIONS ----------------- -------------------------------------------------------------------------------- -- Add two intervals together by adding their respective vectors. function p.interval_add(interval_1, interval_2) return { ["L"] = interval_1["L"] + interval_2["L"], ["s"] = interval_1["s"] + interval_2["s"] } end -- Subtract two intervals by subtracting their respective vectors. function p.interval_sub(interval_1, interval_2) return { ["L"] = interval_1["L"] - interval_2["L"], ["s"] = interval_1["s"] - interval_2["s"] } end -- Stack an interval, or repeatedly add the same interval to itself. function p.interval_mul(interval, amt) return { ["L"] = interval["L"] * amt, ["s"] = interval["s"] * amt } end -- Check whether two intervals are equal to one another. function p.interval_eq(interval_1, interval_2) return interval_1["L"] == interval_2["L"] and interval_1["s"] == interval_2["s"] end -- Given an interval vector and a mos, find its period complement. This is the -- interval to add to produce the period. For single-period mosses, the period -- complement is the same as the equave complement. function p.period_complement(interval, mos) local sign = p.interval_step_count(interval) < 0 and -1 or 1 local period_vector = p.period(mos) return p.interval_sub(p.interval_mul(period_vector, sign), interval) end -- Given an interval vector and a mos, find its equave complement. This is the -- interval to add to produce the equave. function p.equave_complement(interval, mos) local sign = p.interval_step_count(interval) < 0 and -1 or 1 local equave_vector = p.equave(mos, interval) return p.interval_sub(p.interval_mul(equave_vector, sign), interval) end -- Given an interval vector and a mos, period-reduce it. This works like -- modular arithmetic, so passing a negative interval returns a positive one. -- For single-period mosses, period-reducing is the same as octave-reducing, or -- equave-reducing (for nonoctave scales). function p.period_reduce(interval, mos) local step_count = p.interval_step_count(interval) local reduce_amt = math.floor(step_count / p.period_step_count(mos)) local periods = p.interval_mul(p.period(mos), reduce_amt) return p.interval_sub(interval, periods) end -- Given an interval vector and a mos, equave-reduce it. This works like -- modular arithmetic, so passing a negative interval returns a positive one. function p.equave_reduce(interval, mos) local step_count = p.interval_step_count(interval) local reduce_amt = math.floor(step_count / p.equave_step_count(mos)) local equaves = p.interval_mul(p.equave(mos), reduce_amt) return p.interval_sub(interval, equaves) end -- Invert an interval. This makes an interval negative. function p.invert_interval(interval) return p.interval_mul(interval, -1) end -- Intervals usually denote distances between two scale degrees and should be -- positive values. Normalizing makes a negative interval positive again. function p.normalize_interval(interval) return p.interval_step_count(interval) < 0 and p.interval_mul(interval, -1) or interval end -------------------------------------------------------------------------------- ---------------------------- EQUAL-TUNING FUNCTIONS ---------------------------- -------------------------------------------------------------------------------- -- Given a mos and a step ratio, return an equal tuning (or equal division). -- The step ratio is entered as a 2-element array to allow non-simplified -- ratios to be entered. (The rational module isn't suitable since it simplifies -- ratios.) function p.as_et(mos, step_ratio, suffix) local suffix = suffix or nil local et_size = mos.nL * step_ratio[1] + mos.ns * step_ratio[2] return et.new(et_size, mos.equave, suffix) end -- Given a mos and a step ratio, return the number of et-steps for its bright -- generator. function p.bright_gen_to_et_steps(mos, step_ratio) return p.interval_to_et_steps(p.bright_gen(mos), step_ratio) end -- Given a mos and a step ratio, return the number of et-steps for its dark generator. function p.dark_gen_to_et_steps(mos, step_ratio) return p.interval_to_et_steps(p.dark_gen(mos), step_ratio) end -- Given a mos and a step ratio, return the number of et-steps for its period. function p.period_to_et_steps(mos, step_ratio) return p.interval_to_et_steps(p.period(mos), step_ratio) end -- Given a mos and a step ratio, return the number of et-steps for its equave. function p.equave_to_et_steps(mos, step_ratio) return p.interval_to_et_steps(p.equave(mos), step_ratio) end -- Given an interval vector and step ratio, compute the number of et-steps it corresponds to. function p.interval_to_et_steps(interval, step_ratio) return interval["L"] * step_ratio[1] + interval["s"] * step_ratio[2] end -------------------------------------------------------------------------------- ------------------------ EQUAL-TUNING STRING FUNCTIONS ------------------------- -------------------------------------------------------------------------------- -- Given a mos, return its equal temperament suffix as a string (edo, edt, edf, or ed-p/q). function p.et_suffix(mos) if rat.eq(mos.equave, rat.new(2)) then return "edo" elseif rat.eq(mos.equave, rat.new(3)) then return "edt" elseif rat.eq(mos.equave, rat.new(3, 2)) then return "edf" else return "ed" .. rat.as_ratio(mos.equave) end end -- Given a mos and step ratio, return its equal temperament as a string "{steps}\{division}{suffix}". function p.et_string(mos, step_ratio, suffix) local suffix = suffix or nil local et_mos = p.as_et(mos, step_ratio, suffix) return et.as_string(et_mos) end -- Given a mos and step ratio, compute the number of et-steps for its bright gen -- as a string "{steps}\{division}{suffix}". function p.bright_gen_to_et_string(mos, step_ratio, suffix) return p.interval_to_et_string(p.bright_gen(mos), mos, step_ratio, suffix) end -- Given a mos and step ratio, compute the number of et-steps for its dark gen, -- as a string "{steps}\{division}{suffix}". function p.dark_gen_to_et_string(mos, step_ratio, suffix) return p.interval_to_et_string(p.dark_gen(mos), mos, step_ratio, suffix) end -- Given a mos and step ratio, compute the number of et-steps for its period, -- as a string "{steps}\{division}{suffix}". function p.period_to_et_string(mos, step_ratio, suffix) return p.interval_to_et_string(p.period(mos), mos, step_ratio, suffix) end -- Given a mos, compute the number of et-steps for its period, reduced, -- as a string "{steps}\{division}{suffix}". Does not reuqire a step ratio. -- NOTE: no such function for returning only the number of steps is needed since -- that's the same as period_count(). function p.reduced_period_to_et_string(mos, suffix) return p.interval_to_et_string({["L"] = 1, ["s"] = 1}, p.root(mos), {1,0}, suffix) end -- Given a mos and step ratio, compute the number of et-steps for its equave, -- as a string "{steps}\{division}{suffix}". function p.equave_to_et_string(mos, step_ratio, suffix) return p.interval_to_et_string(p.equave(mos), mos, step_ratio, suffix) end -- Given an interval vector and step ratio, compute the number of et-steps it -- corresponds to, as a string "{steps}\{division}{suffix}". Requires info -- about the mos itself. function p.interval_to_et_string(interval, mos, step_ratio, suffix) local suffix = suffix or nil local mos_et = p.as_et(mos, step_ratio, suffix) return et.backslash_display(mos_et, p.interval_to_et_steps(interval, step_ratio)) end -------------------------------------------------------------------------------- ------------------------------- CENT FUNCTIONS --------------------------------- -------------------------------------------------------------------------------- -- Given a mos and a step ratio, return the number of cents for its bright gen. function p.bright_gen_to_cents(mos, step_ratio) local interval_steps = p.interval_to_et_steps(p.bright_gen(mos), step_ratio) local equave_steps = p.equave_to_et_steps(mos, step_ratio) return interval_steps * rat.cents(mos.equave) / equave_steps end -- Given a mos and a step ratio, return the number of cents for its dark gen. function p.dark_gen_to_cents(mos, step_ratio) local interval_steps = p.interval_to_et_steps(p.dark_gen(mos), step_ratio) local equave_steps = p.equave_to_et_steps(mos, step_ratio) return interval_steps * rat.cents(mos.equave) / equave_steps end -- Given a mos and a step ratio, return the number of cents for its period. -- The period is the interval at which the step pattern repeats, so no step -- ratio is needed. function p.period_to_cents(mos) return rat.cents(mos.equave) / p.period_count(mos) end -- Given a mos and a step ratio, return the number of cents for its equave. -- The period is the interval at which the step pattern repeats, and the equave -- is a multiple of that (at least for multi-period mosses), so no step ratio is -- needed. function p.equave_to_cents(mos) return rat.cents(mos.equave) end -- Given an interval vector and step ratio, convert it to cents. This requires info about the mos itself. function p.interval_to_cents(interval, mos, step_ratio) local interval_steps = p.interval_to_et_steps(interval, step_ratio) local equave_steps = p.equave_to_et_steps(mos, step_ratio) return interval_steps * rat.cents(mos.equave) / equave_steps end -------------------------------------------------------------------------------- ----------------------------------- TESTER ------------------------------------- -------------------------------------------------------------------------------- -- Tester function function p.tester() local input_mos = p.new(4,1,3) local step_ratio = {2,1} local interval_vector = {["L"] = 3, ["s"] = 1} --return p.as_string(input_mos, false) --return p.as_et(p.new(5,2), {2,1}) --[[ return p.mode_by_brightness(p.new(5,2), 0) .. " " .. p.mode_by_brightness(p.new(5,2), 6-6) .. "\n" .. p.mode_by_brightness(p.new(5,2), 1) .. " " .. p.mode_by_brightness(p.new(5,2), 6-5) .. "\n" .. p.mode_by_brightness(p.new(5,2), 2) .. " " .. p.mode_by_brightness(p.new(5,2), 6-4) .. "\n" .. p.mode_by_brightness(p.new(5,2), 3) .. " " .. p.mode_by_brightness(p.new(5,2), 6-3) .. "\n" .. p.mode_by_brightness(p.new(5,2), 4) .. " " .. p.mode_by_brightness(p.new(5,2), 6-2) .. "\n" .. p.mode_by_brightness(p.new(5,2), 5) .. " " .. p.mode_by_brightness(p.new(5,2), 6-1) .. "\n" .. p.mode_by_brightness(p.new(5,2), 6) .. " " .. p.mode_by_brightness(p.new(5,2), 6-0) ]]-- return p.as_string(p.new(5,2)) .. "\n" .. p.as_string(p.new(4,5,3)) .. "\n" .. p.as_long_string(p.new(5,2)) .. "\n" .. p.as_long_string(p.new(4,5,3)) .. "\n" .. p.as_link(p.new(5,2)) .. "\n" .. p.as_link(p.new(4,5,3)) .. "\n" .. p.as_long_link(p.new(5,2)) .. "\n" .. p.as_long_link(p.new(4,5,3)) .. "\n" .. p.as_string(p.new(5,0)) .. "\n" .. p.as_string(p.new(4,0,3)) .. "\n" .. p.as_long_string(p.new(5,0)) .. "\n" .. p.as_long_string(p.new(4,0,3)) .. "\n" .. p.as_link(p.new(5,0)) .. "\n" .. p.as_link(p.new(4,0,3)) .. "\n" .. p.as_long_link(p.new(5,0)) .. "\n" .. p.as_long_link(p.new(4,0,3)) .. "\n" .. p.as_string(p.new(0,2)) .. "\n" .. p.as_string(p.new(0,5,3)) .. "\n" .. p.as_long_string(p.new(0,2)) .. "\n" .. p.as_long_string(p.new(0,5,3)) .. "\n" .. p.as_link(p.new(0,2)) .. "\n" .. p.as_link(p.new(0,5,3)) .. "\n" .. p.as_long_link(p.new(0,2)) .. "\n" .. p.as_long_link(p.new(0,5,3)) end return p
このページで使用されているテンプレート:
モジュール:MOS/doc
(
ソースを閲覧
)
モジュール:MOS
に戻る。