Question

I'm programming a game similar to DDR, using MIDI files as music. I wrote a small class to generate the arrows based on the notes in any given MIDI file. In the actual game, the song starts playing when the first arrow reaches the white arrows at the bottom (http://puu.sh/5LuVO.png). My problem is that while the song and the arrows start out in sync, by the end of the song they are off by 3-5 seconds. Any solution? Here is the code for how I am generating the arrows:

Sequence sequence = MidiSystem.getSequence(new File("arbitrary.mid"));
    int trackNumber = 0;
    millisecondsPerTick = sequence.getMicrosecondLength()/(sequence.getTickLength()*(float)1000);
    for (Track track :  sequence.getTracks()) { 
        trackNumber++;
        for (int i=0; i < track.size(); i++) { 
            MidiEvent event = track.get(i);
            MidiMessage message = event.getMessage();
            if (message instanceof ShortMessage) {
                ShortMessage sm = (ShortMessage) message;
                if (sm.getCommand() == NOTE_ON) {
                    int key = sm.getData1();
                    int velocity = sm.getData2();
                    if(velocity>20)
                    songArrows.add(new Arrow(-20, 20, key%4 , false, (int)event.getTick()));
                } 
            } 
        }
    }

So basically I go through the MIDI events, adding an arrow with the same tick as the event to an array. This is the timer listener I am using in the main class to add the arrows to the panel.

generateArrowsTimer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
    for (int i = 0; i < GenerateArrowsTest.songArrows.size(); i++) {
            if(Math.abs(musicTime - GenerateArrowsTest.millisecondsPerTick*((Arrow)GenerateArrowsTest.songArrows.get(i)).getTime())<=10) 
                        movingArrows.add((Arrow)GenerateArrowsTest.songArrows.get(i)); 
                    //arrows show up at the top of the screen as they are added, then move down at 4px per 30 milliseconds, using updateTimer
                }
        if(musicTime >= updateTimer.getDelay()*520/Arrow.movementIncrement && musicTime < updateTimer.getDelay()*600/Arrow.movementIncrement)
                    sequencer.start(); //Starts playing music about when first arrow reaches a white arrow
                    if(sequencer.isRunning())    
                        musicTime += generateArrowsTimer.getDelay(); //delay was initialized to be 30
        repaint();
        }
        });

Why are the arrows getting out of sync with the music?

Was it helpful?

Solution

Your problem is that musicTime is not computed correctly.

A timer is not guaranteed to execute at exactly the desired time; it might be delayed by some random amount. So by adding up the desired delay, any errors will accumulate.

To get the current time, do not use relative time offsets but ask the system for an absolute time stamp. In the case of the Sequencer, use getMicrosecondPosition or getTickPosition.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top