Read MIDI input stream from a live instrument

184 views Asked by At

I'm trying to create a sort of automatic sheet music viewer just for fun which I will eventually try to integrate in my digital piano with a raspberry pi or something, but that's another story. Let's focus on my current problem.

In order to make this work, I have to compare MIDI input coming from my piano, with a MIDI file, representing the sheet music that I'm playing.

As I want to compare the two MIDI in realtime, I found really difficult to get live MIDI directly from my piano.

I've already tried to read 10 seconds of MIDI and write everything on a file and it works flawlessly, but I can't find any way to receive a sort of callback whenever a message is sent from my piano.

This is my current TEST code (it's horrible, I know, but I'm just testing right now) where I basically write to a .mid file 10 seconds of MIDI coming from my piano, and I also try to use a ControllerEventListener, but it doesn't seem to work.

import java.io.File;
import java.io.IOException;

import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiDevice.Info;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Track;
import javax.sound.midi.Transmitter;
import javax.swing.Timer;

public class Main {

    public static void main(String[] args) throws Exception {
        
        Info[] infos = MidiSystem.getMidiDeviceInfo();
        for(int i = 0; i < infos.length; i++) {
            System.out.println(infos[i].getName() + " - " + infos[i].getDescription());
        }
        
        MidiDevice inputDevice = MidiSystem.getMidiDevice(infos[1]);
        
        Sequencer sequencer = MidiSystem.getSequencer();
        Receiver receiver;
        Transmitter transmitter;
        
        inputDevice.open();
        sequencer.open();
        
        transmitter = inputDevice.getTransmitter();
        receiver = sequencer.getReceiver();
        transmitter.setReceiver(receiver);
        
        Sequence seq = new Sequence(Sequence.PPQ, 24);
        
        Track currentTrack = seq.createTrack();
        
        sequencer.setSequence(seq);
        sequencer.setTickPosition(0);
        
        sequencer.addControllerEventListener(new ControllerEventListener() {
            
            @Override
            public void controlChange(ShortMessage event) {
                System.out.println("listener works");
            }
        }, new int[] {127});
        
        sequencer.recordEnable(currentTrack, -1);
        
        sequencer.startRecording();
        
        new Timer(10000, e -> {
            
            Sequence tmp = sequencer.getSequence();
            sequencer.stopRecording();
            
            try {
                MidiSystem.write(tmp, 0, new File("midi.mid"));
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            System.exit(0);
        }).start();
        
    }

}

As you can see, I tried to add a ControllerEventListener to the Sequencer, but it doesn't seems to work when listening events while recording:

sequencer.addControllerEventListener(new ControllerEventListener() {
    @Override
    public void controlChange(ShortMessage event) {
        System.out.println("listener works");
    }
}, new int[] {127});

No matter how hard I tried, but I couldn't find anything that could help me... Any way to receive events on live MIDI instruments?

1

There are 1 answers

0
jjazzboss On BEST ANSWER

I can't find any way to receive a sort of callback whenever a message is sent from my piano.

If you want to be notified in real time when a Note ON MidiMessage arrived from your piano, use a custom Receiver.

...
transmitter = inputDevice.getTransmitter();
receiver = new MyReceiver();
transmitter.setReceiver(receiver);
...


private class MyReceiver implements Receiver
{
    ...
    
    @Override
    public void send(MidiMessage mm, long timeStamp)
    {
        if (mm instanceof ShortMessage sm && sm.getCommand() == ShortMessage.NOTE_ON)
        {           
            // Do something with the NoteOn message, preferably in another thread
        } 
    }
}

If you want that the Sequencer records the MidiMessages in parallel to create a Sequence, get a second transmitter from inputDevice and connect it to the Sequencer's receiver.