Sound plays but doesn't change when source moves.

Started by paulscode, March 06, 2008, 01:04:55

Previous topic - Next topic

paulscode

Hello, I am new to JOAL, and I am having a slight problem.  I created a class from a book I own, for managing sounds for my program.  I seem to be doing something wrong, though, because when I attempt to move the source, the sound output does not change.  I would expect that a source moving away from you would get quieter, but the volume remains constant.  Here is my code for the sound manager:
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import net.java.games.joal.AL;
import net.java.games.joal.ALException;
import net.java.games.joal.ALFactory;
import net.java.games.joal.util.ALut;

public class JOALSoundMan
{
    // where the sound files are stored:
    private final static String SOUND_DIR = "Sounds\\";
    
    // Note: currently configured to only load WAV sounds
    private final static String FILE_EXT = ".wav";
    
    // Global stores for the sounds:
    private HashMap<String, int[]> buffersMap;  // (name, buffer) pairs
    private HashMap<String, int[]> sourcesMap;  // (name, buffer) pairs
    private AL al;    
    
    private float listenerX, listenerY, listenerZ;  // current position
    private float[] listenerOrientation;  // orientation
    private float listenerAngle = 0;  // listener's angle (in radians)
    
    public JOALSoundMan()
    {
        buffersMap = new HashMap<String, int[]>();
        sourcesMap = new HashMap<String, int[]>();
        
        initOpenAL();
        initListener();
    }
    
    private void initOpenAL()
    {
        try
        {
            ALut.alutInit();   // creates an OpenAL context
            al = ALFactory.getAL();  // used to access OpenAL
            al.alGetError();
            
            // System.out.pringln( "JOAL version: " + Version.getVersion());
        }
        catch( ALException e )
        {
            e.printStackTrace();
            System.exit( 1 );
        }
    }    
    
    // Position and orientate the listener:
    private void initListener()
    {
        listenerX = 0.0f;
        listenerY = 0.0f;
        listenerZ = 0.0f;

        // position the listerner at the origin:
        al.alListener3f( AL.AL_POSITION, listenerX, listenerY, listenerZ );
        
        al.alListener3i( AL.AL_VELOCITY, 0, 0, 0 );  // no velocity
        
        // first 3 elements are "look at" point
        // second 3 are the "up direction"
        listenerOrientation = new float[] {listenerX, listenerY, listenerZ - 1.0f, 0.0f, 1.0f, 0.0f };
        
        al.alListenerfv( AL.AL_ORIENTATION, listenerOrientation, 0);
    }
    
    public void cleanUp()
    {
        Set<String> keys = sourcesMap.keySet();
        Iterator<String> iter = keys.iterator();
        
        String nm;
        int[] buffer, source;
        while( iter.hasNext() )
        {
            nm = iter.next();
            
            source = sourcesMap.get(nm);
            System.out.println( "Stopping " + nm);
            al.alSourceStop( source[0] );
            al.alDeleteSources( 1, source, 0 );
            
            buffer = buffersMap.get( nm );
            
            al.alDeleteBuffers( 1, buffer, 0 );
        }
        
        ALut.alutExit();
    }
    
    public boolean load( String nm, boolean toLoop )
    {
        if( sourcesMap.get( nm ) != null )
        {
            System.out.println( nm + " already loaded" );
            return true;
        }
        
        int[] buffer = initBuffer( nm );
        if( buffer == null )
            return false;
        
        int[] source = initSource( nm, buffer, toLoop );
        
        if( source == null )
        {
            // no need for the buffer anymore
            al.alDeleteBuffers( 1, buffer, 0 );
            return false;
        }
        
        if( toLoop )
            System.out.println( "Looping source created for " + nm );
        else
            System.out.println( "Source created for " + nm );
        
        buffersMap.put( nm, buffer );
        sourcesMap.put( nm, source );
        return true;
    }
        
    public boolean load( String nm, float x, float y, float z, boolean toLoop )
    {
        if( load( nm, toLoop ) )
            return setPos( nm, x, y, z );
        else
            return false;
    }
    
