Search code examples
javamidirecording

JAVA MIDI eliminate silence in sequence


I bought a VDRUM kit not long ago that has a MIDI out. I've had a raspberry pi lying around here from a previous project and I've had the idea to use that small computer as recorder.

So I've written a headless application that runs on the PI and listens to the MIDI Out port of the VDRUM kit. Upon receiving an input other than silence i start recording via the jdk libraries and place it on a samba server with timestamp.

That way I save everything I ever play. Now I've setup my audio workstation to receive those MIDI files from the samba share and I noticed, that they often have a very long "silent" part in the beginning of the MIDI file.

Is there a way to programatically remove the silence at the beginning of a midi sequence? I'm new to this MIDI stuff and still learning.

Here's the code that starts and stops the recording:

    public void send(MidiMessage message, long timeStamp) {
        if (message.getStatus() == 254) {
            if (isRecording && timestampOfLastAction.isBefore(LocalTime.now().minus(5, ChronoUnit.SECONDS))) {
                String userDirectory = "/home/pi/";
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss");
                LocalDateTime now = LocalDateTime.now();
                String fileName = dtf.format(now) + ".mid";
                sequencer.stopRecording();
                File midiFile = new File(userDirectory + "/MidiRecorder/" + fileName);
                try {
                    Sequence sequence = sequencer.getSequence();
                    MidiSystem.write(sequence, 0, midiFile);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                Logger.getGlobal().info("STOPPED RECORDING: " + LocalTime.now().toString());
                isRecording = false;
            }

        } else {
            timestampOfLastAction = LocalTime.now();
            if (!isRecording) {
                isRecording = true;
                sequencer = null;
                try {
                    sequencer = MidiSystem.getSequencer();

                    Transmitter transmitter1;
                    Receiver receiver;
                    // Open a connection to the default sequencer (as specified by MidiSystem)
                    sequencer.open();
                    // Get the transmitter class from your input device
                    transmitter1 = this.midiDevice.getTransmitter();
                    // Get the receiver class from your sequencer
                    receiver = sequencer.getReceiver();
                    transmitter1.setReceiver(receiver);
                    Sequence seq = new Sequence(Sequence.PPQ, 24);
                    // And of course a track to record the input on
                    Track currentTrack = seq.createTrack();
                    // Do some sequencer settings
                    sequencer.setSequence(seq);
                    sequencer.setTickPosition(0);
                    sequencer.recordEnable(currentTrack, -1);

                    // And start recording
                    sequencer.startRecording();
                    Logger.getGlobal().info("STARTED RECORDING: " + LocalTime.now().toString());
                } catch (Exception e) {
                    // TODO: handle exception
                } finally {

                }
            }
        }
    }

Solution

  • You need to shift all MidiEvents of the Sequence to remove the leading silence.

    private void removeLeadingSilence(Sequence sequence)
    {
        // Find first event
        long firstEventTick = Long.MAX_VALUE;
        for (Track track : sequence.getTracks())
        {
            if (track.size() > 0)
            {
                firstEventTick = Math.min(firstEventTick, track.get(0).getTick());
            }
        }
    
        // Shift all events
        for (Track track : sequence.getTracks())
        {
            for (int i = 0; i < track.size(); i++)
            {
                MidiEvent me = track.get(i);
                me.setTick(me.getTick() - firstEventTick);
            }
        }
    }