A (mostly) Clojure console application that processes Java source code through Checkstyle (producing source code quality metrics) and creates a musical representation of these metrics using ABC Notation and ultimately MIDI.
The name Aeolian (apart from being a Greek musical mode) refers to a leading company in the production of player pianos, which takes punched paper as input and drives a mechanical piano.
Aeolian was presented to the YOW West conference in May 2017. The slides can be found here.
Currently, Aeolian maps the following source code metrics to aspects of the generated music:
Metric | Affects... | By... |
---|---|---|
Line length | Note and octave selection | Longer lines -> higher pitched notes |
Cyclomatic Complexity | Tempo | Higher complexity -> faster tempo |
Method Length | Accompanying chord | A chord based around first, third, fifth or seventh of key |
Duplication | Key (Major or Minor) | Duplication >= 10% -> minor key |
Git commit author | MIDI instrument (from 7 available instruments) | New instrument for each author change |
Source file name | Karaoke lyrics (if MIDI file is played through a player that supports the KAR format) | New filename lyric for each source file change |
Because there is one note played for each line of code processed, a larger source file will produce a longer song. Likewise, more complex code will result in faster passages in the song, whilst less complex code will result in slower passages.
All generated songs use a default tempo of 140bpm and all notes played are eighth notes.
Note: there are still lots of dimensions to code quality and song composition and structure which haven't been covered by Aeolian. I give this to you as a starting point to build out more complex audio representations of code quality for your own edification.
Assuming you have a Github repo you want to point Aeolian at, then start with this:
./go-gh.sh <github user name> <github repo name>
This script will perform a number of actions:
-
Clone the repo locally.
The repo will appear in a temporary directory underneath the Aeolian install dir (hereafter known as the $WORK_DIR). For example:
$ ./go-gh.sh allegro grunt-maven-plugin Checking GitHub for repo 'grunt-maven-plugin' for user 'allegro'... Cloning repo to /home/amarks/Code/aeolian/tmp.YtpN0tR5yG...
-
Find the commit history for all the source files.
Sample terminal output:
Find commit history for each Java file...
The output from this stage is a file called
blames.txt
in the $WORK_DIR. The fields in this file are:Field Position Prefix Source file 1 None Line number 2 # Git commit author 3 AU= Git commit timestamp 4 TS= -
Generate the metrics.
Sample terminal output:
Generating Checkstyle duplication metrics... Running metrics on all Java files... Processing ExecNpmOfflineMojo... Generating Checkstyle cyclomatic complexity metrics... Generating Checkstyle line length metrics... Generating Checkstyle method length metrics... Generating Checkstyle indentation metrics... Processing AbstractExecutableMojo... Generating Checkstyle cyclomatic complexity metrics... Generating Checkstyle line length metrics... Generating Checkstyle method length metrics... Generating Checkstyle indentation metrics... Processing ExecNpmMojo... ... Building uber metrics file... Joining metrics file with Git commit history...
The Checkstyle configuration files to generate the complexity metrics can be found here and the script in charge of invoking Checkstyle for each source file can be found here.
The output from this stage is a file called
$WORK_DIR\duplication.txt
which contains the summary of the duplication checking on the repo. Additional output is a number of temporary metrics files in $WORK_DIR for each source file processed. Each of these files is named to correspond to the source file and suffixed by a short pseudo-random UID to prevent clashes of source file names across packages.All metrics for each source file are eventually combined into a file with a
.metrics.all
extension. The fields in this file are:Field Position Prefix Source file 1 None Line number 2 # Line length N/A LL= Cyclomatic Complexity N/A CC= Method length N/A ML= The final step in this stage is to join the Git blame and Checkstyle metrics files, producing a file with a
.history
extension. -
Generate the ABC Notation.
Sample terminal output:
Generating ABC notation... Generated /home/amarks/Code/aeolian/tmp.YtpN0tR5yG/grunt-maven-plugin.metrics.history.abc
The generated ABC notation file will appear in $WORK_DIR with the extension
.metrics.history.abc
. -
Generate a playable version of the ABC Notation.
Aeolian was developed using abcmidi (available on OSX and Ubuntu). abcmidi takes an ABC Notation file as input (foo.abc) and generates a MIDI file from the input file (foo1.mid) for use in the next stage of the pipeline.
Sample terminal output:
Generating MIDI... 3.90 September 25 2016 abc2midi writing MIDI file /home/amarks/Code/aeolian/tmp.YtpN0tR5yG/grunt-maven-plugin.metrics.mid
The generated MIDI file will appear in $WORK_DIR with the extension
.metrics.mid
. -
Archive the generated ABC and MIDI files.
Sample terminal output:
Archiving generated files..
Both the ABC and MIDI files will be copied to the archive directory.
-
Play the music.
Aeolian was developed using timidity (available on OSX and Ubuntu).
Sample terminal output from Timidity.
Playing MIDI... Requested buffer size 32768, fragment size 8192 ALSA pcm 'default' set buffer size 32768, period size 8192 bytes Playing /home/amarks/Code/aeolian/tmp.YtpN0tR5yG/grunt-maven-plugin.metrics.mid MIDI file: /home/amarks/Code/aeolian/tmp.YtpN0tR5yG/grunt-maven-plugin.metrics.mid ... Sequence: /home/amarks/Code/aeolian/tmp.YtpN0tR5yG/grunt-maven-plugin.metrics.history Text: @I/home/amarks/Code/aeolian/tmp.YtpN0tR5yG/grunt-maven-plugin.metrics.history Text: @IAEOLIAN ... Playing time: ~160 seconds Notes cut: 0 Notes lost totally: 0
Assuming all steps of the pipeline have been successful, you should now be listening to the quality of the specified code playing.
Copyright © 2015-2020 ThoughtWorks
Distributed under the Eclipse Public License, the same as Clojure.