Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/lua
- function fmtnote(n) return n:sub(1,1):upper()..n:sub(2,2):lower()..n:sub(3,-1) end
- local notes = setmetatable({
- "C", "C#", "D", "Eb", "E", "F", "F#", "G", "Ab", "A", "Bb", "B",
- ["B#"] = 1;["C"] = 1;
- ["C#"] = 2;["Db"] = 2;
- ["D"] = 3;
- ["D#"] = 4;["Eb"] = 4;
- ["E"] = 5;["Fb"] = 5;
- ["E#"] = 6;["F"] = 6;
- ["F#"] = 7;["Gb"] = 7;
- ["G"] = 8;
- ["G#"] = 9;["Ab"] = 9;
- ["A"] = 10;
- ["A#"] = 11;["Bb"] = 11;
- ["B"] = 12;["Cb"] = 12;
- },{__index=function(self, index)
- if type(index)=="number" then
- return rawget(self,((index-1)%12)+1)
- elseif type(index)=="string" then
- return rawget(self,fmtnote(index))
- end
- return rawget(self,index)
- end})
- local intervals = setmetatable({
- "m2","M2","m3","M3","P4","TT","P5","m6","M6","m7","M7","P8",
- "m9","M9","m10","M10","P11","d12","P12","m13","M13","m14","M14",
- "P15","A15";[0]="P1";
- ["d2"]=0;
- ["A1"]=1; ["S"]=1;
- ["d3"]=2; ["T"]=2;
- ["A2"]=3;
- ["d4"]=4;
- ["A3"]=5;
- ["d5"]=6; ["A4"]=6;
- ["d6"]=7;
- ["A5"]=8;
- ["d7"]=9;
- ["A6"]=10;
- ["d8"]=11;
- ["A7"]=12; ["d9"]=12;
- ["A8"]=13;
- ["d10"]=14;
- ["A9"]=15;
- ["d11"]=16;
- ["A10"]=17;
- ["A11"]=18;
- ["d13"]=19;
- ["A12"]=20;
- ["d14"]=21;
- ["A13"]=22;
- ["d15"]=23;
- ["A14"]=24;
- },{__index=function(self, index)
- if type(index)=="string" then
- for i=0, 25 do
- if rawget(self, i)==index then
- return i
- end
- end
- if index:match("^%d+[Ss]?$") then
- return index:match("%d+")+0
- end
- end
- return rawget(self,index)
- end})
- local rawget = rawget
- local chord_mt = {}
- function chord_mt:__index(index)
- if type(index) == "number" then
- index = notes[index]
- end
- if type(index) == "string" and notes[index] then
- local arr={__original = self, __root = index}
- local root = notes[index]
- for _, interval in ipairs(self) do
- table.insert(arr,notes[intervals[interval]+root])
- end
- setmetatable(arr,{__tostring=function(self)
- return table.concat(self,"-")
- end})
- return arr
- end
- end
- local rawget=rawget
- local chords = setmetatable({
- maj = {"P1","M3","P5"}; ["M"] = "maj";
- min = {"P1","m3","P5"}; ["m"] = "min";
- aug = {"P1","M3","A5"}; ["+"] = "aug";
- dim = {"P1","m3","A5"}; --["*"] = "dim";
- dom7 = {"P1","M3","P5","m7"}; ["7"] = "dom7";
- min7 = {"P1","m3","P5","m7"}; ["m7"] = "min7";
- maj7 = {"P1","M3","P5","M7"}; ["M7"] = "maj7";
- aug7 = {"P1","M3","A5","m7"};
- ["+7"] = "aug7"; ["7#5"] = "aug7"; ["7aug5"] = "aug7";
- dim7 = {"P1","m3","d5","d7"}; --["*7"] = "dim7";
- m7dim5 = {"P1","m3","d5","m7"}; ["m7b5"] = "m7dim5"; --["*7"] = "dim7";
- sus2 = {"0S","2S","7S"};
- sus4 = {"0S","5S","7S"};
- key_major = {"0S","2S","4S","5S","7S","9S","11S"};
- key_minor_natural = {"0S","2S","3S","5S","7S","8S","10S"};
- key_minor_harmonic = {"0S","2S","3S","5S","7S","8S","11S"};
- --no melodic cause it changes going up and down
- key_minor = "key_minor_natural";
- key_dorian = {"0S","2S","3S","5S","7S","8S","11S"};
- key_lydian = {"0S","2S","4S","6S","7S","9S","11S"};
- key_pentatonic_major = {"0S","2S","4S","7S","9S"};
- key_pentatonic_minor = {"0S","3S","5S","7S","10S"};
- key_pentatonic = "key_pentatonic_major";
- }, {__index=function(self,index)
- if type(index)=="string" and rawget(self,"key_"..index) then
- return rawget(self,"key_"..index)
- end
- local ind = rawget(self,index)
- local rootNote, intervalstr = index:match("^(..?)%((.-)%)$")
- if rootNote and intervalstr then
- local chord = setmetatable({},chord_mt)
- for interval in intervalstr:gmatch("[^%+%,%s]+") do
- table.insert(chord,interval)
- end
- return chord[rootNote]
- end
- if ind==nil then
- for k,v in pairs(self) do
- local safek = k:gsub("%+","%%+")
- local note,suffix = index:match("^(.[^:]?):("..safek..")$")
- if not (note or suffix) then
- note,suffix = index:match("^(.[^:]?):?("..safek:gsub("^key_","")..")$")
- suffix=k
- end
- if note and suffix then
- return self[suffix][note]
- end
- end
- end
- return ind
- end})
- for k,v in pairs(chords) do
- if type(v)=='table' then
- rawset(v,'__name',k)
- setmetatable(v,chord_mt)
- elseif type(v)=="string" then --alias
- chords[k]=chords[v]
- end
- end
- local guitarStrings = {"E","A","D","G","B","e"}
- local marked_frets = {[0]=true,[3]=true,[5]=true,[7]=true,[9]=true,[12]=true,[15]=true,[17]=true,[19]=true,[21]=true}
- function chordToGuitar(chord)
- local arr={}
- local frets = 22
- local chordFrets={}
- for k, v in ipairs(chord) do
- chordFrets[v]={}
- end
- for str=1, #guitarStrings do
- arr[str]={}
- table.insert(arr[str],guitarStrings[str])
- table.insert(arr[str],"│")
- for fret=0, frets do
- local fretNote = notes[notes[guitarStrings[str]]+fret]
- local inChord = false
- for i,chordNote in ipairs(chord) do
- if chordNote == fretNote then
- inChord = i
- table.insert(chordFrets[fretNote],guitarStrings[str]..fret)
- break
- end
- end
- if inChord then
- table.insert(arr[str],inChord.."│")
- else
- table.insert(arr[str],"-│")
- end
- end
- arr[str] = table.concat(arr[str],"")
- end
- local function fretIndex(str)
- local str, num = str:match("^(.)(%d+)$")
- local stringNo
- for k,v in ipairs(guitarStrings) do
- if v==str then
- stringNo = k
- end
- end
- return tonumber(num), stringNo
- end
- print(" #","int.","note", "frets")
- for k,v in ipairs(chord) do
- table.sort(chordFrets[v],function(a,b)
- local aN, aS = fretIndex(a)
- local bN, bS = fretIndex(b)
- return aN*100+aS < bN*100+bS
- end)
- print("["..k.."]", chord.__original[k],v,table.concat(chordFrets[v],","))
- end
- print()
- local markers = {" "}
- for i=0,frets do
- if marked_frets[i] then
- table.insert(markers,i.."")
- elseif marked_frets[i-1] and #((i-1).."")==2 then
- table.insert(markers,"")
- else
- table.insert(markers," ")
- end
- table.insert(markers," ")
- end
- print(table.concat(markers,""))
- for i=#arr, 1, -1 do
- print(arr[i])
- end
- end
- local whiteKeys={
- C=true,
- D=true,
- E=true,
- F=true,
- G=true,
- A=true,
- B=true
- }
- --[[
- ___________________________
- | | | | | | | | | | | | |
- | | | | | | | | | | | | |
- | | | | | | | | | | | | |
- | | | | | | | | | | | | |
- | T T | T T T |
- | | | | | | | |
- | | | | | | | |
- '''''''''''''''''''''''''''''
- start on C or F
- end on E or B
- | | | |
- | | | |
- | | | |
- | | | |
- T T
- | |
- | |
- | | | | | |
- | | | | | |
- | | | | | |
- | | | | | |
- T T T
- | | |
- | | |
- ]]
- --start on F or C
- --end on E or B
- replacePiano = {
- ["-"] = "─";
- ["#"] = "\27[7m \27[0m"; --"▉";
- ["|"] = "│";
- ["/"] = "┘";
- ["\\"] = "└";
- ["T"] = "┬";
- ["W"] = "┴";
- }
- function chordToPiano(chord)
- local arr = {}
- local whiteStrL = 6
- local blackStrL = 4
- local topChar = "_"
- local bottomChar = "-"
- local emptyChar = " "
- for i=1,blackStrL+whiteStrL+3 do
- arr[i] = {}
- end
- function writeVert(str)
- arr[1][#arr[1]+1] = topChar
- arr[#arr][#arr[#arr]+1] = bottomChar
- --print(str)
- --\27[41mcaca\27[0m
- local i=1
- do
- local str = str
- while true do
- local addStr = str:match("^\27%[%d+m.\27%[%d+m")
- if not addStr then
- addStr = str:match("^.")
- end
- if not addStr then break end
- --print(arr, i+1, #arr)
- arr[i+1][
- #arr[i+1]
- +1] = addStr
- i=i+1
- str = str :sub(#addStr+1,#str)
- end
- end
- for j=i, blackStrL+whiteStrL+1 do
- arr[i+1][#arr[i+1]+1] = emptyChar
- end
- end
- local rootNote = notes[chord[1]]
- local bottomRollNote = rootNote
- repeat
- bottomRollNote = bottomRollNote - 1
- until notes[bottomRollNote] == "F" or notes[bottomRollNote] == "C"
- local topNote = notes[chord[#chord]]
- local topRollNote = topNote
- repeat
- topRollNote = topRollNote + 1
- until notes[topRollNote] == "E" or notes[topRollNote] == "B"
- local currentNote = bottomRollNote
- local currentChordPart = 1
- local nextGoalNote = notes[notes[rootNote]]
- bottomChar = "\\"
- writeVert(("|"):rep(blackStrL+whiteStrL+1))
- bottomChar = "-"
- while true do
- local str = ""
- emptyChar = " "
- if whiteKeys[notes[currentNote]] then
- emptyChar = "#"
- end
- if currentNote == nextGoalNote then
- if currentChordPart == -1 or currentChordPart == 0 then
- str = ""
- else
- str = tostring(currentChordPart):gsub(".","\27[41m%1\27[0m")
- if whiteKeys[notes[currentNote]] then
- emptyChar = " "
- end
- emptyChar = emptyChar:gsub(".","\27[41m%1\27[0m")
- end
- if currentChordPart ~= #chord and currentChordPart ~= -1 then
- currentChordPart = currentChordPart + 1
- nextGoalNote = notes[chord[currentChordPart]]
- else
- currentChordPart = -1
- nextGoalNote = notes[notes[topRollNote]]
- bottomChar = "-"
- end
- end
- if whiteKeys[notes[currentNote]] then
- if whiteKeys[notes[currentNote-1]] then
- --writeVert(("│"):rep(blackStrL+whiteStrL+1))
- else
- writeVert(("|"):rep(blackStrL).."/"..(emptyChar):rep(whiteStrL))
- end
- --print("#emptyChar=", #emptyChar)
- writeVert((emptyChar):rep((blackStrL+whiteStrL+1)-#(str:gsub("\27%[%d+m(.)\27%[%d+m","%1"))) .. str)
- if whiteKeys[notes[currentNote+1]] then
- bottomChar = "-"
- if currentNote == nextGoalNote then
- bottomChar = "/"
- end
- writeVert(("|"):rep(blackStrL+whiteStrL+1))
- bottomChar = "-"
- else
- writeVert(("|"):rep(blackStrL).."\\"..(emptyChar):rep(whiteStrL))
- end
- else
- bottomChar = "W"
- writeVert((emptyChar):rep(blackStrL-#(str:gsub("\27%[%d+m(.)\27%[%d+m","%1"))) .. str .. "T" .. ("|"):rep(whiteStrL))
- bottomChar = "-"
- end
- if currentChordPart == -1 and currentNote == nextGoalNote then
- break
- end
- currentNote = notes[notes[currentNote+1]]
- end
- --writeVert(("|"):rep(blackStrL+whiteStrL+1))
- local lines = {}
- for i,v in ipairs(arr) do
- lines[i] = table.concat(v,""):gsub(".",function(c) return replacePiano[c] or c end)
- end
- print(table.concat(lines,"\n"))
- end
- local input
- repeat
- io.write">"; input=io.read()
- if input =="cls" or input =="clear" then os.execute'clear' end
- local res, err = pcall(function()
- local toGuitar = false
- if input:match":$" then
- toGuitar = true
- input=input:match("^(.-):$")
- end
- local chord = chords[input]
- if notes[fmtnote(input)] and not chord then
- chord = chords[input.."(0)"]
- end
- if chord then
- print()
- local chordType = chord.__original.__name or ""
- print(fmtnote(chord.__root)..chordType:gsub("key_",""))
- if not chordType:match'key' then
- print("5th Down / 4th Up:", notes[notes[chord.__root]+intervals.P4]..chordType)
- print("5th Up / 4th Down:", notes[notes[chord.__root]+intervals.P5]..chordType)
- else
- if chordType:match'[Mm][aA][jJ][oO][rR]' then
- print("Relative Minor:", notes[notes[chord.__root]-intervals['3S']].."minor")
- elseif chordType:match'[Mm][Ii][Nn][oO][rR]' then
- print("Relative Major:", notes[notes[chord.__root]+intervals['3S']].."major")
- end
- end
- print()
- chordToGuitar(chord)
- print()
- print(chord)
- chordToPiano(chord)
- end
- local note,num = input:match("^(.-)(%d+)$")
- if note and notes[note] and num then
- print(" = ", notes[notes[note]+tonumber(num)])
- end
- end)
- if not res then
- print("error:", err)
- end
- until input=="q" or input=="quit" or input=="exit"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement