Introduction
JFugue is an open-source Music library that allows you to play and compose MIDI music using the Java programming language. It uses their "Staccato" format to parse strings of musical instructions. By using JFugue, you can convert the Staccato strings into a MIDI file, as well as import a MIDI file, and convert the content into human-readable strings.
This is going to be the first part of the three-part series. In this guide, we will focus on the most fundamental parts of the JFugue library by learning about the notes, octaves, durations, tempo, instruments, and patterns. Doing so we will be able to re-create a small part of the vocals of the jazz version of Sunday Morning by Maroon 5.
In the second part of the series, we will explore how to create and manipulate chords and chord progressions in JFugue. We will learn how to use setKey()
, distribute()
and allChordsAs()
methods of the ChordProgression
class.
In the third and final part of the series, we will learn about Rhythms in JFugue. By using our previous knowledge, we will be able to finalize the intro of our song. In the final part, we will also take a look at how to save our music to a MIDI file using the JFugue as well as read music from a MIDI file using the JFugue library:
- JFugue Beginners Guide Part I: Notes, Durations, Patterns (you're here)
- JFugue Beginners Guide Part II: Chords and Chord Progressions
- JFugue Beginners Guide Part III: Rhythms, Reading and Writing to MIDI
Let's start by preparing our development environment.
Prerequisites
Since we are going to work with MIDI, an audio output device (speakers or headphones) will be needed to observe the results.
While it is not strictly required, basic musical knowledge would help you more quickly understand some of the musical concepts referred to in the article.
Installation
Download the official JFugue library (the current version is 5.0.9) and add the JAR file to your Java project as an external library.
Alternatively, if you're using Maven, you can download the dependency through your pom.xml
file. Though, you'll also have to add the JitPack repository:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>jfugue</groupId>
<artifactId>jfugue</artifactId>
<version>5.0.9</version>
</dependency>
Or, if you're using Gradle:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
implementation group: 'jfugue', name: 'jfugue', version: '5.0.9'
The main API of the JFugue library is the Player
class. Let's instantiate it:
import org.jfugue.player.Player;
public class MyMusic {
public static void main(String[] args) {
Player player = new Player();
}
}
We'll use this class to interpret and play the sounds we'd like to perform.
Notes, Octaves, and Durations in JFugue
As we mentioned earlier, Staccato represents all the musical features with easy to read and write format. The official JFugue guide refers to Staccato as a “Human-Readable and Machine-Parsable Format”. It is quite flexible, not case-sensitive nor does it mind multiple whitespaces (as long as there is at least one space).
Here's how a Staccato string looks like:
player.play("C D E F G A B");
Run the code and ready your speakers to hear the magical sound of the MIDI.
Note: You will encounter many player.play()
methods along with this tutorial. To avoid complications, you can keep one player.play()
method at the end of your code and replace it each time you see a new one.
This string is pretty human-readable indeed. But you may have noticed that we didn't specify any octave nor the duration for any of the notes. When no octave information is provided, the notes will be played with the default octave, which is 5. This means that the above music string is just as same as the following one:
player.play("C5 D5 E5 F5 G5 A5 B5");
We can indicate the octave number between 1 and 10, where 1 is the lowest and 10 is the highest octave. The octave number must follow right after the note or chord (more on chords later on in the article):
player.play("G4 G3 A3 B2");
Notice that we played the same G note twice but one with the 4th octave and the second is the 3rd, which produces a different sound.
Of course, at the moment our music string doesn't sound very interesting, because all notes have the same duration. Similar to the octaves, when none is specified, the default duration of "q" will be used.
"Q" stands for the "quarter note". Some of the most common durations are "whole", "half", "quarter", "eighth". Each duration is half the time faster or shorter than the previous one and two times slower or longer than the next one. The duration of the "quarter" (¼) note, for instance, is shorter than the "half" (½) note and two times longer than the "eighth" (⅛) note. JFugue lets you be as precise as you like, with durations down to 1/128th.
Here's the full table of durations with the decimal values from the official JFugue Guide:
Duration | Character | Decimal value |
---|---|---|
Whole | W | 1.0 |
Half | H | 0.5 |
Quarter | Q | 0.25 |
Eighth | I | 0.125 |
Sixteenth | S | 0.0625 |
Thirty-second | T | 0.03125 |
Sixty-fourth | X | 0.015625 |
One-twenty-eighth | O | 0.0078125 |
Now that we know of how the durations work in JFugue, we can put it to work in our music string (aka Staccato):
player.play("G4qi G3s A3is B2is");
Notice that we used two duration notations together for G4qi
. With JFugue, you can combine as many or as few duration notations together as you like to create the sound and the feel you want. The order of the duration notations doesn't matter, but for better readability, it is recommended to use them in ascending order. Meaning, start with a greater duration followed by a lesser duration.
Another essential musical concept is Rest. To put it in the simplest sense, rest is a pause, a silence, an absence of music. In JFugue, rest is represented by the letter R
or r
. Similar to the notes, the rest can also have a length and the default duration value is the same as the others - quarter.
Let's try to re-create a fragment of the vocals of the Sunday Morning using all we've learned so far:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
player.play("G5is E5i Ri | G5s Ris E5q Rs | G5q E5i Rs D5q rs C5h Rs");
You can remove all the R
tokens and compare two versions to understand the effect of the rest.
You may have noticed that there is also a |
(pipe) character that we haven't mentioned yet. The pipe in JFugue is merely for better readability and does not affect the audio. You can divide your Staccato by using as many or as few pipes as you like and it will not change the musical output. It looks like a 'bar line' in sheet music but unlike the bar line, a pipe does not specify the number of beats.
Since our Staccato is a string, we can use the string concatenation with the +
operator to combine multiple strings.
Assume that we have separate strings for vocals, we can easily combine them to form a continuous string and play:
String vocals = "Rh G5is E5i Ri | G5s Ris E5q Rs | G5q E5i Rs D5q rs C5h Rs "
+ "C4i A5q G5isa50d0 Rs A5s E5i D5is Rs C5qis "
+ "Rqi A4s G5i E5i Rs | G5is Rs E5q | D5is C5i Rs C5q G4q Ri";
player.play(vocals);
Notice the white space at the end of the first and second string. Since we're concatenating, we want to avoid joining the end of a string with the beginning of the other string like RsC4i
.
This will not throw any errors but JFugue will just skip playing that note(s) since it's unrecognizable. Fortunately, JFugue offers a better way of combining multiple Staccatos, without having to deal with trivial matters such as whitespace at the end of each string. In the next section, we will learn about Patterns and how to use instruments.
Using Patterns and Instruments in JFugue
Every musical element in a Staccato string is a token. Each note, rest and duration, and everything else that we will discover later in the article are tokens. A Pattern wraps a Staccato string made of tokens and allows us to manipulate it in convenient ways.
The Pattern
class provides some efficient methods, such as setTempo()
, setInstrument()
, setVoice()
and more. We can also chain these methods. By using the add()
method, we can add multiple patterns or multiple Staccatos together.
Let's import and create a Pattern
object to wrap our Staccato string and combine multiple strings without having to worry about the whitespaces at the end:
import org.jfugue.pattern.Pattern;
Pattern vocals = new Pattern();
vocals.add("Rh G5is E5i Ri | G5s Ris E5q Rs | G5q E5i Rs D5q rs C5h Rs");
vocals.add("C4i A5q G5isa50d0 Rs A5s E5i D5is Rs C5qis");
vocals.add("Rqi A4s G5i E5i Rs | G5is Rs E5q | D5is C5i Rs C5q G4q Ri");
vocals.add("G3is A3s C4is D4s C4is D4s G4is A4s G4is A4s | E4q rs F4h");
vocals.add("G5is E5i Ri | G5s Ris E5q Rs | G5q E5i Rs A5is rs G5q A5s E5i D5i ri C5h Ri");
vocals.add("C5s A3q C5i Rs | D5i Rs Eb5qs Rs | D5q Eb5i Rs D5is Eb5s D4q Rs | C5i A4q C5h");
player.play(vocals);
Adding strings together this way, not only prevents whitespace errors but also provides us with more functionalities.
If you try to play the above vocals
pattern, you will notice that it sounds too slow. The reason, as you may have already guessed, it has to do with durations. Though, we don't want to shorten the durations, really. We want to amp up the tempo.
We can set a new tempo seamlessly by using setTempo(int)
method of the Pattern
instance:
vocals.setTempo(180);
Now we will notice the difference if we play it:
player.play(vocals);
So far we've only used the default instrument, which is Piano. While it's one of the more versatile instruments, unfortunately, it is not the best fit for every situation. Let's make this more "Jazzy" by adding some instruments:
We can chain several methods on the Pattern
instance, changing the configuration with several settings:
vocals.setTempo(180).setInstrument("TROMBONE");
There are 128 instruments, including, well, not instruments such as "SEASHORE", "BIRDTWEET" and even "HELICOPTER".
You can leave the other lines as it is and run the code to hear the difference. By chaining the setInstrument()
method to the end, we set the "TROMBONE" for all the Staccato strings above that call.
Of course, we can set different instruments for any individual line by embedding the instrument information into the Staccato itself:
Pattern vocals = new Pattern();
vocals.add("I[TROMBONE] Rh G5is E5i Ri | G5s Ris E5q Rs | G5q E5i Rs D5q rs C5h Rs");
vocals.add("I[ALTO_SAX] C4i A5q G5isa50d0 Rs A5s E5i D5is Rs C5qis");
vocals.add("I[TROMBONE] Rqi A4s G5i E5i Rs | G5is Rs E5q | D5is C5i Rs C5q G4q Ri");
vocals.add("I[TRUMPET] G3is A3s C4is D4s C4is D4s G4is A4s G4is A4s | E4q rs F4h");
vocals.add("I[TROMBONE] G5is E5i Ri | G5s Ris E5q Rs | G5q E5i Rs A5is rs G5q A5s E5i D5i ri C5h Ri");
vocals.add("C5s A3q C5i Rs | D5i Rs Eb5qs Rs | D5q Eb5i Rs D5is Eb5s D4q Rs | C5i A4q C5h");
vocals.setTempo(180);
Notice that there is no instrument information in the last line. JFugue will carry on the last instrument selection until any other selection is made. But of course, for better readability, it's better to provide instrument information whenever possible.
Again, JFugue comes with 128 predefined instruments. In case you want to discover your other options on the instruments, here's the complete list.
Similar to the instruments, we can set the tempo value inside the Staccato itself, passing the tempo value into our Pattern
constructor when we instantiate it at the first line.
The final result of the vocals
pattern, should look like this:
Pattern vocals = new Pattern("T180");
vocals.add("I[TROMBONE] Rh G5is E5i Ri | G5s Ris E5q Rs | G5q E5i Rs D5q rs C5h Rs");
vocals.add("I[ALTO_SAX] C4i A5q G5isa50d0 Rs A5s E5i D5is Rs C5qis");
vocals.add("I[TROMBONE] Rqi A4s G5i E5i Rs | G5is Rs E5q | D5is C5i Rs C5q G4q Ri");
vocals.add("I[TRUMPET] G3is A3s C4is D4s C4is D4s G4is A4s G4is A4s | E4q rs F4h");
vocals.add("I[TROMBONE] G5is E5i Ri | G5s Ris E5q Rs | G5q E5i Rs A5is rs G5q A5s E5i D5i ri C5h Ri");
vocals.add("I[TROMBONE] C5s A3q C5i Rs | D5i Rs Eb5qs Rs | D5q Eb5i Rs D5is Eb5s D4q Rs | C5i A4q C5h");
We removed the vocals.setTempo(180);
since setting the tempo at the beginning has the same effect.
And that concludes the vocal part of our song as well as the first part of our tutorial series.
Conclusion
In this tutorial, we covered the most fundamental parts of the JFugue open-source Java Music Library. We learned how to use Notes, Durations, Tempo, and Patterns.
While these were the essentials of the JFugue, it was only a step towards the many possibilities that JFugue has to offer. In the following parts, we will explore how to manipulate chords and chord progressions, the basics of the rhythm, and how to import and export MIDI files using JFugue.
Continue to read the second part of the guide series: JFugue Beginners Guide Part II: Chords and Chord Progressions.