JavaFX中的高刷新率
我正试图用均衡器、频率分析器和声级计来编写一个程序。模型部分似乎工作得很好,但我正在用IHM测试一些错误 我的最后一个错误是液位计。一段时间后(从几毫秒到几秒钟),它会冻结,不再更新。所以,这里是它的一个(简化)版本。我添加了可运行部分来测试和复制bug。当然,当我添加其他也需要频繁刷新的图形组件时,这个错误出现得更快。例如,频率分析由一个大约有1000个点的折线图表示JavaFX中的高刷新率,javafx,refresh,frame-rate,Javafx,Refresh,Frame Rate,我正试图用均衡器、频率分析器和声级计来编写一个程序。模型部分似乎工作得很好,但我正在用IHM测试一些错误 我的最后一个错误是液位计。一段时间后(从几毫秒到几秒钟),它会冻结,不再更新。所以,这里是它的一个(简化)版本。我添加了可运行部分来测试和复制bug。当然,当我添加其他也需要频繁刷新的图形组件时,这个错误出现得更快。例如,频率分析由一个大约有1000个点的折线图表示 public class LevelMeter2 extends Parent implements Runnable {
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");
}
}
我的代码怎么了?如何编写更健壮的代码,使IHM的刷新率更高?或者我如何防止冰冻
感谢您的帮助。您的
run()
实现似乎正在从后台线程更新场景图。如中所述:
JavaFX场景图…不是线程安全的,只能从UI线程(也称为JavaFX应用程序线程)访问和修改。在JavaFX应用程序线程上执行长时间运行的任务不可避免地会导致应用程序UI无响应。”
相反,请使用一个带插图的和。您的call()
实现可以异步收集数据,并通过updateValue()
通知GUI当前状态。然后,您的valueProperty()
侦听器可以安全地调用setValue()
。因为“合并更新以防止FX事件队列饱和,“即使在较旧的硬件上,您的应用程序也能令人满意地执行
或者,如果您的音频源是其中一种类型,则会更新使用相应的MediaPlayer
注册的中的数据模型。下图显示
private XYChart.Data[]系列1数据;
…
audioSpectrumListener=(双时间戳,双持续时间,
浮动[]幅度,浮动[]相位)->{
对于(int i=0;i
我建议您远离
线程,使用JavaFX
包中的内容。在本例中使用。此代码设置为以约60 fps的速率运行。您可以使用Duration.millis()调整该速率
液位计2
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.beans.property.DoubleProperty;
导入javafx.beans.property.IntegerProperty;
导入javafx.beans.property.SimpleDoubleProperty;
导入javafx.beans.property.SimpleIntegerProperty;
导入javafx.scene.Parent;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.scene.transform.Rotate;
导入javafx.util.Duration;
公共最终类LevelMeter2扩展父级
{
private final IntegerProperty levelMeterHeight=新的SimpleIntegerProperty();
时间线;
双值=-20;
专用最终矩形led;
私有最终IntegerProperty高度=新SimpleIntegerProperty();
private final IntegerProperty width=新的SimpleIntegerProperty();
private final DoubleProperty linearValue=新的SimpleDoubleProperty();
私有最终颜色backgroundColor=Color.BLACK;
私人最终双minLinearValue;
专用最终双maxLinearValue;
公共液位计2(内部高度2,内部宽度2)
{
此.height.set(高度2);
此.levelMeterHeight.bind(高度乘以(0.9));
此.width.set(width2);
线性值集(1.0);
minLinearValue=数学功率(10,-60.0/100);
maxLinearValue=Math.pow(10,3.0/100)-minLinearValue;
矩形levelMeterShape=新矩形();
levelMeterShape.widthProperty().bind(宽度);
levelMeterShape.heightProperty().bind(高度);
水平仪形状设定行程(背景色);
this.getChildren().add(levelMeterShape);
led=新矩形();
led.widthProperty().bind(width.multiply(0.8));
led.translateProperty().bind(width.multiply(0.1));
led.heightProperty().bind(levelMeterHeight.multiply(linearValue));
led.setFill(颜色为浅绿色);
旋转=新旋转();
rotate.pivotXProperty().bind(宽度.乘(0.8).除(2));
rotate.pivotYProperty().bind(height.divide(2));
旋转。设定角度(180);
led.getTransforms().add(旋转);
getChildren().add(led);
时间线=新时间线(新关键帧(持续时间.毫秒(16),(事件)->{
设置值(值);
值=(Math.random()-0.5)*10+值;
如果(值>3){
数值=3;
}
如果(值<-60){
值=-60;
}
}));
timeline.setCycleCount(timeline.unfinite);
}
公共双convertdBToLinearValue(双dB)
{
return((双精度)Math.round(100*((Math.pow(10,dB/100)-minLinearValue)/maxLinearValue))/100;
}
公共双转换器LinearValueToDB(双线arValueToDB)
{
返回100*Math.log10(linearValue*maxLinearValue+minLinearValue);
}
公共无效设置值(双dB)
{
如果(dB>3){
dB=3;
}
linearValue.setValue(convertdBToLinearValue(dB));
}
公共无效开始生效()
{
timeline.play();
}
公共void stopAnimation()
{
timeline.stop();
}
}
多个液位计示例:
主要
import java.util.ArrayList;
导入java.util.List;
导入javafx.animation.ParallelTransition;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.layout.HBox;
导入javafx.scene.layout.StackPane;
导入javafx.scene.layout.VBox;
导入javafx.st
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();
}
}
private XYChart.Data<String, Number>[] series1Data;
…
audioSpectrumListener = (double timestamp, double duration,
float[] magnitudes, float[] phases) -> {
for (int i = 0; i < series1Data.length; i++) {
series1Data[i].setYValue(magnitudes[i] + 60);
}
};
>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);
}
}
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();
}
}
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);
}
}
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;
}
}