    // Create arrays for holding various sound files:
    private int[] initBuffer( String nm )
    {
        int[] format = new int[1];
        ByteBuffer[] data = new ByteBuffer[1];
        int[] size = new int[1];
        int[] freq = new int[1];
        int[] loop = new int[1];
        
        // load WAV file into the data arrays
        String fnm = SOUND_DIR + nm + FILE_EXT;
        try
        {
            ALut.alutLoadWAVFile( fnm, format, data, size, freq, loop );
        }
        catch( ALException e )
        {
            System.out.println( "Error loading WAV file: " + fnm );
            return null;
        }
        // System.out.println( "Sound size = " + size[0] );
        // System.out.println( "Sound freq = " + freq[0] );
        
        // create an empty buffer to hold the sound data
        int[] buffer = new int[1];
        al.alGenBuffers( 1, buffer, 0 );
        if( al.alGetError() != AL.AL_NO_ERROR )
        {
            System.out.println( "Could not create a buffer for " + nm );
            return null;
        }
        
        // store data in the buffer
        al.alBufferData( buffer[0], format[0], data[0], size[0], freq[0] );        
        
        return buffer;
    }
    
    // Create a source (a point in space that makes sound):
    private int[] initSource( String nm, int[] buf, boolean toLoop )
    {
        int[] source = new int[1];
        al.alGenSources( 1, source, 0 );
        if( al.alGetError() != AL.AL_NO_ERROR )
        {
            System.out.println( "Error creating source for " + nm );
            return null;
        }
        
        // configure the source
        al.alSourcei( source[0], AL.AL_BUFFER, buf[0] ); // bind buffer
        al.alSourcef( source[0], AL.AL_PITCH, 1.0f );
        al.alSourcef( source[0], AL.AL_GAIN, 1.0f );
        // Position the source at the origin:
        al.alSource3f( source[0], AL.AL_POSITION, 0.0f, 0.0f, 0.0f );
        al.alSource3f( source[0], AL.AL_VELOCITY, 0, 0, 0 ); // no velocity
        if( toLoop )
            al.alSourcei( source[0], AL.AL_LOOPING, AL.AL_TRUE );  // looping
        else
            al.alSourcei( source[0], AL.AL_LOOPING, AL.AL_FALSE ); // play once
        
        if( al.alGetError() != AL.AL_NO_ERROR )
        {
            System.out.println( "Error configuring source for " + nm );
            return null;
        }
        
        return source;
    }
    
    // Move the nm sound to (x, y, z):
    public boolean setPos( String nm, float x, float y, float z )
    {
        int[] source = (int[]) sourcesMap.get( nm );
        if( source == null )
        {
            System.out.println( "No source found for " + nm );
            return false;
        }
        
        al.alSource3f( source[0], AL.AL_POSITION, x, y, z );
        return true;
    }
    
    public boolean play( String nm )
    {
        int[] source = (int[]) sourcesMap.get( nm );
        if( source == null )
        {
            System.out.println( "No source found for " + nm );
            return false;
        }
        
        // System.out.println( "Playing " + nm );
        al.alSourcePlay( source[0] );
        return true;
    }
    
    // move the listener by (x, z) step
    public void moveListener( float xStep, float zStep )
    {
        float x = listenerX + xStep;
        float z = listenerZ + zStep;
        setListenerPos( x, z );
    }
    
    // position the listener at (xNew, zNew)
    public void setListenerPos( float xNew, float zNew )
    {
        float xOffset = xNew - listenerX;
        float zOffset = zNew - listenerZ;
        
        listenerX = xNew;
        listenerZ = zNew;
        al.alListener3f( AL.AL_POSITION, listenerX, listenerY, listenerZ );
        
        // keep the listener facing the same direction by
        // moving the "look at" point by the (x,z) offset
        listenerOrientation[0] += xOffset;
        listenerOrientation[2] += zOffset;
        // no need to change the y-coord since listener only moves over XZ plane
        
        al.alListenerfv( AL.AL_ORIENTATION, listenerOrientation, 0 );
    }
    
    // turn the listener counterclockwise by deg DEGREES
    public void turnListener( int deg )
    {
        setListenerOrientation( (int)Math.toDegrees(listenerAngle) + deg );
    }
    
    // turn the listener counterclockwise by rad RADIANS
    public void turnListener( float rad )
    {
        setListenerOrientation( listenerAngle + rad );
    }
    
