Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
426 views
in Technique[技术] by (71.8m points)

refresh - High refreshing rate in JavaFX

I'm trying to write a program with an equalizer, a frequency analyzer and a sound level meter. The model part seems to work very well but I'm experimenting some bugs with the IHM.

My last bug is with the level meter. After a while (from few milliseconds to few seconds), it freezes and don't update anymore. So, here is a (simplified) version of it. I added the runnable part to test and reproduce the bug. Of course, this bug appears sooner when I add other graphical components which also need to refresh very frequently. For example, the frequency analyze is represented by a line-chart with something like 1000 points.

public class LevelMeter2 extends Parent implements Runnable {

    private IntegerProperty levelMeterHeight = new SimpleIntegerProperty();

    private Rectangle led;

    private IntegerProperty height = new SimpleIntegerProperty();
    private IntegerProperty width = new SimpleIntegerProperty();
    private DoubleProperty linearValue = new SimpleDoubleProperty();
    private Color backgroundColor=Color.BLACK;
    private double minLinearValue, maxLinearValue;

    public LevelMeter2 (int height2, int width2) {
        this.height.set(height2);
        this.levelMeterHeight.bind(height.multiply(0.9));
        this.width.set(width2);

        linearValue.set(1.0);
        minLinearValue = Math.pow(10, -60.0/100);
        maxLinearValue = Math.pow(10, 3.0/100)-minLinearValue;

        Rectangle levelMeterShape = new Rectangle();
        levelMeterShape.widthProperty().bind(width);
        levelMeterShape.heightProperty().bind(height);
        levelMeterShape.setStroke(backgroundColor);

        this.getChildren().add(levelMeterShape);

        led = new Rectangle();
        led.widthProperty().bind(width.multiply(0.8));
        led.translateXProperty().bind(width.multiply(0.1));
        led.heightProperty().bind(levelMeterHeight.multiply(linearValue));
        led.setFill(Color.AQUA);
        Rotate rotate = new Rotate();
        rotate.pivotXProperty().bind(width.multiply(0.8).divide(2));
        rotate.pivotYProperty().bind(height.divide(2));
        rotate.setAngle(180);
        led.getTransforms().add(rotate);
        this.getChildren().add(led);
        }

    public double convertdBToLinearValue (double dB) {
        return ((double)Math.round(100 * ((Math.pow(10, dB/100)-minLinearValue)/maxLinearValue)) ) /100   ;
        //return (Math.pow(10, dB/100)-minLinearValue)/maxLinearValue;
    }

    public double convertLinearValueTodB (double linearValue) {
        return 100*Math.log10(linearValue*maxLinearValue+minLinearValue);
    }

    public void setValue (double dB) {
        if (dB>3) {
            dB=3;
        }   
        linearValue.setValue(convertdBToLinearValue(dB));
    }

    @Override
    public void run() {
        int i = 0;
        double value=-20;
        while (i<1000) {
            setValue(value);
            value = (Math.random()-0.5)*10+value;
            if (value>3) {
                value=3;
            }
            if (value<-60) {
                value=-60;
            }
            i++;
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("END OF WHILE");
    }
}

And a "Main" to test it :

public class MainGraph extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        HBox pane = new HBox();
        LevelMeter2 levelMeter = new LevelMeter2(300,30);
        Thread t = new Thread(levelMeter);
        pane.getChildren().add(levelMeter);

        t.start();
        Scene scene = new Scene(pane, 300, 300);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Test IHM");
        primaryStage.setOnCloseRequest( event -> {
            System.out.println("FIN");
            System.exit(0);     
        });
        primaryStage.show();
    }    
}

What's wrong with my code ? How can I write a more robust code that will allow me high refresh rates of my IHM ? Or how can I prevent from freezing ?

Thank you for you help.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I would suggest you move away from Threads and use something from JavaFX Animation package. In this example Timeline is used. This code is set to run at a rate of about 60 fps. You can adjust that using Duration.millis().

