How do I handle sound?

Right,

So I want to learn how to make a simple sound engine in Java. Any one got any good links for sites that holds a good tutorial for learning it?

I know there’s tons of libraries out there for doing it, like paulscode that’s posted in this forum, but I really want to learn how to do it myself, I just want something that can play sound effects and continuous music.

Cheers! :slight_smile:

Using Java’s inbuilt audio clips:



import java.applet.Applet;
import java.applet.AudioClip;

public class Sound {
	private AudioClip clip;
	public Sound(String name){
		try
		{
			clip = Applet.newAudioClip(Game.class.getResource(name));
		}catch (Throwable e){
			e.printStackTrace();
		}
	}
	public void play(){
		try{
			new Thread(){
				public void run(){
					clip.play();
				}
			}.start();
		}catch(Throwable e){
			e.printStackTrace();
		}
	}
}

to set a sound you can go


Sound mySoundFile = new Sound("sound/music.wav");

then


mySoundFile.play();

I was perhaps where you were about two years ago.

I don’t know that this is what I’d call a “good” site for learning sound in Java. I’ve had to read through it many times.

http://docs.oracle.com/javase/tutorial/sound/TOC.html

It needs more concrete examples, earlier in the tutorial, imho. (There are some in the section on working with various format conversions, about five sections in.) In any event, I think that is a place to start and come back to repeatedly, and I’ll be happy to answer questions that come up.

I never really got into the Applet AudioClip–it seemed to be a bit too limited for the sorts of things I want to do. With javax.sound.sampled, I’ve been able to build a couple little audio systems, e.g., http://www.hexara.com/VSL/JTheremin.htm & http://www.hexara.com/VSL/AudioMixerDemoWarOfWorlds.htm

I’ve occasionally found useful info here: http://jsresources.org/
I’m guessing you already know about them.

Hm, thanks guys! :slight_smile:

I’ll have to look into it more. I’ll come back to ask what I did wrong when I manage to do something that sets my comp on fire. xD

Please be VERY careful playing sound in java. A lot of examples around the interwebs involving play() look good in THEORY, but I’ve found that they start generating a lot of hanging threads stuck in “ready” state for no particularly good reason. Assuming that you’d like to play WAV sound effects, In my game I created the following function:

    private void playSfx(final InputStream fileStream) {
        ActivityManager.getInstance().submit(new Runnable() {
            @Override
            public void run() {
                try {
                    BufferedInputStream bufferedStream = new BufferedInputStream(fileStream);
                    AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(bufferedStream);

                    final int BUFFER_SIZE = 128000;
                    SourceDataLine sourceLine = null;

                    AudioFormat audioFormat = audioInputStream.getFormat();
                    DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);

                    sourceLine = (SourceDataLine) AudioSystem.getLine(info);
                    sourceLine.open(audioFormat);

                    if (sourceLine == null) {
                        return;
                    }

                    sourceLine.start();
                    int nBytesRead = 0;
                    byte[] abData = new byte[BUFFER_SIZE];
                    while (nBytesRead != -1) {
                        try {
                            nBytesRead = bufferedStream.read(abData, 0, abData.length);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if (nBytesRead >= 0) {
                            sourceLine.write(abData, 0, nBytesRead);
                        }
                    }

                    sourceLine.drain();
                    sourceLine.close();
                    bufferedStream.close();
                    audioInputStream.close();

                } catch (IOException e) {
                    e.printStackTrace();
                } catch (UnsupportedAudioFileException e) {
                    e.printStackTrace();
                } catch (LineUnavailableException e) {
                    e.printStackTrace();
                    System.exit(1);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.exit(1);
                }
            }
        });
    }

In my case I use a custom class called AcitivityManager to deal with threads (as the play function will otherwise lock until the sound is done). The code for it is below:

public final class ActivityManager {
    private static ActivityManager _activityManager = null;

    private final ExecutorService _executorService;

    public static ActivityManager getInstance() {
        if (_activityManager == null) {
            _activityManager = new ActivityManager();
        }
        return _activityManager;
    }

    private ActivityManager() {
        _executorService = Executors.newCachedThreadPool();
    }

    public void submit(final Runnable task) {
        _executorService.submit(task);
    }
}

