A string based language for representing musical notation using only ASCII characters. Created as a group project for Bjarne Stroustrup's "Design Using C++" course (COMS 4995) by Brennan McManus, Dean Deng, and Conder Shou. Implemented as an expansion of the C++11 MIDI-format parsing library midifile. A presentation summarizing the content below can be found here.
Electric Boogaloo represents real musical concepts programmatically: program with Notes
and Keys
, not unsigned integers. It also provides a flexible, string-based input language for the representation of music. The following is an comparison of how to write hot cross buns, with examples written using each of Electric Boogaloo, the original Midifile, and MidiWriterJS.
Electric Boogaloo:
#include "MidiOutput.hpp"
using namespace smf;
int main() {
Track melody{"E D C . E D C . C C D D E D C ."};
MidiOutput out{ melody };
out.write("hot_cross_buns.mid");
return 0;
}
Midifile:
idiFile midifile;
int track = 0;
int channel = 0;
int tpq = midifile.getTPQ();
int count = options.getInteger("note-count");
for (int i=0; i<count; i++) {
int starttick = int(starttime(i) / 4.0 * tpq);
int key = pitch(i); // Get the next key [64, 62, 60, 64, 62, 60 …]
int endtick = starttick + int(duration(mt) / 4.0 * tpq);
midifile.addNoteOn (track, starttick, channel, key, velocity(mt));
midifile.addNoteOff(track, endtick, channel, key);
}
midifile.sortTracks(); // Need to sort tracks since added events are
// appended to track in random tick order.
string filename = options.getString("output-file");
midifile.write(filename);
MidiWriterJS:
var MidiWriter = require('midi-writer-js');
var track = new MidiWriter.Track();
track.addEvent([
new MidiWriter.NoteEvent({pitch: ['E4','D4'], duration: '4'}),
new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'}),
new MidiWriter.NoteEvent({pitch: ['E4','D4'], duration: '4'}),
new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'}),
new MidiWriter.NoteEvent({pitch: ['C4', 'C4', 'C4', 'C4', 'D4', 'D4', 'D4', 'D4'], duration: '8'}),
new MidiWriter.NoteEvent({pitch: ['E4','D4'], duration: '4'}),
new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'})
], function(event, index) {
return {sequential:true};
}
);
var write = new MidiWriter.Writer(track);
console.log(write.dataUri());
A Pitch
is our elemental unit of music, closely tied to the lower-level MIDI format. A Pitch
consists of:
- a base pitch (the pitch’s place in an octave) - one of
"C", "D", "E", "F", "G", "A", "B"
- an accidental (an optional addition of sharpness or flatness) -
"#"
or"b"
- and an octave (how high or low the octave of this pitch is) -
"^x"
or"_x"
, where x is the number of octaves above or below
When this comes together, we can use this system to represent pitches like “C^4”
and “Eb_3”
A Note
is any single musical moment, and consists of one or more Pitches
, and a length. This represents the Pitch
or Pitches
, if any, that occur in the given moment, and how long the given moment lasts. A Note
can be:
- a note in the traditional sense, like A or E♭ (
"A"
and"Eb"
, respectively) - a rest (represented with
"."
) - or a chord, such as an A minor chord (written as
"A/C/E"
or"ACE"
).
Notes
are implemented as a vector of Pitches
and an integer length.
A Track
is a single musical line, consisting of any number of Notes
. Tracks
can be layered on top of each other, and are implemented as a vector of Notes
.
MidiOutput
handles the logic for converting a Track
or multiple Tracks
to MIDI-format files. MidiOutputs
are where Electric Boogaloo interfaces with Midifile's existing MIDI writing utilities.
Contains class structure and logic for representing musical notes, and conversions between these structures and their string representation.
Contains headers and type definitions for the library.
Contains example programs that demonstrate the use of the string-based input language to represent musical ideas.
Builds the programs in ./cs4995-programs.