JavaFX AudioClip.play() - java

I am creating a JavaFX application that uses sounds for example for Button hovering. I create an JavaFX AudioClip to create and play the sound. It works ok so far (meaning: I hear the sound).
When the play(); method is called, the sound is played immediatly. If I hover the button 10 times, I hear the sound 10 times.
BUT: in the background JavaFX is creating hundreds of threads when the play() method returns (several hundred for each call). I cannot even see what it actually is, because there are so many, Eclipse does not even show them properly (there is just a white area and a crazy jumping scrollbar up and down).
This causes a massive lag and I do not understand what JavaFX is doing here! It is always the same sound, so I do have cached it into a hashmap already, but the problem is not instantiating the AudioClip, it is clearly stacking up when the play() method returns.
I have been looking into this for hours, but I can't figure out a workaround to reduce the lag (other than maybe reduce the size of the soundfiles, which I did).
final AudioClip soundClip;
if (audioClipCache.get(url.toString()) != null) {
// Log.info("Playing sound from cache...");
soundClip = audioClipCache.get(url.toString());
} else {
// Log.info("Caching sound...");
soundClip = new AudioClip(url.toString());
audioClipCache.put(url.toString(), soundClip);
}
Platform.runLater(new Runnable() {
#Override
public void run() {
soundClip.play(soundVolume);
}
});
Forget the hashmap to cache the AudioClips for a moment, that does not make any difference whatsoever. So called the following code, say, 10 times in a row, leads Java to go crazy for about 10 seconds.
AudioClip soundClip = new AudioClip(url.toString());
soundClip.play(soundVolume);
That works as it should (as in 5.000.000 examples across the internet), but it produces Threads (and Lag) like crazy after the play(); method is called (several hundred threads per hover / call (as descibed)).

I think you have a problem elsewhere in your code. I've distilled your description down to this (admittedly simplistic) example:
#Override
public void start(Stage arg0) throws Exception
{
Thread.sleep(10000);
AudioClip audioClip = new AudioClip(Paths.get("src/main/resources/stackoverflow/audio/alert.wav").toUri().toString());
for (int i = 0; i < 100; i++)
{
int volume = i;
Platform.runLater(() -> audioClip.play(volume));
Thread.sleep(10);
}
arg0.show();
}
Watching Java Mission Control at the ten second mark, I see 100 threads get created all at once. However, they all immediately die (they've played the short alert sound and are done). So the "Live Thread" graph spikes up by 100 threads and then drops right back to where it was within a couple of seconds.
Is there something elsewhere in your code that is holding onto a resource or a thread reference? That's my best suggestion for trying to find out why you're multiplying.

Related

Avoiding short stutter in animation when loading in/displaying FXML

When loading in a new FXML and setting the center of a BorderPane there is a brief 'freeze' of the application where existing animation, whether from a Timeline, or from a gif in an image view, will stop. I'm using this code to change the centerView:
#FXML
public void handleChangeView(ActionEvent event) {
Task<Parent> loadTask = new Task<>() {
#Override
public Parent call() throws IOException {
String changeButtonID = ((ToggleButton) event.getSource()).getId();
Parent newOne = getFxmls().get(changeButtonID);
if (newOne == null) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/" + changeButtonID + ".fxml"));
newOne = loader.load();
getFxmls().put(changeButtonID, newOne);
}
return newOne ;
}
};
loadTask.setOnSucceeded(e -> {
getMainUI().setCenter(loadTask.getValue());
});
loadTask.setOnFailed(e -> loadTask.getException().printStackTrace());
Thread thread = new Thread(loadTask);
thread.start();
}
And while this does the job in keeping the UI responsive in the load time, when the center stage displays for the first time there is a noticable lag. Looking at a CPU profile:
I'm not sure if the delay is from the initialize function running, or the loading of the elements. Here's a gif of the program, you can see the visible delay:
By loading it up in VLC and progresing frame-by-frame the delay looks to be 5 or 6 frames in a 30 fps video, meaning about a 200ms delay which implies the animation freezes for more than the 50ms or so initialize method. (Maybe the entire load method freezes the animation?)
The question is, is it possible to keep the animation smooth during this?
//********* EDIT **********//
So I went through the project and cut out as many methods and classes as possible, reducing the entire game to 6 minimal classes. I STILL have the delay in pressing the character button ('you' button). With such a minimal example I'm lost to what could be wrong. I've uploaded this to google drive for anyone to take a look.
https://drive.google.com/open?id=17A-PB2517bPJc8Dek-yp2wGXsjTyTj1d
There is nothing "wrong" with your code (at least in regards to this issue).
The issue you are experiencing also has nothing to do with the loading of the FXML (which is very slow and you have correctly handled off FX-Thread).
The stutter happens for These reasons:
relativly large hierarchy in character.fxml
lots of CSS (delete the main.css and you will notice; the stutter is slightly less prominent)
dynamically changing the scene graph (adding/removing Nodes during runtime)
Every time you replace the center of the mainView with some large Node, it causes the JavaFx runtime to completely re-layout and re-style (at least) that node. This happens on the FX-Thread, hence you notice the stutter.
One possible mitigation is a classic game dev technique: Pre-allocating as much as possible.
Simply load all necessary FMXLs once during startup and put them into the scene graph. In your click handlers then, change the visibility or (Z-)position of the Nodes you want to show/hide.
This is a good use case for a StackPane for example.
I adapted your code a little to demonstrate what I mean:
Prototype
Check out these ressources to learn more:
http://gameprogrammingpatterns.com/
https://stackoverflow.com/a/26537688/1271937
https://www.javaworld.com/article/2074652/core-java/javaone-2012-javafx-graphics-tips-and-tricks.html

