/** * Copyright (c) 2003-2009, Xith3D Project Group all rights reserved. * * Portions based on the Java3D interface, Copyright by Sun Microsystems. * Many thanks to the developers of Java3D and Sun Microsystems for their * innovation and design. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the 'Xith3D Project Group' nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A * RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE */ package org.xith3d.sound.drivers.joal; import java.util.ArrayList; import java.util.List; import org.openmali.vecmath2.Tuple3f; import net.java.games.joal.AL; import net.java.games.joal.ALC; import net.java.games.joal.ALCcontext; import net.java.games.joal.ALCdevice; import net.java.games.joal.ALFactory; import org.xith3d.sound.SoundBuffer; import org.xith3d.sound.SoundDriver; import org.xith3d.sound.SoundException; import org.xith3d.sound.SoundSource; import org.xith3d.utility.logging.X3DLog; /** * Sound Driver Implementation for JOAL (Java Open Audio Library, OpenAL * binding). * * @author David Yazel * @author Marvin Froehlich (aka Qudus) */ public class SoundDriverImpl implements SoundDriver { private AL al; private ALC alc; private List< SoundSource > sources; private List< SoundBufferImpl > buffers; private ALCdevice device; private ALCcontext context; private List< SoundSource > availableSources; private float listenerVolume = 1.0f; private float dopplerVelocity = 1.0f; private float dopplerFactor = 0.0f; private boolean isOnline; protected AL getAL() { return ( al ); } protected String decodeSoundError( int error ) { switch ( error ) { case AL.AL_NO_ERROR: return ( "NO ERROR" ); case AL.AL_INVALID_ENUM: return ( "INVALID ENUM" ); case AL.AL_INVALID_VALUE: return ( "INVALID VALUE" ); case AL.AL_INVALID_NAME: return ( "INVALID NAME" ); case AL.AL_INVALID_OPERATION: return ( "INVALID OPERATION" ); case AL.AL_OUT_OF_MEMORY: return ( "OUT OF MEMORY" ); default: return ( "UNKNOWN ERROR" ); } } protected void checkError() { int error = getAL().alGetError(); if ( error != AL.AL_NO_ERROR ) throw new Error( decodeSoundError( error ) ); } /** * {@inheritDoc} */ public void newFrameSync() { // step through and remove all the processed buffers int sourceList[] = new int[ sources.size() ]; for ( int i = 0; i < sourceList.length; i++ ) { SoundSourceImpl ss = (SoundSourceImpl)sources.get( i ); sourceList[ i ] = ss.handle; ss.stop(); } } /** * {@inheritDoc} */ public void newFrameAsync() { } /** * {@inheritDoc} */ public void setListenerVelocity( Tuple3f velocity ) { al.alListener3f( AL.AL_VELOCITY, velocity.getX(), velocity.getY(), velocity.getZ() ); checkError(); } /** * {@inheritDoc} */ public void setListenerPosition( Tuple3f position ) { al.alListenerfv( AL.AL_POSITION, new float[] { position.getX(), position.getY(), position.getZ() }, 0 ); checkError(); } /** * {@inheritDoc} */ public void setListenerOrientation( Tuple3f direction, Tuple3f up ) { al.alListenerfv( AL.AL_ORIENTATION, new float[] { direction.getX(), direction.getY(), direction.getZ(), up.getX(), up.getY(), up.getZ() }, 0 ); } /** * {@inheritDoc} */ public void setListenerVolume( float gain ) { al.alListenerf( AL.AL_GAIN, gain ); checkError(); this.listenerVolume = gain; } /** * {@inheritDoc} */ public float getListenerVolume() { return ( listenerVolume ); } /** * {@inheritDoc} */ public void setDopplerVelocity( float velocity ) { al.alDopplerVelocity( velocity ); checkError(); this.dopplerVelocity = velocity; } /** * {@inheritDoc} */ public float getDopplerVelocity() { return ( dopplerVelocity ); } /** * {@inheritDoc} */ public void setDopplerFactor( float factor ) { al.alDopplerFactor( factor ); this.dopplerFactor = factor; } /** * {@inheritDoc} */ public float getDopplerFactor() { return ( dopplerFactor ); } /** * {@inheritDoc} */ public SoundSource allocateSoundSource() throws SoundException { if ( availableSources.size() == 0 ) throw new SoundException( "no sound sources available" ); SoundSource s = availableSources.remove( availableSources.size() - 1 ); sources.add( s ); return ( s ); } /** * {@inheritDoc} */ public SoundBuffer allocateSoundBuffer() { return ( new SoundBufferImpl( this ) ); } /** * {@inheritDoc} */ public void delete( SoundSource source ) { sources.remove( source ); ( (SoundSourceImpl)source ).releaseCachedResources(); availableSources.add( source ); } /** * {@inheritDoc} */ public void delete( SoundBuffer buffer ) { // TODO } /** * {@inheritDoc} */ public int getNumAvailableSources() { return ( availableSources.size() ); } /** * {@inheritDoc} */ public int getNumSources() { return ( sources.size() + availableSources.size() ); } /** * {@inheritDoc} */ public void shutdown() { if ( !isOnline() ) { return; } // stop all the sources int[] sourceList = new int[ sources.size() + availableSources.size() ]; int i = 0; for ( int j = 0; j < sources.size(); j++ ) { SoundSourceImpl ss = (SoundSourceImpl)sources.get( j ); sourceList[ i++ ] = ss.handle; ss.stop(); } for ( int j = 0; j < availableSources.size(); j++ ) { SoundSourceImpl ss = (SoundSourceImpl)availableSources.get( j ); sourceList[ i++ ] = ss.handle; ss.stop(); } // destroy the sources if ( sourceList.length > 0 ) { al.alDeleteSources( sourceList.length, sourceList, 0 ); checkError(); } // delete all the buffers int[] bufferList = new int[ buffers.size() ]; for ( int j = 0; i < bufferList.length; j++ ) { SoundBufferImpl ss = buffers.get( j ); bufferList[ j ] = ss.handle; } if ( bufferList.length > 0 ) { al.alDeleteBuffers( bufferList.length, bufferList, 0 ); checkError(); } X3DLog.debug( "Making sound context current" ); alc.alcMakeContextCurrent( context ); X3DLog.debug( "Destroying context" ); alc.alcDestroyContext( context ); X3DLog.debug( "Closing device" ); alc.alcCloseDevice( device ); this.isOnline = false; } /** * {@inheritDoc} */ public final boolean isOnline() { return ( isOnline ); } public SoundDriverImpl() { try { al = ALFactory.getAL(); alc = ALFactory.getALC(); } catch ( Exception e ) { throw new RuntimeException( e ); } try { device = alc.alcOpenDevice( System.getProperty( "XITH3D_OPENAL_DEVICE" ) ); } catch ( SecurityException ignore ) { // Ignore a SecurityException for Applet deployment } if ( device == null ) throw new Error( "No sound device found" ); context = alc.alcCreateContext( device, null ); alc.alcMakeContextCurrent( context ); checkError(); sources = new ArrayList< SoundSource >(); buffers = new ArrayList< SoundBufferImpl >(); availableSources = new ArrayList< SoundSource >(); al.alDistanceModel( AL.AL_INVERSE_DISTANCE ); checkError(); // allocate all available sound sources boolean done = false; int n = 0; while ( !done ) { try { SoundSourceImpl ss = new SoundSourceImpl( this ); availableSources.add( ss ); if ( n++ == 60 ) break; } catch ( Error e ) { X3DLog.print( e ); done = true; } } setListenerVolume( 1.0f ); this.isOnline = true; X3DLog.debug( "OpenAL sound driver initialized with ", availableSources.size(), " available sources" ); } }