fork(5) download
  1. -- converts an interval string into a note value
  2. -- an interval string consists of any number of #'s and b's
  3. -- followed by a non-negative scale degree
  4. -- (string) -> (number)
  5. local intvToNote = function (str)
  6. local a, b = str:match "^([#b]*)(%d+)$"
  7. local acc = 0
  8. a:gsub(".", function (c) acc = acc + (c == "#" and 1 or -1) end)
  9. b = tonumber(b)
  10. return acc * 7 + (b * 2 - 1) % 7 - 1
  11. end
  12.  
  13. -- converts a note value into a MIDI pitch value between 0 and 11 inclusive
  14. -- a note value is simply the number of perfect fifths from C
  15. -- (number) -> (number)
  16. local noteToMIDI = function (x)
  17. return x * 7 % 12
  18. end
  19.  
  20. -- gets the number of sharps in a note value
  21. -- (number) -> (number)
  22. local noteGetAcc = function (x)
  23. return math.floor((x + 1) / 7)
  24. end
  25.  
  26. -- converts a note value into a note name with proper accidentals
  27. -- (number) -> (string)
  28. local noteToStr; do
  29. local NAME = {[0] = 'C', 'D', 'E', 'F', 'G', 'A', 'B'}
  30. noteToStr = function (x)
  31. local acc = noteGetAcc(x)
  32. return NAME[x * 4 % 7] .. ("#"):rep(acc) .. ("b"):rep(-acc)
  33. end; end
  34.  
  35. -- converts a MIDI pitch value into a note name with sharp and octave name
  36. -- (number) -> (string)
  37. local MIDItoStr; do
  38. local NAME = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}
  39. MIDItoStr = function (x)
  40. return NAME[x % 12 + 1] .. math.floor(x / 12)
  41. end; end
  42.  
  43. local LH_LOWEST = -.25 -- left-hand octave offset
  44. local RH_CENTRE = 0 -- right-hand octave offset
  45. LH_LOWEST = 41 + 12 * LH_LOWEST
  46. RH_CENTRE = 60 + 12 * RH_CENTRE
  47. -- converts a chord over a bass note into a MIDI voicing
  48. -- a chord is a list of interval strings, basic order of chord tones is assumed:
  49. -- root to highest extension, followed by added/suspended tones
  50. -- members of lowest triad must use "0" as interval if omitted (or some other nil value)
  51. -- the value corresponding to key 0 indicates an optional slash bass
  52. -- a voicing consists of MIDI pitch values for the bass and four voices
  53. -- ([string], number) -> (number, [number])
  54. local voicing; do
  55. local map = function (f, t)
  56. local z = {}
  57. for k, v in next, t do z[k] = f(v) end
  58. return z
  59. end
  60. voicing = function (chord, trsp)
  61. chord = map(function (x) return x end, chord) -- clone
  62. if chord[0] == "0" then chord[0] = nil end
  63. for i = #chord, 4, -1 do if chord[i] == "0" then table.remove(chord, i) end end
  64. assert(chord[1] and chord[1] ~= "0", "Missing bass note")
  65. local c = map(intvToNote, chord)
  66. local bass = (c[0] or c[1]) + trsp
  67.  
  68. -- select notes
  69. local vo = {}
  70. local addVoice; do
  71. local selected = {}
  72. for i, v in pairs(chord) do if v == "0" then selected[i] = true end end
  73. addVoice = function (i, f)
  74. if #vo >= 4 then return end
  75. if not selected[i] and (not f or f(c[i])) then
  76. local n = noteToMIDI(c[i] + trsp)
  77. for _, v in ipairs(vo) do if n == v then return end end -- no duplicates
  78. selected[i] = true; table.insert(vo, n)
  79. end
  80. end; end
  81.  
  82. local VOICES = #c
  83. -- 1st pass: suspended note
  84. addVoice(2, function (n) return n ~= intvToNote("3") and n ~= intvToNote("b3") end)
  85. -- 2nd pass: altered extensions
  86. for i = 5, VOICES do addVoice(i, function (n) return noteGetAcc(n) ~= 0 end) end
  87. -- 3rd pass: altered fifth
  88. addVoice(3, function (n) return n ~= intvToNote("5") end)
  89. -- 4th pass: other extensions
  90. for i = VOICES, 4, -1 do addVoice(i) end
  91. -- 5th pass: third, fifth
  92. for i = 2, 3 do addVoice(i) end
  93. -- 6th pass: slash bass (optional)
  94. -- if chord[0] then addVoice(0); c[0] = nil end
  95. -- 7th pass: root
  96. addVoice(1)
  97. table.sort(vo)
  98.  
  99. -- raise bass note to left-hand range
  100. bass = noteToMIDI(bass)
  101. while bass < LH_LOWEST do bass = bass + 12 end
  102.  
  103. -- use average pitch value for voicing
  104. local avg = 0
  105. for _, v in ipairs(vo) do avg = avg + v end
  106. local COUNT = #vo
  107. while avg < (RH_CENTRE - 12) * COUNT do
  108. avg = avg + 12 * COUNT -- raise notes just below right-hand octave
  109. for i, v in ipairs(vo) do vo[i] = v + 12 end
  110. end
  111. while avg < RH_CENTRE * COUNT do
  112. avg = avg + 12 -- invert voicing until average pitch above right-hand octave
  113. table.insert(vo, table.remove(vo, 1) + 12)
  114. end
  115. -- swap end notes for slightly open voicing
  116. vo[1], vo[math.min(COUNT + 1, 4)] = vo[COUNT] - 12, vo[1] + 12
  117. -- if COUNT == 4 then
  118. -- vo[1], vo[4] = vo[4] - 12, vo[1] + 12
  119. -- else -- double one note
  120. -- local top = avg + vo[1] + 12
  121. -- local down = avg + vo[COUNT] - 12
  122. -- local centre = RH_CENTRE * COUNT
  123. -- if math.abs(down - centre) <= math.abs(top - centre) then
  124. -- table.insert(vo, 1, vo[COUNT] - 12)
  125. -- else
  126. -- table.insert(vo, vo[1] + 12)
  127. -- end
  128. -- end
  129.  
  130. return bass, vo
  131. end; end
  132.  
  133. local test = function (chord, name)
  134. -- for i, v in ipairs(chord) do print(i, intvToNote(v), noteToStr(intvToNote(v))) end
  135. for i = -7, 7 do
  136. local lh, rh = voicing(chord, i)
  137. io.write(("%14s"):format(noteToStr(i) .. name ..
  138. (chord[0] and "/" .. noteToStr(i + intvToNote(chord[0])) or "")))
  139. io.write "\t\t\t"
  140. for _, v in ipairs(chord) do if v ~= "0" then
  141. io.write((" %3s"):format(noteToStr(intvToNote(v) + i)))
  142. end end
  143. if chord[0] and chord[0] ~= "0" then
  144. io.write((" / %3s"):format(noteToStr(intvToNote(chord[0]) + i)))
  145. end
  146. io.write "\t\t\t"
  147. io.write((" %3s"):format(MIDItoStr(lh)))
  148. for _, v in ipairs(rh) do io.write((" %3s"):format(MIDItoStr(v))) end
  149. io.write '\n'
  150. end
  151. io.write '\n'
  152. end
  153.  
  154. test({"1", "3", "#5"}, "+")
  155. test({"1", "b3", "5", [0] = "2"}, "m")
  156. test({"1", "3", "5", [0] = "5"}, "")
  157. test({"1", "4", "5", "b7", "9"}, "9sus")
  158. test({"1", "3", "5", "b7", "b9", "13"}, "7(b9,13)")
  159. test({"1", "0", "5"}, "5")
  160. test({"1", "4", "5", "2"}, "sus42")
  161.  
