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.
Related
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 have an app that connects reads file on remote server. File dynamically updates that's why I use Timer class to reread this file periodically.
Workflow is the following:
Open window where text will be displayed.
Start reading file (reread once per 15 sec using Timer)
In 15 seconds window is filled with data or I receive exceptions in log. Exceptions are suppressed and I continue trying to read data.
Exceptions are my problem, because user doesn't know what is happening now with an app.
There are at least two Exceptions I ran at:
- If file is absent, I receive FileNotFoundException.
- If server is on maintenance I receive other Exception (I catch it, so its name doesn't matter).
Here is how above looks like in code:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final RemoteReader reader = new RemoteReader();
Timer timer = new Timer(15000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
reader.getMainPanel().readData();
} catch (IOException e) {
//Here is a counter that increases after each exception throw
if(counter >5) {
JOptionPane.showOptionDialog(chat,
e.getMessage(),
e.getClass().getName(),
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.INFORMATION_MESSAGE,
null,
new String[]{"Retry", "Cancel"}, //on Retry - make another 5 tries to read file, on cancel - close the window
null);
counter = 0;
}
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
});
timer.start();
}
});
}
public String readData() throws IOException {
// read and process data before returning it
// but for test purpose:
//BufferedReader br = new BufferedReader(new InputStreamReader(this.url.openStream()));
throw new IOException("cannot read file");
}
What I want to do is to add JProgressBar. On openning the main window progress bar appears and then data is read. If IOException throws 5 times in a row, show option dialog. Otherwise hide progress bar and show data. If remote file becomes unavailable, show option dialog. And pressing the retry button, show progress bar... then workflow starts from the very beginning.
Some code examples would help me, but I don't expect solution for the whole issue - advice, how it should be done in right way from design point of view will be enough. Samples of Oracle a little bit vague for me.
Even if WatchService, seen here, is not available, I'd still use SwingWorker, seen here and here.
You have considerable latitude in pacing the doInBackground() thread, e.g. Thread.sleep(15 * 1000).
You can setProgress() in the background and listen for any PropertyChangeEvent in the GUI.
You can update any GUI components in process(), which runs on the EDT.
I'm using jLayer 1.0 to play a simple mp3 file. It works fine apart from the first 3 seconds or so (adding 5 seconds of silence to the start of the track fixes the issue). This is the code I'm using:
try
{
final Player player = new Player(getClass().getResourceAsStream("1.mp3"));
new Thread()
{
public void run()
{
try
{
player.play();
}
catch (Exception e) { System.out.println(e); }
}
}.start();
}
catch (Exception e)
{
System.out.println("Something went wrong!");
System.out.println(e);
}
I'm probably doing something stupid (I'm pretty sure I don't need to put the thread in the try block anyway) but it does play apart from the distortion.
Audio sample of distortion: http://www.filedropper.com/broken
Original track: http://www.filedropper.com/dennyschneidemessermodernwarstory
Music by: denny schneidemesser (by-nc-nd)
Any idea of what is is going on?
Issue completely fixed after updating to v1.0.1.
The version hosted on this page:
http://introcs.cs.princeton.edu/java/faq/mp3/mp3.html
Is outdated.
I have this Timer that is supposed to initiate various actions at 1 second intervals. It's a pretty simple idea that simulates a 5 second countdown (literally). At the start, a JLabel is updated to set its text to "5". Simultaneously, a little mp3 sound file plays that voices the number that the user sees on the screen. Then, one second later, the text is changed to "4" and a different mp3 plays that voices the number 4. And so on until we get to zero.
This all works, but I can't get the visual updating to synchronize precisely with the audio part. The mp3 always seems to play just slightly before the screen is updated. At first, I thought that I just needed to put a little extra silence at the beginning of each mp3 on a trial and error basis until things synched up. But no matter how much silence I prepend to each mp3, I still hear the audio before the screen updates. All that changes is the lag between each "one second" update.
Anyway, here is the code that I am working with. Can anyone help me get this to synchronize? Maybe I need a second timer? I'm not sure how that would work. Thanks in advance!
class Countdown extends JFrame implements ActionListener {
private Timer countdownTimer = new Timer(1000, this);
int countdownSeconds;
MyJFrame myFrame;
public Countdown(MyJFrame thisFrame) {
int countdownSeconds = 5;
countdownTimer.start();
myFrame = thisFrame;
}
#Override
public void actionPerformed(ActionEvent e) {
if (countdownSeconds == 0) {
myFrame.updateCountdown(myFrame, "Go");
SoundEffect.play("launch.mp3");
countdownTimer.stop();
} else {
myFrame.updateCountdown(myFrame, Integer.toString(countdownSeconds));
if (countdownSeconds == 5) {SoundEffect.play("five.mp3");}
if (countdownSeconds == 4) {SoundEffect.play("four.mp3");}
if (countdownSeconds == 3) {SoundEffect.play("three.mp3");}
if (countdownSeconds == 2) {SoundEffect.play("two.mp3");}
if (countdownSeconds == 1) {SoundEffect.play("one.mp3");}
countdownSeconds--;
}
}
}
public void updateCountdown(MyJFrame thisFrame, String numSec) {
lblCountdown.setText(numSec);
}
import java.io.FileInputStream;
import javazoom.jl.player.Player;
public class SoundEffect {
public static void play(String mp3File) {
try {
FileInputStream mp3_file = new FileInputStream(mp3File);
Player mp3 = new Player(mp3_file);
mp3.play();
}
catch(Exception e) {
System.out.println(e);
}
}
}
I highly doubt you will ever be able to sync those perfectly, but I can explain why the current approach will not work.
Swing components must be updated on the Event Dispatch Thread, as you do with the Timer. When you update the text of the label, it will schedule a repaint on the Event Dispatch Thread. Note the word schedule, and not perform.
However, the Event Dispatch Thread is currently busy playing your sound, so the actual repaint operation will only occur after you called mp3.play().
Now you could (if allowed, not sure about the threading rules for playing MP3's) try to play the mp3 on a different Thread (e.g. by using a secondary non-Swing timer). But since you never have full control over when the actual repaint is going to happen and only can control when the repaint is scheduled, the visual and auditive updates can still be out-of-sync.
The major part of the problem comes down to:
if (countdownSeconds == 5) {SoundEffect.play("five.mp3");}
..leading to..
public class SoundEffect {
public static void play(String mp3File) {
try {
FileInputStream mp3_file = new FileInputStream(mp3File);
Player mp3 = new Player(mp3_file);
mp3.play();
}
catch(Exception e) {
System.out.println(e);
}
}
}
Whoa! This is not the time to be loading the clips!
Instead they should be loaded before the timer ever starts. I think the file I/O is the real cause of the (perceptible) lag or delay.
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