Author Topic: I know this isn't a Java forum, but...  (Read 2172 times)

0 Members and 1 Guest are viewing this topic.

Offline Aardwolf

  • 211
  • Posts: 16,384
I know this isn't a Java forum, but...
If you know how to do sound properly in Java, please help me out. I've figured out that the problem with my FreeSpace Turn-Based Strategy Game is that the sounds are loading each time I play them.

Problem: I can't figured out how to play sounds more than once non-sequentially without reloading them.

Solution: Ask you guys.

My current (rather sucky) code is as follows:

Code: [Select]
package tbsg;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.image.*;
import java.applet.*;
import javax.sound.sampled.*;
import java.io.*;
public class SoundPlayer
{
    public static SuperList sounds = new SuperList();
    public static boolean enough = true;
    public static Clip loadSoundAsName(String s, double volume, String name)
    {
//        if(((SuperSound)sounds.get(new SoundFinder(name))) == null)
//        {
        if(enough)
        {
            try
            {
                // From file
                AudioInputStream stream = AudioSystem.getAudioInputStream(new File(s));
                // At present, ALAW and ULAW encodings must be converted
                // to PCM_SIGNED before it can be played
                AudioFormat format = stream.getFormat();
                if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED)
                {
                    format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), format.getSampleSizeInBits()*2, format.getChannels(), format.getFrameSize()*2, format.getFrameRate(), true);
                    stream = AudioSystem.getAudioInputStream(format, stream);
                }
                DataLine.Info info = new DataLine.Info(Clip.class, stream.getFormat(), 128/*((int)stream.getFrameLength() * format.getFrameSize()) * 0*/);
                sounds.add(new SuperSound(name, stream, info));
                Clip clip = (Clip)AudioSystem.getLine(info);
                Runtime rt = Runtime.getRuntime();
                if(clip.getBufferSize() < rt.freeMemory())
                {
                    clip.open(stream);
                    return clip;
                }
                else
                {
                    enough = false;
                    JOptionPane.showMessageDialog(null, "There is not enough memory to continue playing sounds.\nIf you would like to continue running sounds in this program,\ntry saving your game and running it again.");
                }
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
        return null;
//        }
//        else
//        {
//            Clip c = getClip(name, volume);
//            return c;
//        }
    }
    public static void setClipVolume(Clip clip, double volume)
    {
        try
        {
            FloatControl gainControl = (FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN);
            float vol = gainControl.getMinimum() + (gainControl.getMaximum() - gainControl.getMinimum()) * (float)volume;
            gainControl.setValue(vol);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
    public static Clip getClip(String name, double vol)
    {
        Clip temp =  getClip(((SuperSound)sounds.get(new SoundFinder(name))).info, ((SuperSound)sounds.get(new SoundFinder(name))).stream);
        setClipVolume(temp, vol);
        return temp;
    }
    public static Clip getClip(String file, double vol, String name)
    {
//        sounds.remove(new SoundFinder(name));
//        loadSoundAsName(file, vol, name);
//        return getClip(name, vol);
//        Clip temp =  getClip(((SuperSound)sounds.get(new SoundFinder(name))).info, ((SuperSound)sounds.get(new SoundFinder(name))).stream);
//        setClipVolume(temp, vol);
//        return temp;
        return null;
    }
    public static Clip getClip(DataLine.Info info, AudioInputStream stream)
    {
        try
        {
            Clip clip = (Clip)AudioSystem.getLine(info);
            clip.open(stream);
            return clip;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }
}
class SuperSound
{
    public String name;
    public DataLine.Info info;
    public AudioInputStream stream;
    public SuperSound(String n, AudioInputStream s, DataLine.Info i)
    {
        name = n;
        info = i;
        stream = s;
    }
    public String toString()
    {
        return name;
    }
}
class SoundFinder implements Qualifier
{
    private String name;
    public SoundFinder(String n)
    {
        name = n;
    }
    public boolean assess(Object o)
    {
        if(o instanceof SuperSound)
            return (((SuperSound)o).name.equals(name));
        return false;
    }
}

The game thread is here:
http://www.hard-light.net/forums/index.php/topic,42596.0.html

Oh, and SuperList is just a list class I created extending ArrayList but with more methods.
« Last Edit: October 04, 2006, 05:23:20 pm by Aardwolf »

 

Offline vyper

  • 210
  • The Sexy Scotsman
Re: I know this isn't a Java forum, but...
Couldn't you load the stream into an array entry? SoundArray[0] = AudioSystem.getAudioInputStream(format, stream)

Of course this will up the memory requirements, and if you get it wrong and it keeps loading the sounds over and over into memory it WILL make the Java VM go asplody.

Been a while since I did the Java boogie mind...
"But you live, you learn.  Unless you die.  Then you're ****ed." - aldo14

 

Offline Aardwolf

  • 211
  • Posts: 16,384
Re: I know this isn't a Java forum, but...
Well, you see, the problem with that is that it doesn't solve the problem. That's just another way of storing it. And the thing that that would store is not the sound data, I think, but a reference to the file, or possibly the file data; either way, it doesn't help me to play sounds more than once. I mean, with the code shown below, it plays everything, every time, but it uses up more and more memory and causes the program to run slower and slower until eventually it stops.

I'm pretty sure the problem is the sound, because when I changed the buffer size (from 1024 to 128) the peformance improved slightly.

 

Offline aldo_14

  • Gunnery Control
  • 213
Re: I know this isn't a Java forum, but...
Create a singleton cache class for all your re-useable assets and sort out some form of a load-strategy (pre-cache or load on demand), so you can fetch-by-reference

something simple could work, i.e. (hand wavey code)

Code: [Select]
public class SoundLib {

private HashMap soundLib;

private Static SoundLib _instance_ = null;

public static SoundLib getInstance() {
if(_instance_ == null) { _instance_ = new SoundLib();}
return _instance_;
}

private SoundLib(){
soundLib = new HashMap<Sound, String>(); //or is it String, Sound?  i.e. String is a key - filename
}

//psuedo...
public void addSound(String filename, Sound s){
//could maybe also have the loading-by-filename code here
soundLib.put(filename, s);
}

public void playSound(String filename){
soundLib.get(filename).playSoundThing();
}

}

I'd suggest looking at extending the above to run as a thread, and synchronizing these methods :)  Possibly using the observer pattern to send 'playsound' type events to a sound handler subsystem which makes the play calls.

That's totally offhand, of course; I've not done much non-trivial work with the sound APIs - it might be worth looking for OAL wrappers, I'm sure there will be some.

NB: I believe you should look to minize the number of object creations (i.e. Object o = s.getObject() type operations) because it places a memory load, and probably also upon the gc algorithm (along the same lines, try to use constant values where possible, although this is a clear tradeoff with safe coding practice).  I found a superb little J2ME tutorial a while back with a bunch of simple but handy optimizations which, er, I've since almost completely forgot, but which i'll look to dig up later.
« Last Edit: October 05, 2006, 03:09:05 am by aldo_14 »

 
Re: I know this isn't a Java forum, but...
I'd like to know why people are suggesting arrays for storing named assets. Yes, Java does have Hashtables.
'And anyway, I agree - no sig images means more post, less pictures. It's annoying to sit through 40 different sigs telling about how cool, deadly, or assassin like a person is.' --Unknown Target

"You know what they say about the simplest solution."
"Bill Gates avoids it at every possible opportunity?"
-- Nuke and Colonol Drekker

 

Offline aldo_14

  • Gunnery Control
  • 213
Re: I know this isn't a Java forum, but...
I'd like to know why people are suggesting arrays for storing named assets. Yes, Java does have Hashtables.

Simple indexing, I'd wager.  I believe an array lookup via a stored constant index is faster than calculating a hash function then looking up.  Plus it's a really simple reference to use for A Data Structure.  Everyone knows how to use an array, after all.

 

Offline Aardwolf

  • 211
  • Posts: 16,384
Re: I know this isn't a Java forum, but...
Please note that I already have a way to store the sounds that does not require reloading them. The problem is that it seems that I have to reload them, from the file (I think), for them to play a second nonconsecutive time.

Maybe I wasn't clear on this earlier?

 

Offline aldo_14

  • Gunnery Control
  • 213
Re: I know this isn't a Java forum, but...
Please note that I already have a way to store the sounds that does not require reloading them. The problem is that it seems that I have to reload them, from the file (I think), for them to play a second nonconsecutive time.

Maybe I wasn't clear on this earlier?

Um

Dare I ask the obvious? http://java.sun.com/j2se/1.5.0/docs/api/javax/sound/sampled/Clip.html;
Quote
Playback of an audio clip may be started and stopped using the start  and stop methods. These methods do not reset the media position; start causes playback to continue from the position where playback was last stopped. To restart playback from the beginning of the clip's audio data, simply follow the invocation of stop  with setFramePosition(0), which rewinds the media to the beginning of the clip.

 

Offline Aardwolf

  • 211
  • Posts: 16,384
Re: I know this isn't a Java forum, but...
Problem solved. New release will come out sometime soon.