Panel Fails to Show Up Concurrently with Sound File In Java Swing

走远了吗. 提交于 2019-12-25 14:23:36

问题


After the dialog of my game finishes, a scary pop-up is suppose to show with a screeching noise. When I click the button (BPanel), the picture appears "corrupted" until the sound file finishes playing. After the scream finishes, the picture pops out.

Is it possible to simply synchronize the two together? Note that the problem is happening in the Woc class where Scaryface.png and the Sound class is used.

Main method:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Game {
    public static void main(String args[]) {
        Woc WineOrCheese = new Woc("Wine or Cheese");
        WineOrCheese.applyBackground("TitleBackground.png");
        WineOrCheese.applyButton("Play.png", 250, 200, 400, 200);
    }
}

Woc is the JFrame

import static java.lang.System.out;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


public class Woc extends JFrame {

private JFrame window;
private Woc.BPanel background;

private BufferedImage backgroundImg;
final int HEIGHT = 600;
final int WIDTH = 900;
private BufferedImage scaryImg;

public Woc(String text) {
    /*********************************
     * Sets up window. *
     ********************************/
    window = new JFrame(text);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setLayout(null);

    window.setVisible(true);
    window.setSize(WIDTH, HEIGHT);
    window.setLocationRelativeTo(null);
}

public void applyBackground(String ImgName) {
    try {
        backgroundImg = ImageIO.read(getClass().getResource(ImgName));
    } catch (IOException e) {
        out.println("No image detected");
    }

    background = new Woc.BPanel(backgroundImg, 0, 0, WIDTH, HEIGHT);

    window.add(background);
    background.setBounds(0, 0, WIDTH, HEIGHT);
}

public void applyButton(String ImgName, int x1, int y1, int width,
        int height) {
    BufferedImage buttonImg = null;
    try {
        buttonImg = ImageIO.read(getClass().getResource(ImgName));
    } catch (IOException e) {
    }

    Woc.BPanel button = new Woc.BPanel(buttonImg, x1, y1, width, height);

    window.add(button);
    button.setBounds(0, 0, WIDTH, HEIGHT);
    button.addMouseListener(new Clicker());

}

public static void play(String filename) {
    try {
        Clip clip = AudioSystem.getClip();
        clip.open(AudioSystem.getAudioInputStream(new File(filename)));
        clip.start();
    } catch (Exception exc) {
        exc.printStackTrace(System.out);
    }
}

private class BPanel extends JPanel {
    public BufferedImage img;
    public int x1, y1, width, height;

    public BPanel(BufferedImage img, int x1, int y1, int width, int height) {
        super();
        this.img = img;
        this.x1 = x1;
        this.y1 = y1;
        this.width = width;
        this.height = height;
        // this.setOpaque(false);
        this.setBackground(new Color(0, 0, 0, 0));
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, x1, y1, width, height, null);
    }
}

private class Clicker implements MouseListener {

    @Override
    public void mouseClicked(MouseEvent arg0) {

        //Dialog of game is here in the form of JOptionPane.

        applyBackground("Scaryface.png");
        for (int k = 0; k < 10; k++) {
            for (int z = 0; z < 10; z++) {
                out.println(".");
            }
        }

        Sound scary = null;
        try {
            scary = new Sound("scary.wav", window);
        } catch (Exception e) {
        }

    }

    @Override
    public void mouseEntered(MouseEvent arg0) {

    }

    @Override
    public void mouseExited(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseReleased(MouseEvent arg0) {
        // TODO Auto-generated method stub

    }

}

Sound class:

import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.*;

public class Sound {

    public Sound(String s, JFrame win) throws InterruptedException {
        Clip play = null;
        try {
            File in = new File(s);
            AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(in);
            play = AudioSystem.getClip();
            play.open(audioInputStream);
            FloatControl volume = (FloatControl) play.getControl(FloatControl.Type.MASTER_GAIN);
            volume.setValue(1.0f); // Reduce volume by 10 decibels.
            play.start();
            // Loop until the Clip is not longer running.
            // We loop this way to allow the line to fill, otherwise isRunning will
            // return false
            do {
                Thread.sleep(10);
            } while (play.isRunning());
            play.drain();
        } catch (UnsupportedAudioFileException | IOException | LineUnavailableException ex) {
            ex.printStackTrace();
        } finally {
            try {
                play.close();
            } catch (Exception exp) {
            }
        }
    }
}

By the way, are there any tips of how to make my game much easier to write? Any methods or classes that can improve and ease the pain I have to go through to? (Okay fine not real pain but it's still painful)


回答1:


do { 
    Thread.sleep(10); 
} while (play.isRunning());

and

play.drain();

Are blocking the Event Dispatching Thread with the aforementioned code which is preventing the UI from been updated. See Concurrency in Swing for more details

Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify

You shouldn't be using a MouseListener with buttons, but instead a ActionListener, there is more then one way to trigger a button. See How to Use Buttons, Check Boxes, and Radio Buttons and How to Write an Action Listeners for more details

If you want to know when the audio has finished, you should be using a LineListener, for example. With this, you can choose what to do when the audio has completed. Personally, I would pass the an instance of LineListener to your Sound class, as your Sound class shouldn't care about anything other then playing the sound

Without the while loop, the sound is unable to play. How do I resolve this issue because this was the only way I was able to play the sound

Don't close the Clip until after the audio has completed, for this I would use (another) LineListener.

The following example basically takes your Sound class and applies a LineListener to it. One is used to notified the interested party (the one who started the sound) about line events and one is used to monitor the clip internally and close it when it stops.

The example use a ReentrantLock and Condition to stop the code execution until the clip has completed, in this example, this will stop the JVM from terminating, but you don't need, I just used to provide a basic demonstration

import java.io.File;
import java.io.IOException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class TestAudio {

    public static void main(String[] args) {
        ReentrantLock lockWait = new ReentrantLock();
        Condition conWait = lockWait.newCondition();
        try {
            new Sound("...", new LineListener() {
                @Override
                public void update(LineEvent event) {
                    if (event.getType().equals(LineEvent.Type.STOP)) {
                        System.out.println("Line has stopped");
                        lockWait.lock();
                        try {
                            conWait.signal();
                        } finally {
                            lockWait.unlock();
                        }
                    }
                }
            });
            System.out.println("Waiting for audio to finish");
            lockWait.lock();
            try {
                conWait.await();
            } finally {
                lockWait.unlock();
            }
            System.out.println("Audio has finished");
        } catch (InterruptedException | LineUnavailableException | IOException | UnsupportedAudioFileException exp) {
            exp.printStackTrace();
        }
    }

    public static class Sound {

        private Clip play;

        public Sound(String s, LineListener listener) throws InterruptedException, LineUnavailableException, IOException, UnsupportedAudioFileException {
            play = null;
            File in = new File(s);
            AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(in);
            play = AudioSystem.getClip();
            play.addLineListener(new LineListener() {
                @Override
                public void update(LineEvent event) {
                    if (event.getType().equals(LineEvent.Type.STOP)) {
                        System.out.println("Audio stopped, closing clip");
                        play.close();
                    }
                }
            });
            play.addLineListener(listener);
            play.open(audioInputStream);
            FloatControl volume = (FloatControl) play.getControl(FloatControl.Type.MASTER_GAIN);
            volume.setValue(1.0f); // Reduce volume by 10 decibels.
            play.start();
        }
    }

}

For a more complex example, using Swing and Clip, have a look at Playing multiple sound clips using Clip objects



来源:https://stackoverflow.com/questions/34758718/panel-fails-to-show-up-concurrently-with-sound-file-in-java-swing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!