This is a bit lower level, but the high level classes seem to have thread issues in some OSs, whereas this logic appears to be stable and run fine in all environments. Basically all I’m doing here is manually spoon-feeding audio data to the audio device. Works well playing multiple sounds in rapid succession with no issues.

Below is an example of how I call my sound logic:

    public static void playSound(final SoundEffect soundEffect) {
        switch (soundEffect) {
            case BoxHit:
                getInstance().playSfx(getInstance().getClass().getResourceAsStream("boxhit.wav"));
                break;
            case CannonShot:
                getInstance().playSfx(getInstance().getClass().getResourceAsStream("cannonshot.wav"));
                break;
            case Coin:
                getInstance().playSfx(getInstance().getClass().getResourceAsStream("coin.wav"));
                break;
            case Jump:
                getInstance().playSfx(getInstance().getClass().getResourceAsStream("jump.wav"));
                break;
        }
    }

I’ve tried many different ways and this seems to be the only workable java-only solution that was stable.

This null check is a little late:

sourceLine = (SourceDataLine) AudioSystem.getLine(info);
sourceLine.open(audioFormat);
if (sourceLine == null) {
   return;
}

Resources should rather be closed in a finally block.
playSfx would be more flexible to use if it was just a Runnable and leaving the concrete thread handling to the caller.
I would the inner while loop break after the first read failure.

See my implementation here.

Please do yourself a favor and add exception handling. Exceptions are thrown for a reason. :wink:

I’ll add that when my exams are completed. (Daily Tests till MAY). Also see my article here

Using a Thread and a SourceDataLine per sound is terrible advice, unresponsive and liable to fail on some systems. Check out a software mixing library such as TinySound instead.

Using the same Thread to read from disk as write to the audio line is also not a great idea, and liable to cause problems.

Have you tested this with Java 7?

I’d expect to get Mark/Reset errors when dealing with InputStreams. Java7 is much more rigorous about making sure InputStreams are markable & resetable than Java 6 was, and more than a few audio programs broke as a result of the upgrade. wav files generally aren’t markable/resetable.

Thus, I’d consider passing in URL’s rather than InputStreams, and creating the AudioInputStream directly from the URL rather than the InputStream.

It’s beyond my experience to know whether anything is gained by the way you handle the threads. It seems interesting, but I don’t understand what you are trying to fix. I’ve never had problems with lingering audio threads. Other problems, but not that!

Does TinySound do mixing down to a single SDL? I didn’t know I was duplicating his wheel. I’ve also started using a simple mixing algorithm, consolidating down to a single SourceDataLine. It’s posted on a neighbor thread with source code.

