Advertisement
itchyzombie

tc2.lua

Mar 11th, 2020
373
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.64 KB | None | 0 0
  1. #!/usr/bin/lua
  2.  
  3. function fmtnote(n) return n:sub(1,1):upper()..n:sub(2,2):lower()..n:sub(3,-1) end
  4. local notes = setmetatable({
  5.       "C", "C#", "D", "Eb", "E", "F", "F#", "G", "Ab", "A", "Bb", "B",
  6.       ["B#"] = 1;["C"] = 1;
  7.       ["C#"] = 2;["Db"] = 2;
  8.       ["D"] = 3;
  9.       ["D#"] = 4;["Eb"] = 4;
  10.       ["E"] = 5;["Fb"] = 5;
  11.       ["E#"] = 6;["F"] = 6;
  12.       ["F#"] = 7;["Gb"] = 7;
  13.       ["G"] = 8;
  14.       ["G#"] = 9;["Ab"] = 9;
  15.       ["A"] = 10;
  16.       ["A#"] = 11;["Bb"] = 11;
  17.       ["B"] = 12;["Cb"] = 12;
  18. },{__index=function(self, index)
  19.     if type(index)=="number" then
  20.        return rawget(self,((index-1)%12)+1)
  21.     elseif type(index)=="string" then
  22.        return rawget(self,fmtnote(index))
  23.     end
  24.     return rawget(self,index)
  25. end})
  26.  
  27. local intervals = setmetatable({
  28.       "m2","M2","m3","M3","P4","TT","P5","m6","M6","m7","M7","P8",
  29.       "m9","M9","m10","M10","P11","d12","P12","m13","M13","m14","M14",
  30.       "P15","A15";[0]="P1";
  31.       ["d2"]=0;
  32.       ["A1"]=1; ["S"]=1;
  33.       ["d3"]=2; ["T"]=2;
  34.       ["A2"]=3;
  35.       ["d4"]=4;
  36.       ["A3"]=5;
  37.       ["d5"]=6; ["A4"]=6;
  38.       ["d6"]=7;
  39.       ["A5"]=8;
  40.       ["d7"]=9;
  41.       ["A6"]=10;
  42.       ["d8"]=11;
  43.       ["A7"]=12; ["d9"]=12;
  44.       ["A8"]=13;
  45.       ["d10"]=14;
  46.       ["A9"]=15;
  47.       ["d11"]=16;
  48.       ["A10"]=17;
  49.       ["A11"]=18;
  50.       ["d13"]=19;
  51.       ["A12"]=20;
  52.       ["d14"]=21;
  53.       ["A13"]=22;
  54.       ["d15"]=23;
  55.       ["A14"]=24;
  56. },{__index=function(self, index)
  57.     if type(index)=="string" then
  58.       for i=0, 25 do
  59.           if rawget(self, i)==index then
  60.              return i
  61.           end
  62.        end
  63.        if index:match("^%d+[Ss]?$") then
  64.           return index:match("%d+")+0
  65.        end
  66.     end
  67.     return rawget(self,index)
  68. end})
  69. local rawget = rawget
  70. local chord_mt = {}
  71. function chord_mt:__index(index)
  72.      if type(index) == "number" then
  73.         index = notes[index]
  74.      end
  75.      if type(index) == "string" and notes[index] then
  76.         local arr={__original = self, __root = index}
  77.         local root = notes[index]
  78.         for _, interval in ipairs(self) do
  79.             table.insert(arr,notes[intervals[interval]+root])
  80.         end
  81.         setmetatable(arr,{__tostring=function(self)
  82.         return table.concat(self,"-")
  83.         end})
  84.         return arr
  85.      end
  86. end
  87. local rawget=rawget
  88. local chords = setmetatable({
  89. maj = {"P1","M3","P5"}; ["M"] = "maj";
  90. min = {"P1","m3","P5"}; ["m"] = "min";
  91. aug = {"P1","M3","A5"}; ["+"] = "aug";
  92. dim = {"P1","m3","A5"}; --["*"] = "dim";
  93. dom7 = {"P1","M3","P5","m7"}; ["7"] = "dom7";
  94. min7 = {"P1","m3","P5","m7"}; ["m7"] = "min7";
  95. maj7 = {"P1","M3","P5","M7"}; ["M7"] = "maj7";
  96. aug7 = {"P1","M3","A5","m7"};
  97.     ["+7"] = "aug7"; ["7#5"] = "aug7"; ["7aug5"] = "aug7";
  98. dim7 = {"P1","m3","d5","d7"}; --["*7"] = "dim7";
  99. m7dim5 = {"P1","m3","d5","m7"}; ["m7b5"] = "m7dim5"; --["*7"] = "dim7";
  100. sus2 = {"0S","2S","7S"};
  101. sus4 = {"0S","5S","7S"};
  102.  
  103. key_major = {"0S","2S","4S","5S","7S","9S","11S"};
  104. key_minor_natural = {"0S","2S","3S","5S","7S","8S","10S"};
  105. key_minor_harmonic = {"0S","2S","3S","5S","7S","8S","11S"};
  106. --no melodic cause it changes going up and down
  107. key_minor = "key_minor_natural";
  108.  
  109. key_dorian = {"0S","2S","3S","5S","7S","8S","11S"};
  110. key_lydian = {"0S","2S","4S","6S","7S","9S","11S"};
  111.  
  112. key_pentatonic_major = {"0S","2S","4S","7S","9S"};
  113. key_pentatonic_minor = {"0S","3S","5S","7S","10S"};
  114. key_pentatonic = "key_pentatonic_major";
  115.  
  116. }, {__index=function(self,index)
  117.      if type(index)=="string" and rawget(self,"key_"..index) then
  118.        return rawget(self,"key_"..index)
  119.      end
  120.      local ind = rawget(self,index)
  121.      local rootNote, intervalstr = index:match("^(..?)%((.-)%)$")
  122.      if rootNote and intervalstr then
  123.         local chord = setmetatable({},chord_mt)
  124.         for interval in intervalstr:gmatch("[^%+%,%s]+") do
  125.             table.insert(chord,interval)
  126.         end
  127.         return chord[rootNote]
  128.      end
  129.      if ind==nil then
  130.         for k,v in pairs(self) do
  131.             local safek = k:gsub("%+","%%+")
  132.             local note,suffix = index:match("^(.[^:]?):("..safek..")$")
  133.             if not (note or suffix) then
  134.                 note,suffix = index:match("^(.[^:]?):?("..safek:gsub("^key_","")..")$")
  135.             suffix=k
  136.             end
  137.             if note and suffix then
  138.                return self[suffix][note]
  139.             end
  140.         end
  141.      end
  142.      return ind
  143. end})
  144. for k,v in pairs(chords) do
  145.     if type(v)=='table' then
  146.        rawset(v,'__name',k)
  147.        setmetatable(v,chord_mt)
  148.     elseif type(v)=="string" then --alias
  149.        chords[k]=chords[v]
  150.     end
  151. end
  152. local guitarStrings = {"E","A","D","G","B","e"}
  153. local marked_frets = {[0]=true,[3]=true,[5]=true,[7]=true,[9]=true,[12]=true,[15]=true,[17]=true,[19]=true,[21]=true}
  154. function chordToGuitar(chord)
  155.     local arr={}
  156.     local frets = 22
  157.     local chordFrets={}
  158.     for k, v in ipairs(chord) do
  159.         chordFrets[v]={}
  160.     end
  161.     for str=1, #guitarStrings do
  162.         arr[str]={}
  163.         table.insert(arr[str],guitarStrings[str])
  164.         table.insert(arr[str],"│")
  165.     for fret=0, frets do
  166.         local fretNote = notes[notes[guitarStrings[str]]+fret]
  167.         local inChord = false
  168.         for i,chordNote in ipairs(chord) do
  169.             if chordNote == fretNote then
  170.            inChord = i
  171.            table.insert(chordFrets[fretNote],guitarStrings[str]..fret)
  172.            break           
  173.         end
  174.         end
  175.         if inChord then
  176.            table.insert(arr[str],inChord.."│")
  177.         else
  178.         table.insert(arr[str],"-│")
  179.         end
  180.     end
  181.             arr[str] = table.concat(arr[str],"")
  182.  
  183.     end
  184.     local function fretIndex(str)
  185.          local str, num = str:match("^(.)(%d+)$")
  186.          local stringNo
  187.          for k,v in ipairs(guitarStrings) do
  188.              if v==str then
  189.             stringNo = k
  190.          end
  191.          end
  192.          return tonumber(num), stringNo
  193.     end
  194.     print(" #","int.","note", "frets")
  195.     for k,v in ipairs(chord) do
  196.         table.sort(chordFrets[v],function(a,b)
  197.         local aN, aS = fretIndex(a)
  198.         local bN, bS = fretIndex(b)
  199.         return aN*100+aS < bN*100+bS
  200.         end)
  201.         print("["..k.."]", chord.__original[k],v,table.concat(chordFrets[v],","))
  202.     end
  203.     print()
  204.  
  205.     local markers = {"  "}
  206.     for i=0,frets do
  207.         if marked_frets[i] then
  208.            table.insert(markers,i.."")
  209.         elseif marked_frets[i-1] and #((i-1).."")==2 then
  210.            table.insert(markers,"")
  211.         else
  212.         table.insert(markers," ")
  213.         end
  214.         table.insert(markers," ")
  215.     end
  216.     print(table.concat(markers,""))
  217.  
  218.     for i=#arr, 1, -1 do
  219.         print(arr[i])
  220.     end
  221. end
  222. local whiteKeys={
  223.     C=true,
  224.     D=true,
  225.     E=true,
  226.     F=true,
  227.     G=true,
  228.     A=true,
  229.     B=true
  230. }
  231. --[[
  232.  ___________________________
  233. |  | | | |  |  | | | | | |  |
  234. |  | | | |  |  | | | | | |  |
  235. |  | | | |  |  | | | | | |  |
  236. |  | | | |  |  | | | | | |  |
  237. |   T   T   |   T   T   T   |
  238. |   |   |   |   |   |   |   |
  239. |   |   |   |   |   |   |   |
  240. '''''''''''''''''''''''''''''
  241. start on C or F
  242. end on E or B
  243.  
  244.   | | | |
  245.   | | | |
  246.   | | | |
  247.   | | | |
  248.    T   T
  249.    |   |
  250.    |   |
  251.  
  252.   | | | | | |  
  253.   | | | | | |  
  254.   | | | | | |  
  255.   | | | | | |  
  256.    T   T   T  
  257.    |   |   |  
  258.    |   |   |  
  259.  
  260.  
  261. ]]
  262. --start on F or C
  263. --end on E or B
  264. replacePiano = {
  265.     ["-"] = "─";
  266.     ["#"] = "\27[7m \27[0m"; --"▉";
  267.     ["|"] = "│";
  268.     ["/"] = "┘";
  269.     ["\\"] = "└";
  270.     ["T"] = "┬";
  271.     ["W"] = "┴";
  272. }
  273. function chordToPiano(chord)
  274.     local arr = {}
  275.     local whiteStrL = 6
  276.     local blackStrL = 4
  277.  
  278.     local topChar = "_"
  279.     local bottomChar = "-"
  280.     local emptyChar = " "
  281.     for i=1,blackStrL+whiteStrL+3 do
  282.     arr[i] = {}
  283.     end
  284.  
  285.     function writeVert(str)
  286.         arr[1][#arr[1]+1] = topChar
  287.         arr[#arr][#arr[#arr]+1] = bottomChar
  288.         --print(str)
  289.         --\27[41mcaca\27[0m
  290.         local i=1
  291.         do
  292.             local str = str
  293.             while true do
  294.            
  295.                 local addStr = str:match("^\27%[%d+m.\27%[%d+m")
  296.                 if not addStr then
  297.                     addStr = str:match("^.")
  298.                 end
  299.                 if not addStr then break end               
  300.                 --print(arr, i+1, #arr)
  301.                 arr[i+1][
  302.                 #arr[i+1]
  303.                 +1] = addStr
  304.                 i=i+1
  305.                 str = str :sub(#addStr+1,#str)
  306.             end
  307.         end
  308.         for j=i, blackStrL+whiteStrL+1 do
  309.             arr[i+1][#arr[i+1]+1] = emptyChar
  310.         end
  311.     end
  312.  
  313.     local rootNote = notes[chord[1]]
  314.     local bottomRollNote = rootNote
  315.     repeat
  316.         bottomRollNote = bottomRollNote - 1
  317.     until notes[bottomRollNote] == "F" or notes[bottomRollNote] == "C"
  318.  
  319.     local topNote = notes[chord[#chord]]
  320.     local topRollNote = topNote
  321.     repeat
  322.         topRollNote = topRollNote + 1
  323.     until notes[topRollNote] == "E" or notes[topRollNote] == "B"
  324.  
  325.     local currentNote = bottomRollNote
  326.     local currentChordPart = 1
  327.     local nextGoalNote = notes[notes[rootNote]]
  328.     bottomChar = "\\"
  329.     writeVert(("|"):rep(blackStrL+whiteStrL+1))
  330.     bottomChar = "-"
  331.     while true do
  332.         local str = ""
  333.         emptyChar = " "
  334.         if whiteKeys[notes[currentNote]] then
  335.             emptyChar = "#"
  336.         end
  337.         if currentNote == nextGoalNote then
  338.             if currentChordPart == -1 or currentChordPart == 0 then
  339.                 str = ""
  340.             else
  341.                 str = tostring(currentChordPart):gsub(".","\27[41m%1\27[0m")
  342.                 if whiteKeys[notes[currentNote]] then
  343.                     emptyChar = " "
  344.                 end
  345.                 emptyChar = emptyChar:gsub(".","\27[41m%1\27[0m")
  346.             end
  347.             if currentChordPart ~= #chord and currentChordPart ~= -1 then
  348.                 currentChordPart = currentChordPart + 1
  349.                 nextGoalNote = notes[chord[currentChordPart]]
  350.             else
  351.                 currentChordPart = -1
  352.                 nextGoalNote = notes[notes[topRollNote]]
  353.                 bottomChar = "-"
  354.             end
  355.         end
  356.         if whiteKeys[notes[currentNote]] then
  357.             if whiteKeys[notes[currentNote-1]] then
  358.                 --writeVert(("│"):rep(blackStrL+whiteStrL+1))
  359.             else
  360.                 writeVert(("|"):rep(blackStrL).."/"..(emptyChar):rep(whiteStrL))
  361.             end
  362.             --print("#emptyChar=", #emptyChar)
  363.             writeVert((emptyChar):rep((blackStrL+whiteStrL+1)-#(str:gsub("\27%[%d+m(.)\27%[%d+m","%1"))) .. str)
  364.  
  365.             if whiteKeys[notes[currentNote+1]] then
  366.                 bottomChar = "-"
  367.                 if currentNote == nextGoalNote then
  368.                     bottomChar = "/"
  369.                 end
  370.                 writeVert(("|"):rep(blackStrL+whiteStrL+1))
  371.                 bottomChar = "-"
  372.             else
  373.                 writeVert(("|"):rep(blackStrL).."\\"..(emptyChar):rep(whiteStrL))
  374.             end
  375.         else
  376.             bottomChar = "W"
  377.             writeVert((emptyChar):rep(blackStrL-#(str:gsub("\27%[%d+m(.)\27%[%d+m","%1"))) .. str .. "T" .. ("|"):rep(whiteStrL))
  378.             bottomChar = "-"
  379.         end
  380.         if currentChordPart == -1 and currentNote == nextGoalNote then
  381.             break
  382.         end
  383.         currentNote = notes[notes[currentNote+1]]
  384.  
  385.     end
  386.     --writeVert(("|"):rep(blackStrL+whiteStrL+1))
  387.     local lines = {}
  388.     for i,v in ipairs(arr) do
  389.         lines[i] = table.concat(v,""):gsub(".",function(c) return replacePiano[c] or c end)
  390.     end
  391.     print(table.concat(lines,"\n"))
  392.    
  393. end
  394. local input
  395. repeat
  396.     io.write">"; input=io.read()
  397.     if input =="cls" or input =="clear" then os.execute'clear' end
  398.     local res, err = pcall(function()
  399.     local toGuitar = false
  400.     if input:match":$" then
  401.        toGuitar = true
  402.        input=input:match("^(.-):$")
  403.     end
  404.     local chord = chords[input]
  405.     if notes[fmtnote(input)] and not chord then
  406.         chord = chords[input.."(0)"]
  407.     end
  408.     if chord then
  409.         print()
  410.  
  411.           local chordType = chord.__original.__name or ""
  412.           print(fmtnote(chord.__root)..chordType:gsub("key_",""))
  413.           if not chordType:match'key' then
  414.             print("5th Down / 4th Up:", notes[notes[chord.__root]+intervals.P4]..chordType)
  415.             print("5th Up / 4th Down:", notes[notes[chord.__root]+intervals.P5]..chordType)
  416.           else
  417.             if chordType:match'[Mm][aA][jJ][oO][rR]' then
  418.                 print("Relative Minor:", notes[notes[chord.__root]-intervals['3S']].."minor")
  419.             elseif chordType:match'[Mm][Ii][Nn][oO][rR]' then
  420.                 print("Relative Major:", notes[notes[chord.__root]+intervals['3S']].."major")
  421.             end
  422.           end
  423.             print()
  424.  
  425.           chordToGuitar(chord)
  426.           print()
  427.           print(chord)
  428.  
  429.           chordToPiano(chord)
  430.     end
  431.     local note,num = input:match("^(.-)(%d+)$")
  432.     if note and notes[note] and num then
  433.         print(" = ", notes[notes[note]+tonumber(num)])
  434.     end
  435.     end)
  436.     if not res then
  437.         print("error:", err)
  438.     end
  439. until input=="q" or input=="quit" or input=="exit"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement