3D Sound Engine

Yes, same out of both sides.

Do you have any experience with converting stereo to mono, I’ve read that it gives you two half-volume tracks. I guess this is just a natural side effect of how the wav was initially designed.

I actually got everything to work just fine. Is a No-attenuation (ambient) sample required to have a stereo track? I tried playing my death sound as an ambient sound at position 0,0,0 attenuation_none, but it would still be position-based for some reason. When I made the track stereo, it worked.

I believe for OpenAL that ambient sounds should be stereo (I’d have to verify that). Alternately you can position them wherever the listener is at (which can be problematic if the listener moves). I’ll have to look at the code for LibraryJavaSound (I thought I was checking for ATTENUATION_NONE, and not panning in that case, but it’s been a while since I looked at that part of the code)

Hey, paul. So far everything is working great!

Question though. I like how I can name my BGM source, and then fade it out. My question though. I have a bunch of Scene objects that are the different “states” the game can be in (main menu, map editor, game), and we want them to have different background music. Scenes have an “entered” and a “exited” method, and currently it looks like this (example shown is gamescene)

    
public void sceneEntered() {
    OpenGLGameWindow.getInstance().setCursor(OpenGLGameWindow.CursorType.RETICULE);
    Game.sound.backgroundMusic("GameBGM", "gameBGM.ogg", true);
}

public void sceneExited() {
    Game.sound.fadeOut("GameBGM", null, "gameBGM.ogg", 1000);
}