@nsigma – One fellow at StackOverflow was suggesting it’s better to let the sound mixing occur in hardware, rather than do it up front (as I do). In that case, running multiple SourceDataLines would probably be part of the process. Of course, we’ve run into systems that only allow one output at a time, so I don’t know what this fellow is thinking…maybe thinking in terms of some ideal, or future with sound cards that provide this service. (He also thinks mixing requires more complicated math than summation, e.g., an equation such as A + B - AB (where A & B are two volumes between 0 and 1). I’m thinking efforts like this may have their place but aren’t strictly necessary.

I haven’t ran into anyone having issues with my implementation yet.; I use java 7 yes. The thread hangs any other way I do it – whatever the reason. And yes, that null check is 1 line too late; I have no idea how it got there :stuck_out_tongue:

AFAIK that’s what it does - I still haven’t actually tried it out :persecutioncomplex: I assumed you realised that, as software mixing is mentioned a number of times in the TinySound thread, and you have posted in it … :wink:

Then one fellow at StackOverflow is a muppet! :slight_smile: Few cards support hardware mixing, and it’s an older feature not a future one. That’s why every OS now has a sound server that does mixing in software. If you have a system that supports multiple SDL’s then it’s almost certainly mixing in software, just outside Java. This is an inefficient way of outputting multiple sounds from a single application - it’s primarily designed for allowing multiple applications to output audio at the same time.

The guy seems to be proving he’s an idiot. ::slight_smile: Be interesting to see the link exactly - found that equation linked to in an article that talks about combining sounds without clipping. It presumably acts like a limiter to stop the sound peaking, but it must cause some distortion at higher levels. A better approach is something like the automatic gain control used in JASS (http://www.cs.ubc.ca/~kvdoel/jass/) that tracks the highest output and reduces all values (not a sample at a time) to keep within range. I adapted that in some old code using JASS so that the maximum value reduced slightly over time (a few seconds) so that a short peak wouldn’t cause everything to become quiet permanently - closer to the behaviour of a proper limiter. It’s a better approach to avoiding clipping.

I assumed that the TinySound library was creating wrappers, using multiple output lines. PaulsCode does the same, doesn’t it? Why else did he start out on a separate project to make a mixer (yet uncompleted afaik)?

In any event, I really do need to find the time to look at the code behind TinySound, and set it up an run it.


Thanks for the explanations about hardware/software mixing stages.

Here’s the link to the StackOverflow question:

You probably have some good advice and recommendations for the fellow’s main question (the system you wrote or coauthored, I’m thinking).

This article was cited as a way to prevent peak overflow.
http://www.vttoth.com/CMS/index.php/technical-notes/68
It seems dubious to me, especially with the exponential growth in the number of calculations required with multiple lines.

I’d just forget all that crap and figure out OpenAL using LWJGL or JogAmp.

(See also discussions on Java2D vs OpenGL)

Cas :slight_smile:

TinySound and PaulsCode have a completely different architecture.

The guy’s quite amusing - suggesting using FFT to mix audio. :o And the idea that Java isn’t fast enough to mix a few sounds together is hilarious. I’ve been using Java for DSP for almost 10 years now. People have written entire sequencers and softsynths in pure Java - http://youtu.be/9sYGcziGtHI

Not really a correct comparison, though. OpenGL is direct access to a hardware accelerated (and programmable) graphics pipeline. OpenAL is a fairly limited API, and presumably in most cases running on top of the platform’s primary audio system? The low-level bits of JavaSound also give direct access to the native audio system, so it’s possible to achieve far more scope and flexibility using it (or alternative bindings like PortAudio) and programming audio code in Java itself. Most people on here should take the advice to use a library, be it OpenAL, TinySound, etc. It really depends what features you require now and in the future, but I’d tend to prefer things written in Java as it’s easier (possible) to get in and fiddle with the code if you ever need to. Just ignore Clip and all the code posted above! :wink:

I totally forgot about this thread (which I posted on, too :clue: ):


Probably had something to do with barely understanding the discussion at the time. I’m doing a little better now.

Heh, and here I thought that this thread was dead. :stuck_out_tongue:

And seems like there’s almost as much dispute over how to handle sound as to j2d vs opengl. Though I’m slowly coming to terms with a thought that keeps creeping up in my mind… “Just use LWJGL/LibGDX/OpenAL and be done with it!”… :stuck_out_tongue:

[quote]PaulsCode does the same, doesn’t it? Why else did he start out on a separate project to make a mixer (yet uncompleted afaik)?
[/quote]
PaulsCode does not include a software mixer last I checked. Instead, it’s just an abstraction of different sound libraries – LWJGL, JOGL and Java2D. TinySound, on the other hand, mixes all channels in software into a single JavaSound clip (if I remember correctly).

Regarding Paul’s 3D sound library – IMO it’s not the best.

OpenAL just sounds different than Java2D. This is obvious when you run the same audio file through the two different libraries. Java2D has crackles, pops, and glitches – especially when stopping a cilp – and often leads to slightly different volumes. So you might have fine-tuned your game to the LWJGL backend, but then testing it with the Java2D backend everything might sound a little off. And then you begin to question why you have a Java2D backend in the first place…

The reason Paul went lengths to abstract sound is for “maximum compatibility” – but realistically if your user doesn’t support OpenAL, they probably won’t be able to run your game!

So why bother with the Java2D backend at all? Then you are left with only two: JOGL or LWJGL. Obviously if your game is based on LWJGL, it makes no sense to use a JOGL backend, and vice versa. So really you just have a huge, clunky, multi-threaded and IMHO poorly designed API that wraps LWJGL.

With that said, Paul has written some fantastic decoders that are not very dependent on the rest of the library. :slight_smile:

[quote]“Just use LWJGL/LibGDX/OpenAL and be done with it!”…
[/quote]
For a beginner, it’s easier to just use a wrapper: TinySound, Pauls Sound System, LibGDX, or even SlickUtil if you must.

Arh, but I want to use OpenGL for my game, so I might as well use the stuff that libraries like LJWGL offers. :slight_smile: (I started making my game in pure java2d, but it started dropping frames when placing more than 100 towers xD)