Most musical compositions are built atop sequences of carefully selected notes. In the context of a Programmable Sound System the question arises, how best to represent them?
Fiddle offers you four options to represent notes. These can be freely mixed and matched in your compositions.
MIDI Files store notes in a .mid
file. These can be performed
with the MIDIFile Node. MIDI files have
been around for some time and are an excellent means to exchange notes with
other composition tools. But because the .mid
file format is binary,
special tooling is required to modify them. Moreover, the range of applications
of MIDI Files is fairly broad and this means that all .mid
files are not
the same. There are 3 subformats, an MPE (Polyphonic Expression) extension
spec and a next generation (MIDI 2) coming online. For these reasons MIDI files
may not be the best representation for storing your compositional elements.
Note Grid
files store notes in a Fiddle-specific, human editable
(json) .fgrd
file. Fiddle's Grid Editor
is used to author note grid files using a familiar grid interface.
Note grid files can be performed with the NoteGrid Node
which accepts the name of a .fgrd
file produced by the Grid Editor.
The note grid interface is widely used because it's highly visual. The ability to quickly "paint" notes, rhythms and even note-expression makes it an excellent interface for a variety of uses. Where this representation falls short is in its ability to express randomness or variation. It's really a fairly low-level representation for musical ideas where higher-order musical expression (tempo changes, key changes, etc) are implicit in the data.
The ChucK programming language can be used to represent notes. It's amazingly simple to generate notes procedurally with ChucK. It's a little more cumbersome to represent non-procedural notes in ChucK. Here is a code fragment for expressing a musical phrase in ChucK:
// define a scale as a collection of integral semitones off
// a base note. -1 represents a rest.
[0, 2, -1, 7, 9] @=> int notes[];
// define durations for a 5 note sequence
[0.25::second, 0.125::second,
0.125::second, 0.375::second,
0.375::second] @=> dur times[];
// perform
for(int i; i< notes.size();i++)
{
60 + notes[i] => float note;
inst.noteOn(note, 1);
times[i] => now;
inst.noteOff(note);
}
The dual array representation (notes and times) works sufficiently well for small note sequences. It's easy to imagine extending the data representation to support expression channels, etc. It can be said that the Fiddle Runtime has done that work for you through its Notestream and Player interfaces. As you can see, for lengthy, pre-determined note sequences, the level of noise in the dual array representation is relatively high in ChucK code. It's hard to see the forest for the trees. This is the motivation for storing "piles of notes" elsewhere an triggering them with ChucK.
The digital music landscape is replete with options for a concise, expressive, readable ascii notation for music. ABC notation is one. It's attractiveness lies in its simplicity, its open-source support ecosystem and the volume of existing source material available.
ABC Syntax is an ascii (editable) notation, a Domain Specific Language, for representing musical notes. Fiddle supports ABC notation via the Code Editor as well as AbcNotes node and the underlying DbAbc chugin.
ABC notation allows us to combine pitch and timing requests for a single note. It also allows us to express a music program comprised of chords, repeats, grace-notes, key-changes and other musical commodities.
Here's an excerpt from a simple example that shows how we can embed an ABC program within our ChucK program. As previously mentioned, for large "piles of notes" it's advised to store them in a separate file. For a small note pattern, it's great to have the option to keep code and notes near one another.
// header: Meter, default note Length, Key
"X:1\nM:4/4\nL:1/4\nK:C\n" => string header;
// two measures of 1/4 notes
"|C D E F| G A B c" => string scale;
// one measure of 1/8 notes (repeated)
"|:C/ D/ E/ F/ G/ A/ B/ c/:|" => string scale2;
DbAbc abc; // instance of abc parser
// combine strings and parse abc here:
header + scale + scale2 => abc.open => int success;
// parse and perform abc here. AbcMsg conveys events.
AbcMsg amsg;
while(abc.read(amsg, 0/*track*/))
{
if(amsg.when > 0::second)
amsg.when => now;
if((amsg.data1 & 0xF0) == 0x90)
{
// note down
amsg.data2 => float midiNote;
amsg.data3 / 127.0 => float velocity;
// send midiNote and velocity to instrument
}
else
if((amsg.data1 & 0xF0) == 0x80)
{
// note up
amsg.data2 => float midiNote;
// send noteUp to instrument
}
else
{
// other control information (like pitch-bend)
}
}
Here is a chuckable example.
As your ABC program becomes larger, it is useful to store the ABC into a separate file as demonstrated here with cooleys.abc.
In addition to delivering musical notes (pitch, decorators and timing),
you can also use ABC to characterize overall performance dynamics. Through
the multi-voice capability
you can assign a specific voice to the task of establishing composition dynamics
like master tempo, volume, etc. Now, the combination of measure-rest-counts,
eg Z4
and inline decorators
open a range of possibilities to "program" tempo and volume changes, etc.
Some examples:
Once you make the move to storing your tunes in .abc
files you unlock the
augmented editing capabilities in the Code Editor.
These capabilities are courtesy abcjs.
To summarize:
If you want to use abc notation in the Graph Editor, use the AbcNotes node.
The example project, in C., is built atop an ABC representation of the score here.