This results in a clipping though when we start the new Game.sound.background music in the main menu (using a different source name. The disadvantage though is that if we were to use Game.sound.fadeOutIn() in sceneExited, we’d have to know the scene we are switching to (probably a useful feature we’ll eventually have anyway). But for now, is there a way you know of that we’d be able to fade IN the background music source, short of some weird hack?

One option, with your current setup, could be to start up a temporary thread from new scene’s sceneEntered() method, which checks if the music source is still playing (from a while loop, with say 30ms sleep in each iteration in order not to peg the CPU). When you leave the previous scene in sceneExited(), do a fadeOut like you have now. Once the source is finished playing, the thread can do a fadeIn with the new music file, and then kill itself.

A more efficient way that might work if it can fit into your infrastructure, would be to do a fadeOutIn from the new scene’s sceneEntered() method, rather than doing the fadeOut from the previous scene’s sceneExited() method.

FadeOutIn only requires the new source name and sample name, if I recall, so I think this would be a bit more elegant.

Hi Paul,

Something I noticed you haven’t taken into consideration is OpenAL’s AL_SOURCE_RELATIVE feature. A RELATIVE (as opposed to ABSOLUTE) source will always be positioned relative to the listener’s position. So if your mono file is at <0,0,0> and you move your listener, as long as the source position is RELATIVE instead of ABSOLUTE, the sound will be played as if it were an ambient sound (i.e. with no 3D effects, since it’s being played at the same spot as the listener). e.g. With default listener orientation, a relative source at <5,0,0> will always pan to the right speaker, even if the listener moves about.

In my own extension of your library I’ve done a little something like this for SourceLWJGLOpenAL:

    public void setRelative(boolean relative) {
        this.relative = relative;
        // make sure we are assigned to a channel:
        if (channel != null && channel.attachedSource == this
                && channelOpenAL != null && channelOpenAL.ALSource != null) {
            AL10.alSourcei(channelOpenAL.ALSource.get(0), 
                    AL10.AL_SOURCE_RELATIVE, 
                    relative ? AL10.AL_TRUE : AL10.AL_TRUE);
            checkALError();
        }
    }

    public boolean isRelative() {
        return relative;
    }

And inside play()…

... as we are initializing the source information ...
                AL10.alSourcei(id, AL10.AL_SOURCE_RELATIVE, isRelative() ? AL10.AL_TRUE : AL10.AL_FALSE);
                checkALError();

To add the same functionality to SourceJavaSound, I adjusted the values used when calculating pan/pitch/gain based on whether isRelative() returns true.

I also extended your sound system with other features such as seeking and acquiring the length of a non-streamed sound, playing multiple sources simultaneously, etc. Soon I hope to include effects (reverb, lowpass, etc) and other goodies.

For ambient sources, most folks just use Stereo files and set attenuation to ATTENUATION_NONE. Your way works too though. When you finish, if you post your source code, I’ll incorporate some of your work into the official library if you find it works well and has equivalents that work in both OpenAL and JavaSound.

Hi Paul,

I seem to be experiencing a bug with the SoundSystem.libraryCompatible() method.

If I call ‘SoundSystem.libraryCompatible( LibraryJavaSound.class );’ it always returns false.
But ‘sound = new SoundSystem( LibraryJavaSound.class );’ instantiates without errors, and sound will play just fine.

Im using Windows 7 64-bit, with JRE 7 64-bit.

[s]Could you post the console output after calling ‘SoundSystem.libraryCompatible( LibraryJavaSound.class );’ to see what it says is wrong?

Also, could you run the following after doing it the other way ‘sound = new SoundSystem( LibraryJavaSound.class );’[/s]

–EDIT-- Never mind, I figured out what the problem is. I never updated the libraryCompatible() method after I added the mixer profiling code. So it is still checking for the “Java Sound Audio Engine”, which has been removed from recent versions of Java, so the method will always return false. I’ll fix this at some point (in the mean time, just do it the way you are doing it)

hey Paul, I’m having some problems loading sounds with SoundSystem.newSource();

I’m loading all my sounds in a loop, and for the first N sounds I have this error :


Error in class 'LibraryJavaSound'
    Unable to open file 'sounds/sword1.ogg' in method 'loadSound'

The problem are not the ogg files, if I reverse the loop, the sounds that are the end of the list (that is, the first ones to be loaded) fail, and the rest (including the ones which failed before) load successfully.

This is how I’m starting the SoundSystem :


SoundSystemConfig.addLibrary( LibraryJavaSound.class );
SoundSystemConfig.setCodec( "ogg", CodecJOrbis.class );
SoundSystemConfig.setDefaultFadeDistance(100);
SoundSystemConfig.setNumberStreamingBuffers(3);
SoundSystemConfig.setNumberNormalChannels(3);
 mySoundSystem = new SoundSystem( LibraryJavaSound.class );

If I play all my sounds using quickPlay, they all work perfectly.

Any clues ?

Thanks in advance.

That is really strange. The only place where that message gets printed:

        URL url = filenameURL.getURL();

        if( errorCheck( url == null, "Unable to open file '" +
                                     filenameURL.getFilename() +
                                     "' in method 'loadSound'" ) )
            return false;

And the code for FilenameURL.getURL():

        if( url == null )
        {
            // Check if the file is online or inside the JAR:
            if( filename.matches( SoundSystemConfig.PREFIX_URL ) )
            {
                // Online
                try
                {
                    url = new URL( filename );
                }
                catch( Exception e )
                {
                    errorMessage( "Unable to access online URL in " +
                                  "method 'getURL'" );
                    printStackTrace( e );
                    return null;
                }
            }
            else
            {
                // Inside the JAR
                url = getClass().getClassLoader().getResource(
                      SoundSystemConfig.getSoundFilesPackage() + filename );
            }
        }
        return url;

Therefore, the only way this message could occur is if the following returned ‘null’:

                url = getClass().getClassLoader().getResource(
                      SoundSystemConfig.getSoundFilesPackage() + filename );

From the Java API, the only way this would happen:

[quote] A URL object for reading the resource, or null if the resource could not be found or the invoker doesn’t have adequate privileges to get the resource.
[/quote]
Since the resource exists, this must mean the invoker doesn’t have adequate privileges to get the resource?? That doesn’t sound right… I’ll have to think about this some more.

Just a thought. Could this be a concurrency problem? For example: when there’s a lot going on in a Constructor, and the code which makes the resource call executes before the Constructor is guaranteed to have completed.

What is the class referred to in the .getClass() part? Is it possible it hasn’t finished instantiation?

The class referenced by getClass() is FilenameURL. I don’t think there is a concurrency problem, though. First off, the constructors are quite simple:

/**
 * Constructor: Saves handles to the url and identifier.  The identifier should
 * look like a filename, and it must have the correct extension so SoundSystem
 * knows what format to use for the file referenced by the URL instance.
 * @param url URL interface to a file.
 * @param identifier Identifier (filename) for the file.
 */
    public FilenameURL( URL url, String identifier )
    {
        // grab a handle to the message logger:
        logger = SoundSystemConfig.getLogger();

        filename = identifier;
        this.url = url;
    }

/**
 * Constructor: Saves a handle to the filename (used later to generate a URL
 * instance).  The file may either be located within the
 * JAR or at an online location.  If the file is online, filename must begin
 * with "http://", since that is how SoundSystem recognizes URL names.
 * @param filename Name of the file.
 */
    public FilenameURL( String filename )
    {
        // grab a handle to the message logger:
        logger = SoundSystemConfig.getLogger();

        this.filename = filename;
        url = null;
    }

And then the getURL() method, where the resource is accessed, can only be called after it is instantiated, because both the instantiation and the call to getURL() are done in sequence on a single thread (I could post the relevant code trace to demonstrate, but it would get a bit long).

I’ll try and recreate the problem so I can track down the cause. Teletubo, do you have a simple test-case that I could use? (does this happen any time you create lots of new sources in a loop? If so, how many?)

Damn, my bad, Paul. Sorry. :emo:

The thing is that I was looping thru the filenames, and AFTER the loop setting SoundSystemConfig.setSoundFilesPackage(path);
I just realized that when writing a test case.

That explains why the first few failed: The ones queued for later loading got the new path settings and loaded successfully.

Thanks for the attention and the great lib anyway.

Hi

I’m going to switch to JOAL in a few hours, I will use JavaSound as a fallback. I cross my fingers ;D Thank you so much for your nice libraries.

Hi

LibraryJOAL works fine with JOAL 1.1.3. Thank you very much ;D

Hi I was hoping you could lend some clarity in why I cant get this to play wav’s and mp3’s?

Its playing midi files fine but wav’s and mp3’s from free wav sites seem to not work(Just using for testing atm) Ive tried about 5 different wav’s from different sites and a few different mp3’s so far

Im currently using the SoundSystemJPCT implementation as i dont need the sound in 3d so much as just a library that can play lots of formats

This is how im using your library


public class Sound 
{
    SoundSystemJPCT soundSystem = new SoundSystemJPCT();
    
    public static void main(String args[])
    {
        Sound sound = new Sound();
    }
    public Sound() 
    {
        // Load some library and codec plug-ins:
        try
        {
            SoundSystemConfig.addLibrary( LibraryLWJGLOpenAL.class );
            SoundSystemConfig.addLibrary( LibraryJavaSound.class );
            SoundSystemConfig.setCodec( "wav", CodecWav.class );
            SoundSystemConfig.setCodec( "ogg", CodecJOgg.class );
            //SoundSystemConfig.setCodec( "ogg", CodecIBXM.class );
            //SoundSystemConfig.setCodec( "ogg", CodecJSpeex.class );
            //SoundSystemConfig.setCodec( "ogg", CodecJOrbis.class );
        }
        catch( SoundSystemException e )
        {
            System.err.println("error linking with the plug-ins" );
        }
        String filename[] = {"wonderland.wav","blind.wav","T Bird - Diggin' in the Dirt.mp3"};
        
        for(int i = 0; i < filename.length; i++)
        {  
            soundSystem.newSource( "Source "+i, filename[i], false );
            soundSystem.play( "Source "+i );
            sleep( 10000 );
        }
        
        soundSystem.cleanup();
}

This is the output I get from the debug window


Starting up SoundSystemJPCT...
Initializing LWJGL OpenAL
    (The LWJGL binding of OpenAL.  For more information, see http://www.lwjgl.org)
OpenAL initialized.

Error in class 'CodecWav'
    Unsupported audio format in method 'initialize'
    ERROR MESSAGE:
        could not get audio input stream from input stream
    STACK TRACE:
        javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1102)
        paulscode.sound.codecs.CodecWav.initialize(CodecWav.java:128)
        paulscode.sound.libraries.LibraryLWJGLOpenAL.loadSound(LibraryLWJGLOpenAL.java:392)
        paulscode.sound.libraries.LibraryLWJGLOpenAL.newSource(LibraryLWJGLOpenAL.java:640)
        paulscode.sound.SoundSystem.CommandNewSource(SoundSystem.java:1800)
        paulscode.sound.SoundSystem.CommandQueue(SoundSystem.java:2415)
        paulscode.sound.CommandThread.run(CommandThread.java:121)
Error in class 'CodecWav'
    Audio input stream null in method 'readAll'
Error in class 'LibraryLWJGLOpenAL'
    Sound buffer null in method 'loadSound'
Error in class 'LibraryLWJGLOpenAL'
    Source 'Source 0' was not created because an error occurred while loading wonderland.wav
Error in class 'LibraryLWJGLOpenAL'
    Source 'Source 0' not found in method 'play'
Error in class 'CodecWav'
    Unsupported audio format in method 'initialize'
    ERROR MESSAGE:
        could not get audio input stream from input stream
    STACK TRACE:
        javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1102)
        paulscode.sound.codecs.CodecWav.initialize(CodecWav.java:128)
        paulscode.sound.libraries.LibraryLWJGLOpenAL.loadSound(LibraryLWJGLOpenAL.java:392)
        paulscode.sound.libraries.LibraryLWJGLOpenAL.newSource(LibraryLWJGLOpenAL.java:640)
        paulscode.sound.SoundSystem.CommandNewSource(SoundSystem.java:1800)
        paulscode.sound.SoundSystem.CommandQueue(SoundSystem.java:2415)
        paulscode.sound.CommandThread.run(CommandThread.java:121)
Error in class 'CodecWav'
    Audio input stream null in method 'readAll'
Error in class 'LibraryLWJGLOpenAL'
    Sound buffer null in method 'loadSound'
Error in class 'LibraryLWJGLOpenAL'
    Source 'Source 1' was not created because an error occurred while loading blind.wav
Error in class 'LibraryLWJGLOpenAL'
    Source 'Source 1' not found in method 'play'
Error in class 'LibraryLWJGLOpenAL'
    No codec found for file 'T Bird - Diggin' in the Dirt.mp3' in method 'loadSound'
Error in class 'LibraryLWJGLOpenAL'
    Source 'Source 2' was not created because an error occurred while loading T Bird - Diggin' in the Dirt.mp3
Error in class 'LibraryLWJGLOpenAL'
    Source 'Source 2' not found in method 'play'

SoundSystemJPCT shutting down...
    Author: Paul Lamb, www.paulscode.com

How do i use the other codecs that are commented out? they arent in import paulscode.sound.codecs to import
also what should i use for mp3’s?

@duindain Are you sure you use the latest version of the codecs? Have you tried to convert your files into ogg format?

Mp3 format doesn’t currently have a codec plug-in (use ogg instead). Could you post a link to one of the wav files that has the problem (so I can run some tests)?