>Main

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication342 extends Application
{

    @Override
    public void start(Stage primaryStage)
    {

        LevelMeter2 levelMeter = new LevelMeter2(300, 30);

        Button button = new Button("Start");
        button.setOnAction((event) -> {
            switch (button.getText()) {
                case "Start":
                    levelMeter.startAnimation();
                    button.setText("Stop");
                    break;
                case "Stop":
                    levelMeter.stopAnimation();
                    button.setText("Start");
                    break;
            }
        });

        HBox pane = new HBox(levelMeter, button);
        Scene scene = new Scene(pane, 300, 300);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Test IHM");
        primaryStage.setOnCloseRequest(event -> {
            System.out.println("FIN");
            System.exit(0);
        });
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}

LevelMeter2

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Parent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;

public final class LevelMeter2 extends Parent
{

    private final IntegerProperty levelMeterHeight = new SimpleIntegerProperty();

    Timeline timeline;
    double value = -20;

    private final Rectangle led;

    private final IntegerProperty height = new SimpleIntegerProperty();
    private final IntegerProperty width = new SimpleIntegerProperty();
    private final DoubleProperty linearValue = new SimpleDoubleProperty();
    private final Color backgroundColor = Color.BLACK;
    private final double minLinearValue;
    private final double maxLinearValue;

    public LevelMeter2(int height2, int width2)
    {
        this.height.set(height2);
        this.levelMeterHeight.bind(height.multiply(0.9));
        this.width.set(width2);

        linearValue.set(1.0);
        minLinearValue = Math.pow(10, -60.0 / 100);
        maxLinearValue = Math.pow(10, 3.0 / 100) - minLinearValue;

        Rectangle levelMeterShape = new Rectangle();
        levelMeterShape.widthProperty().bind(width);
        levelMeterShape.heightProperty().bind(height);
        levelMeterShape.setStroke(backgroundColor);

        this.getChildren().add(levelMeterShape);

        led = new Rectangle();
        led.widthProperty().bind(width.multiply(0.8));
        led.translateXProperty().bind(width.multiply(0.1));
        led.heightProperty().bind(levelMeterHeight.multiply(linearValue));
        led.setFill(Color.AQUA);
        Rotate rotate = new Rotate();
        rotate.pivotXProperty().bind(width.multiply(0.8).divide(2));
        rotate.pivotYProperty().bind(height.divide(2));
        rotate.setAngle(180);
        led.getTransforms().add(rotate);
        getChildren().add(led);

        timeline = new Timeline(new KeyFrame(Duration.millis(16), (event) -> {
            setValue(value);

            value = (Math.random() - 0.5) * 10 + value;
            if (value > 3) {
                value = 3;
            }
            if (value < -60) {
                value = -60;
            }

        }));
        timeline.setCycleCount(Timeline.INDEFINITE);
    }

    public double convertdBToLinearValue(double dB)
    {
        return ((double) Math.round(100 * ((Math.pow(10, dB / 100) - minLinearValue) / maxLinearValue))) / 100;
    }

    public double convertLinearValueTodB(double linearValue)
    {
        return 100 * Math.log10(linearValue * maxLinearValue + minLinearValue);
    }

    public void setValue(double dB)
    {
        if (dB > 3) {
            dB = 3;
        }
        linearValue.setValue(convertdBToLinearValue(dB));
    }

    public void startAnimation()
    {
        timeline.play();
    }

    public void stopAnimation()
    {
        timeline.stop();
    }
}

Multiple LevelMeters Example:

Main

import java.util.ArrayList;
import java.util.List;
import javafx.animation.ParallelTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication342 extends Application
{

    @Override
    public void start(Stage primaryStage)
    {
        List<LevelMeter2> levelMeter2s = new ArrayList();
        List<Timeline> metersTimelines = new ArrayList();
        for (int i = 0; i < 9; i++) {
            LevelMeter2 levelMeter2 = new LevelMeter2(300, 30);
            levelMeter2s.add(levelMeter2);
            metersTimelines.add(levelMeter2.getTimeline());
        }

        ParallelTransition parallelTransition = new ParallelTransition();
        parallelTransition.getChildren().addAll(metersTimelines);

        Button button = new Button("Start");
        button.setOnAction((event) -> {
            switch (button.getText()) {
                case "Start":
                    parallelTransition.play();
                    button.setText("Stop");
                    break;
                case "Stop":
                    parallelTransition.stop();
                    button.setText("Start");
                    break;
            }
        });

        HBox hBox = new HBox();
        hBox.getChildren().addAll(levelMeter2s);

        VBox vBox = new VBox(hBox, new StackPane(button));
        Scene scene = new Scene(vBox, 300, 350);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Test IHM");
        primaryStage.setOnCloseRequest(event -> {
            System.out.println("FIN");
            System.exit(0);
        });
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}

LevelMeter2

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Parent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;

public final class LevelMeter2 extends Parent
{

    private final IntegerProperty levelMeterHeight = new SimpleIntegerProperty();

    Timeline timeline;
    double value = -20;

    private final Rectangle led;

    private final IntegerProperty height = new SimpleIntegerProperty();
    private final IntegerProperty width = new SimpleIntegerProperty();
    private final DoubleProperty linearValue = new SimpleDoubleProperty();
    private final Color backgroundColor = Color.BLACK;
    private final double minLinearValue;
    private final double maxLinearValue;

    public LevelMeter2(int height2, int width2)
    {
        this.height.set(height2);
        this.levelMeterHeight.bind(height.multiply(0.9));
        this.width.set(width2);

        linearValue.set(1.0);
        minLinearValue = Math.pow(10, -60.0 / 100);
        maxLinearValue = Math.pow(10, 3.0 / 100) - minLinearValue;

        Rectangle levelMeterShape = new Rectangle();
        levelMeterShape.widthProperty().bind(width);
        levelMeterShape.heightProperty().bind(height);
        levelMeterShape.setStroke(backgroundColor);

        this.getChildren().add(levelMeterShape);

        led = new Rectangle();
        led.widthProperty().bind(width.multiply(0.8));
        led.translateXProperty().bind(width.multiply(0.1));
        led.heightProperty().bind(levelMeterHeight.multiply(linearValue));
        led.setFill(Color.AQUA);
        Rotate rotate = new Rotate();
        rotate.pivotXProperty().bind(width.multiply(0.8).divide(2));
        rotate.pivotYProperty().bind(height.divide(2));
        rotate.setAngle(180);
        led.getTransforms().add(rotate);
        getChildren().add(led);

        timeline = new Timeline(new KeyFrame(Duration.millis(25), (event) -> {
            setValue(value);

            value = (Math.random() - 0.5) * 10 + value;
            if (value > 3) {
                value = 3;
            }
            if (value < -60) {
                value = -60;
            }

        }));
        timeline.setCycleCount(Timeline.INDEFINITE);
    }

    public double convertdBToLinearValue(double dB)
    {
        return ((double) Math.round(100 * ((Math.pow(10, dB / 100) - minLinearValue) / maxLinearValue))) / 100;
    }

    public double convertLinearValueTodB(double linearValue)
    {
        return 100 * Math.log10(linearValue * maxLinearValue + minLinearValue);
    }

    public void setValue(double dB)
    {
        if (dB > 3) {
            dB = 3;
        }
        linearValue.setValue(convertdBToLinearValue(dB));
    }

    public void startAnimation()
    {
        timeline.play();
    }

    public void stopAnimation()
    {
        timeline.stop();
    }

    public Timeline getTimeline()
    {
        return timeline;
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...