0

I want to have a ****seperate Class**** to have the sounds and i want to call upon that class to play the sounds, but if there is a better way, pls tell me.


Story (can be skipped):

First i used AudioClips that were stored in final statics that i called everytime i needed the sound to play. For single stuff like the background music, this was perfect, but if i wanted to use the sound more than once at a time, like my turrets that shoot, they sometimes play the sound perfectly and sometimes it gets restarted or it is canceled completely. Then i switched to Clips after reading trhough some posts and that some guy found a solution by doin Clips, after using a tutorial.


Sound Class:

package Game.main;

import java.net.URL;

import javax.sound.sampled.AudioInputStream;

import javax.sound.sampled.AudioSystem;

import javax.sound.sampled.Clip;

import javax.sound.sampled.DataLine;

import javax.sound.sampled.Mixer;

public class Sound {
    public static final Sound backgroundMusic4 = new Sound("/backgroundMusic4.wav");
    public static final Sound blockDestroy = new Sound("/blockDestroy.wav");
    public static final Sound bulletHit = new Sound("/bulletHit.wav");
    public static final Sound button = new Sound("/button.wav");
    public static final Sound camera = new Sound("/camera.wav");
    public static final Sound teleporter = new Sound("/teleporter.wav");
    public static final Sound turretShot = new Sound("/turretShot.wav");
    public static final Sound walking = new Sound("/walking.wav");

    public static Clip clip;

