3

I am creating an application where the user can add multiple MIDI notes to a collection, from here they can press the "Play" button and the application will iterate through each note and play them through the speakers.

I've created a MIDIMessage class that holds details of the note the user has chose to add to the list, the details stored being the Pitch,Velocity,Delay and Channel.

Each of these messages is saved to an ArrayList with the type as MIDIMessage.

I then go on to attatch the iterator of the collection to an iterator object and play a sound whilst there is still an element in the collection that hasn't been used.

For some reason even if I only add one note to the collection there is always two notes playing with exactly the same pitch, length and velocity.

Also each note plays at the same time no matter how many are present in the collection, I assumed there would be some sort of delay in between them.

Following is the code I am using at the moment:

MIDIMessage:

package javatest;

public class MIDIMessage
{
    private int pitch;
    private int velocity;
    private int channel;

    public MIDIMessage(int p, int v, int c)
    {
        pitch = p;
        velocity = v;
        channel = c;
    }

    public int GetPitch()
    {
        return this.pitch;
    }

    public int GetVelocity()
    {
        return this.velocity;
    }

    public int GetChannel()
    {
        return this.channel;
    }
}

Adding a note to the collection:

public void AddToList()
    {
        int channel = jComboBoxChannels.getSelectedIndex();
        int pitch = jComboBoxPitch.getSelectedIndex();
        int velocity = ((Integer)jSpinnerVelocity.getValue());    
        collection.add(new MIDIMessage(pitch,velocity,channel));
    }

Playing the notes:

try
        {
            jButton1.setEnabled(false);
            Sequencer sequencer = MidiSystem.getSequencer();
            sequencer.open();
            Sequence sequence = new Sequence(Sequence.PPQ,1);
            Track track = sequence.createTrack();

            Iterator itr = collection.iterator();
            int i = 0;

            while(itr.hasNext())
            {
                MIDIMessage msg = (MIDIMessage)itr.next();

                ShortMessage noteOnMsg = new ShortMessage();
                //Signal/Channel/Pitch/Velocity
                noteOnMsg.setMessage(ShortMessage.NOTE_ON, msg.GetChannel(),msg.GetPitch(),msg.GetVelocity());

                ShortMessage noteOffMsg = new ShortMessage();
                //Signal/Channel/Pitch/Velocity
                noteOffMsg.setMessage(ShortMessage.NOTE_OFF,msg.GetChannel(),msg.GetPitch(),msg.GetVelocity());

                track.add(new MidiEvent(noteOnMsg,0));
                track.add(new MidiEvent(noteOffMsg,1));
                //i = i+1;
            }

            sequencer.setSequence(sequence);
            sequencer.setTempoInBPM(120);
            sequencer.setLoopCount(1);

            sequencer.start();

            Thread.sleep(1000);
        }
Jamie Keeling
  • 9,806
  • 17
  • 65
  • 102
  • The duplicates are because the loop count is 1 - this means repeat everything once (after the original play through) - set it to zero to not loop at all. – BarrySW19 Jul 18 '18 at 16:13
  • How odd. It's been over 7 years since I asked this SO question and the reason I needed it was to help get a degree for where I am now. I'll look into this again one day. – Jamie Keeling Jul 19 '18 at 15:34

1 Answers1

1

I'm not sure why the notes are played twice, but this is definitely wrong:

            track.add(new MidiEvent(noteOnMsg,0));
            track.add(new MidiEvent(noteOffMsg,1));

As you can see here, a MidiEvent consists of the message and a MIDI tick. You always add a noteOnMsg on tick 0 and a noteOffMsg on tick 1, which explains why for multiple notes, they are all played at the same time. You want to do something like this instead:

startTick = 0;

while(itr.hasNext()) {
   [...]
   track.add(new MidiEvent(noteOnMsg,startTick));
   track.add(new MidiEvent(noteOffMsg,startTick + how_long_the_note_is_played));
   startTick += difference_between_this_note_and_the_next;
   [...]
}

I'm not sure what your delay means and if you can use it here, so I'm using descriptive variable names instead.

Note that you'll have to use MIDI ticks here - if you want to convert seconds in MIDI ticks, see this SO question (or rather its answer):

[How long is one MIDI tick?]

The formula is 60000 / (BPM * PPQ) (milliseconds).

Community
  • 1
  • 1
schnaader
  • 49,103
  • 10
  • 104
  • 136
  • I'm sorry I don't understand, how do I find out how long the note has played and the different between the current note and next? Also the delay was an error, I was C/P code from an old source file. – Jamie Keeling Jan 13 '11 at 14:41
  • You *define* how long the note is played and what the difference between the current note and the next one is. If you want to just play a note every "beat", try to use `Sequence.PPQ` for both variables. – schnaader Jan 13 '11 at 14:47
  • What i'm trying to say is I don't understand how I would define the length of a note, is there some sort of method I can use to get the length of one?. As an example I've noticed you can call new MidiEvent(noteOnMsg,1).getTick); but I have a feeling that to use it I will need to create the MidiEvent seperately and then add it to the track after, allowing me to set the Tick to a variable.. – Jamie Keeling Jan 13 '11 at 15:18
  • The tick parameter is a simple `long`, imagine a timeline from 0 to infinity where you place your MIDI events (note on, note off) on. In my answer, I also told you how long such a tick is in milliseconds, and in my comment I said that using the value of `Sequence.PPQ` will be the length of one "beat" (you're using 120 BPM). `getTick` and `setTick` methods are only useful if you want to retrieve/change the tick of a particular `MidiEvent`. – schnaader Jan 13 '11 at 15:43
  • I still don't understand, from your answer you state how I can supply the tick parameter using the startTick and the length of how long each note is played. What i'm trying to get at is I don't understand how I can find out how long each note is played for. I don't understand how I can use the tick to find this out. – Jamie Keeling Jan 14 '11 at 13:18
  • I think the method I was using was causing some sort of bug, I rewrote it from the ground up using a different example and I've had no problems so far. Thanks for the help, it's greatly appreciated. – Jamie Keeling Jan 20 '11 at 21:44