Skip to content

Instantly share code, notes, and snippets.

@WizardOfArc
Created October 26, 2018 15:33
Show Gist options
  • Select an option

  • Save WizardOfArc/c1c03a96b48161db90286087ec3c8671 to your computer and use it in GitHub Desktop.

Select an option

Save WizardOfArc/c1c03a96b48161db90286087ec3c8671 to your computer and use it in GitHub Desktop.
This builds an HTML table for a chart to find what chords contain a given note you are looking to play.
import java.io.FileWriter
object NumberHelper {
def safeMod(num: Int): Int = {
val modded = num % 12
if (modded < 0){
modded + 12
} else {
modded
}
}
}
case class Note(value: Int){
def -(other: Note) = {
Notes.forInt(value - other.value)
}
def +(other: Note) = {
Notes.forInt(value + other.value)
}
def show = s"$value"
}
object Notes {
val One = Note(0)
val FlatTwo = Note(1)
val Two = Note(2)
val FlatThree = Note(3)
val Three = Note(4)
val Four = Note(5)
val Tritone = Note(6)
val Five = Note(7)
val FlatSix = Note(8)
val Six = Note(9)
val FlatSeven = Note(10)
val Seven = Note(11)
val allNotes = List(
One, FlatTwo, Two, FlatThree,
Three, Four, Tritone, Five,
FlatSix, Six, FlatSeven, Seven
)
val diatonicNotes = List(One, Two, Three, Four, Five, Six, Seven)
val simpleChordTones = List(One, FlatThree, Three, Five)
def forInt(num: Int) = {
allNotes(NumberHelper.safeMod(num))
}
}
case class Root (
val name: String,
val value: Note
)
object Roots {
val One = Root("I", Notes.forInt(0))
val FlatTwo = Root("bII", Notes.forInt(1))
val Two = Root("II", Notes.forInt(2))
val FlatThree = Root("bIII", Notes.forInt(3))
val Three = Root("III", Notes.forInt(4))
val Four = Root("IV", Notes.forInt(5))
val Tritone = Root("bV", Notes.forInt(6))
val Five = Root("V", Notes.forInt(7))
val FlatSix = Root("bVI", Notes.forInt(8))
val Six = Root("VI", Notes.forInt(9))
val FlatSeven = Root("bVII", Notes.forInt(10))
val Seven = Root("VII", Notes.forInt(11))
val allRoots = List(
One, FlatTwo, Two, FlatThree,
Three, Four, Tritone, Five,
FlatSix, Six, FlatSeven, Seven
)
def forInt(num: Int): Root = allRoots(NumberHelper.safeMod(num))
def forNote(note: Note): Root = forInt(note.value)
}
case class Quality (
val name: String,
val value: Note
)
object Qualities {
val All = Quality("all", Notes.forInt(0))
val FlatNine = Quality("b9", Notes.forInt(1))
val Sus2 = Quality("sus2", Notes.forInt(2))
val Minor = Quality("minor", Notes.forInt(3))
val Major = Quality("major", Notes.forInt(4))
val Sus4 = Quality("sus4", Notes.forInt(5))
val Dim = Quality("dim or #4", Notes.forInt(6))
val MajorOrMinor = Quality("major or minor", Notes.forInt(7))
val Flat6 = Quality("b6", Notes.forInt(8))
val Six = Quality("6", Notes.forInt(9))
val Dom7 = Quality("7", Notes.forInt(10))
val Major7 = Quality("major7", Notes.forInt(11))
val allQualities = List(
All, FlatNine, Sus2, Minor,
Major, Sus4, Dim, MajorOrMinor,
Flat6, Six, Dom7, Major7
)
def forInt(num: Int) = allQualities(NumberHelper.safeMod(num))
def forNote(note: Note) = forInt(note.value)
}
case class Chord(root: Root, quality: Quality){
def isDiatonic: Boolean = {
Notes.diatonicNotes.contains(root.value) &&
Notes.diatonicNotes.contains(root.value + quality.value )
}
def isSimple: Boolean = {
Notes.simpleChordTones.contains(quality.value)
}
def display: String = s"${root.name} ${quality.name}"
}
object HTMLHelper {
def style = """
| <style>
| th {
| background-color: #afafaf;
| }
| td {
| border: solid black 1px;
| padding: 5px;
| }
| .diatonic {
| font-weight: bold;
| border: solid black 4px;
| }
| .simple {
| background-color: #7faf7f;
| }
| </style>
""".stripMargin
def makePage(content: String): String = {
s"""
|<!DOCTYPE HTML>
|<html>
| <head>
| $style
| </head>
| <body>
| ${content}
| </body>
|</html>
""".stripMargin
}
def makeTable(rows: Seq[String]): String = {
s"""<table cellspacing="0" cellpadding="0">
| ${rows.mkString("\n")}
</table>
""".stripMargin
}
}
object ChordFinderChartBuilder {
def toRow(entry: (Note, Seq[(Note, Chord)])): String = {
val (note,cells) = entry
s"""<tr>
| <th>${note.show}</th>
| ${cells.map{toCell }.mkString("\n")}
|</tr>""".stripMargin
}
def toCell(value: (Note, Chord)): String = {
val (_, chord) = value
val simpleOption = if(chord.isSimple){
Some("simple")
} else { None }
val diatonicOption = if(chord.isDiatonic){
Some("diatonic")
} else { None }
val classlist = List(simpleOption, diatonicOption).flatten.mkString(" ")
val simple = if(classlist.nonEmpty){
s""" class="$classlist" """
} else { "" }
s"""<td$simple>${chord.display}</td>"""
}
def baseChart = {
for {
a <- Notes.allNotes
b <- Qualities.allQualities
} yield (a, Chord(Roots.forNote(a - b.value), b))
}
def main(args: Array[String]): Unit = {
val grouped = baseChart.groupBy(_._1).toList.sortBy(_._1.value)
val rows = grouped map toRow
val contents = HTMLHelper.makePage(HTMLHelper.makeTable(rows ) )
val fw = new FileWriter("chart.html")
fw.write(contents)
fw.close()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment