I'm working on a 2D Game in Java and have the problem that the game's sounds will get cut off after playing for a couple of milliseconds when I run the game from an executable Jar file.
The sounds play correctly when I run the game from eclipse.
I load the sound files (.wav) like this:
AudioInputStream ais = AudioSystem.getAudioInputStream(this.getClass().getResource("sounds/sound.wav"));
AudioFormat af = ais.getFormat();
int size = (int) (af.getFrameSize() * ais.getFrameLength());
byte[] audio = new byte[size];
DataLine.Info info = new DataLine.Info(Clip.class, af, size);
ais.read(audio, 0, size);
sound= (Clip) AudioSystem.getLine(info);
sound.open(af, audio, 0, size);
I play the sounds later in the game like this:
public void playClip(final Clip clip, final int loop){
Runnable soundPlayer = new Runnable() {
Clip sound = clip;
#Override
public void run() {
try {
sound.setMicrosecondPosition(0);
sound.loop(loop);
} catch (Exception e) {
e.printStackTrace();
}
}
};
new Thread(soundPlayer).start();
}
Do you have any idea why the program behaves like that and how to fix that problem?
Thanks in advance for your help!
Related
I have sound effects for a game. Upon the game frame opening, the first sound lags behind. After that sound plays, no more lag is experienced.
Here's my clip player:
public enum SoundEffect
{
WALL("ping_pong_8bit_plop"),
PADDLE("ping_pong_8bit_beeep"),
POINT("ping_pong_8bit_peeeeeep");
public static enum Volume
{
MUTE, UNMUTE
}
public static Volume volume = Volume.MUTE;
private Clip clip;
SoundEffect (String file)
{
try
{
AudioInputStream inputStream = AudioSystem.getAudioInputStream(this.getClass().getResource(file+".wav"));
AudioFormat format = inputStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
clip = (Clip)AudioSystem.getLine(info);
clip.open(inputStream);
}
catch (UnsupportedAudioFileException uae)
{
uae.printStackTrace();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
catch (LineUnavailableException lue)
{
lue.printStackTrace();
}
}
public void play()
{
if (volume != Volume.MUTE)
{
if (clip.isRunning())
clip.stop();
clip.flush();
clip.setFramePosition(0);
clip.start();
}
}
static void init()
{
values();
}
}
So when I call SoundEffect.WALL.play() for example, it plays fine overall, but the very first time it plays there is a huge lag spike. What can I do to solve this, preferably still using Clips?
I had the same bug, the first play of a clip was about 1-2 sec delayed, no matter when the play()-method is called. What I did is I played a clip in the main() method which is a half seccond long and contains nothing (no sound). This means that the play() method of the actual sound isn't called the first time, and for me, it works. Hope it helped.
Use a separate Thread
Thread music = new Thread(new Runnable() {
#Override public void run() { your code }
};
music.start();
I use eclipse on linux Ubuntu and I have this code for loading image and setting it as background in one of my JPanels:
public class MenuState extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private GameStateManager gsm;
private int width;
private int height;
public MenuState(GameStateManager gsm)
{
this.gsm = gsm;
width = gsm.getWidth();
height = gsm.getHeight();
SizeManager sm = new SizeManager();
this.setLayout(new BorderLayout());
sm.set_size(this, width, height);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
BufferedImage background_image;
try {
background_image = ImageIO.read(new File("src/res/img/menu_background.png"));
g.drawImage(background_image, 0, 0, width, height, null);
} catch (IOException e) {
e.printStackTrace();
}
}
public void actionPerformed(ActionEvent e) {
}
and it works only in eclipse. When I export it to jar file or runnable jar file program does not show images. I also tried to use
this.getClass().getResource()
and similar codes but it does not work in eclipse. But I am maybe doing something wrong.
I also have this code in another class to play audio:
public class AudioManager {
private Clip clip;
public void play(String audio_name, boolean repeat)
{
try {
File audioFile = new File(audio_name);
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile);
AudioFormat format = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
clip = (Clip) AudioSystem.getLine(info);
clip.open(audioStream);
if(repeat)
{
clip.loop(clip.LOOP_CONTINUOUSLY);
}
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void stop()
{
clip.stop();
}
and it plays audio in eclipse but not after I export project.
I suppose File works only for files on disk and not for files in jar file but it is only way I made it works in eclipse.
So, what should I do?
And additional problem:
Sound played in eclipse is lagging, I improved it increasing available memory for eclipse but little lags occur when loading program and changing JPanels in JFrame by clicking on JButtons.
Thanks for any advice.
What String do you put into the getResource method ?
When you use getResource or getResourceAsStream, you must specify the package path. If your file is in the package "com.test.example", then you must put getResource("/com/test/example/my_file.png").
So, with your background image, you must load your image with :
background_image = ImageIO.read(this.getClass().getResource("/res/img/menu_background.png"));
You should do the same way with your audio file.
I also recommend you to load your image outside of the paintComponent method. Otherwise, each time your panel is repainted, the image is reloaded.
I have an application that plays audio files.
My problem is that only allows me to pause playback from the position that the music is currently in, but what I want is to pause and continuing playing from that point forward. So far, I could not make that change.
I want to move the slider from another position and be able to play the music from that position on the track.
File juntos = new File("C:/juntos.wav");
if(controlPlay){
//Sliderprogreso is bar Slider
Sliprogreso.setEnabled(true);
controlReproducir=true;
Btngrabar.setEnabled(false);
Btnguardar.setEnabled(false);
Btnplay.setText("Pause");
try {
AudioInputStream stream;
AudioFormat format;
DataLine.Info info;
stream = AudioSystem.getAudioInputStream(juntos);
format = stream.getFormat();
frames =stream.getFrameLength();
durationInSeconds = (frames+0.0) / format.getFrameRate();
System.out.println("duracion:"+durationInSeconds);
Sliprogreso.setMaximum((int) durationInSeconds);
info = new DataLine.Info(Clip.class, format);
clip = (Clip) AudioSystem.getLine(info);
clip.open(stream);
clip.start();
controlPlay=false;
TimerTask timerTask=new TimerTask(){
public synchronized void run() {
{
double timeNow=(durationInSeconds*clip.getFramePosition())/frames;
System.out.println("frames:"+frames);
System.out.println("clipframe:"+clip.getFramePosition());
System.out.println("time now");
Sliprogreso.setValue((int)Math.round(timeNow));
if((int)Math.round(timeNow)==(int)durationInSeconds){
System.out.println("se cancelo");
this.cancel();
Btnplay.setText("Play");
Btngrabar.setEnabled(true);
Btnguardar.setEnabled(true);
controlPlay=true;
}
}
}
};
timer.scheduleAtFixedRate(timerTask, 30, 30);
}
catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
else{
clip.stop();
timer.cancel();
Btnplay.setText("Play");
controlPlay=true;
Btngrabar.setEnabled(true);
Btnguardar.setEnabled(true);
}
Have you looked at the Clip API? There are methods for pausing and for setting the "playhead" to any position, using either frames or elapsed time.
From within your TimerTask:
if (positionOnSliderUpdatedByUser)
{
clip.stop();
int desiredMicrosecond = yourConversionFunction( sliprogreso.getValue() );
clip.setMicrosecondPosition(desiredMicrosecond);
clip.start();
positionOnSliderUpdatedByUser = false;
}
I'm not sure if I am reading your code correctly, especially with your lack of formatting. I'm taking it that you are updating the slider regularly with the song position, but that if the user changes the slider, then you want to move to that point in the song. Thus, there should be some sort of process to flag when the user changes the slider (as opposed to the song changing the slider). I'm using the flag name "positionOnSliderUpdatedByUser".
I have faced an error with playing sounds on button click in java. I have a field of buttons and if any button is pressed application plays a sound. But application throws me an exception:
javax.sound.sampled.LineUnavailableException: unable to obtain a line
Method look like this and is called when button was pressed:
public void playSound() {
try {
File file = new File("Sounds/sound.wav");
AudioInputStream hitStream = AudioSystem.getAudioInputStream(file);
AudioFormat format = hitStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(hitStream);
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
}
Sound play at the beginning then it crashes. I googled the exception, found that the clip should be closed after playing sound. I thought I should add lines after clip.start()
if (!clip.isRunning()) {
clip.stop();
clip.close();
}
But then the sounds don't play and whole application start to lag. What is the correct solution for this error?
ok so im working on my program which will play a song once a button is clicked, i have the code down, and my audio is playing when the button is clicked,but the only problem now is that my program is going completely bonkers and just playing the audio when the button is clicked!!!! the program just freezes the audio keeps playing but the program will not do it's other functions! What am i doing wrong? and how do i fix it?
heres the code for the action listeners button
in myFrame.java
sound sound = new sound();
private File pokemonBattle = new File(".\\audio\\"+"sound.wav");
private void dualButtonActionPerformed(java.awt.event.ActionEvent evt)
{
// TODO add your handling code here:
for (int i = 0; i<1; i++){
c = deck.getNextCard();
p1hand[i] = c;
p1.setCard(c); //ignore all this
c = deck.getNextCard();
p2hand[i] = c;
p2.setCard(c);
}
pokemon1.setEnabled(true);
pokemon2.setEnabled(true);
pokemon1.setIcon(p1hand[0].getImage()); //ignore all this as well
pokemon2.setIcon(p2hand[0].getImage());
textArea.setText("Pokemon 1:"+p1hand[0].toString()+"\nPokemon 2:
"+p2hand[0].toString());
p1ResultLabel.setText("");
p2ResultLabel.setText("");
//this is where im calling my audio
sound.playAudio(pokemonBattle);//this is where im calling my play audio method
}
sound.java where i have playAudio()
import java.io.File;
import javax.sound.sampled.*;
public class sound {
public void playAudio(File sf){
AudioFormat audioFormat;
AudioInputStream audioInputStream;
SourceDataLine sourceDataLine;
try
{
audioInputStream = AudioSystem.getAudioInputStream(sf);
audioFormat = audioInputStream.getFormat();
System.out.println(audioFormat);
DataLine.Info dataLineInfo = new
DataLine.Info(SourceDataLine.class,audioFormat);
sourceDataLine =(SourceDataLine)AudioSystem.getLine(dataLineInfo);
byte tempBuffer[]=new byte[100000];
int cnt;
sourceDataLine.open(audioFormat);
sourceDataLine.start();
while((cnt=audioInputStream.read
(tempBuffer,0,tempBuffer.length))!=-1){
if(cnt>0){
sourceDataLine.write(tempBuffer,0,cnt);
}
}
}
catch (Exception e)
{
e.printStackTrace();
System.exit(0);
}
}
}
You need to play the audio in a separate thread. The program becomes unresponsive because it is playing your audio on the event dispatch thread instead of drawing the UI and responding to user interaction.
instead of:
sound.playAudio(pokemonBattle);
do:
Thread t = new Thread( new SoundPlayer( sound, pokemonBattle );
t.start();
and also:
public class SoundPlayer implements Runnable
{
private final sound sound;
private final File soundFile;
public SoundPlayer( sound sound, File soundFile )
{
this.sound = sound;
this.soundFile = soundFile;
}
public void run()
{
sound.playAudio( soundFile );
}
}
Joseph is right. But in this instance the problem is
while((cnt=audioInputStream.read
(tempBuffer,0,tempBuffer.length))!=-1){
if(cnt>0){
sourceDataLine.write(tempBuffer,0,cnt);
}
}
You while loop will run infinitely hence freezing up your application. Since you are reading and writing the entire buffer at once there is no need for the while loop. Do this outside of a loop and you will be fine.