JFrame white and unresponsive with a while loop

I am having troubles with programming a game called "Coup" and to explain what the problem is, i need to briefly explain what the game is about. Coup is a card game where players try to eliminate each other and be the last player left. To do this, players can choose a number of actions but they can also lie to other players. In this game, other players can call "bullshit" or block another players action. Because of this, i need to make Response windows which will let other players respond with a challenge, a block or do nothing. The result of the action is then dependent on the responses. For example, a player tries to get foreign aid. I can decide to block his action or to let him continue. There are consequences if i block but that isnt the problem here.
The real problem is this: I want to give all my players a chance to give a response (Block or do nothing) via a JFrame and I use a while loop to wait for all players responses. However, this while loop doesnt do anything except keep the method from progressing since the result depends on players reactions. With the while loop, the JFrame I constructed appears white and doesnt contain the two buttons i made. At this point i can even press the close button and it wont respond due to the while loop I made which is dependent on my responseless window. Is there anyway to fix this?
if (turnCount == players[currentPlayerNum].getPlayerNum()){
for(int i=0; i<players.length;i++){
//If it is not the players turn and they have one or two lives, make response window
if (players[i].getPlayerNum() != turnCount){
if (players[i].getLives() != 0){
//foreignAidBlock constructs the response window
players[i].foreignAidBlock(turnCount);
}
else{
//Not applicable since out of game/not in game
players[i].setResponse("NA");
}
}
else{
//Not applicable since out of game/not in game
players[i].setResponse("NA");
}
}
//Idea: PAUSE TILL RESPONSES COME IN
while(players[0].getResponse() != null || players[1].getResponse() != null || players[2].getResponse() != null || players[3].getResponse() != null)
{
System.out.println(players[0].getResponse());
System.out.println(players[1].getResponse());
System.out.println(players[2].getResponse());
System.out.println(players[3].getResponse());
}
The result i get from running this part of the code is:
"NA
null
NA
NA"
continuously. I use a null to represent a player that hasnt answered yet. At the end of the method, I reset all of my players abilities back to null.
All my response window will do is change the response of the player from null to "B" or "N". But without access to the buttons I cannot do this. Does anybody have any tips on how to use something to "pause" my thread besides using a while loop?
As Abdul Fatir mentioned, it is because the UI thread is being blocked.
I would however not reccomend directly messing with threads. There is a SwingWorker class exactly for this scenario and it is reccommended you use this.
For more info check this blog article - Swing Worker Example
You can also find some useful info in the following SO question - How do I use SwingWorker in Java
Here is quick use example from the official javadoc:
Say we want to find the "Meaning of Life" and display the result in a JLabel.
final JLabel label;
class MeaningOfLifeFinder extends SwingWorker<String, Object> {
#Override
public String doInBackground() {
return findTheMeaningOfLife();
}
#Override
protected void done() {
try {
label.setText(get());
} catch (Exception ignore) {
}
}
}
(new MeaningOfLifeFinder()).execute();
The while loop is blocking your UI Thread. Perform your while loop checks in a different Thread.
Thread t = new Thread(new Runnable(){...});
t.start();
This way your UI won't be blocked and controls will remain responsive.

Java graphics not updating correctly

In my program, a the graphics on my screen are supposed to move across the screen. I have a loop that calls the draw function(the draw function is display.draw()), but whenever the amount of times the loop runs is greater than one, the display doesn't update after each time like it should. Instead, it waits until the loop finishes to update the display.
below is the refreshing function that only apparently runs at the end.
public void refresh()
{
myPanel.removeAll();
myPanel.add(display.draw());
myPanel.validate();
myPanel.repaint();
}
And here's the loop. I added a 1 second sleep after each iteration to make sure it just wasn't moving faster than I could see.
for(int i = 0; i < 2; i++)
{
myGraphics.rearrange();
this.refresh();
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
}
}
myGraphics.rearrange() simply changes the values of the variables which the drawing function uses,
changing the x and y variables for the positions of all the objects.
What is happening that it's not updating?
You're calling Thread.sleep(...) in a Swing program on the Swing event thread. What this does is put the entire application to sleep and so is not something that you should be doing. Instead use a Swing Timer to drive your animation.
For example:
Similar question 1
Similar question 2
Similar question 3
For more specific help, consider posting more code, preferably an mcve.

Particles system, a delay in an array

