Working with Instruments - DhrBaksteen/ArduinoOPL2 GitHub Wiki

An Instrument is a struct that defines all the parameters of the two operators of a channel as well as the parameters for the channel itself. They provide a quick way to start playing music or a simple interface to change the parameters of the OPL2.

Loading a predefined instrument

The OPL2 library comes with a great number of predefined instruments. It's quite easy to use them:

#include <SPI.h>
#include <OPL2.h>
#include <midi_instruments.h>

// Load a trumpet instrument and assign it to channel 0.
Instrument trumpet = opl2.loadInstrument(INSTRUMENT_TRUMPET);
opl2.setInstrument(0, trumpet);

opl2.playNote(0, 5, NOTE_C);    // Play note C-5 on channel 0.
delay(500);                     // Hold the note for 500ms.
opl2.setKeyOn(0, false);        // Release the note.

The loadInstrument function is used to load an instrument from an array of 12 bytes that contains the raw OPL2 register values. After loading the instrument we need to assign it to a channel with the setInstrument function. That's all we need to start playing some notes using out trumpet instrument.

Creating and changing an instrument

First we need to create a new instrument that allows us to tweak the operator values:

Instrument myInstrument = opl2.createInstrument();

This gives you a new empty melodic instrument with all numerical values set to 0 and all boolean values set to false.

Now say that we want to make this instrument into a piano. We can do this by changing the operators as follows:

myInstrument.operators[OPERATOR1].frequencyMultiplier = 1;
myInstrument.operators[OPERATOR1].keyScaleLevel = 1;
myInstrument.operators[OPERATOR1].outputLevel = 15;
myInstrument.operators[OPERATOR1].attack = 15;
myInstrument.operators[OPERATOR1].decay = 1;
myInstrument.operators[OPERATOR1].sustain = 5;

myInstrument.operators[OPERATOR2].keyScaleLevel = 1;
myInstrument.operators[OPERATOR2].outputLevel = 4;
myInstrument.operators[OPERATOR2].attack = 13;
myInstrument.operators[OPERATOR2].decay = 2;
myInstrument.operators[OPERATOR2].sustain = 7;
myInstrument.operators[OPERATOR2].release = 12;

myInstrument.feedback = 3;

After defining the instrument's parameters we need to assign it to a channel. The following will assign the instrument to channel 0 and set its output level to 80%. The output level is an optional parameter. If you omit it then the default output level of 100% will apply.

opl2.setInstrument(0, myInstrument, 0.8);

Finally we need to play a note to hear our instrument play:

opl2.playNote(0, 5, NOTE_C);    // Play note C-5.
delay(500);                     // Delay 500ms.
opl2.setKeyOn(0, false);        // Release the key (stop playing).
delay(500);

Let's try altering the instrument. Let's see what happens if we set the release parameter of the second operator to 0 and play another C-5 note. Add the following:

myInstrument.operators[OPERATOR2].release = 0;
opl2.setInstrument(0, myInstrument, 0.8);

opl2.playNote(0, 5, NOTE_C);
delay(500);
opl2.setKeyOn(0, false);
delay(500);

Notice how every time we make a change to an instrument we need to 'upload' it to the OPL2 using the setInstrument command.

You will notice that the second time the note is played it is immediately stopped. Let's make another change to keep the not playing as long as the key is held down. We'll also add back a short release perion on the second operator.

myInstrument.operators[OPERATOR1].hasSustain = true;
myInstrument.operators[OPERATOR2].hasSustain = true;
myInstrument.operators[OPERATOR2].release = 6;
opl2.setInstrument(0, myInstrument, 0.8);

opl2.playNote(0, 5, NOTE_C);
delay(500);
opl2.setKeyOn(0, false);
delay(500);

Since we enabled sustain on the instrument it will keep playing at sustain level as long as the key is held down. You will notice that when the key is released the release portion of the instrument starts and the sound dies down over a short time.

Creating an instrument from a channel

We've seen before how we can create a new instrument and change its parameters. The OPL2 library also allows to directly change register values of the YM3812 and this is what you may want to use in your project. For example if we take the code from above again then the final change we did on the instrument could also be implemented as follows:

opl2.setSustain(0, OPERATOR1, true);    // Enable sustain on CH 0, OP 1.
opl2.setSustain(0, OPERATOR2, true);    // Enable sustain on CH 0, OP 2.
opl2.setRelease(0, OPERATOR2, 6);       // Set release to 6 on CH 0, OP 2.

This will not change the parameters of myInstrument, but only the register values of the chip.

You can create an instrument out of the current register values of an OPL2 channel. This allows you to save the changes we've just made to an Instrument that you can reuse on a different OPL2 channel for example.

Instrument mySecondInstrument = opl2.getInstrument(0);

Notice that the values saved in myInstrument and mySecondInstrument will be the same.

⚠️ **GitHub.com Fallback** ⚠️