'Java MIDI: Connecting MIDI Keyboard to Default Synthesizer

I gotta a MIDI Keyboard. All I want is an example of how I can setup the default software synthesizer to play sounds as I play the Keyboard.

package cleffsgame;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;

public class CheckDevices {

    public static void main(String[] args) {
        MidiDevice device;
        // display each device's properties
        for (MidiDevice.Info info: MidiSystem.getMidiDeviceInfo()) {

            try {
                device = MidiSystem.getMidiDevice(info);

                System.out.println("\nDevice: ");
                System.out.println("Name: " + device.getDeviceInfo().getName());
                System.out.println("Vendor: " + device.getDeviceInfo().getVendor());
                System.out.println("Version: " + device.getDeviceInfo().getVersion());
                System.out.println("Description: " + device.getDeviceInfo().getDescription());
                System.out.println("Transmitters: " + device.getMaxTransmitters());
                System.out.println("Receivers: " + device.getMaxReceivers());

            } catch (MidiUnavailableException ex) {
                Logger.getLogger(CheckDevices.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

By running the code above, I get the following output:

run:

Device: Name: Gervill Vendor: OpenJDK Version: 1.0 Description: Software MIDI Synthesizer Transmitters: 0 Receivers: -1

Device: Name: Oxygen 49 Vendor: M-Audio Version: Unknown version Description: Oxygen 49 Transmitters: -1 Receivers: 0

Device: Name: Oxygen 49 Vendor: M-Audio Version: Unknown version Description: Oxygen 49 Transmitters: 0 Receivers: -1

Device: Name: Real Time Sequencer Vendor: Oracle Corporation Version: Version 1.0 Description: Software sequencer Transmitters: -1 Receivers: -1 BUILD SUCCESSFUL (total time: 2 seconds)

But when I run the code below, no sound is played when I hit the keys.

package cleffsgame;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Transmitter;

/**
 *
 * @author umberto
 */
public class Test {

    public static void main(String[] args) {
        MidiDevice inputDevice = null, synthDevice = null;
        Transmitter transmitter = null;
        Synthesizer synthesizer = null;
        Receiver receiver = null;

        try {
            inputDevice = MidiSystem.getMidiDevice(MidiSystem.getMidiDeviceInfo()[1]);
            synthDevice = MidiSystem.getMidiDevice(MidiSystem.getMidiDeviceInfo()[0]);

        } catch (MidiUnavailableException ex) {
            Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
        }

        // goodDevice must be Oxygen 49 transmitter (MIDI Input)
        if (inputDevice != null && synthDevice != null) {
            try {
                transmitter = inputDevice.getTransmitter();
                System.out.println("Transmitter: " + inputDevice.getDeviceInfo());
                System.out.println(String.format("T/R: %s/%s", inputDevice.getMaxTransmitters(), inputDevice.getMaxReceivers()));

                receiver = synthDevice.getReceiver();
                System.out.println("Receiver: " + synthDevice.getDeviceInfo());
                System.out.println(String.format("T/R: %s/%s", synthDevice.getMaxTransmitters(), synthDevice.getMaxReceivers()));

                transmitter.setReceiver(receiver);
                System.out.println("GoodDevice is open... check sound\n");
                inputDevice.open();
                synthDevice.open();

            } catch (Exception ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }


}

It seems there is more to do than connecting a Synthesizer.receiver to a InputDevice.transmitter as I got from the docs https://docs.oracle.com/javase/tutorial/sound/MIDI-synth.html.



Solution 1:[1]

This works for me

transmitter.setReceiver(new Receiver() {
    @Override
    public void send(MidiMessage message, long timeStamp) {
        receiver.send(message, -1);
    }
    @Override
    public void close() {
        receiver.close();
    }
});

Solution 2:[2]

It might be a good idea to open the devices before acquiring their transmitter/receiver.

But the actual problem is that this program exits immediately, so the devices aren't open for a useful amount of time.

Solution 3:[3]

Recently tried to do the same thing. Java tutorials and Stackoverflow answers weren't working. The problem was that, I was passing the keyboard's Note On timestamp to the internal synth. The fix was to pass -1 as the timestamp instead.

This example uses Kotlin to call the Java MIDI APIs.

JDK 17, MacOS 11.3, Kotlin 1.6

import javax.sound.midi.*

class ReceiverAdapter (val targetReceiver: Receiver) : Receiver {

    override fun send(midiMsg: MidiMessage, timeStamp: Long) {
        targetReceiver.send(midiMsg, -1); // only works for me when i pass -1 as timestamp instead of original value from keyboard
    }
    override fun close() {  }
}

class MidiKeyboardInternalSynthExample {

    companion object {

        fun doIt(keyboardDeviceName: String){

            // connect external keyboard to internal synth
            val midiTransmitter = find1stMidiTransmitterWithName(keyboardDeviceName)
            if(midiTransmitter != null) {
                val synth = MidiSystem.getSynthesizer()!!
                val midiReceiver = ReceiverAdapter(synth.receiver)
                midiTransmitter.transmitter.receiver = midiReceiver
                synth.open()
                midiTransmitter.open()
            }
            else {
                println("Didn't find MIDI transmitter named '$keyboardDeviceName'.")
            }
        }

        fun find1stMidiTransmitterWithName(deviceName: String): MidiDevice? =
            MidiSystem.getMidiDeviceInfo()
                ?.filter { info -> info.name == deviceName }
                ?.map { info -> MidiSystem.getMidiDevice(info) }
                ?.filter { device -> device.maxTransmitters == -1 }
                ?.first()
    }
}

fun main(args: Array<String>) {
    MidiKeyboardInternalSynthExample.doIt("LPK25")
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jizhihaoSAMA
Solution 2 CL.
Solution 3