オルタナティブ・ブログ > An Agile Way >

アジャイルに行こう!

11年前に書いた awk スクリプト発掘(ギターコードを見る chordview)

»

ディスクを整理していたら、11年前に書いた awk スクリプトが出てきた。ギターのコード名を入力すると、そのコードフォームや構成音が表示される。コードブック代わりに使おうと思っていた。

当時、フォークギターからジャズギターへ転向しようと考えており、ローコードだけでなくハイポジションでの、6,5,4弦にルート音を持つ各ギターコードと、テンションの位置を勉強していた。

Jazzでは、例えば、メジャーコードは、1,3,5 度の音だけでなくM7と6と9、ドミナントコードは、1,3,5,b7 に加えて b9, 9, #9, #11, b13, 13 もテンションとして許される。それらを、キャラクタベースのCUIで表示するプログラムだ。通常のロックやフォークのコードではなく、自分のアドリブのインサイド音やテンション感を養成するために作った。例えば、Cの4弦ルートだと、こんなキャラクタを吐く。

      D 型フォーム4弦ベース

       度数                     フォーム                  音名
  10f                      10f                      10f
--9+---+--3+---+---+     --9+---+--*+---+---+     --D+---+--E+---+---+
--6+---+-M7+--R+---+     --6+---+-M7+--*+---+     --A+---+--B+--C+---+
---+---+--5+---+---+     ---+---+--*+---+---+     ---+---+--G+---+---+
--R+---+---+---+---+     --*+---+---+---+---+     --C+---+---+---+---+
(5)+---+---+---+---+     (5)+---+---+---+---+     --G+---+---+---+---+
---+---+---+---+---+     ---+---+---+---+---+     ---+---+---+---+---+

プログラムとして今見ると(末尾添付)、split を使った見やすい初期設定、データ設定関数、仮引数を使ったローカル変数、などいくつか工夫されているなー、と思い出す。awk には関数ローカル変数がないが、仮引数はローカルである。そこで、ローカル名前空間を実現するために仮引数をローカル変数として使う。そして、本当の仮引数とローカル変数はタブで区切る、というコンベンションだ。そして、当時としては、関数名は「かなり長い」部類に入ると思う。ぼくは UNIX が好きな割には、長い名前を付ける方が好きだった。

awk スクリプトとしては、ぼくが書いた一番長いものかもしれない。現在はLL流行であり、Ruby, Perl, Python などがよく使われているけど、ぼくのホームスクリプト言語は、awk/sed/bsh だ。当時、~/bin の下に毎日1つずつ、"Myコマンド"をためて行ったように思う。

ちなみに、JUDE の開発でぼくがコードを書いたときは、ユニットテストを Python (正確には、JPython、今の Jython) で書いた。そのころは、JUnitは存在せず、テストはスクリプトで書くのが「正しい」ことのように思えたから。製品コードは固く、テストコードは軽く、柔らかく、が生産性が高いように思えた。ぼくが最初に書いた雑誌記事は、「Java オブジェクトを操作するスクリプト言語」というDDJJ(DDJの日本語版-翔泳社が出していた)の記事。そこでも、Python, Pnuts(戸松さん作), Tcl/Tk などで Java のオブジェクトを操作する方法を書いている。Ruby では、そのころ前田修吾さんの、Demi があった。今でも、ここに記事がある。

「Java オブジェクトを操作するスクリプト言語」http://www.objectclub.jp/technicaldoc/java/scripting-java

このプログラムは、名前が泣かせる。コードビュー(chordview)。当時、無くなりかけていた codeview というデバッガへのオマージュ。