    public Sound(String fileLocation){
        Mixer.Info[] mixInfos = AudioSystem.getMixerInfo();

        Mixer mixer = AudioSystem.getMixer(mixInfos[0]);

        DataLine.Info dataInfo = new DataLine.Info(Clip.class, null);

        try{
            clip =(Clip) mixer.getLine(dataInfo);
        }catch(Exception e){
            e.printStackTrace();
        }

        try{
            URL soundURL = getClass().getResource(fileLocation);
            AudioInputStream audioStream = AudioSystem.getAudioInputStream(soundURL);
            clip.open(audioStream);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public void play(){
        clip.loop(clip.LOOP_CONTINUOUSLY);
    }

    public void playOnce(){
        clip.start();
    }

    public void stop(){
        clip.stop();
    }
}

Turret Class (As example) [Only the last method "shoot()" matters]:

package Game.main.IngameObjects;

import java.applet.AudioClip;
import java.awt.Graphics;
import java.awt.Rectangle;

import Game.main.Controller;
import Game.main.Game;
import Game.main.GameObject;
import Game.main.Sound;
import Game.main.Textures;
import Game.main.classes.EntityC;

public class Turret extends GameObject implements EntityC{

    private boolean activated;
    private String direction;
    private Game game;
    private Textures tex;
    private Camera activationCam;
    private Controller c;

    private long lastShot=0;

    public Turret (double x, double y, Textures tex, Game game, Controller c, String direction){
        super(x,y);
        this.game=game;
        this.tex=tex;
        this.c=c;
        if(!direction.equalsIgnoreCase("up") && !direction.equalsIgnoreCase("down") && !direction.equalsIgnoreCase("left") && !direction.equalsIgnoreCase("right")){
            System.err.println("ERROR 1: Wrong Direction in Constructor in Class \"java.Game.main.IngameObjects.Camera\"");
            System.exit(1);
        }else{
            this.direction=direction;
        }
    }

    public Turret (double x, double y, Textures tex, Game game, Controller c , Camera cam, String direction){
        super(x,y);
        this.game=game;
        this.tex=tex;
        this.c=c;
        if(!direction.equalsIgnoreCase("up") && !direction.equalsIgnoreCase("down") && !direction.equalsIgnoreCase("left") && !direction.equalsIgnoreCase("right")){
            System.err.println("ERROR 1: Wrong Direction in Constructor in Class \"java.Game.main.IngameObjects.Camera\"");
            System.exit(1);
        }else{
            this.direction=direction;
        }
        this.activationCam=cam;
    }

    public void tick() {
        if(activationCam==null){
            shoot();
        }else if(activationCam.getActivated()){
            shoot();
        }
    }

    public void render(Graphics g) {
        if(activated){
            if(direction.equalsIgnoreCase("up")){
                g.drawImage(tex.turretActi.get(0), (int)x, (int)y, null);
            }else if(direction.equalsIgnoreCase("down")){
                g.drawImage(tex.turretActi.get(1), (int)x, (int)y, null);
            }else if(direction.equalsIgnoreCase("left")){
                g.drawImage(tex.turretActi.get(2), (int)x, (int)y, null);
            }else if(direction.equalsIgnoreCase("right")){
                g.drawImage(tex.turretActi.get(3), (int)x, (int)y, null);
            }
        }else{
            if(direction.equalsIgnoreCase("up")){
                g.drawImage(tex.turretDeac.get(0), (int)x, (int)y, null);
            }else if(direction.equalsIgnoreCase("down")){
                g.drawImage(tex.turretDeac.get(1), (int)x, (int)y, null);
            }else if(direction.equalsIgnoreCase("left")){
                g.drawImage(tex.turretDeac.get(2), (int)x, (int)y, null);
            }else if(direction.equalsIgnoreCase("right")){
                g.drawImage(tex.turretDeac.get(3), (int)x, (int)y, null);
            }
        }
    }

    public Rectangle getBounds() {
        return new Rectangle((int)x, (int)y, 32, 32);
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

    public void activate() {
        activated=true;
    }

    public void deactivate() {
        activated=false;
    }

    public boolean getActivated() {
        return activated;
    }

    public void setX(double x) {
        this.x=x;
    }

    public void setY(double y) {
        this.y=y;
    }

    public void shoot(){
        if(System.currentTimeMillis() - lastShot >= 1000){
            Sound.turretShot.playOnce();
            lastShot = System.currentTimeMillis();
            c.addEntity(new Bullet(x,y,game,tex,c,direction));
        }
    }
}

Now i have the problem that the turret Shoots once and the sound plays and then its gone (example video: https://youtu.be/q8TR2nR8hUI ). I tried to give every object his own Sound Clip but that very wastefull and last time it reduced my ticks and fps to 1 every 5 seconds or so.

Thats why i wanted to ask for a solution or some heplfull ideas and code.

I red something about and Byte array and dataStreamOutput, or that i could clone the clips but i cant get either one to work.

I am sorry for my somehow bad english, i am german so pls overlook any mistake (unless you can't read it)

greg-449
  • 109,219
  • 232
  • 102
  • 145
  • *"Only the last method "shoot()" matters"* Only the [mcve] gets good attention. Remove the irrelevant code and compact it all into a single source file. [Generate some simple sounds at run-time](http://stackoverflow.com/a/8632160/418556). – Andrew Thompson Oct 15 '16 at 12:30
  • If you want to play the same sound more than once simultaneously, you must create multiple `Clip` objects. There is no way around that. – VGR Oct 15 '16 at 15:04

1 Answers1

0

Java Clips are not set up for concurrent playback. At best, and this is often done, you can take a playing Clip and reset it back to the start and restart it (i.e., interrupting it before it completes). The next logical alternative is to instantiate multiple copies of the Clip (as suggested in the comment by VGR) and write a layer of code to manage these instances.

There are a couple libraries that allow concurrent playback of clip-equivalents. TinySound is publicly available via github. There is a major thread about it by the dev at java-gaming.org. Several members there have made successful use of this library.

I've been working on my own sound library, and it includes concurrent playback of clip-equivalents. I haven't opened my sources, yet, though. But if you wish to explore writing your own code to do this, I'm happy to reply on a thread either here or at java-gaming.org. The basic plan is to store the data in an array (can be either as bytes or as PCM normals or whatever) and play it back via a SourceDataLine (SDL) that is set to read from this data file. There is also the option of either running several of these through a single SDL output line (mixing them together before outputting) or giving each its own SDL. It is a bit of work to set this all up, hence many prefer to find a library.

Phil Freihofner
  • 7,645
  • 1
  • 20
  • 41