Success #stdin #stdout 0s 2832KB
stdin
Standard input is empty
stdout
           Cb+			  Cb  Eb   G			  B3  G4 D#5  G5  B5
           Gb+			  Gb  Bb   D			 F#3 F#4  D5 F#5 A#5
           Db+			  Db   F   A			 C#4  F4 C#5  F5  A5
           Ab+			  Ab   C   E			 G#3  E4  C5  E5 G#5
           Eb+			  Eb   G   B			 D#3  G4 D#5  G5  B5
           Bb+			  Bb   D  F#			 A#3 F#4  D5 F#5 A#5
            F+			   F   A  C#			  F3  F4 C#5  F5  A5
            C+			   C   E  G#			  C4  E4  C5  E5 G#5
            G+			   G   B  D#			  G3  G4 D#5  G5  B5
            D+			   D  F#  A#			  D3 F#4  D5 F#5 A#5
            A+			   A  C#  E#			  A3  F4 C#5  F5  A5
            E+			   E  G#  B#			  E3  E4  C5  E5 G#5
            B+			   B  D# F##			  B3  G4 D#5  G5  B5
           F#+			  F#  A# C##			 F#3 F#4  D5 F#5 A#5
           C#+			  C#  E# G##			 C#4  F4 C#5  F5  A5

        Cbm/Db			  Cb Ebb  Gb /  Db			 C#4 F#4  D5 F#5  B5
        Gbm/Ab			  Gb Bbb  Db /  Ab			 G#3 F#4 C#5 F#5  A5
        Dbm/Eb			  Db  Fb  Ab /  Eb			 D#3  E4 C#5  E5 G#5
        Abm/Bb			  Ab  Cb  Eb /  Bb			 A#3 G#4 D#5 G#5  B5
         Ebm/F			  Eb  Gb  Bb /   F			  F3 F#4 D#5 F#5 A#5
         Bbm/C			  Bb  Db   F /   C			  C4  F4 C#5  F5 A#5
          Fm/G			   F  Ab   C /   G			  G3  F4  C5  F5 G#5
          Cm/D			   C  Eb   G /   D			  D3  G4 D#5  G5  C6
          Gm/A			   G  Bb   D /   A			  A3  G4  D5  G5 A#5
          Dm/E			   D   F   A /   E			  E3  F4  D5  F5  A5
          Am/B			   A   C   E /   B			  B3  E4  C5  E5  A5
         Em/F#			   E   G   B /  F#			 F#3  G4  E5  G5  B5
         Bm/C#			   B   D  F# /  C#			 C#4 F#4  D5 F#5  B5
        F#m/G#			  F#   A  C# /  G#			 G#3 F#4 C#5 F#5  A5
        C#m/D#			  C#   E  G# /  D#			 D#3  E4 C#5  E5 G#5

         Cb/Gb			  Cb  Eb  Gb /  Gb			 F#3 F#4 D#5 F#5  B5
         Gb/Db			  Gb  Bb  Db /  Db			 C#4 F#4 C#5 F#5 A#5
         Db/Ab			  Db   F  Ab /  Ab			 G#3  F4 C#5  F5 G#5
         Ab/Eb			  Ab   C  Eb /  Eb			 D#3 G#4 D#5 G#5  C6
         Eb/Bb			  Eb   G  Bb /  Bb			 A#3  G4 D#5  G5 A#5
          Bb/F			  Bb   D   F /   F			  F3  F4  D5  F5 A#5
           F/C			   F   A   C /   C			  C4  F4  C5  F5  A5
           C/G			   C   E   G /   G			  G3  G4  E5  G5  C6
           G/D			   G   B   D /   D			  D3  G4  D5  G5  B5
           D/A			   D  F#   A /   A			  A3 F#4  D5 F#5  A5
           A/E			   A  C#   E /   E			  E3  E4 C#5  E5  A5
           E/B			   E  G#   B /   B			  B3 G#4  E5 G#5  B5
          B/F#			   B  D#  F# /  F#			 F#3 F#4 D#5 F#5  B5
         F#/C#			  F#  A#  C# /  C#			 C#4 F#4 C#5 F#5 A#5
         C#/G#			  C#  E#  G# /  G#			 G#3  F4 C#5  F5 G#5

        Cb9sus			  Cb  Fb  Gb Bbb  Db			  B3 F#4 C#5  E5  A5
        Gb9sus			  Gb  Cb  Db  Fb  Ab			 F#3  E4  B4 C#5 G#5
        Db9sus			  Db  Gb  Ab  Cb  Eb			 C#4 F#4  B4 D#5 G#5
        Ab9sus			  Ab  Db  Eb  Gb  Bb			 G#3 F#4 C#5 D#5 A#5
        Eb9sus			  Eb  Ab  Bb  Db   F			 D#3  F4 A#4 C#5 G#5
        Bb9sus			  Bb  Eb   F  Ab   C			 A#3  F4  C5 D#5 G#5
         F9sus			   F  Bb   C  Eb   G			  F3  G4  C5 D#5 A#5
         C9sus			   C   F   G  Bb   D			  C4  F4 A#4  D5  G5
         G9sus			   G   C   D   F   A			  G3  F4  C5  D5  A5
         D9sus			   D   G   A   C   E			  D3  G4  C5  E5  A5
         A9sus			   A   D   E   G   B			  A3  E4  B4  D5  G5
         E9sus			   E   A   B   D  F#			  E3 F#4  B4  D5  A5
         B9sus			   B   E  F#   A  C#			  B3 F#4 C#5  E5  A5
        F#9sus			  F#   B  C#   E  G#			 F#3  E4  B4 C#5 G#5
        C#9sus			  C#  F#  G#   B  D#			 C#4 F#4  B4 D#5 G#5

    Cb7(b9,13)			  Cb  Eb  Gb Bbb Dbb  Ab			  B3 G#4  C5 D#5  A5
    Gb7(b9,13)			  Gb  Bb  Db  Fb Abb  Eb			 F#3  E4 A#4 D#5  G5
    Db7(b9,13)			  Db   F  Ab  Cb Ebb  Bb			 C#4  F4  B4  D5 A#5
    Ab7(b9,13)			  Ab   C  Eb  Gb Bbb   F			 G#3 F#4  C5  F5  A5
    Eb7(b9,13)			  Eb   G  Bb  Db  Fb   C			 D#3  E4  C5 C#5  G5
    Bb7(b9,13)			  Bb   D   F  Ab  Cb   G			 A#3  G4  B4  D5 G#5
     F7(b9,13)			   F   A   C  Eb  Gb   D			  F3 F#4  D5 D#5  A5
     C7(b9,13)			   C   E   G  Bb  Db   A			  C4  E4 A#4 C#5  A5
     G7(b9,13)			   G   B   D   F  Ab   E			  G3  F4  B4  E5 G#5
     D7(b9,13)			   D  F#   A   C  Eb   B			  D3 F#4  C5 D#5  B5
     A7(b9,13)			   A  C#   E   G  Bb  F#			  A3 F#4 A#4 C#5  G5
     E7(b9,13)			   E  G#   B   D   F  C#			  E3  F4 C#5  D5 G#5
     B7(b9,13)			   B  D#  F#   A   C  G#			  B3 G#4  C5 D#5  A5
    F#7(b9,13)			  F#  A#  C#   E   G  D#			 F#3  E4 A#4 D#5  G5
    C#7(b9,13)			  C#  E#  G#   B   D  A#			 C#4  F4  B4  D5 A#5

           Cb5			  Cb  Gb			  B3 F#4 F#5  B5
           Gb5			  Gb  Db			 F#3 F#4 F#5 C#6
           Db5			  Db  Ab			 C#4 G#4 G#5 C#6
           Ab5			  Ab  Eb			 G#3 G#4 G#5 D#6
           Eb5			  Eb  Bb			 D#3 D#4 D#5 A#5
           Bb5			  Bb   F			 A#3  F4  F5 A#5
            F5			   F   C			  F3  F4  F5  C6
            C5			   C   G			  C4  G4  G5  C6
            G5			   G   D			  G3  G4  G5  D6
            D5			   D   A			  D3  A4  A5  D6
            A5			   A   E			  A3  E4  E5  A5
            E5			   E   B			  E3  E4  E5  B5
            B5			   B  F#			  B3 F#4 F#5  B5
           F#5			  F#  C#			 F#3 F#4 F#5 C#6
           C#5			  C#  G#			 C#4 G#4 G#5 C#6

       Cbsus42			  Cb  Fb  Gb  Db			  B3 F#4 C#5  E5  B5
       Gbsus42			  Gb  Cb  Db  Ab			 F#3 F#4  B4 C#5 G#5
       Dbsus42			  Db  Gb  Ab  Eb			 C#4 F#4 C#5 D#5 G#5
       Absus42			  Ab  Db  Eb  Bb			 G#3 G#4 C#5 D#5 A#5
       Ebsus42			  Eb  Ab  Bb   F			 D#3  F4 A#4 D#5 G#5
       Bbsus42			  Bb  Eb   F   C			 A#3  F4  C5 D#5 A#5
        Fsus42			   F  Bb   C   G			  F3  G4  C5  F5 A#5
        Csus42			   C   F   G   D			  C4  F4  C5  D5  G5
        Gsus42			   G   C   D   A			  G3  G4  C5  D5  A5
        Dsus42			   D   G   A   E			  D3  G4  D5  E5  A5
        Asus42			   A   D   E   B			  A3  E4  B4  D5  A5
        Esus42			   E   A   B  F#			  E3 F#4  B4  E5  A5
        Bsus42			   B   E  F#  C#			  B3 F#4 C#5  E5  B5
       F#sus42			  F#   B  C#  G#			 F#3 F#4  B4 C#5 G#5
       C#sus42			  C#  F#  G#  D#			 C#4 F#4 C#5 D#5 G#5