    // set the listener's orientation to be deg DEGREES
    // in the counterclockwise direction around the y-axis
    public void setListenerOrientation( int deg )
    {
        listenerAngle = (float)Math.toRadians( deg );
        double angle = (double) listenerAngle;
        float xLen = -1.0f * (float) Math.sin( angle );
        float zLen = -1.0f * (float) Math.cos( angle );
        
        // face in the (xLen, zLen direction by adding the
        // values to te listener position
        listenerOrientation[0] = listenerX + xLen;
        listenerOrientation[2] = listenerZ + zLen;
        al.alListenerfv( AL.AL_ORIENTATION, listenerOrientation, 0 );
    }
    
    // set the listener's orientation to be rad RADIANS
    // in the counterclockwise direction around the y-axis
    public void setListenerOrientation( float rad )
    {
        listenerAngle = rad;
        double angle = (double) listenerAngle;
        float xLen = -1.0f * (float) Math.sin( angle );
        float zLen = -1.0f * (float) Math.cos( angle );
        
        // face in the (xLen, zLen) direction by adding the
        // values to te listener position
        listenerOrientation[0] = listenerX + xLen;
        listenerOrientation[2] = listenerZ + zLen;
        al.alListenerfv( AL.AL_ORIENTATION, listenerOrientation, 0 );
    }
    
}


And here is the code for loading, playing, and moving the sound in my program:
        String soundName = "tada";

        JOALSoundMan soundMan = new JOALSoundMan();

        if( !soundMan.load( soundName, true ) )
            System.exit( 1 );
        // default position for sound is (0,0,0)
        soundMan.play( soundName );

        // move the sound along the negative z-axis
        float step = 0.1f;
            // I have tried using a larger number like 100.0f;

        float zPos = 0.0f;

        for( int i=0; i < 50; i++ )
        {
            zPos -= step;
            soundMan.setPos( soundName, 0, 0, zPos );
            try
            {
                Thread.sleep( 250 ); // sleep for 0.25 secs
            }
            catch( InterruptedException ex ) {}
        }

        soundMan.cleanUp();


Anybody have an idea what I am doing wrong?

Matzon

wrong forum!
LWJGL has its own OpenAL binding, which is not compatible with JOAL.
Please use the JOAL forum instead:
http://www.javagaming.org/forums/index.php?board=26.0

paulscode

Aha!

Actually, I would prefer to use the lwjgl binding then.  Could you direct me to a good "Getting Started" type guide for the lwjgl binding?

Thank you very much!


paulscode

Great, that is an awesome guide.  Ok, so I recreated my Sound Manager in lwjgl, but I am having the same problem.  The sound is supposed to be moving away from the listener, but the volume does not change.

Here is the code for the Sound Manager:
import java.io.InputStream;

import java.nio.IntBuffer;
import java.nio.FloatBuffer;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10;
import org.lwjgl.util.WaveData;

public class SoundManager
{    
    // Global stores for the sounds:
    private HashMap<String, IntBuffer> buffersMap;  // (name, buffer) pairs
    private HashMap<String, IntBuffer> sourcesMap;  // (name, buffer) pairs

    private FloatBuffer listenerPosition;  // Listener's position
    private FloatBuffer listenerOrientation;  // Listener's orientation
    private FloatBuffer listenerVelocity;  // Listener's velocity
    
    private float listenerAngle = 0;  // listener's angle (in radians)
    
    public SoundManager()
    {
        buffersMap = new HashMap<String, IntBuffer>();
        sourcesMap = new HashMap<String, IntBuffer>();
        
        initOpenAL();
        initListener();
    }
    
    private void initOpenAL()
    {
        try
        {
            AL.create();
        }
        catch( LWJGLException e )
        {
            e.printStackTrace();
            return;
        }
        AL10.alGetError();        
    }    
    
