Tuning Systems

In this tutorial we explore the use of different tuning systems. The example class allows a musical piece to be rendered in one of four tuning systems, the equal temperment system used in modern times, just intonation, pythagorean tuning, and mean-tone. This demonstrates that with the ability of jMusic to play audio notes at any frequency you are not limited to the chromatic tuning normally associated with MIDI systems. It is possible to implement tuning schemes that don't even follow the conventional pattern of 12 notes to the octave.

The differences in tunings sounds like this [1199kb each]:

Equal

Just

Pythagorean

Mean

Click here to view source.

Lets have a closer look.

import jm.music.data.*;
import jm.JMC;
import jm.util.Write;
import jm.audio.Instrument;

Here we import the usual suspects for a jMusic piece, we need the Instrument class for the audio rendering.

public final class Tuning_Systems implements JMC{

public static void main(String[] args){
new Tuning_Systems();
}

The class is declared and implements the JMC which allows us to use the jMusic constatans such as C (crotchet) for a rhythm value of 1.0 etc.

The main method is quite simple, and simply calls the constructor to create an instance of the class.

public Tuning_Systems() { 
// Batok Mikrokosmos vol. IV
int[] pitchSop = {G4,A4,B4,BF4,A5,AS5,B4,A4,
BF4,AF4,G4,A4,B4,BF4,C5,CS5,
D5,CS5,B4,C5,BF4,C5,B4,A4,BF4,G4,D5,
CS5,B4,C5,BF4,C5,B4,A4,BF4,G4,
REST,E4,A4,BF4,REST,E4,A4,B4,REST,E4,A4,C5,REST};
double[] rhythmSop = {Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,2.5,
Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,
C,Q,Q,Q,C,Q,Q,Q,C,Q,Q,Q,2.5};
int[] pitchAlto = {G4,D4,CS4,D4,G4,D4,A3, REST, E4,
DS4,CS4,REST,D4,CS4,B3,
FS4,CS4,REST,FS4,CS4,REST,FS4,CS4,REST,F3};
double[] rhythmAlto = {DC,C,DC,C,DC,C,2.5,DC,C,
DC,C,DC,C,DC,C,
Q,Q,DC,Q,Q,DC,Q,Q,DC,2.5};

The constructor takes no argments.

The first task is to have some music to text the tuning systems with. We have used the first section of the one of Bela Bartok's simple but interesting piano works. The pitch and rhythm values are in separate arrays, because we need to process the pitches later on.

     public static final double []

EQUAL = {1.0, 1.059, 1.122, 1.189, 1.26, 1.335, 1.414,
1.498, 1.587, 1.682, 1.782, 1.888},

JUST = {1.0, 1.067, 1.125, 1.2, 1.25, 1.333, 1.406, 1.5, 1.6,
1.667, 1.8, 1.875},

PYTHAGOREAN = {1.0, 1.053, 1.125, 1.185, 1.265, 1.333, 1.404,
1.5, 1.58, 1.687, 1.778, 1.898},

MEAN = {1.0, 1.07, 1.118, 1.196, 1.25, 1.337, 1.398, 1.496,
1.6, 1.672, 1.789, 1.869};

The above excerpt is not from Tuning_Systems.java, it is from JMC.java
Because this class implements JMC, it has acess to all of the constants that are declared there.

In the current version of Tuning_Systems.java, EQUAL (equal temperment) is used.  Equal temperment basically sacrifices portability (the ability to transpose) for authenticity (true intervals).  

If transposition is not an issue, then it may be a good idea to use a tuning system that uses truer intervals.

These arrays hold the ratio values of each semitone scale degree as a multiple of the root of the scale. For example, C4 is the first (zeroth) scale degree and has a ration of 1:1.0 with itself, C# has a ration of 1:1.059 in the chromatic system, and so on.

Here is a table of all the values for these tuning systems.




       	// current mode        
double[] modeRatios = EQUAL;

As stated above, the currently active tuning system is assigned to the variable modeRatios.   This is where you can experiment with the different tuning systems that are in JMC, or (even better) make you own up!

       	//create a score        
Score score = new Score("Bartok", 130);

//add the parts to the score

score.addPart(createPart(pitchSop, rhythmSop, 0, modeRatios));
score.addPart(createPart(pitchAlto, rhythmAlto, 1, modeRatios));

A score is created and two parts are added, the soprano and alto parts. A separate method, creatPart, is used for this purpose. We will see more about this method below. For now we are only concerned that it returns a Part that is added to the score.

       	//save the score as an audio file        
Instrument[] insts = new Instrument[2];
for (int i=0; i<2; i++) {
insts[i] = new SquareLPFInst(22050, 500);
}

Write.au(score, "TuningSystems.au", insts);
}

Next, the score is rended as an audio file. This requires the declaration of a couple of jMusic audio instruments. We use instances of the SquareLPFinst that takes a sample rate and low pass filter cutoff frequency as arguments. These instruments are added to the instrument array, insts, which is passed to the Write() method.

If you don't have the Instrument it can be downloaded from the jMusic web site, via the instrument page. or you can simply use another instrument of your choice.

   /*
*This method converts the pitch and rhythm data into notes
* and packs them into a part.
*/
private Part createPart(int[] pitches, double[] rhythms,
int instrument, double[] modeRatios) {
Part part = new Part("", instrument);
Phrase phrase = new Phrase();

// add notes

for (int i=0; i<pitches.length; i++) {
if (pitches[i] == REST ) {
int pitch = REST;
phrase.addNote(new Note(pitch, rhythms[i]));
} else {

// assumes we're in C maj or A min

int degree = pitches[i]%12;
double pitch = FRQ[pitches[i] - degree] * modeRatios[degree];
phrase.addNote(new Note(pitch, rhythms[i]));
}
}
part.addPhrase(phrase);
return part;
}

This method, mentioned earlier, is where all the interesting work of this example is done. Each of the pitches is turned into the appropriate frequency for the current tuning system. Notes are created using that pitch and added to a Phrase with is added to a Part and returned to the calling method.

In more detail, each pitch is processed in turn;

for (int i=0; i<pitches.length; i++) {

If the pitch is a REST then a rest-note is created with the corresponding rhythm value and added to the phrase.

Otherwise the pitch's frequency needs to be calculated. First, we work out which semitone degree it is.

int degree = pitches[i]%12;

Next we calulate the frequency by looking up the appropriate ratio in the current tuning ratio array and multiplying the root frequency by that ration value.

double pitch = FRQ[pitches[i] - degree] * modeRatios[degree];

Finally, we create a note of that frequency with the corresponding rhythmValue and add it to the Phrase.

phrase.addNote(new Note(pitch, rhythms[i]));

Now that the Phrase is created we add it to the Part which is then returned.


Rending a retuned MIDI file

For those of you that don't want to go to the trouble of writing out your music as pitch and rhythm arrays, below is a link to a modified version of the above class that reads in a MIDI file and renders out an audio file in the specified tuning system. This should save you some time :)

Tuning_Systems_MIDI.java

 



© 2002 Andrew R Brown