Skip to content

FM Presets

Robert Hargreaves edited this page Aug 3, 2024 · 9 revisions

There are 128 presets, roughly in line with the General MIDI spec. They are based on Wohlstand's XG bank from libOPNMIDI. The interface defaults all FM channels to instrument 0 (Grand Piano) on start-up.

Changing Presets

Sending a MIDI program change (0xC) message will select a pre-defined FM preset.

If MIDI channel 10 is mapped to an FM channel, the interface will make use of a separate bank of percussion instruments. By default, MIDI channel 10 is set to the PSG noise channel. To map MIDI channel 10 to FM channel 6 (for example), use the SysEx sequence 00 22 77 00 09 05.

Defining Presets

Currently the only way to change the built-in presets is to modify the FmChannel definitions in presets.c and re-compile the ROM.

static const FmChannel M_BANK_0_INST_0_GRANDPIANO = 
    { 2, 0, 3, 0, 0, 0, 0, { 
      { 1, 0, 26, 1, 7, 0, 7, 4, 1, 39, 0 }, // op 1
      { 2, 7, 31, 3, 23, 0, 9, 15, 1, 4, 0 }, // op 2
      { 4, 6, 24, 1, 9, 0, 6, 9, 7, 36, 0 }, // op 3
      { 1, 3, 27, 2, 4, 0, 10, 4, 6, 2, 0 } // op 4
    } };

The structure of FmChannel is as follows:

typedef struct FmChannel {
    u8 algorithm;
    u8 feedback;
    u8 stereo;       // ignored in presets - set to 0
    u8 ams;
    u8 fms;
    u8 octave;       // ignored in presets - set to 0
    u16 freqNumber;  // ignored in presets - set to 0
    Operator operators[4];
} FmChannel;

typedef struct Operator {
    u8 multiple;
    u8 detune;
    u8 attackRate;
    u8 rateScaling;
    u8 decayRate;
    u8 amplitudeModulation;
    u8 sustainLevel;
    u8 sustainRate;
    u8 releaseRate;
    u8 totalLevel;
    u8 ssgEg;
} Operator;

Note the fields whose values are ignored. These are determined by whatever note is played, and the MIDI pan value of each channel.

Pay also close attention to the order of the operators. They are defined in sequential order (1, 2, 3, 4), rather than register order (1, 3, 2, 4). Check out the algorithm diagrams as these use the same ordering.

Percussive Presets

These are defined in a similar way to FmChannel but have an additional key value, which is the MIDI pitch value that the preset should be played with when the note/key is triggered.

static const PercussionPreset P_BANK_0_INST_30_CASTANETS = 
    { { 4, 3, 3, 0, 0, 0, 0, { 
        { 9, 0, 31, 0, 11, 0, 15, 0, 15, 23, 0 }, 
        { 4, 0, 31, 2, 20, 0, 15, 0, 15, 13, 0 },
        { 1, 0, 31, 0, 19, 0, 15, 0, 15, 15, 0 }, 
        { 2, 0, 31, 2, 20, 0, 15, 0, 15, 13, 0 } } },
      62 // MIDI pitch 
    };

The structure of PercussionPreset is defined as:

typedef struct PercussionPreset {
    FmChannel channel;
    u8 key;
} PercussionPreset;