DEV Community

Cover image for Representing Music with Code
AranaDeDoros
AranaDeDoros

Posted on

Representing Music with Code

Chordal: a Scala DSL for music theory

Another post about my previous DSL experiments. This time, the target was a different hobby of mine: music. Chordal is another DSL experiment in Scala to represent music elements; working with Notes to create Chords, and then using those same Chords to build Progressions.

I thought "Chordal" was an original name until I found plenty of repos with the same one. Oh well.


Some of the challenges I faced came from building the AST. Music theory is a huge world with a lot of inherent structure and composition rules baked in. The atomic unit could be considered the pitch, which gains meaning once paired or grouped: two or three of them become a note, then a triad. Add one more and you've got a tetrachord. Keep stacking and you're into extended chords, which open up a lot of contexts that are genuinely hard to represent cleanly.

Then there's the diatonic vs. chromatic scale problem. The chromatic scale is useful for movement/transposition (going from one note to another by some number of steps), but that convenience comes with a catch: correct representation. Enter enharmonics, the fact that a note or chord can be spelled in different ways and still be "the same" pitch, but not the same note in context. Db and C# are a good example: same pitch, different note depending on what's around it.

What a rant. There are probably some errors, both in theory and in code, but I can live with that for now. It was a ride.

Features

  • Working enharmonics.
  • Extension methods to work with strings as notes.
  • Useful rendering of notes.
  • Harmony
    • Chord creation
    • Triads.
    • Tetrachords (subject to model changes, partial support).
    • Add chords.
    • Power chords.
    • Suspended chords.
    • Partial extension support.
    • Progressions
    • Generic list-like implementation.
    • Quick default methods for building common progressions.

Usage

DSL

package org.aranadedoros.chordal

import chords.*
import dsl.*
import dsl.Notes.{A, D, E, G}
import extensions.Ninth
import notes.{dim, major, note, pow, Note}

import scala.language.postfixOps

// work in progress, subject to change
val C = Note("C")

val chord1 =
  chord {
    root(C)
    quality(MajorTriad)
    withExtensions(Ninth)
  }
println(chord1)

val chord2 =
  chord {
    root("D".note)
    sus(Sus4)
  }
println(chord2)

val chord3 =
  chord {
    root(E)
  }
println(chord3)

val dMajor: Triad =
  D major
println(dMajor)

val eMajorWithBassOnG: SlashChord =
  E.major / G
println(eMajorWithBassOnG)

val g5: PowerChord =
  G pow
println(g5.name)

val adim7 =
  A.dim withSeventh MinorSeventh
println(adim7)
Enter fullscreen mode Exit fullscreen mode

Output

C9
Dsus4
E5
(D,F#,A)
(E,G#,B)/G
G5
(A,C,Eb,G)
Enter fullscreen mode Exit fullscreen mode

Overall, this project turned out really satisfying. It scratched my DSL itch for a good long while. I don't think I'll be doing another one anytime soon.

https://github.com/AranaDeDoros/Chordal

Top comments (0)