Skip to content

FM Presets

Robert Hargreaves edited this page Jun 5, 2025 · 28 revisions

There are 128 built-in presets. They are based on Wohlstand's XG bank from libOPNMIDI. The interface defaults all FM channels to instrument 0 (Grand Piano) on start-up. You can also define up to 128 user presets (each taking place of a built-in preset).

MDMI CLI

Don't want to mess around with SysEx? The Mega Drive MIDI Interface CLI can be used to send these SysEx messages for you and also provide the following functionality:

  • Load presets from DMP, TFI, WOPN files
  • Dump presets to DMP, TFI files

Selecting a Preset

Sending a MIDI program change (0xC) message will select an FM preset.

Percussion

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.

User Presets

Store current FM channel parameters to a preset

You can store the current parameters of any MIDI channel currently assigned to an FM channel by sending CC 87, with the value of the MIDI program you want to define. The UI should indicate a preset has been stored this way. You can then retrieve these parameters at any time using the MIDI program change message.

Load a user FM preset

A preset can be loaded via SysEx as follows:

SysEx:

00 22 77 0A <type> <program> ...
  • type: Type of preset. FM = 0.
  • program: The MIDI program the user preset will be stored under (0-127)

Followed by channel FM parameters:

... <alg> <fb> <ams> <fms> ...
  • alg: Algorithm (0-7)
  • fb: Feedback (0-7)
  • ams: AMS (0-3)
  • fms: FMS (0-7)

Then the sets of operator FM parameters (repeat 4 times):

... <mul> <dt> <ar> <rs> <dr> <am> <sl> <sr> <rr> <tl> <ssg>
  • mul: Multiple (0-15)
  • dt: Detune (0-7)
  • ar: Attack Rate (0-31)
  • rs: Rate Scaling (0-3)
  • dr: Decay Rate (0-31)
  • am: Amplitude Modulation (0-1)
  • sl: Sustain Level (0-15)
  • sr: Sustain Rate (0-15)
  • rr: Release Rate (0-15)
  • tl: Total Level (0-127)
  • ssg: SSG-EG (0-15)

You should get a message on the UI saying a preset has been loaded. Check out the load-fm-preset script for an example of how to construct the SysEx message.

Clear a user FM preset

SysEx:

00 22 77 0B <type> <program>
  • type: Type of preset. FM = 0.
  • program: The MIDI program (0-127) to clear.

Clear all user FM presets

SysEx:

00 22 77 0C <type>

Dump FM preset

First send a dump request using SysEx:

00 22 77 0D <type> <program>
  • type: Type of preset. FM = 0.
  • program: The MIDI program (0-127) to dump.

MDMI will then respond with the preset data, in the same format as that which you would use to load a preset, except that the command ID will be 0E (rather than 0A when loading):

SysEx response:

00 22 77 0E <type> <program> <alg> <fb> <ams> <fms> ...

Dump FM channel parameters

First send a dump request using SysEx:

00 22 77 0F <type> <midi_channel>
  • type: Type of channel. FM = 0.
  • midi_channel: The MIDI channel (0-15) assigned to the FM channel from which to dump the parameters

MDMI will then respond with the channel data, in the same format as that which you would use to load a preset, except that the command ID in the response will be 10:

SysEx response:

00 22 77 10 <type> <midi_channel> <alg> <fb> <ams> <fms> ...

Changing Built-In Presets

You can change the built-in presets by modifying the FmPreset definitions in presets.c.

static const FmPreset M_BANK_0_INST_0_GRANDPIANO = 
    { 2, 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 FmPreset is as follows:

typedef struct FmPreset {
    u8 algorithm : 3;
    u8 feedback : 3;
    u8 ams : 2;
    u8 fms : 3;
    Operator operators[4];
} FmPreset;

typedef struct Operator {
    u8 multiple : 4;
    u8 detune : 3;
    u8 attackRate : 5;
    u8 rateScaling : 2;
    u8 decayRate : 5;
    u8 amplitudeModulation : 1;
    u8 sustainLevel : 4;
    u8 sustainRate : 5;
    u8 releaseRate : 4;
    u8 totalLevel : 7;
    u8 ssgEg : 4;
} Operator;

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 FmPreset 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.

typedef struct PercussionPreset {
    FmPreset preset;
    u8 key;
} PercussionPreset;

For example:

static const PercussionPreset P_BANK_0_INST_30_CASTANETS = 
    { { 4, 3, 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 
    };
Clone this wiki locally