I am learning java at the moment and trying to make different features and systems. Right now I am trying to make a really simple particle system, but I have problem.
public void eDelay()
{
try
{
Thread.sleep(17);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
public void engine()
{
int i = 0;
while (i < particles.size())
{
Particle a = (Particle) particles.get(i);
eDelay();
a.move();
i = i + 1;
}
}
public void actionPerformed(ActionEvent e)
{
engine();
repaint();
}
This is a part of my code where the particles suppose to move. It works fine and they do move, but I want them to move one after another one with a delay(17). However, they move all at once and not one after another.
(particles) is an arrayList with 50 elements in it, each element containst a position for a particle.
(a.move()) is just a movement method where particle moves up.
Don't worry for the rest of the code, the problem is in this part.
Graphics, positioning of the particles, movement of the particles, and other things work fine.
Going to make my comment an answer:
"Don't worry for the rest of the code, the problem is in this part. - Graphics, positioning of the particles, movement of the particles, and other things work fine."`
How can you say this when you know what the cause of your problem is? You could be creating a Swing application (which is part of the rest of the code) and calling Thread.sleep(...) on the event thread. So make no assumptions, tell us all of the story, show more pertinent code, and let's help you solve this.
Edit: I now see that this is in fact a Swing application and so that's likely your problem, although you don't show us where the Thread.sleep is called.Calling Thread.sleep(...) puts the thread that calls it asleep, and if this is the Swing event thread (or EDT), it will put your entire application to sleep -- not a good thing to do. So don't call Thread.sleep on the event thread and use a Swing Timer instead. The tutorials will show you how.
And of course again, don't make any assumptions.
You should call repaint in the engine() loop instead of actionPerformed().
Ok guys, this is actually a bit weird but I just rewrote the engine class again into a for loop and it works.
public void engine()
{
goingcrazy = goingcrazy + gameTimer.getDelay();
for(int i = 0; i < particles.size(); i++)
{
if (goingcrazy > 1000*i)
{
Particle a = (Particle) particles.get(i);
a.move();
}
}
}
I already posted a link for the full code so if you are interested you just need to change the old engine class with the one I just posted.

How can I get wait/sleep to work the way I want in java?

I'm trying to accomplish something very simple. First, load my layout (main.xml). Then wait 1 second, modify an image, wait 1 second and modify it to a third image. (My end goal is more complex, of course, but I can't even get this to work).
Basically, I get a black screen when the app loads. It stays that way until all the waiting is over, then it shows the final image. Here's my code:
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageCard[0] = (ImageView)findViewById(R.id.imageView11);
Thread.sleep(1000);
ImageCard[0].setImageDrawable(getResources().getDrawable(R.drawable.secondimage));
Thread.sleep(1000);
ImageCard[0].setImageDrawable(getResources().getDrawable(R.drawable.thirdimage));
ImageCard[0] is the image I'm trying to change. It loads in main.xml and shows fine if I remove the rest of the code.
If I change the sleep time to 5000, it then takes 10 seconds before it finally comes away from the black screen and shows the third image. This is how I know it's not just loading slow, but that it's actually waiting.
It's like it's not running sequentially. I'm no java expert, so I'm assuming I'm doing something dumb...
Thanks for any help!
I think you are blocking the UI Thead. Try Handler.postDelayed on a static Handler object.
Ok heres your problem, you can never do a sleep(...) when you are in the UIThread. The UIThread is never suppose to be locked up, it causes a lot of very bad things to happen in android. But there is a very easy way around it, just get off the UIThread and hop back on it when you need to. Heres what i would recommend:
public void onCreate(...)
{
super.onCreate(...);
myActivity.setContentView(R.layout.main);
new Thread(this).start();
}
public void run()
{
try
{
changeLayout(R.layout.main2);
Thread.sleep(5000);
changeLayout(R.layout.main3);
Thread.sleep(10000)
changeLayout(R.layout.main4);
}catch(Exception e){}
}
public void changeLayout(int id)
{
this.id = id;
myActivity.post(new Runnable()
{
public void run()
{
myActivity.setContentView(id);
}
});
}
private int id;
Of course with this example your class must implement Runnable to work. Only the UIThread can access the UI, no other thread can. Thats why you have to hop on and off the UIThread. Hope this worked!
Try adding ImageCard[0].invalidate() when you want it to draw.
I think Hovercraft Full of Eels is pointing you in the right direction. Essentially, you're not doing any multi-threading, you're telling the main thread to wait which means that it never completes the drawing. I'm not sure about Android, but Swing uses double-buffering by default (to avoid screen flashes), but that means that what is drawn, is actually drawn on to a buffer, not the window itself which is why you don't see anything. You could try disabling the double buffering (which Android is likely using) but that could cause other issues.
You might want to actually do multi-threading, or, I'm sure Android likely has a Timer component. If it does, I'd suggest you use it over Thread.sleep or actual multi-threading. Using a Timer you can have it fire an event after one second. That event will execute the other code.
do you have that code in constructor or in init() function? if yes, draw just the first picture and the Thread.sleep() function move after the place which the constructor or the init() function was called from.
then call repaint() function or something.

Categories

Resources