    // Position and orientate the listener:
    private void initListener()
    {
        // Set the listener's initial position:
        listenerPosition = BufferUtils.createFloatBuffer( 3 ).put( 
            new float[] { 0.0f, 0.0f, 0.0f } );
        listenerPosition.flip();
        
        // Set the listener's initial orientation:
        // The first 3 elements are the "look at" point
        // The second 3 elements are the "up direction"
        listenerOrientation = BufferUtils.createFloatBuffer( 6 ).put (
            new float[] { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f } );
        listenerOrientation.flip();
        
        // Set the listener's initial velocity:
        listenerVelocity = BufferUtils.createFloatBuffer( 3 ).put (
            new float[] { 0.0f, 0.0f, 0.0f } );
        listenerVelocity.flip();
        
        AL10.alListener( AL10.AL_POSITION, listenerPosition );
        AL10.alListener( AL10.AL_VELOCITY, listenerVelocity );
        AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );
    }
    
    public void cleanUp()
    {
        Set<String> keys = sourcesMap.keySet();
        Iterator<String> iter = keys.iterator();
        
        String name;
        IntBuffer buffer, source;
        while( iter.hasNext() )
        {
            name = iter.next();
            
            source = sourcesMap.get( name );
            System.out.println( "Stopping " + name );
            AL10.alSourceStop( source );
            AL10.alDeleteSources( source );
            
            buffer = buffersMap.get( name );            
            AL10.alDeleteBuffers( buffer );
        }
    }
    
    public boolean load( String name, boolean toLoop )
    {
        if( sourcesMap.get( name ) != null )
        {
            System.out.println( name + " already loaded" );
            return true;
        }
        
        IntBuffer buffer = initBuffer( name );
        if( buffer == null )
            return false;
        
        IntBuffer source = initSource( name, buffer, toLoop );
        
        if( source == null )
        {
            // no need for the buffer anymore
            AL10.alDeleteBuffers( buffer );
            return false;
        }
        
        if( toLoop )
            System.out.println( "Looping source created for " + name );
        else
            System.out.println( "Source created for " + name );
        
        buffersMap.put( name, buffer );
        sourcesMap.put( name, source );
        return true;
    }
        
    public boolean load( String name, float x, float y, float z, boolean toLoop )
    {
        if( load( name, toLoop ) )
            return setPos( name, x, y, z );
        else
            return false;
    }
    
    // return a handle to the given resource
    private InputStream getResource( String resourceName )
    {
        return getClass().getClassLoader().getResourceAsStream( resourceName );
    }
    
    // Load a file, and create a buffer for it:
    private IntBuffer initBuffer( String filename )
    {
        WaveData waveFile = WaveData.create( getResource( "Sounds/" + filename ) );
        
        IntBuffer buffer = BufferUtils.createIntBuffer( 1 );
        AL10.alGenBuffers( buffer );
        if( AL10.alGetError() != AL10.AL_NO_ERROR )
        {
            System.out.println( "Error loading file: " + filename );
            return null;
        }
        AL10.alBufferData( buffer.get( 0 ), waveFile.format, waveFile.data, waveFile.samplerate );
        return buffer;
    }
    
    // Create a source (a point in space that makes sound):
    private IntBuffer initSource( String name, IntBuffer buffer, boolean toLoop )
    {
        IntBuffer source = BufferUtils.createIntBuffer( 1 );
        AL10.alGenSources( source );
        
        // Check for errors:
        if( AL10.alGetError() != AL10.AL_NO_ERROR )
        {
            System.out.println( "Error creating a source for: " + name );
            return null;
        }        
        
        // Position the source at the origin:
        FloatBuffer sourcePosition = BufferUtils.createFloatBuffer( 3 ).put( 
            new float[] { 0.0f, 0.0f, 0.0f } );
        sourcePosition.flip();
        
        // The source has no initial velocity:
        FloatBuffer sourceVelocity = BufferUtils.createFloatBuffer( 3 ).put( 
            new float[] { 0.0f, 0.0f, 0.0f } );
        sourceVelocity.flip();

        AL10.alSourcei( source.get( 0 ), AL10.AL_BUFFER, buffer.get(0) );
        AL10.alSourcef( source.get( 0 ), AL10.AL_PITCH, 1.0f );
        AL10.alSourcef( source.get( 0 ), AL10.AL_GAIN, 1.0f );
        AL10.alSource( source.get( 0 ), AL10.AL_POSITION, sourcePosition );
        AL10.alSource( source.get( 0 ), AL10.AL_VELOCITY, sourceVelocity );
        
        if( toLoop )
            AL10.alSourcei( source.get( 0 ), AL10.AL_LOOPING, AL10.AL_TRUE );  // looping
        else
            AL10.alSourcei( source.get( 0 ), AL10.AL_LOOPING, AL10.AL_FALSE );  // looping
        
        return source;
    }
    
    // Move the nm sound to (x, y, z):
    public boolean setPos( String name, float x, float y, float z )
    {
        IntBuffer source = sourcesMap.get( name );
        if( source == null )
        {
            System.out.println( "No source found for " + name );
            return false;
        }
        
        // Create a FloatBuffer with the given coordinates:
        FloatBuffer sourcePosition = BufferUtils.createFloatBuffer( 3 ).put( 
            new float[] { x, y, z } );
        sourcePosition.flip();
        
        AL10.alSource( source.get( 0 ), AL10.AL_POSITION, sourcePosition );
        return true;
    }
    
    public boolean play( String name )
    {
        IntBuffer source = sourcesMap.get( name );
        if( source == null )
        {
            System.out.println( "No source found for " + name );
            return false;
        }
        
        AL10.alSourcePlay( source.get( 0 ) );
        return true;
    }
    
    // move the listener by (x, z) step
    public void moveListener( float xStep, float zStep )
    {
        float x = listenerPosition.get( 0 ) + xStep;
        float z = listenerPosition.get( 2 ) + zStep;
        setListenerPos( x, z );
    }
    
    // position the listener at (xNew, zNew)
    public void setListenerPos( float xNew, float zNew )
    {
        float xOffset = xNew - listenerPosition.get( 0 );
        float zOffset = zNew - listenerPosition.get( 2 );
        
        // We are not changing the y-coord:
        // ( Listener only moves over XZ plane )
        listenerPosition.put( new float[] {xNew, 0.0f, zNew} );
        listenerPosition.flip();
        
        AL10.alListener( AL10.AL_POSITION, listenerPosition );  // update the listener's position
        
        // Keep the listener facing the same direction by
        // moving the "look at" point by the offset values:
        listenerOrientation.put( 0, listenerOrientation.get( 0 ) + xOffset );
        listenerOrientation.put( 2, listenerOrientation.get( 2 ) + zOffset );
        listenerOrientation.flip();
        
        AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );  // update the listener's orientation
    }
    
    // turn the listener counterclockwise by "angle" radians
    public void turnListener( float angle )
    {
        setListenerOrientation( listenerAngle + angle );
    }
    
    // set the listener's orientation to be "angle" radians
    // in the counterclockwise direction around the y-axis
    public void setListenerOrientation( float angle )
    {
        listenerAngle = angle;
        
        float xOffset = -1.0f * (float) Math.sin( angle );
        float zOffset = -1.0f * (float) Math.cos( angle );
        
        // face in the (xLen, zLen) direction by adding the
        // offset values to the listener's "look at" point:
        listenerOrientation.put( 0, listenerOrientation.get( 0 ) + xOffset );
        listenerOrientation.put( 2, listenerOrientation.get( 2 ) + zOffset );
        listenerOrientation.flip();
        
        AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );  // update the listener's orientation
    }
}


And here is the code for loading, playing, and moving the sound in my program:
        String soundName = "tada.wav";

        SoundManager soundManager = new SoundManager();

        if( !soundManager.load( soundName, true ) )
            System.exit( 1 );
        // default position for sound is (0,0,0)
        soundManager.play( soundName );

        // move the sound along the negative z-axis
        //float step = 0.1f;
        float step = 100f;
        float zPos = 0.0f;

        for( int i=0; i < 50; i++ )
        {
            zPos -= step;
            soundManager.setPos( soundName, 0, 0, zPos );
            try
            {
                Thread.sleep( 250 ); // sleep for 0.25 secs
            }
            catch( InterruptedException ex ) {}
        }

        soundManager.cleanUp();


Any ideas about what I might be doing wrong?

princec

You need to set the attenuation with alDistanceModel (see this).

Cas :)

paulscode

Ok, that is a good reference for how attenuation works.  However, it doesn't include any examples or source code to show how to actually use it.  Anybody have some source code for setting up the Distance Model and creating a source that gets quieter as it moves away from the listener?

paulscode

I figured out my problem - I was using stereo files instead of mono.

Thanks for all the great help, guys!