史料保存の意味で、以下に全コードをペーストしておこう。540行だ。このコードは、古い awk では動かない。nawk と gawk で動作を確認してる。chmod +x してそのまま実行(#! /bin/gawk -f が使える場合)、もしくは、

> gawk -f chordview

である。以下にコード。

#! /bin/gawk -f
##############################################################
#
#   chordview  ...  ギターコード構成音の表示
#
#   Author:  K.hira
#   Date:    Oct.6,'95
#
##############################################################
BEGIN {
# set Tone Name
#      0  1  2  3 4  5  6  7  8  9  10 11
#      1  2  3  4 5  6  7  8  9  10 11 12
split("A  Bb B  C Db D  Eb E  F  Gb G  Ab A", ToneName1)
split("A  A# B  C C# D  D# E  F  F# G  G# A", ToneName2)
split("3# 2b 5# 0 5b 2# 3b 4# 1b 6b 1# 4b 3#", Staffs)

# degree of major scale -> tone no.
split("0 2 4 5 7 9 10 0 2 4 5 7 9", DegTone)

# degree name to tone interval
ToneFromDeg[1] = 0; ToneFromDeg["m2"] = 1; ToneFromDeg[2] =  2; ToneFromDeg["m3"] = 3;
ToneFromDeg[3] = 4; ToneFromDeg["M3"] = 4; ToneFromDeg[4] = 5; ToneFromDeg["b5"] = 6; ToneFromDeg["#4"] = 6;
ToneFromDeg[5] = 7; ToneFromDeg["#5"] = 8; ToneFromDeg["m6"] = 8; ToneFromDeg[6] = 9;
ToneFromDeg[7] = 10; ToneFromDeg["M7"] = 11; ToneFromDeg[8] = 12; ToneFromDeg["b9"] = 13;
ToneFromDeg[9] = 14; ToneFromDeg["#9"] = 15; ToneFromDeg[11] = 17; ToneFromDeg["#11"] = 18;
ToneFromDeg[b13] = 20; ToneFromDeg[13] = 21;

# set StringTuning for 6 strings
StringTuning[1] = toneNoFromName("E"); StringTuning[2] = toneNoFromName("B");
StringTuning[3] = toneNoFromName("G"); StringTuning[4] = toneNoFromName("D");
StringTuning[5] = toneNoFromName("A"); StringTuning[6] = toneNoFromName("E");

# maximum number of tension including 7th
MaxTen = 4

setBasicChordForms()
printf "コードネームを入力してください: " > "/dev/tty"
}

# toneNo -> Degree
function degFromToneNo(toneNo) {
for (i in ToneFromDeg) {
  if (ToneFromDeg[i] == toneNo) return i
}
return "-"
}

# Tone Number -> Tone name (hint is likely root of major I)
function toneNameFromToneNo(toneNo,hint) {
if (Staffs[hint] ~ /#/) return ToneName2[toneNo]
else return ToneName1 [toneNo]
}

# Tone Name -> Tone Number
function toneNoFromName(name, i) {
for (i = 1; i <= 12; i++) {
  if (name == ToneName1[i])
   return i
}
for (i = 1; i <= 12; i++) {
  if (name == ToneName2[i])
   return i
}
return -1
}

#  Position -> Tone Number
function toneNoFromPos(string, flet) {
return StringTuning[string] + flet
}

#  Tone Number + string -> flet
function fletFromToneNo(toneNo, string) {
return (toneNo - StringTuning[string])%12
}

#  generic matcning fucntion
function match2(str, pattern, ret,  n) {
n = split(pattern, pat)
    for (i = 1; i <= n; i++) {
  sub(/^[ \t]+/,"",str)
        if (match(str, pat[i])) {
   if (RSTART == 1) {
             ret[i] = substr(str, RSTART, RLENGTH)
    str = substr(str, RSTART+RLENGTH)
   }
        }
    }   
# unparsed invalid string
return str
}

# generic incriment function
function incrAll(str, step,   i,n,a,ret) {
n = split(str,a)
ret = a[1]++
for (i = 2; i <=n; i++) ret = ret " " a[i]++
return ret
}

#
# chordRep is "Chord Representation"
#
# ChordRep["root"]  ... Root Tone No
# ChordRep["tonal"]  ... major/minor/dim
# ChordRep["ten"]   ... tension
# ChordRep["acc"]   ... chord accesary
#
function clearChordRep(chordRep,  i) {
for (i in chordRep) chordRep[i] = ""
}
#
# ChordSys is "Chord System"
#
# ChordSys["root"]
# ChordSys["tonal"]  major,minor
# ChordSys["role"]   major,minor,dominant,dim
# ChordSys[0..11]    tones from root
#
function clearChordSys(chordSys,  i) {
for (i in chordSys) chordSys[i] = ""
}
function setChordTone(chordSys, deg, val) {
if (val == 0) chordSys[ToneFromDeg[deg]] = ""
else chordSys[ToneFromDeg[deg]] = val
}
function isChordTone(chordSys, deg) {
return chordSys[ToneFromDeg[deg]]
}
# chordRep -> ChordSys
function chordRepToChordSys(chordRep,chordSys  ,i) {
chordSys["root"] = chordRep["root"]
chordSys["tonal"] = chordRep["tonal"]
setChordTone(chordSys,1,1)

# find hidden 7th
for (i = 1; i <= MaxTen; i++) {
  if (match(chordRep["ten",i], "(M7|M9)")) setChordTone(chordSys,"M7",1)
  else if (match(chordRep["ten",i], /(7|9|11|13)/)) setChordTone(chordSys,7,1)
}

# determine 5th
if (chordRep["acc"] == "-5") setChordTone(chordSys,"b5",1)
else if (chordRep["acc"] == "+5") setChordTone(chordSys,"#5",1)
else setChordTone(chordSys,5,1)

# sus4
if (chordRep["acc"] == "sus4") {
  setChordTone(chordSys,4,1)
  setChordTone(chordSys,"M3","")
}
 
# add9
if (chordRep["acc"] == "add9" || chordRep["acc"] == "+9") setChordTone(chordSys,9,1)

# get other tensions
for (i = 1; i <= MaxTen; i++) {
  if (chordRep["ten",i]) setChordTone(chordSys,chordRep["ten",i], 1)
}

# adjust 69
if (isChordTone(chordSys,6) && isChordTone(chordSys,9)) setChordTone(chordSys,7,"")

# determine tonality(role)  and 3rd
if (chordRep["tonal"] == "major") {
  setChordTone(chordSys,"M3",1)
  if (isChordTone(chordSys,7)) {
   chordSys["role"] = "dominant"
  } else {
   chordSys["role"] = "major"
  }
} else if (chordRep["tonal"] == "minor") {
  setChordTone(chordSys,"m3",1)
  if (isChordTone(chordSys,"b5")) {
   chordSys["role"] = "halfdim"
  } else {
   chordSys["role"] = "minor"
  }
} else if (chordRep["tonal"] == "dim") {
  chordSys["role"] = "dim"
  setChordTone(chordSys,"m3",1)
  setChordTone(chordSys,5,"")
  setChordTone(chordSys,"b5",1)
  setChordTone(chordSys,7,"")
  setChordTone(chordSys,6,1)
}
}
function printChordSys(chordSys,  i,majroot) {
printf "   含む音: "
majroot = chordSys["root"]
if (chordSys["role"] == "minor") majroot = (majroot+3)%12
for (i = 0; i < 22; i++) {
  if (chordSys[i] == 1) printf("%s(%s)  ", toneNameFromToneNo((i+chordSys["root"]-1)%12+1, majroot), degFromToneNo(i))
}
print ""
}
function getChordRep(str, chordRep,  chordStr,i,tension) {
tension = " (#9|b9|9|#11|11|b13|13)"
sub(/maj/,"M",str)

err = match2(str, "[A-G](b|#)? (m|dim) (7|M7|6|9|M9|11) (sus4|aug|\\+5|-5|b5|add9|\\+9)" tension tension tension, chordStr)

# error
if (err !~ /^[ \t]*$/) {
  print "そのコードは知りません" str > "/dev/tty"
  print "読めない記号" err > "/dev/tty"
  return 1
}

chordRep["root"] = toneNoFromName(chordStr[1])

if (chordStr[2] == "m") chordRep["tonal"] = "minor"
else if (chordStr[2] == "dim") chordRep["tonal"] = "dim"
else chordRep["tonal"] = "major"

chordRep["ten",1] = chordStr[3]

if (chordStr[4] == "aug" || chordStr[4] == "+5") chordRep["acc"] = "+5"
else if (chordStr[4] == "b5" || chordStr[4] == "-5") chordRep["acc"] = "-5"
else chordRep["acc"] = chordStr[4]

for ( i = 1; i <= MaxTen-1; i++) {
  if (chordStr[4+i] != "") chordRep["ten",i+1] = chordStr[4+i]
}
return 0
}
function setFingerBoard(chordSys, fingerBoard,  i,j,fl) {
for (i = 0; i <= 22; i++) {
  if (!chordSys[i])
   continue;
  for (j = 1; j <= 6; j++ ) {
   fl = fletFromToneNo(i+chordSys["root"], j)

   fingerBoard[j,fl] = i   # or degree
   fingerBoard[j,fl+12] = i   # or degree
  }
}
}
function printFingerBoard(chordSys,fingerBoard,  i,j,mark,tmpstr) {
print "             3f      5f              9f         12f         15f"
for (i = 1; i <=6; i++) {
  for (j = 0; j <= 15; j++) {
   if (fingerBoard[i,j] == "") mark = ""
   else if (fingerBoard[i,j] == 0) mark = "R"
   else mark = degFromToneNo(fingerBoard[i,j])

   if (j == 0) {
    if (mark == "") printf" x|"
    else printf "%2s|", mark
   } else {
    if (mark == "") printf "---+"
    else {
     tmpstr = sprintf("%3s+", mark)
     gsub(/ /,"-",tmpstr)
     printf tmpstr
    }
   }
  }
  printf "\n"
}
}
function printFormByName(form, toneNo,  i,tn,majroot,tmpstr,cform) {
# copy
for (i in form) {
  cform[i] = form[i]
}
majroot = toneNo
if (cform["role"] == "minor" || cform["role"] == "halfdim") majroot = (majroot+3) %12
for (i in cform) {
  if (cform[i] == 0) {
  } else {
   if (cform[i] ~ /-/) {
    tmpstr = cform[i]
    sub(/-/,"",tmpstr)
    tn = ToneFromDeg[tmpstr]
    cform[i] = toneNameFromToneNo((tn+toneNo-1)%12+1, majroot)
   } else {
    tn = ToneFromDeg[cform[i]]
    cform[i] = toneNameFromToneNo((tn+toneNo-1)%12+1, majroot)
   }
  }
}
return printForm(form, cform, toneNo)
}
function printFormByDot(form, toneNo,  i,cform) {
# copy
for (i in form) {
  cform[i] =  form[i]
}
if (cform["role"] == "major") {
  for (i in cform) {
   if (cform[i] == 1 || cform[i] == 3 || cform[i]  == 5)
    cform[i] = "*"
  }
} else if (cform["role"] == "minor") {
  for (i in cform) {
   if (cform[i] == 1 || cform[i] == "m3" || cform[i] == 5)
    cform[i] = "*"
  }
} else if (cform["role"] == "dominant") {
  for (i in cform) {
   if (cform[i] == 1 || cform[i] == 3 || cform[i] == 5 || cform[i] == 7)
    cform[i] = "*"
  }
} else if (cform["role"] == "halfdim") {
  for (i in cform) {
   if (cform[i] == 1 || cform[i] == "m3" || cform[i] == "b5" || cform[i] == 7)
    cform[i] = "*"
  }
} else if (cform["role"] == "dim") {
  for (i in cform) {
    cform[i] = "*"
  }
}

return printForm(form, cform, toneNo)
}
function printForm(form, cform, toneNo,  mark,i,j,tmpstr,found,fl,start,retstr) {
# find position
retstr = ""
found = 0
for (i = 1; i <=6; i++) {
  for (j = 0; j <= 5; j++) {
   if (form[i,j] == 1) {
    fl = (fletFromToneNo(toneNo, i) - j + 13) % 12
    found = 1; break;
   }
   if (found) break;
  }
}
if (fl != 0) { retstr = retstr sprintf("  %df\n", fl) }
else { retstr = retstr sprintf(" \n", fl) }

for (i = 1; i <=6; i++) {
  start = 1
  if (fl == 0) {
   if (cform[i,1] == 0) {
    retstr = retstr sprintf("   |")
   } else if (cform[i,1] == 1) {
    retstr = retstr sprintf("  R|")
   } else {
    if (cform[i,1] ~  /-/) {
     tmpstr = cform[i,1]
     sub(/-/,"",tmpstr)
     retstr = retstr sprintf("(%s)|",tmpstr)
    } else {
     tmpstr = sprintf("%3s|",cform[i,1])
     retstr = retstr tmpstr
    }
   }
   start = 2
  }
  for (j = start; j <= 5; j++) {
   if (cform[i,j] == 0) {
    retstr = retstr sprintf("---+")
   } else if (cform[i,j] == 1) {
    retstr = retstr sprintf("--R+")
   } else {
    if (cform[i,j] ~  /-/) {
     tmpstr = cform[i,j]
     sub(/-/,"",tmpstr)
     retstr = retstr sprintf("(%d)+",tmpstr)
    } else {
     tmpstr = sprintf("%3s+",cform[i,j])
     gsub(/ /,"-",tmpstr)
     retstr = retstr tmpstr
    }
   }
  }
  retstr = retstr "\n"
}
return retstr "\n"
}
function printForm3(form, toneNo) {
print ""
print "       度数                     フォーム                  音名"
joinLines3(printForm(form,form,toneNo),printFormByDot(form,toneNo), printFormByName(form,toneNo))
}
function clearFingerBoard(fingerBoard, i,j) {
for (i = 1; i <= 6; i++)  {
  for (j = 1; j <= 15; j++)
   fingerBoard[i,j] = ""
}

}
function joinLines5(a,b,c,d,e,   i,n) {
n = split(a,as,"\n")
split(b,bs,"\n")
split(c,cs,"\n")
split(d,ds,"\n")
split(e,es,"\n")

for (i = 1; i <=n; i++) {
printf "%-20s     %-20s     %-20s     %-20s     %-20s\n",as[i],bs[i],cs[i],ds[i],es[i]
}
}
function joinLines4(a,b,c,d,   i,n) {
n = split(a,as,"\n")
split(b,bs,"\n")
split(c,cs,"\n")
split(d,ds,"\n")

for (i = 1; i <=n; i++) {
printf "%-20s     %-20s     %-20s     %-20s\n",as[i],bs[i],cs[i],ds[i]
}

}
function joinLines3(a,b,c,   i,n) {
n = split(a,as,"\n")
split(b,bs,"\n")
split(c,cs,"\n")

for (i = 1; i <=n; i++) {
printf "%-20s     %-20s     %-20s\n",as[i],bs[i],cs[i]
}
}
function joinLines2(a,b,   i,n) {
n = split(a,as,"\n")
split(b,bs,"\n")

for (i = 1; i <=n; i++) {
printf "%-20s     %-20s\n",as[i],bs[i]
}
}
function waitkey() {
printf "-- press return --"
getline
print ""
}
/^\?/ || /^[Hh]/ {
print "コード名のE  C, Cm7, Cadd9, C7, Csus4, Cm7b5, Cdim, Caug" > "/dev/tty"
print "               C69, C#9b13, C913, ..." > "/dev/tty"
printf "コードネームを入力してください: " > "/dev/tty"
next
}
/^[Qq]/ || /^[Ee]xit/ {
exit
}
/^[ \t]*$/ {
printf "コードネームを入力してください: " > "/dev/tty"
next
}
{
err = getChordRep($0, ChordRep)
if (err) next

chordRepToChordSys(ChordRep, ChordSys)
print ""
printChordSys(ChordSys)
print ""
setFingerBoard(ChordSys, FingerBoard)

print "   含む音の指盤上の位置(度数)"
print ""
printFingerBoard(ChordSys, FingerBoard)
print ""

print "   基本的なコードフォーム"
print ""
if (ChordSys["role"] == "major") {
  print "      A 型フォーム弦ベース"
  printForm3(BC_MA, ChordSys["root"]); waitkey()
  print "      C 型フォーム弦ベース"
  printForm3(BC_MC, ChordSys["root"]); waitkey()
  print "      D 型フォーム弦ベース"
  printForm3(BC_MD, ChordSys["root"]); waitkey()
  print "      G 型フォーム弦ベース"
  printForm3(BC_MG, ChordSys["root"]); waitkey()
  print "      E 型フォーム弦ベース"
  printForm3(BC_ME, ChordSys["root"])
} else if (ChordSys["role"] == "minor") {
  print "      Am 型フォーム弦ベース"
  printForm3(BC_mA, ChordSys["root"]); waitkey()
  print "      Cm 型フォーム弦ベース"
  printForm3(BC_mC, ChordSys["root"]); waitkey()
  print "      Dm 型フォーム弦ベース"
  printForm3(BC_mD, ChordSys["root"]); waitkey()
  print "      Gm 型フォームE6弦ベース"
  printForm3(BC_mG, ChordSys["root"]); waitkey()
  print "      Em 型フォーム弦ベース"
  printForm3(BC_mE, ChordSys["root"])
} else if (ChordSys["role"] == "dominant") {
  print "      A7 型フォーム5弦ベース"
  printForm3(BC_7A, ChordSys["root"]); waitkey()
  print "      C7 型フォーム5弦ベース"
  printForm3(BC_7C, ChordSys["root"]); waitkey()
  print "      D7 型フォーム4弦ベース"
  printForm3(BC_7D, ChordSys["root"]); waitkey()
  print "      G7 型フォーム"
  printForm3(BC_7G, ChordSys["root"]); waitkey()
  print "      E7 型フォーム"
  printForm3(BC_7E, ChordSys["root"])
} else if (ChordSys["role"] == "halfdim") {
  printForm3(BC_hdA, ChordSys["root"]); waitkey()
  printForm3(BC_hdC, ChordSys["root"]); waitkey()
  printForm3(BC_hdD, ChordSys["root"]); waitkey()
  printForm3(BC_hdG, ChordSys["root"]); waitkey()
  printForm3(BC_hdE, ChordSys["root"])
} else if (ChordSys["role"] == "dim") {
  printForm3(BC_dim1, ChordSys["root"]); waitkey()
  printForm3(BC_dim2, ChordSys["root"]); waitkey()
  printForm3(BC_dim3, ChordSys["root"]); waitkey()
  printForm3(BC_dim4, ChordSys["root"]); waitkey()
  printForm3(BC_dim5, ChordSys["root"]); waitkey()
  printForm3(BC_dim6, ChordSys["root"]); waitkey()
  printForm3(BC_dim7, ChordSys["root"]); waitkey()
  printForm3(BC_dim8, ChordSys["root"])
}

clearChordRep(ChordRep)
clearChordSys(ChordSys)
clearFingerBoard(FingerBoard)
printf "コードネームを入力" > "/dev/tty"
}
# Basic Chords are Major/Minor/Dominant/Diminished
function setBasicChordForms() {
######## include data
BC_MA["role"]="major";BC_MA["type"]="A";BC_MA[1,1]="5";BC_MA[1,3]="6";BC_MA[2,1]="9";BC_MA[2,3]="3";BC_MA[3,2]="M7";BC_MA[3,3]="1";BC_MA[4,3]="5";BC_MA[5,1]="1";BC_MA[6,1]="-5";
BC_MC["role"]="major";BC_MC["type"]="C";BC_MC[1,1]="3";BC_MC[1,4]="-5";BC_MC[2,1]="M7";BC_MC[2,2]="1";BC_MC[2,4]="9";BC_MC[3,1]="5";BC_MC[3,3]="6";BC_MC[4,3]="3";BC_MC[5,4]="1";BC_MC[6,4]="-5";
BC_MD["role"]="major";BC_MD["type"]="D";BC_MD[1,1]="9";BC_MD[1,3]="3";BC_MD[2,1]="6";BC_MD[2,3]="M7";BC_MD[2,4]="1";BC_MD[3,3]="5";BC_MD[4,1]="1";BC_MD[5,1]="-5";
BC_MG["role"]="major";BC_MG["type"]="G";BC_MG[1,1]="6";BC_MG[1,3]="M7";BC_MG[1,4]="1";BC_MG[2,1]="3";BC_MG[2,4]="-5";BC_MG[3,1]="1";BC_MG[3,3]="9";BC_MG[4,1]="5";BC_MG[4,3]="6";BC_MG[5,3]="3";BC_MG[6,4]="1";
BC_ME["role"]="major";BC_ME["type"]="E";BC_ME[1,1]="M7";BC_ME[1,2]="1";BC_ME[1,4]="9";BC_ME[2,2]="5";BC_ME[2,4]="6";BC_ME[3,3]="3";BC_ME[4,1]="6";BC_ME[4,3]="M7";BC_ME[4,4]="1";BC_ME[5,4]="5";BC_ME[6,2]="1";
BC_7A["role"]="dominant";BC_7A["type"]="A7";BC_7A[1,1]="5";BC_7A[1,2]="b13";BC_7A[1,3]="13";BC_7A[1,4]="-7";BC_7A[2,1]="9";BC_7A[2,2]="#9";BC_7A[2,3]="3";BC_7A[3,1]="7";BC_7A[3,3]="-1";BC_7A[4,3]="5";BC_7A[5,1]="1";BC_7A[6,1]="-5";
BC_7C["role"]="dominant";BC_7C["type"]="C7";BC_7C[1,1]="3";BC_7C[1,5]="b13";BC_7C[2,2]="1";BC_7C[2,3]="b9";BC_7C[2,4]="9";BC_7C[2,5]="#9";BC_7C[3,1]="-5";BC_7C[3,2]="b13";BC_7C[3,3]="13";BC_7C[3,4]="7";BC_7C[4,3]="3";BC_7C[5,2]="-7";BC_7C[5,4]="1";BC_7C[6,4]="-5";
BC_7D["role"]="dominant";BC_7D["type"]="D7";BC_7D[1,1]="9";BC_7D[1,2]="#9";BC_7D[1,3]="3";BC_7D[2,1]="13";BC_7D[2,2]="7";BC_7D[2,4]="-1";BC_7D[3,3]="5";BC_7D[3,4]="b13";BC_7D[4,1]="1";BC_7D[4,2]="b9";BC_7D[5,1]="-5";
BC_7G["role"]="dominant";BC_7G["type"]="G7";BC_7G[1,1]="13";BC_7G[1,2]="7";BC_7G[1,4]="-1";BC_7G[2,1]="3";BC_7G[2,4]="-5";BC_7G[3,1]="1";BC_7G[3,2]="b9";BC_7G[3,3]="9";BC_7G[3,4]="#9";BC_7G[4,1]="-5";BC_7G[4,2]="b13";BC_7G[4,3]="13";BC_7G[4,4]="7";BC_7G[5,3]="3";BC_7G[6,4]="1";
BC_7E["role"]="dominant";BC_7E["type"]="E7";BC_7E[1,1]="1";BC_7E[1,2]="b9";BC_7E[1,3]="9";BC_7E[1,4]="#9";BC_7E[2,1]="5";BC_7E[2,2]="b13";BC_7E[2,3]="13";BC_7E[2,4]="-7";BC_7E[3,2]="3";BC_7E[4,1]="7";BC_7E[4,3]="-1";BC_7E[5,3]="5";BC_7E[6,1]="1";
BC_mA["role"]="minor";BC_mA["type"]="Am";BC_mA[1,1]="5";BC_mA[1,3]="6";BC_mA[1,4]="-7";BC_mA[2,1]="9";BC_mA[2,2]="m3";BC_mA[2,4]="11";BC_mA[3,1]="-7";BC_mA[3,3]="1";BC_mA[4,3]="5";BC_mA[5,1]="1";BC_mA[6,1]="-5";
BC_mC["role"]="minor";BC_mC["type"]="Cm";BC_mC[1,2]="11";BC_mC[1,4]="5";BC_mC[2,2]="1";BC_mC[2,4]="9";BC_mC[3,1]="-5";BC_mC[3,4]="7";BC_mC[4,2]="m3";BC_mC[4,4]="11";BC_mC[5,2]="-7";BC_mC[5,4]="1";BC_mC[6,4]="-5";
BC_mD["role"]="minor";BC_mD["type"]="Dm";BC_mD[1,1]="9";BC_mD[1,2]="m3";BC_mD[1,4]="11";BC_mD[2,2]="-7";BC_mD[2,4]="1";BC_mD[3,1]="11";BC_mD[3,3]="5";BC_mD[4,1]="1";BC_mD[5,1]="-5";
BC_mG["role"]="minor";BC_mG["type"]="Gm";BC_mG[1,2]="-7";BC_mG[1,4]="1";BC_mG[2,2]="11";BC_mG[2,4]="5";BC_mG[3,3]="9";BC_mG[3,4]="m3";BC_mG[4,1]="-5";BC_mG[4,4]="7";BC_mG[5,2]="m3";BC_mG[5,4]="11";BC_mG[6,4]="1";
BC_mE["role"]="minor";BC_mE["type"]="Em";BC_mE[1,1]="1";BC_mE[1,3]="9";BC_mE[2,1]="5";BC_mE[3,1]="m3";BC_mE[3,3]="11";BC_mE[4,1]="-7";BC_mE[4,3]="1";BC_mE[5,1]="11";BC_mE[5,3]="5";BC_mE[6,1]="1";
BC_dim1["role"]="dim";BC_dim1["type"]="1";BC_dim1[1,2]="m3";BC_dim1[2,1]="6";BC_dim1[3,2]="b5";BC_dim1[4,1]="1";
BC_dim2["role"]="dim";BC_dim2["type"]="2";BC_dim2[1,2]="6";BC_dim2[2,1]="m3";BC_dim2[3,2]="1";BC_dim2[4,1]="b5";
BC_dim3["role"]="dim";BC_dim3["type"]="3";BC_dim3[1,2]="b5";BC_dim3[2,1]="1";BC_dim3[3,2]="6";BC_dim3[4,1]="m3";
BC_dim4["role"]="dim";BC_dim4["type"]="4";BC_dim4[1,2]="1";BC_dim4[2,1]="b5";BC_dim4[3,2]="m3";BC_dim4[4,1]="6";
BC_dim5["role"]="dim";BC_dim5["type"]="5";BC_dim5[2,4]="1";BC_dim5[3,2]="b5";BC_dim5[4,4]="m3";BC_dim5[5,3]="6";
BC_dim6["role"]="dim";BC_dim6["type"]="6";BC_dim6[2,4]="b5";BC_dim6[3,2]="1";BC_dim6[4,4]="6";BC_dim6[5,3]="m3";
BC_dim7["role"]="dim";BC_dim7["type"]="7";BC_dim7[2,4]="6";BC_dim7[3,2]="m3";BC_dim7[4,4]="1";BC_dim7[5,3]="b5";
BC_dim8["role"]="dim";BC_dim8["type"]="8";BC_dim8[2,4]="m3";BC_dim8[3,2]="6";BC_dim8[4,4]="b5";BC_dim8[5,3]="1";
BC_hdA["role"]="halfdim";BC_hdA["type"]="A";BC_hdA[1,4]="-7";BC_hdA[2,2]="m3";BC_hdA[2,4]="11";BC_hdA[3,1]="7";BC_hdA[3,3]="-1";BC_hdA[4,2]="b5";BC_hdA[5,1]="1";BC_hdA[6,1]="-5";
BC_hdC["role"]="halfdim";BC_hdC["type"]="C";BC_hdC[1,2]="11";BC_hdC[1,3]="b5";BC_hdC[2,2]="1";BC_hdC[3,1]="-5";BC_hdC[3,4]="7";BC_hdC[4,2]="m3";BC_hdC[4,4]="11";BC_hdC[5,2]="-7";BC_hdC[5,4]="1";
BC_hdD["role"]="halfdim";BC_hdD["type"]="Dm";BC_hdD[1,2]="m3";BC_hdD[1,4]="11";BC_hdD[2,2]="7";BC_hdD[2,4]="-1";BC_hdD[3,1]="11";BC_hdD[3,2]="b5";BC_hdD[4,1]="1";
BC_hdG["role"]="halfdim";BC_hdG["type"]="Gm";BC_hdG[1,2]="-7";BC_hdG[1,4]="1";BC_hdG[2,2]="11";BC_hdG[2,3]="b5";BC_hdG[3,4]="m3";BC_hdG[4,4]="7";BC_hdG[6,4]="-1";
BC_hdE["role"]="halfdim";BC_hdE["type"]="Em";BC_hdE[1,1]="-1";BC_hdE[1,4]="m3";BC_hdE[2,4]="7";BC_hdE[3,1]="m3";BC_hdE[3,3]="11";BC_hdE[4,1]="-7";BC_hdE[4,3]="1";BC_hdE[5,1]="11";BC_hdE[5,2]="b5";BC_hdE[6,1]="-1";
########
}

Comment(3)