Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- foldl = function(f, z, l)
- for i = 1, #l do z = f(z, l[i]) end
- return z
- end
- foldl1 = function(f, l)
- return foldl(f, table.remove(l, 1), l)
- end
- foldr = function(f, z, l)
- for i = #l, 1, -1 do z = f(l[i], z) end
- return z
- end
- foldr1 = function(f, l)
- return foldr(f, table.remove(l), l)
- end
- map = function(f, l)
- local z = table.sub(l)
- for k in pairs(l) do z[k] = f(rawget(l, k)) end
- return z
- end
- filter = function(pr, l)
- return foldl(function(c, v)
- return pr(v) == true and table.insert(c, v) or c
- end, {}, l)
- end
- zipWith = function(f, a, b)
- local n = {}
- for i = 1, math.min(#a, #b) do n[i] = f(a[i], b[i]) end
- return n
- end
- elem = function(t, x)
- return foldl(function(c, v) return c or v == x end, false, t)
- end
- enum = function(i, j)
- local z = {}
- for k = i, j do z[#z + 1] = k end
- return z
- end
- table.join = function(a, b)
- return foldl(function(c, v)
- table.insert(c, v); return c
- end, a, b)
- end
- table.sub = function(t, i, j)
- local n = {}
- i = i or 1
- j = j or #t
- table.move(t, i, j, 1, n)
- return n
- end
- table.rotate = function(t, k)
- local n = {}
- for i = 1, #t do
- n[i] = t[(i + k - 1) % #t + 1]
- end
- return n
- end
- tableStr = function(t, ind)
- ind = ind or 0
- local str = '{\n'
- local mLen = string.len(#t) + 1
- for k in pairs(t) do
- local kStr = k
- local xStr = rawget(t, k)
- if type(xStr) == "boolean" then
- xStr = xStr and "True" or "False"
- elseif type(xStr) == "string" then
- xStr = '"' .. xStr .. '"'
- elseif type(xStr) == "table" then
- xStr = tableStr(xStr, ind + 1)
- end
- str = str .. string.rep(' ', ind + mLen - string.len(kStr)) .. kStr .. ": " .. xStr .. '\n'
- end
- str = str .. string.rep(' ', ind) .. '}'
- if ind == 0 then str = str .. '\n' end
- return str
- end
- tableStr2 = function(t)
- local str = '{'
- for k in pairs(t) do
- local kStr = k
- local xStr = rawget(t, k)
- if type(xStr) == "boolean" then
- xStr = xStr and "True" or "False"
- elseif type(xStr) == "string" then
- xStr = '"' .. xStr .. '"'
- elseif type(xStr) == "table" then
- xStr = tableStr2(xStr)
- end
- str = str .. ' ' .. kStr .. ": " .. xStr .. ","
- end
- return str == '{' and "{ }" or string.sub(str, 1, string.len(str) - 1) .. " }"
- end
- cartProd = function(...)
- local _cartProd = function(a, b)
- local l = {}
- for _, v1 in pairs(a) do
- for _, v2 in pairs(b) do
- local _v1 = v1
- if type(_v1) ~= "table" then _v1 = {_v1} end
- if type(v2) ~= "table" then v2 = {v2} end
- l[#l + 1] = table.join(_v1, v2)
- end
- end
- return l
- end
- return foldr1(_cartProd, table.pack(...))
- end
- lexgt = function(a, b)
- for i = 1, #a do
- if b[i] == nil then return 1
- elseif a[i] > b[i] then return 1
- elseif a[i] < b[i] then return -1
- end
- end
- if #a < #b then return -1 end
- return 0
- end
- revlexgt = function(a, b)
- if #a > #b then return 1 end
- if #a < #b then return -1 end
- for i = #a, 1, -1 do
- if a[i] > b[i + #b - #a] then return 1
- elseif a[i] < b[i + #b - #a] then return -1
- end
- end
- return 0
- end
- --main
- DESC = [[
- Usage: lua chords.lua filename [option]
- Options:
- -u: use unicode characters
- -n: allow omitted extensions
- -nl: allow omitted chord tones
- -a: allow all avoid tones
- -f: allow all chords without checking
- -m: disallow all minor ninths
- -s: disallow suspended chords]]
- -- command line argument flags
- for i = 2, #arg do
- if arg[i] == "-u" then bUnicode = true
- elseif arg[i] == "-n" then bNoUpper = true
- elseif arg[i] == "-nl" then bNoLower = true
- elseif arg[i] == "-a" then bAllowAvoid = true
- elseif arg[i] == "-f" then bAllowAll = true
- elseif arg[i] == "-m" then bStrict = true
- elseif arg[i] == "-s" then bNoSus = true end
- end
- -- semitone offsets from chord root
- local SEMITONES = {{0}
- ,{ 4, 3, 5, 2}
- ,{ 7, 6, 8}
- ,{10, 9, 11}
- ,{14, 13, 15}
- ,{17, 16, 18}
- ,{21, 20}}
- -- chord factor strings
- local CHORD_STR = {{"1"}
- ,{"3" , "b3" , "4" , "2"}
- ,{"5" , "b5" , "#5"}
- ,{"b7", "6" , "M7"}
- ,{"9" , "b9" , "#9"}
- ,{"11", "b11", "#11"}
- ,{"13", "b13"}}
- if bNoUpper then for i = 5, 7 do table.insert(SEMITONES[i], -1) table.insert(CHORD_STR[i], "") end end
- if bNoLower then for i = 2, 4 do table.insert(SEMITONES[i], -1) table.insert(CHORD_STR[i], "") end end
- if bNoSus then
- table.remove(SEMITONES[2], 4)
- table.remove(SEMITONES[2], 3)
- table.remove(CHORD_STR[2], 4)
- table.remove(CHORD_STR[2], 3)
- end
- local chordStr = function(l)
- local sym = "" -- chord symbol
- local scale = {} -- scale table
- local unalt = 4 -- extension index
- -- highest extension resulting from stacking thirds
- while (l[unalt] ~= -1 and unalt < 7) do unalt = unalt + 1 end
- -- highest unaltered extension in the chord
- while ((l[unalt] ~= SEMITONES[unalt][1] or l[unalt] == -1) and unalt > 4) do unalt = unalt - 1 end
- -- do not allow abbreviated extensions sixth chords
- if l[4] == 9 then unalt = 4 end
- -- chord symbol for the lower structure
- sym = (l[2] == 3 and "m" or "")
- .. (l[4] == 11 and "M" or "")
- .. (l[4] == 9 and "6" or l[4] ~= -1 and tostring(unalt * 2 - 1) or "")
- .. (l[3] == 6 and "b5" or l[3] == 8 and "#5" or "")
- if sym == "m6b5" then sym = "dim7" end
- -- append alterations
- for i = 5, 7 do
- if l[i] ~= -1 then
- local acc = l[i] - SEMITONES[i][1]
- if acc ~= 0 or i > unalt then
- -- add correct accidentals
- -- acc > 0 and string.rep('#', acc) or acc < 0 and string.rep('b', -acc) or '/'
- sym = sym .. (acc == 1 and '#' or acc == -1 and 'b' or '/')
- .. tostring(i * 2 - 1)
- end
- end
- end
- -- special cases for the lower structure
- sym = sym .. (l[2] == 5 and "sus4" or l[2] == 2 and "sus2" or "")
- .. (l[2] == -1 and "no3" or "")
- .. (l[3] == -1 and "no5" or "")
- if sym == "" then sym = "M" end
- if sym == "mb5" then sym = "dim" end
- -- correctly label added chords
- sym = string.gsub(sym, "^/", "add")
- if foldl(function(a, b) return b ~= -1 or a end, false, table.sub(l, 5, 7)) then
- sym = string.gsub(sym, "^b", "addb")
- sym = string.gsub(sym, "^#", "add#")
- end
- if bUnicode then
- sym = string.gsub(string.gsub(sym, "b", "\u{266D}"), "#", "\u{266F}")
- end
- -- remove omitted notes from the chord table
- l = filter(function(x) return x ~= -1 end, l)
- -- reduce octaves from the chord tables
- scale = map(function(x) return x % 12 end, table.sub(l))
- -- sort the scale table
- table.sort(scale)
- -- replace chord table with spellings of chord factors
- local flatten = foldl(table.join, {}, CHORD_STR)
- for k, v in pairs(foldl(table.join, {}, SEMITONES)) do
- l = map(function(x)
- return x == v and flatten[k] or x
- end, l)
- end
- -- reorder the scale table to the largest inversion
- -- scale = foldl(function(l, k)
- -- local n = table.rotate(l, k)
- -- n = map(function(x) return (x - n[1]) % 12 end, n)
- -- return revlexgt(l, n) == 1 and l or n
- -- end, scale, enum(1, #scale - 1))
- -- return a 2-tuple containing formatted string and scale table
- return {string.format("%-24s%-24s%s", table.concat(l, "-"), table.concat(scale, " "), sym), scale}
- end
- local validChord = function(l)
- if bAllowAll then return true end
- -- strict definition of avoid note:
- -- any chord tone forming a minor ninth above another chord tone
- -- but allow b9 unless M7 is present
- -- also allow b13 when both b3 and b7 are present
- -- but disallow minor seconds (e.g. #9 and b11, #5 and 6) and octaves (e.g. b3 and #9)
- local red = map(function(x) return x % 12 end, l)
- return (foldl1(function(a, b)
- return a and b
- end, zipWith(function(a, b)
- return a == -1 or b == -1
- or b - a ~= 13
- or ((b == 13 and l[4] ~= 11)
- or (b == 20 and l[2] == 3 and l[4] == 10))
- and not bStrict
- end
- ,table.sub(l, 1, 3), table.sub(l, 5, 7)))
- and not (l[2] == 2 and l[5] == 15) -- special case
- or bAllowAvoid)
- and foldl1(function(a, b)
- return a and b
- end, zipWith(function(a, b) return a == -1 or b == -1 or b - a > 1 end
- ,table.sub(l, 1, 6), table.sub(l, 2, 7)))
- and foldl1(function(t, x)
- return t and (l[x] == -1 or not elem(table.sub(red, 1, x - 1), red[x]))
- end, enum(1, 7))
- end
- local getChords = function()
- return map(chordStr, filter(validChord, cartProd(table.unpack(SEMITONES))))
- end
- if not arg[1] then
- print(DESC)
- else
- local ALL_CHORDS = {}
- for i = 7, bAllowAll and 2 or 3, -1 do
- if SEMITONES[i][#SEMITONES[i]] == -1 then table.remove(SEMITONES[i]) end
- local chordTable = getChords()
- -- this sorts the chords by the reduced scale
- table.sort(chordTable, function(x, y) return lexgt(x[2], y[2]) == -1 end)
- file = io.open(arg[1] .. "_" .. tostring(i * 2 - 1) .. ".txt", "w")
- file:write(tableStr(map(function(x) return x[1] end, chordTable)))
- file:close()
- ALL_CHORDS = table.join(chordTable, ALL_CHORDS)
- SEMITONES[i] = {-1}
- end
- -- this sorts the chords by the reduced scale
- table.sort(ALL_CHORDS, function(x, y) if #x[2] > #y[2] then return false end if #y[2] > #x[2] then return true end return lexgt(x[2], y[2]) == -1 end)
- -- this sorts the chords by the chord name
- -- table.sort(ALL_CHORDS, function(x, y) return string.sub(x[1], 49) < string.sub(y[1], 49) end)
- file = io.open(arg[1] .. "_all.txt", "w")
- file:write(tableStr(map(function(x) return x[1] end, ALL_CHORDS)))
- file:close()
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement