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.
I would suggest you move away from
Threads
and use something fromJavaFX
Animation
package. In this exampleTimeline
is used. This code is set to run at a rate of about 60 fps. You can adjust that usingDuration.millis()
.Multiple LevelMeters Example:
Your implementation of
run()
appears to be updating the scene graph from a background thread. As discussed in Concurrency in JavaFX:Instead, use a
Task
, illustrated here and here. Your implementation ofcall()
can collect data asynchronously and notify the GUI of the current state viaupdateValue()
. YourvalueProperty()
listener can then invokesetValue()
safely. Because "Updates are coalesced to prevent saturation of the FX event queue," your application will perform satisfactorily even on older hardware.Alternatively, if your audio source is one of the supported
Media
types, thisAudioBarChartApp
updates the data model of aBarChart
in anAudioSpectrumListener
registered with the correspondingMediaPlayer
. The image below displays pink noise.