I used following logic to load the sound from a different thread to load it while my game goes on.
Though it works for very small wav files, sometimes, I have to wait till it loads.
How can I make sure that it is loaded beforehand?
public class MusicPlayer implements Runnable {
String sound;
Player p;
public MusicPlayer(String sound)
{
this.sound = sound;
InputStream is = getClass().getResourceAsStream("/"+sound);
try
{
p = Manager.createPlayer(is, "audio/X-wav");
}
catch(Exception e)
{}
}
public void start()
{
Thread t = new Thread(this);
t.start();
}
public void run()
{
try
{
p.start();
}
catch(Exception e)
{}
}
}
Most reliable solution is to create a progress bar and wait for the sounds to get loaded, so this will keep the user entertained a little as he can see something moving
Try
t.setPriority(Thread.MAX_PRIORITY);
Just in case anybody needs it, The best way I found is actually initialize the object in Main thread itself. If not the fastest, it will be the most reliable solution.
You could add a delay I.E Thread.sleep(100); this will delay the thread for 100 milliseconds
Related
I'm creating a program for a game that clicks in random intervals for X seconds with Y time between each click. Here is the code that does this.
try {
Util.autoCode rand = new Util.autoCode();
Robot robot = new Robot();
int NoC;
NoC = Integer.parseInt(this.numberOfClicksTF.getText().trim());
if (NoC == 0) {
while (NoC == 0) {
// robot.mousePress(InputEvent.BUTTON1_MASK);
System.out.println("Infinite Press");
Thread.sleep(rand.clickDelay());
System.out.println("Infinite Release");
// robot.mouseRelease(InputEvent.BUTTON1_MASK);
Thread.sleep(rand.interval());
break;
}
} else {
for (int i = 0; i < NoC; i++) {
//robot.mousePress(InputEvent.BUTTON1_MASK);
System.out.println("Click Press");
Thread.sleep(rand.clickDelay());
System.out.println("Click Release");
// robot.mouseRelease(InputEvent.BUTTON1_MASK);
Thread.sleep(rand.interval());
}
}
} catch (AWTException ex) {
Logger.getLogger(MainFrame.class
.getName()).log(Level.SEVERE, null, ex);
} catch (NumberFormatException ex) {
Logger.getLogger(MainFrame.class
.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
}
I commented out the mouse press and release just so I could do a check to make sure the timing was right, and that it would perform each click. This code is started by JButton in a JFrame. Whenever I press the start button, it starts the code and nothing but force closing it in Netbeans will stop it. The goal is to have a start button initiate, and a stop button interrupt the code, but not close the JFrame. I have been looking everywhere and haven't been able to find a straight answer.
Any help is welcomed and appreciated!
You need tu use SwingWorker, for example:
in your JFrame you may have a SwingWorker and it "builder" method:
private SwingWorker worker;
private SwingWorker getWorker() {
worker = worker == null ? worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
while (true) {
System.out.println("doInBackground!");
Thread.sleep(1000);
}
}
} : worker;
return worker;
}
now, in the swing main thread (used for visual components updates) you need to call the SwingWorker with your background task:
private void jButtonActionPerformed(java.awt.event.ActionEvent evt) {
if (getWorker().getState().equals(SwingWorker.StateValue.STARTED)) {
worker.cancel(true);
worker = null;
} else {
getWorker().execute();
}
}
You can click on the button as many times as you wish, the app will create and run a SwingWorker or kill and set to null the current SwingWorker.
Literally ran into your exact same problem this week trying to make almost the same thing. First, let me suggest reading up on multithreading in Java. It may seem complicated, but trust me it's necessary to understand why your program is not working. (I'll give a run down of what's happening below)
Essentially, your GUI is running on an Event Dispatcher Thread that needs to be separated from your robot. That is because when you call thread.sleep(), you essentially tell the GUI thread to sleep too, resulting in loss of GUI control. Start your robot and GUI in separate threads and have your JButtons invoke methods to get your robot thread to start and stop. For example:
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new GUI();
}
});
}
To create your GUI(I don't know what your GUI is like, so I just wrote new GUI() to substitute for how you created yours), that way it's in the thread it's supposed to be in. Then in your eventListener, spawn a new thread holding the robot object everytime the start button is pressed:
if(actionEvent.getSource().equals(playButton)) { //listening for the play button
if(robotThread == null) {
autoClicker= new AutoClicker();
Thread = new Thread(autoClicker);
Thread.start();
}
}
And initiate your robot object in the autoClicker() class like you did, and have autoClicker implement runnable.
public class AutoClicker implements Runnable{
private Robot robot;
public void run(){
try {
robot = new Robot();
} catch (AWTException e) {e.printStackTrace();}
//you need to learn about synchronization first
while(true){
synchronized(this){
//do clicks and stuff here
}
}
}
}
This way, your GUI and robots are in separate threads. In order to make JButtons start and stop your robot, you're going to need to learn synchronization to know how to properly multithread.
Another thing is that you should probably use the wait() method instead of sleep(), as sleep() would cause a lot more problems that's too much to go through in a single answer. Again, you need to learn concurrency/synchronization first.
If you want comment below and we can work out a way for me to DM you my code I'm working on right now, which is the exact same thing as you're trying to make. I can explain every part of it to you to help you on your way.
So I tried to implement a consumer - producer pattern in android the scenario is as such:
public class CameraPreview extends SurfaceView ... {
.......
public ArrayBlockingQueue<ByteBuffer> mProcessingQueue;
public CameraPreview(){
mProcessingQueue = new ArrayBlockingQueue<ByteBuffer>(10);
HandlerThread handlerThread = new HandlerThread("Image Processing Thread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable() {
#Override
public void run() {
new ImageProcessingThread().start();
}
});
}
public void onPreviewFrame(final byte[] data, Camera camera){
.......
if(!mProcessingQueue.offer(byteBuffer)) {
byteBuffer.clear();
Log.v("IMAGE_AVOIDED", count + "");
} else {
Log.v("IMAGE_PUSHED", count + "");
}
}
public void processImage(ByteBuffer image){
.. call to opencv jni function ..
}
public class ImageProcessingThread extends Thread{
int count = 0;
#Override
public void run() {
super.run();
while(mImageProcessingRunning){
try {
ByteBuffer image = mProcessingQueue.take();
Log.v("IMAGE_TAKEN", count++ + "");
processImage(image);
image.clear();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
However when I run this code .. it never produces the IMAGE_AVOIDED log statement .. it alternates between IMAGE_TAKEN and IMAGE_PUSHED .. one frame is pushed .. and one frame processed ..
I was hoping I'd be able to constantly place frames into mProcessingQueue and when the consumer thread was ready, to take from the queue .. this way I could drop frames if the queue was full and only place the latest frames into the queue as to get closer to real time ..
I'm sure its a synchronization problem .. maybe I'm running the same two threads in one process and therefore the scheduler is hoping back and forth through tasks ?
Any ideas / pointers ??
So it turns out that the above implementation is correct .. however I was doing some calculations in the onPreviewFrame function call .. which gave the consumer thread enough time to execute the work at hand .. Thanks all !
I don't have any code to show in particular to link because this is quite a general question...you see, I made a small game applet with 8 AudioClip variables, and these were supposed to play every time a certain action was taken by the user. Ex: Player clicks = Shooting sound plays. The thing is, the moment I added these AudioClip files my program just freezes terribly and becomes borderline unplayable if the freezing is particularly bad.
The simple way I did it is here (From the book, actually)
AudioClip playerShooting;
playerShooting=getAudioClip(getDocumentBase(),"PlayerShooting.wav");
Then I would just use the following whenever the user clicked:
playerShooting.play():
My program worked smoothly before I added these wav files. They aren't even that long. Where did I go wrong here, and, how can I remove the random freezing?
EDIT:
This is the code I am running:
public void mouseClicked(MouseEvent e)
{
if(playerAlive)
{
timeShotsAfter=System.currentTimeMillis();
if((timeShotsAfter-timeShotsBefore)>=100)
{
if(shotIndex<10)
{
playerShooting(); //Here is where I call the function
shotFiredX[shotIndex]=currentX;
shotFiredY[shotIndex]=currentY;
shotSize[shotIndex]=20;
}
if(shotIndex<10)
shotIndex++;
else
shotIndex=0;
timeShotsBefore=System.currentTimeMillis();
}
else{}
toggleShooting=false;
}
}
This is the function:
public void playerShooting()
{
new Thread(
new Runnable() {
public void run() {
try {
playerShooting.play();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
I was having the same problem a while back. What I did to solve my problem was update to JDK 8. In previous JDK versions, audio files appear to have been an afterthought and can be buggy. If you are still having problems, JDK 8 has the ability to play mp3 files which are significantly smaller than wav (you may want to try this). Make sure you use sound.stop() when your clips are done as this might free up some memory.
Play the audio clip in another thread?
EDIT:
new Thread(
new Runnable() {
public void run() {
try {
playerShooting.play():
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
Edit:
I'm not quite sure if this part is correct:
getAudioClip(getDocumentBase(),"PlayerShooting.wav");
Try adding System.out.println(getDocumentBase()); to see whether the location is correct.
I use Clips in a game. The clips play fine, but after some "shots", the following problem occurs
Exception in thread "PulseAudio Eventloop Thread" java.lang.IllegalStateException: drain failed
at org.classpath.icedtea.pulseaudio.EventLoop.native_iterate(Native Method)
at org.classpath.icedtea.pulseaudio.EventLoop.run(EventLoop.java:133)
at java.lang.Thread.run(Thread.java:724)
My code:
public static Clip[] sounds;
...
sounds = new Clip[3];
sounds[0] = getClip("gun.wav");
sounds[1] = getClip("click.wav");
sounds[2] = getClip("over.wav");
...
private void playSound(Clip clp) {
final Clip clip = clp;
Runnable soundPlayer = new Runnable() {
#Override
public void run() {
try {
if(clip.isActive() || clip.isRunning()) clip.stop();
clip.setMicrosecondPosition(0);
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
}
};
new Thread(soundPlayer).start();
}
public void shoot() { //runs when space is clicked
if(canShoot) playSound(sounds[0]);
}
So I had a similar problem on OS X, where the clip was sometimes not playing if you tried to stop it and restart it from the beginning. I fixed it by calling flush() right after stop():
if(clip.isActive() || clip.isRunning()) {
clip.stop();
clip.flush();
}
You shouldn't need to spawn a background thread to interact with the Clip. The most commonly used methods of Clip, like "start" and "stop" operate asynchronously, meaning they do not block, so it should be okay to call them from the GUI/AWT/Swing thread.
This page has some reasonable examples:
http://www3.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html
I see you are using IcedTea and PulseAudio. You may experience different behavior when using javax.sound in this JVM, as opposed to the Oracle JVM, since the implementations of javax.sound are significantly different between these two products.
I am trying to play an mp3 file on button press or selection from a list (which I have managed successfully). However, I cannot seem to stop the song being played multiple times on the same button press.
What I would like to do is play the song in a new thread, disable playing the song again until the thread has closed, then allow playing again.
My code is as follows:
public class SoundFactory {
private Player player;
private static boolean running = false;
private String getFile(String name) {
String f = "sound" + File.separator + name + ".mp3";
return f;
}
public void playMP3(String name) {
if (!running) {
running = true;
try {
FileInputStream fis = new FileInputStream(getFile(name));
BufferedInputStream bis = new BufferedInputStream(fis);
player = new Player(bis);
} catch (Exception e) {
System.out.println("Problem playing file " + name);
System.out.println(e);
}
// run in new thread to play in background
new Thread() {
public void run() {
try {
player.play();
} catch (Exception e) {
System.out.println(e);
}
}
}.start();
//running = false;
}
}
public void close() {
if (player != null) player.close();
}
}
The file is played via:
SoundFactory sf = new SoundFactory();
sf.playMp3("song name");
on a JButton click
I am new to threading so I apologise in advance if this has an obvious solution!
It sounds to me like you are getting multiple click events fired at once instead of just one. A little logging should verify this. Your method as is, is wide open to race conditions.
The two events can be so close together that when the one checks running it see !running as true. Before that one can do running = true, the second event also sees !running as true and enters the if clause. They then both set running to true and spawn a thread to play the mp3.
What you need to do is make your method synchronized.
public synchronized void playMP3(String name)
http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
If count is an instance of SynchronizedCounter, then making these
methods synchronized has two effects:
First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing
a synchronized method for an object, all other threads that invoke
synchronized methods for the same object block (suspend execution)
until the first thread is done with the object.
Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent
invocation of a synchronized method for the same object. This
guarantees that changes to the state of the object are visible to all
threads.
Just to clarify my last comment, here is a test program showing where running = false should be placed.
public class Test {
public static boolean running = false;
public synchronized void runner() {
if(!running) {
running = true;
System.out.println("I'm running!");
new Thread() {
public void run() {
for(int i=0; i<10000; i++) {} // Waste some time
running = false; // This is only changed once the thread completes its execution.
}
}.start();
} else {
System.out.println("Already running.");
}
}
public static void main(String[] args) {
Test tester = new Test();
tester.runner();
tester.runner(); // The loop inside the Thread should still be running so this should fail.
for(int i=0; i<20000; i++) {} // Waste even more time.
tester.runner(); // The loop inside the Thread should be done so this will work.
}
}
It outputs:
I'm running!
Already running.
I'm running!
It's been years since I've worked with Swing and had forgotten that its event dispatcher is single threaded. So your issue is more likely this than a race condition. It still doesn't hurt to get into writing things to be thread safe from the beginning as it gets you used to it and thinking that way.
Definite warning on using the synchronized method... It can be horrible on performance if only a small part of your method needs to be synchronized. In this case your whole method needs to be thread safe.
If only a small part needs to be thread safe you need to use synchronized blocks.
Thread safe per instance:
public class myClass {
public void myFunc() {
// bunch of code that doesn't need to be thread safe.
synchronized(this) {
// Code that needs to be thread safe per instance
}
// More code that doesn't need thread safety.
}
}
Thread safe across all instances.
public class myClass {
static Object lock = new Object();
public void myFunc() {
// bunch of code that doesn't need to be thread safe.
synchronized(lock) {
// Code that needs to be thread safe across all instances.
}
// More code that doesn't need thread safety.
}
}
Thread safe in a static method.
public class myClass {
public static void myFunc() {
// bunch of code that doesn't need to be thread safe.
synchronized(MyClass.class) {
// Code that needs to be thread safe.
}
// More code that doesn't need thread safety.
}
}
Probably way more information than you want, but I've just seen threaded programming taught so poorly many, many times.
You need to call JButton.setEnabled(false); right before you start playing the mp3, and then call JButton.setEnabled(true); when the mp3 finishes playing.
Obviously, you should replace JButton with your button's object (eg: playButton.setEnabled()).