MIDI – MidiMessage Program Change with Instrument From Different Bank



The soundbank of the default synthesizer I’m using contains a variety of different instruments. For example, this code snippet…

Synthesizer synthesizer = MidiSystem.getSynthesizer();
synthesizer.open();
Instrument instruments = synthesizer.getDefaultSoundbank().getInstruments();
for (Instrument i : instruments)
    System.out.println(i);

… prints the following:

Instrument: Piano 1      bank #0 preset #0
Instrument: Piano 2      bank #0 preset #1
[...]
Instrument: Applause     bank #0 preset #126
Instrument: Gun Shot     bank #0 preset #127
Instrument: SynthBass101 bank #128 preset #38
Instrument: Trombone 2   bank #128 preset #57
[...]
Instrument: Machine Gun  bank #128 preset #127
Instrument: Echo Pan     bank #256 preset #102
Instrument: String Slap  bank #256 preset #120
[...]
Instrument: Lasergun     bank #256 preset #127
[...]
Instrument: Starship     bank #1024 preset #125
Instrument: Carillon     bank #1152 preset #14
[...]
Instrument: Choir Aahs 2 bank #4096 preset #52

I can play an instrument from any of these banks through MidiChannel, the programChange method, and noteOn, like so (this plays instrument 14 from bank 1152, “Carillon”):

MidiChannel channel = synthesizer.getChannels()[0];
if (channel != null) {
    channel.programChange(1152, 14);
    channel.noteOn(70, 100);
}

I want to add a program change event to a sequence’s track so I can play the “Carillon” instrument in the sequence. I tried doing this with a ShortMessage:

Sequence sequence = new Sequence(Sequence.PPQ, 2);
Track track = sequence.createTrack();
ShortMessage pcMessage = new ShortMessage(ShortMessage.PROGRAM_CHANGE, 0, 14, 0);
track.add(new MidiEvent(pcMessage, 0));

But this changes the instrument to instrument 14 in bank 0 (“Tubular-bell”) when I’m instead looking for instrument 14 in bank 1152. Attempting to change either of the last two arguments in the ShortMessage constructor to 1152 resulted in a javax.sound.midi.InvalidMidiDataException for the data byte value being out of range. The other MidiMessage subclasses don’t seem to contain an option to load instruments from other banks, either.

How can I use an instrument from a different bank in the MidiEvent’s MidiMessage?

Answer

In the MIDI protocol itself, bank numbers are split into two 7-bit parts and transmitted as the value of two controllers, “Bank Select” and “Bank Select LSB”:

... = new ShortMessage(ShortMessage.CONTROL_CHANGE, 0, 0,  1152 >> 7);   // = 9
... = new ShortMessage(ShortMessage.CONTROL_CHANGE, 0, 32, 1152 & 0x7f); // = 0
... = new ShortMessage(ShortMessage.PROGRAM_CHANGE, 0, 14, 0);

Please note that different standards (GS, XG, GM2) name the two parts of the bank select number differently. In this case, the Carillon is from the GS standard, which defines it as “variation number 9”, which is the MSB controller. But the controller name does not matter; you get the correct instrument as long as you set controller 0 to 9.



Source: stackoverflow