Java - Lag when playing short audio clip frequently - java

After some days of research on the internet, I came here looking for help. I'm currently developing a short 2D game for friends (and really just for fun), and I learned about Clips some days ago. In this game, the player can gather objects (almost like the coins in Mario). My problem is that I have a very short sound (~ 1 sec for 50kB) played when gathering a coin, and if the player gather let's say 3 coins in 1 second, then the Clip make the game lag. If the precedent Clip has ended, then there is no lag. But if the precedent Clip has not ended, then trying to play the Clip again make the game lag (very much).
I have a very small computer, not very powerful, but this problem is really annoying. I get the exact same problem with a sound when the player throws a weapon. I have a short clip, and if the player throws this weapon too fast, the game lag ...
Here is the things I already tried :
Use an array of clips (of this same sound), and play a clip that is not currently playing
Use different clips (of this same sound), and same as before
Make multiple copies (of this same sound), and load it in different clips
Make a separate thread for playing sounds, but I'm not comfortable at all with threads :/
But none of these change this problem ...
Now here is part of my code. First is the class that I use to load a sound into Clip.
package sound;
import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
// My own package
import utils.Utils;
public class SoundLoader {
public static Clip loadSound(String path) {
Utils.log("Loading " + path + " ... ");
try {
URL url = SoundLoader.class.getResource(path);
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
AudioFormat format = audioIn.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip clip = (Clip)AudioSystem.getLine(info);
clip.open(audioIn);
Utils.log("success\n");
return clip;
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
e.printStackTrace();
Utils.log("failed !\n");
return null;
}
}
}
Now is the class where I'll manage all sounds :
package sound;
import javax.sound.sampled.Clip;
// My own package
import principal.Handler;
public class SoundManager {
private Clip woosh;
private Clip coin1;
private Clip coin2;
public SoundManager(Handler handler) {
woosh = SoundLoader.loadSound("/resources/sounds/woosh2.wav");
coin1 = SoundLoader.loadSound("/resources/sounds/coin1.wav");
coin2 = SoundLoader.loadSound("/resources/sounds/coin2.wav");
}
public void wooshClip() {
startClip(woosh);
}
public void coin1Clip() {
startClip(coin1);
}
public void coin2Clip() {
startClip(coin2);
}
public synchronized void startClip(Clip clip) {
clip.stop();
clip.setFramePosition(0);
clip.start();
}
public void loopClip(Clip clip) {
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
public void stopClip(Clip clip) {
clip.stop();
}
}
And finally, when I want to play a Clip, I use the public method startClip() from my "Player" class (representing obviously the player).
handler.getSoundManager().wooshClip();
As I see my code, if the Clip is already playing, then the method starClip() should just stop it and replay it. Why is it lagging so much ? And why is it lagging even if I use different Clips ? What does make my game lag if I'm just stopping a Clip and replay it ?
I hope you will be able to help me ... It's rare that I don't find an answer on the web. If you need anything more to answer me just let me know !
Thanks for your time spent on this long question !
EDIT : I tried my game on a much powerful computer and there is absolutely no lag because of the audio clips ... But my question remains ! How could a 50kB clip make so much lag ?? I really don't understand the problem ...
Solution
I just discovered a solution while working on this project : now my entities that should play a sound when gathered by the player (like the sound of a coin in Mario) contain the audio clip of their "death". I don't understand exactly why it works now but it does ...

Use javafx.scene.media.AudioClip from JavaFX instead of javax.sound.sampled.Clip. AudioClip has lower latency.

Related

audio playing in constructor instead of play method

so I've been working on my game library and I just got working on the sound aspect of it. But the problems are that the Sound starts playing from the constructor instead of the play method and also the stop doesn't work for the constructor and only the play method.
I tried debugging the code but I didn't get any results from it. I also tried using the stop method before doing the play method but that didn't work either
below is the code,
import java.io.*;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.*;
// class stuff
private Clip clip;
private FloatControl fc;
public SoundLoader(File file) {
try {
InputStream audioSource = new FileInputStream(file);
InputStream bufferedInput = new BufferedInputStream(audioSource);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(bufferedInput);
AudioFormat baseFormat = audioInputStream.getFormat();
AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(),
baseFormat.getSampleSizeInBits(),
baseFormat.getChannels(),
baseFormat.getFrameSize(),
baseFormat.getFrameRate(),
false
);
AudioInputStream decodedAudioInputStream = AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
clip = AudioSystem.getClip();
clip.open(decodedAudioInputStream);
fc = (FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN);
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void play(boolean loop){
if(clip == null || isRunning())return;
stop();
while(!clip.isRunning())
clip.start();
if(loop)clip.loop(Clip.LOOP_CONTINUOUSLY);
}
and here is an example of what's happening in log form,
clip starts running from constructor
same clip starts running from play method
stop method stops the clip from running from the play method
constructor keeps on playing
If anyone knows why this is happening it would be nice if you could reply to this. Thanks
edit: I changed clip and fc to not static because I use testing something out with static and then I forgot to change it back to normal
okay I solved it. It was just a case of me blacking out while I was coding and I was playing the sound in the main method of the project. I'm glad that it wasn't a problem with the Clip class

Trouble playing the whole audio clip

I play sound effects (WAV files) in a game with javax.sound.sampled.Clip. However, the effect that has 2 seconds is not played entirely, only a part of it. If I add clip.loop(2) then the sound is played multiple times correctly, i.e. the whole 2 seconds. What is wrong and how to fix it?
package com.zetcode;
import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JOptionPane;
public enum SoundEffect {
EXPLODE("resources/explosion.wav"),
HITGROUND("resources/impact.wav"),
CANNON("resources/cannon.wav");
private Clip clip;
SoundEffect(String soundFileName) {
try {
URL url = this.getClass().getClassLoader().getResource(soundFileName);
try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url)) {
clip = AudioSystem.getClip();
clip.open(audioInputStream);
}
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
JOptionPane.showMessageDialog(null, "Cannot play sound", "Error",
JOptionPane.ERROR_MESSAGE);
}
}
public void play() {
if (clip.isRunning()) {
clip.stop();
}
clip.setFramePosition(0);
clip.start();
}
static void init() {
values();
}
}
Are you aware and accounting for the fact that when a clip is played (via start() method), the thread playing the code is a daemon? Unlike regular threads, a daemon thread will not prevent a Java program from closing. If your program ends before the sound has finished executing, the daemon status thread also ends even if it is not finished.
Are you trying to run the sample code SoundClipTest or SoundEffectDemo? It looks to me like SoundClipTest is going to exit as soon as the program executes the start() method, whereas the SoundEffectDemo will persist and allow the sound to finish playing.
** Edit 1 **
Am just now noticing that you have made your SoundEffect an enum. I've never seen that approach used before.
One approach I've used is to create a class called GameSound, and have its constructor preload each Clip individually and hold them in memory, ready for playback. Then, I create a method or methods for their playback.
gameSound.playExplosion();
The GameSound class would have instance variables for each sound effect.
private Clip explosion;
private Clip cannon;
And GameSound constructor would have:
URL url = this.getClass().getResource("resources/explosion.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
explosion = AudioSystem.getClip();
explosion.open(ais);
and GameSound would have method:
public void playExplosion()
{
if (explosion.isRunning()) {
explosion.stop();
}
explosion.setFramePosition(0);
explosion.start();
}
If you prefer to have the play method take an argument and use that on a switch structure to select which sound to play, that is fine, too, especially as it eliminates some code duplication.
This approach might work better (easy to add to GameSound as code gets more complex), and shouldn't be too difficult to code. I'd be curious to learn whether or not changing to this plan makes the problem go away or not.
** Edit 2 **
Sorry for mistakes in the above code. I fixed them when I made the following example. One thought, though, when I went to listen to the sound effects you linked, several of the explosions were kind of truncated to begin with. Have you played the sounds in another app (like Windows Groove Music, or from the website where you got them) and verified that they are different when you play them in your code?
Here is a quickie "GameSound". I downloaded the Explosion+2.wav file from your reference as it was longer than the others and has a clear ending, and am referencing that.
package stackoverflow.janbodnar;
import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
public class GameSound {
private final Clip explosion;
public GameSound() throws LineUnavailableException,
IOException, UnsupportedAudioFileException
{
URL url = this.getClass().getResource(
"resources/Explosion+2.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
explosion = AudioSystem.getClip();
explosion.open(ais);
}
public void playExplosion()
{
if (explosion.isRunning()) {
explosion.stop();
}
explosion.setFramePosition(0);
explosion.start();
}
}
And here is a simple Swing button that calls GameSound.
package stackoverflow.janbodnar;
import java.io.IOException;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class GameSoundTest extends JFrame
{
private static final long serialVersionUID = 1L;
private final GameSound gameSound;
public GameSoundTest() throws LineUnavailableException,
IOException, UnsupportedAudioFileException
{
gameSound = new GameSound();
JButton button = new JButton("Play Explosion");
button.addActionListener(e -> gameSound.playExplosion());
getContentPane().add(button);
}
private static void createAndShowGUI()
{
JFrame frame;
try
{
frame = new GameSoundTest();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
frame.setBounds(0, 0, 200, 200);
frame.setVisible(true);
}
catch (LineUnavailableException | IOException
| UnsupportedAudioFileException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}
This all assumes, of course, that there is a subfolder called "resources" with the explosion SFX in it.

CheckBox sound stopping

Music works just fine when i click on the box, but music won't stop when I click on it again. Then if i click on the unchecked box again, the music plays again so 2 times at once! Please help me with stopping the music!
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import sun.audio.AudioPlayer;
import sun.audio.AudioStream;
private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {
InputStream inputStream = getClass().getResourceAsStream("panda - desiigner (donald trump remix).au");
AudioStream audioStream = null;
try {
audioStream = new AudioStream(inputStream);
} catch (IOException ex) {
Logger.getLogger(kekFrame.class.getName()).log(Level.SEVERE, null, ex);
}
int check;
if(jCheckBox1.isSelected() == true){
check = 1;
} else {
check = 0;
}
switch (check) {
case 1 : AudioPlayer.player.start(audioStream);
System.out.println("Music has started playing");
break;
case 0 : AudioPlayer.player.stop(audioStream);
System.out.println("Music has stopped playing");
break;
}
}
/**
* #param args the command line arguments
*/
// Variables declaration - do not modify
private javax.swing.JCheckBox jCheckBox1;
Assuming you have properly created a Clip, and can access both it and a boolean (isSelected, say) that reflects the state of the JCheckBox, the following simple code should work:
if (isSelected)
{
clip.setFramePosition(0);
clip.start();
}
else
{
clip.stop();
}
This can be included in the JCheckBox's ActionListener.
More information about using Clips can be found on the Java Tutorial's "Audio Thread" with specifics about the Clips here. There are clearer examples elsewhere if you do a search on how to use a Java Clip. The official Java Tutorial for audio emphasizes background and high-level concepts at the expense of practical examples, imho, which makes for difficult reading for those of us who are just starting out.
sun.audio.AudioPlayer is no longer supported! Even if it works on your PC, there is no guarantee it will work on other systems. Unfortunately, this is a situation where obsolete code examples live on in blogs and unofficial tutorials, which can happen a lot as languages evolve and the parties that wrote the tutorials don't update or maintain their posts.
In response to the OP's request, here is an example adapted from one of my JavaFX gui's. I don't use Swing anymore and am not keen to go back.
In the code where the JavFX Button is being built:
btnPlay = new Button("Play");
btnPlay.setOnAction(e -> handlePlay(e));
This calls the following method:
private void handlePlay(ActionEvent e)
{
if (!isPlaying)
{
clip.setFramePosition(0);
clip.start();
((Button)e.getSource()).setText("STOP");
isPlaying = true;
}
else
{
clip.stop();
((Button)e.getSource()).setText("PLAY");
isPlaying = false;
}
}
In this code, isPlaying is an instance variable that in this case merely tells us whether the button is on or off. The clip may very well play to its end and stop on its own while the button is still in a "playing" state. It would require wiring up a LineListener to have the button toggle back when the clip finishes playing.
Maybe you can adapt the above into something useful? It seems to me the choice of JCheckBox is dubious, and a JToggleButton might be a better choice. Examples of writing Listeners for Swing Buttons can be found here.

Cannot Figure Out Why Audio File Is Not Playing (Java)

Kind of a noob to coding, but I've been creating Pacman for a computer science class, and for some reason, I cannot get my audio to work.
Basically, here is the just of what I have written.
public void paint(Graphics g)
{
PlayAudio();
//.... a lot of other stuff
}
My PlayAudio method:
public void PlayAudio()
{
try
{
Clip clickClip = AudioSystem.getClip();
File filePath = new File("opening.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(filePath);
clickClip.open(ais);
clickClip.start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
It compile and executes, but the audio file does not play. Thanks for the help!
I have confirmed that my opening.wav:
Is a .wav file.
Has the right name.
Is in the same directory.
Java 7 include new classes to play audio. Media and MediaPlayer. Example:
String media = "media.mp3";
Media hit = new Media(media);
MediaPlayer mediaPlayer = new MediaPlayer(hit);
mediaPlayer.play();
you need this libreries :
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

Java: How to implement a queue system to play audioclips?

First question/post on here, hopefully I've done it right!
Using java, I need a method to somehow add audio files to a queue and play the next file once the last one has finished because at the minute they just play over the top of each other. I am using Audiosystem to play the sound files.
I thought of using an array to store the sound clips waiting to be played but got stumped and didn't know where to go from there.
Hopefully someone can help, thanks.
import javax.sound.midi.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class MidiPlayer{
public static void main(String[] args) {
try {
Sequencer sequencer = MidiSystem.getSequencer();
if (sequencer == null)
throw new MidiUnavailableException();
sequencer.open();
FileInputStream is = new FileInputStream("music.mid");
Sequence Seq = MidiSystem.getSequence(is);
sequencer.setSequence(Seq);
sequencer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
here is a sample code that shows you how to play MIDI files in your java program, hope it helps

Categories

Resources