问题:创建一个非常准确的Swing Timer [英] Issues: Creating a very accurate Swing Timer

查看:122
本文介绍了问题:创建一个非常准确的Swing Timer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了使 SwingTimer 准确,我喜欢@Tony Docherty建议的逻辑和示例
在CR上。这是链接

In order to make SwingTimer accurate, I like the logic and example suggested by @Tony Docherty On CR. Here is the Link.

为了突出显示给定的单词,一次又一次,总会有几微秒的延迟。如果我有话要强调说:你好怎么样,每个单词的值分别是(延迟):200,300,400 ms,那么计时器的实际时间总是更多。说而不是200毫秒,它需要216毫秒。像这样,如果我有很多单词......最后,额外的延迟是显而易见的。

In order to highlight the given words, again and again, there is always a few microsecond delays. If I have words to highlight say: "hello how are" and the values for each word are (delays): 200,300,400 ms respectively, then the actual time taken by the timer is always more. Say instead of 200 ms, it takes 216 ms. Like this, if I have many words..in the end, the extra delay is noticeable.

我必须突出显示每个字母:'h''e'' l''''0'每个应该得到200 /长度(即5)=约40毫秒。设置每个字母后的延迟。

I have to highlight each letter say: 'h''e''l''l''0' each should get 200/length(i.e 5) = 40 ms approx. Set the delay after each letter.

我的逻辑是,在当前时间说 startTime ,就在开始之前处理。另外,计算 totalDelay ,即totalDelay + = delay / .length()。

My logic is, take the current time say startTime, just before starting the process. Also, calculate the totalDelay which is totalDelay+=delay/.length().

现在检查条件:( startTime + totalDelay-System.currentTime
如果这是-ve,这意味着时间消耗更多,所以跳过这封信。检查直到有一个正的延迟。这意味着我现在正在添加时间,并在进程开始时对进程所花费的时间进行超额检查。

Now check the condition: (startTime+totalDelay-System.currentTime) if this is -ve, that means the time consumption is more, so skip the letter. Check till there is a positive delay.This means I am adding the timings till now, and overcheck it with the difference in the time taken by the process when it got started.

这可能会导致跳过以突出显示字母。

This may result into skipping to highlight the letters.

但是出了点问题。什么,我很难搞清楚。这可能是循环事情的一些问题。我已经看到它进入循环(检查时间是否为-ve)只是两次。但事实并非如此。而且我也不确定是否会设置下一次延迟。有什么想法?

But something is wrong. What, it’s difficult for me to make out. It's some problem with the looping thing maybe. I have seen it is entering the loop (to check whether the time is -ve ) just twice. But this should not be the case. And I am also not sure about setting up my next delay. Any ideas?

这是一个SSCCE:

    import java.awt.Color;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.lang.reflect.InvocationTargetException;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextPane;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.DefaultStyledDocument;
    import javax.swing.text.StyleConstants;
    import javax.swing.text.StyledDocument;

    public class Reminder {
        private static final String TEXT = "arey chod chaad ke apnee saleem ki gali anarkali disco chalo";
        private static final String[] WORDS = TEXT.split(" ");
        private JFrame frame;
        private Timer timer;
        private StyledDocument doc;
        private JTextPane textpane;
        private int[] times = new int[100];
      private long totalDelay=0,startTime=0;

        private int stringIndex = 0;
        private int index = 0;

        public void startColoring() {
              times[0]=100;times[9]=200;times[10]=200;times[11]=200;times[12]=200;
              times[1]=400;times[2]=300;times[3]=900;times[4]=1000;times[5]=600;times[6]=200;times[7]=700;times[8]=700;

      ActionListener actionListener = new ActionListener() {
       @Override
       public void actionPerformed(ActionEvent actionEvent) 
       {

       doc.setCharacterAttributes(stringIndex, 1, textpane.getStyle("Red"), true);
        stringIndex++;

 try {

 if (stringIndex >= doc.getLength() || doc.getText(stringIndex, 1).equals(" ")|| doc.getText(stringIndex, 1).equals("\n"))
 {
                            index++;
  }
    if (index < WORDS.length) {

       double delay = times[index];
     totalDelay+=delay/WORDS[index].length();

  /*Check if there is no -ve delay, and you are running according to the time*/
  /*The problem is here I think. It's just entered this twice*/
   while(totalDelay+startTime-System.currentTimeMillis()<0)
      { 
      totalDelay+=delay/WORDS[index].length();
      stringIndex++;
     /*this may result into the end of current word, jump to next word.*/
    if (stringIndex >= doc.getLength() || doc.getText(stringIndex, 1).equals(" ") || doc.getText(stringIndex, 1).equals("\n"))
       {
   index += 1;
   totalDelay+=delay/WORDS[index].length();
       }
      }

     timer.setDelay((int)(totalDelay+startTime-System.currentTimeMillis()));

                        } 
else {
         timer.stop();
    System.err.println("Timer stopped");
       }
                    } catch (BadLocationException e) {
                        e.printStackTrace();
                    }
                }
            };

            startTime=System.currentTimeMillis();
            timer = new Timer(times[index], actionListener);
            timer.setInitialDelay(0);
            timer.start();
        }

        public void initUI() {
            frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel panel = new JPanel();
            doc = new DefaultStyledDocument();
            textpane = new JTextPane(doc);
            textpane.setText(TEXT);
            javax.swing.text.Style style = textpane.addStyle("Red", null);
            StyleConstants.setForeground(style, Color.RED);
            panel.add(textpane);
            frame.add(panel);
            frame.pack();
            frame.setVisible(true);
        }

        public static void main(String args[]) throws InterruptedException, InvocationTargetException {
            SwingUtilities.invokeAndWait(new Runnable() {
                @Override
                public void run() {
                    Reminder reminder = new Reminder();
                    reminder.initUI();
                    reminder.startColoring();
                }
            });
        }
    }

更新:

为了更好地理解:

@Tony Docherty给出的EG:

The EG given by @Tony Docherty :

让我们用测试这个词说它需要突出显示1秒,因此每个字母突出显示250ms。
按照你原来的方式做事,确实意味着你为每个字母设置一个250ms的计时器,但是如果每个周期实际上需要260ms并且让'e'周期花费400ms(可能是由于GC或其他使用CPU的话)循环)到这个词的结尾你将比你应该花费180ms。将继续为每个单词构建此错误,直到错误如此之大,突出显示不再在视觉上同步。

Lets take the word "Test" and say it needs to be highlighted for 1 second, therefore each letter is highlighted for 250ms. Doing things the way you originally, did meant that you set a timer for 250ms for each letter but if each cycle actually took 260ms and lets say the 'e' cycle took 400ms (maybe due to GC or something else using CPU cycles) by the end of the word you would have taken 180ms more than you should have. This error will continue to build for each word until the error is so large highlighting is no longer visually in sync.

我正在尝试的方式,而不是反复说这封信需要突出显示x个时间,计算每个字母相对于开头的时间。序列即T = 250,e = 500,s = 750,t = 1000。

The way I am trying, is rather than repeatedly saying this letter needs to be highlighted for x amount of time, calculate the time for each letter relative to the beginning of the sequence ie T = 250, e = 500, s = 750, t = 1000.

因此要获得实际的时间延迟,您需要添加开始时间并减去当前时间。要使用上面给出的时间来运行示例:

So to get the actual time delay you need to add the start time and subtract the current time. To run through the example using the timings I gave above:

StartTime   Letter   Offset    CurrentTime    Delay  ActualTimeTaken   
100000         T       250       100010        240      250  
100000         e       500       100260        240      400  
100000         s       750       100660         90      100  
100000         t      1000       100760        240      250  

所以你现在应该能够看到每个字母的时间被调整,以考虑到前一个字母的任何超时时间。当然,时间超限可能很大,你必须跳过突出显示下一个字母(或者可能超过1),但至少我将保持大致同步。

So you should be able to see now that the timing for each letter is adjusted to take account of any overrun of time from the previous letter. Of course it is possible that a timing overrun is so great that you have to skip highlighting the next letter (or maybe more than 1) but at least I will remaining broadly in sync.

EDITED SSCCE

Update2

在第一阶段,我会考虑每个单词的时间。也就是说,当用户按下ESC键时,会为特定单词存储时间(他在背景中播放歌曲时会这样做。)按下ESC键时,当前单词会突出显示,当前单词会占用当前单词。 word存储在一个数组中。我一直在存储时间。当用户结束时,现在我想根据设置的时间突出显示单词。所以在这里,用户的时机非常重要。如果时间很快,那么单词的突出显示也会很快,反之亦然。

In first phase, I take the timings for each word. That is, when the user hits ESC key, the time is stored for a particular word (he does it as the song is played in background.) When the ESC key is pressed, the current word is highlighted and the time spent on the current word is stored in an array. I keep on storing the timings. When the user ends, now I would like to highlight the words as per the set timings. So here, the timing by the user is important. If the timings are fast, so is the highlighting of words or if slow, vice-versa.

新的更新:进度

下面的答案有不同的逻辑,但令我惊讶的是,它们的工作方式大致相同。我发现所有逻辑(包括我的)的一个非常非常奇怪的问题是它们似乎在几条线上完美地工作,但在那之后它们获得了速度,这也不是很慢,而是有很大差异。

The answers below have different logic, but to my surprise, they work more or less the same. A very very weird problem I have found out with all the logic (including mine) is that they seem to work perfectly for few lines, but after that they gain speed, that's also not slowly, but with a huge difference.

此外,如果您认为我应该以不同的方式思考,您的建议将受到高度赞赏。

Also if you think I should think in a different way, your suggestions are highly appreciated.

推荐答案

好的,所以我一直在查看一些代码(我在上一个关于卡拉OK计时器的问题中发布的代码)

Okay so I have been looking at the some code (the code I posted in your last question about Karaoke timer)

使用该代码我提出了一些测量系统使用 System.nanoTime()通过 System.out.println()这将帮助我们看看发生了什么:

Using that code I put up some measuring system using System.nanoTime() via System.out.println() which will help us to see what is happening:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

public class KaraokeTest {

    private int[] timingsArray = {1000, 1000, 9000, 1000, 1000, 1000, 1000, 1000, 1000, 1000};//word/letters timings
    private String[] individualWordsToHighlight = {" \nHello\n", " world\n", " Hello", " world", " Hello", " world", " Hello", " world", " Hello", " world"};//each individual word/letters to highlight
    private int count = 0;
    private final JTextPane jtp = new JTextPane();
    private final JButton startButton = new JButton("Start");
    private final JFrame frame = new JFrame();
    //create Arrays of individual letters and their timings
    final ArrayList<String> chars = new ArrayList<>();
    final ArrayList<Long> charsTiming = new ArrayList<>();

    public KaraokeTest() {
        initComponents();
    }

    private void initComponents() {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);

        for (String s : individualWordsToHighlight) {
            String tmp = jtp.getText();
            jtp.setText(tmp + s);
        }
        jtp.setEditable(false);

        startButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                startButton.setEnabled(false);
                count = 0;
                charsTiming.clear();
                chars.clear();

                for (String s : individualWordsToHighlight) {
                    for (int i = 0; i < s.length(); i++) {
                        chars.add(String.valueOf(s.charAt(i)));
                        //System.out.println(String.valueOf(s.charAt(i)));
                    }
                }

                //calculate each letters timings
                for (int x = 0; x < timingsArray.length; x++) {
                    for (int i = 0; i < individualWordsToHighlight[x].length(); i++) {
                        individualWordsToHighlight[x] = individualWordsToHighlight[x].replace("\n", " ").replace("\r", " ");//replace line breaks
                        charsTiming.add((long) (timingsArray[x] / individualWordsToHighlight[x].trim().length()));//dont count spaces
                        //System.out.println(timingsArray[x] / individualWordsToHighlight[x].length());
                    }
                }

                Timer t = new Timer(1, new AbstractAction() {
                    long startTime = 0;
                    long acum = 0;
                    long timeItTookTotal = 0;
                    long dif = 0, timeItTook = 0, timeToTake = 0;
                    int delay = 0;

                    @Override
                    public void actionPerformed(ActionEvent ae) {
                        if (count < charsTiming.size()) {

                            if (count == 0) {
                                startTime = System.nanoTime();
                                System.out.println("Started: " + startTime);
                            }

                            timeToTake = charsTiming.get(count);
                            acum += timeToTake;

                            //highlight the next word
                            highlightNextWord();

                            //System.out.println("Acum " + acum);
                            timeItTook = (acum - ((System.nanoTime() - startTime) / 1000000));
                            timeItTookTotal += timeItTook;
                            //System.out.println("Elapsed since start: " + (System.nanoTime() - startTime));
                            System.out.println("Time the char should take: " + timeToTake);
                            System.out.println("Time it took: " + timeItTook);
                            dif = (timeToTake - timeItTook);
                            System.out.println("Difference: " + dif);
                            //System.out.println("Difference2 " + (timeToTake - dif));

                            //calculate start of next letter to highlight less the difference it took between time it took and time it should actually take
                            delay = (int) (timeToTake - dif);

                            if (delay < 1) {
                                delay = 1;
                            }

                            //restart timer with new timings
                            ((Timer) ae.getSource()).setInitialDelay((int) timeToTake);//timer is usually faster thus the entire highlighting will be done too fast
                            //((Timer) ae.getSource()).setInitialDelay(delay);
                            ((Timer) ae.getSource()).restart();

                        } else {//we are at the end of the array
                            long timeStopped = System.nanoTime();
                            System.out.println("Stopped: " + timeStopped);
                            System.out.println("Time it should take in total: " + acum);
                            System.out.println("Time it took using accumulator of time taken for each letter: " + timeItTookTotal
                                    + "\nDifference: " + (acum - timeItTookTotal));
                            long timeItTookUsingNanoTime = ((timeStopped - startTime) / 1000000);
                            System.out.println("Time it took using difference (endTime-startTime): " + timeItTookUsingNanoTime
                                    + "\nDifference: " + (acum - timeItTookUsingNanoTime));
                            reset();
                            ((Timer) ae.getSource()).stop();//stop the timer
                        }
                        count++;//increment counter
                    }
                });
                t.setRepeats(false);
                t.start();
            }
        });

        frame.add(jtp, BorderLayout.CENTER);
        frame.add(startButton, BorderLayout.SOUTH);

        frame.pack();
        frame.setVisible(true);
    }

    private void reset() {
        startButton.setEnabled(true);
        jtp.setText("");
        for (String s : individualWordsToHighlight) {
            String tmp = jtp.getText();
            jtp.setText(tmp + s);
        }
        JOptionPane.showMessageDialog(frame, "Done");
    }

    private void highlightNextWord() {
        //we still have words to highlight
        int sp = 0;
        for (int i = 0; i < count + 1; i++) {//get count for number of letters in words (we add 1 because counter is only incrementd after this method is called)
            sp += 1;
        }

        while (chars.get(sp - 1).equals(" ")) {
            sp += 1;
            count++;
        }

        //highlight words
        Style style = jtp.addStyle("RED", null);
        StyleConstants.setForeground(style, Color.RED);
        ((StyledDocument) jtp.getDocument()).setCharacterAttributes(0, sp, style, true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new KaraokeTest();
            }
        });
    }
}

我电脑上的输出是:


开始时间:10289712615974

Started: 10289712615974

char应该花费的时间:166

Time the char should take: 166

花费的时间:165

差异1

...

char应该采取的时间:166

Time the char should take: 166

花费的时间:155

差异11

...

char应该采取的时间:166

Time the char should take: 166

花费的时间:5

差异161

停止:10299835063084

Stopped: 10299835063084

它应该占用的总时间:9960

Time it should take in total: 9960

使用累加器所花费的时间信:5542

Time it took using accumulator of time taken for each letter: 5542

差异:4418

使用差异花费的时间(endTime-startTime):10122

Time it took using difference (endTime-startTime): 10122

差异:-162

因此我的结论是Swing Timer i实际上运行速度比我们预期的要快,因为计时器 s actionPerformed 中的代码不一定会长到这些字母预期突出显示时间这当然会导致雪崩效应,即定时器运行得越快/越慢,差异将变得越大/越小,定时器在 restart(..)上的下一次执行将需要在不同的时间,即更快或更慢。

Thus my conclusion is the Swing Timer is actually running faster than we expect as the code in the Timers actionPerformed will not necessarily take as long as the letters expected highlighting time this of course causes an avalanche effect i.e the faster/slower the timer runs the greater/less the difference will become and timers next execution on restart(..) will take be at a different time i.e faster or slower.

在代码中执行此操作:

//calculate start of next letter to highlight less the difference it took between time it took and time it should actually take
delay = (int) (timeToTake - dif);


//restart timer with new timings
//((Timer) ae.getSource()).setInitialDelay((int)timeToTake);//timer is usually faster thus the entire highlighting will be done too fast
((Timer) ae.getSource()).setInitialDelay(delay);
((Timer) ae.getSource()).restart();

产生更准确的结果(Ive的最大延迟是每个字母快4ms):

Produces a more accurate result (maximum latency Ive had is 4ms faster per letter):


开始时间:10813491256556

Started: 10813491256556

char应该采取的时间:166

Time the char should take: 166

花费的时间:164

差异2

...

char应该占用的时间:166

Time the char should take: 166

花费的时间:164

差异2

...

char应该采取的时间:166

Time the char should take: 166

花费的时间:162

差异4

停止:10823452105363

Stopped: 10823452105363

它应该占用的总时间:9960

Time it should take in total: 9960

使用累加器所花费的时间信:9806

Time it took using accumulator of time taken for each letter: 9806

差异:154

使用差异花费的时间(endTime-startTime):9960

Time it took using difference (endTime-startTime): 9960

差异:0

这篇关于问题:创建一个非常准确的Swing Timer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