Path: blob/main/src/lwjgl/java/paulscode/sound/libraries/LibraryLWJGLOpenAL.java
8650 views
package paulscode.sound.libraries;12import java.nio.ByteBuffer;3import java.nio.IntBuffer;4import java.nio.FloatBuffer;5import java.net.URL;6import java.util.HashMap;7import java.util.Iterator;8import java.util.Set;9import javax.sound.sampled.AudioFormat;1011// From the lwjgl library, http://www.lwjgl.org12import org.lwjgl.BufferUtils;13import org.lwjgl.LWJGLException;14import org.lwjgl.openal.AL;15import org.lwjgl.openal.AL10;1617import paulscode.sound.Channel;18import paulscode.sound.FilenameURL;19import paulscode.sound.ICodec;20import paulscode.sound.Library;21import paulscode.sound.ListenerData;22import paulscode.sound.SoundBuffer;23import paulscode.sound.SoundSystemConfig;24import paulscode.sound.SoundSystemException;25import paulscode.sound.Source;2627/**28* The LibraryLWJGLOpenAL class interfaces the lwjgl binding of OpenAL.29*<b><br><br>30* This software is based on or using the LWJGL Lightweight Java Gaming31* Library available from32* http://www.lwjgl.org/.33*</b><br><br>34* LWJGL License:35*<br><i>36* Copyright (c) 2002-2008 Lightweight Java Game Library Project37* All rights reserved.38*<br>39* Redistribution and use in source and binary forms, with or without40* modification, are permitted provided that the following conditions are41* met:42* <br>43* * Redistributions of source code must retain the above copyright44* notice, this list of conditions and the following disclaimer.45*<br>46* * Redistributions in binary form must reproduce the above copyright47* notice, this list of conditions and the following disclaimer in the48* documentation and/or other materials provided with the distribution.49*<br>50* * Neither the name of 'Light Weight Java Game Library' nor the names of51* its contributors may be used to endorse or promote products derived52* from this software without specific prior written permission.53* <br>54* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS55* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED56* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR57* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR58* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,59* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,60* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR61* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF62* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING63* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS64* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.65* <br><br><br></i>66*<b><i> SoundSystem LibraryLWJGLOpenAL License:</b></i><br><b><br>67*<b>68* You are free to use this library for any purpose, commercial or otherwise.69* You may modify this library or source code, and distribute it any way you70* like, provided the following conditions are met:71*<br>72* 1) You must abide by the conditions of the aforementioned LWJGL License.73*<br>74* 2) You may not falsely claim to be the author of this library or any75* unmodified portion of it.76*<br>77* 3) You may not copyright this library or a modified version of it and then78* sue me for copyright infringement.79*<br>80* 4) If you modify the source code, you must clearly document the changes81* made before redistributing the modified source code, so other users know82* it is not the original code.83*<br>84* 5) You are not required to give me credit for this library in any derived85* work, but if you do, you must also mention my website:86* http://www.paulscode.com87*<br>88* 6) I the author will not be responsible for any damages (physical,89* financial, or otherwise) caused by the use if this library or any part90* of it.91*<br>92* 7) I the author do not guarantee, warrant, or make any representations,93* either expressed or implied, regarding the use of this library or any94* part of it.95* <br><br>96* Author: Paul Lamb97* <br>98* http://www.paulscode.com99* </b>100*/101public class LibraryLWJGLOpenAL extends Library102{103/**104* Used to return a current value from one of the synchronized105* boolean-interface methods.106*/107private static final boolean GET = false;108/**109* Used to set the value in one of the synchronized boolean-interface methods.110*/111private static final boolean SET = true;112/**113* Used when a parameter for one of the synchronized boolean-interface methods114* is not aplicable.115*/116private static final boolean XXX = false;117118/**119* Position of the listener in 3D space.120*/121private FloatBuffer listenerPositionAL = null;122/**123* Information about the listener's orientation.124*/125private FloatBuffer listenerOrientation = null;126/**127* Velocity of the listener.128*/129private FloatBuffer listenerVelocity = null;130/**131* Map containing OpenAL identifiers for sound buffers.132*/133private HashMap<String, IntBuffer> ALBufferMap = null;134135/**136* Whether or not the AL_PITCH control is supported.137*/138private static boolean alPitchSupported = true;139140/**141* Constructor: Instantiates the source map, buffer map and listener142* information. Also sets the library type to143* SoundSystemConfig.LIBRARY_OPENAL144*/145public LibraryLWJGLOpenAL() throws SoundSystemException146{147super();148ALBufferMap = new HashMap<String, IntBuffer>();149reverseByteOrder = true;150}151152/**153* Initializes OpenAL, creates the listener, and grabs up audio channels.154*/155@Override156public void init() throws SoundSystemException157{158boolean errors = false; // set to 'true' if error(s) occur:159160try161{162// Try and create the sound system:163AL.create();164errors = checkALError();165}166catch( LWJGLException e )167{168// There was an exception169errorMessage( "Unable to initialize OpenAL. Probable cause: " +170"OpenAL not supported." );171printStackTrace( e );172throw new LibraryLWJGLOpenAL.Exception( e.getMessage(),173LibraryLWJGLOpenAL.Exception.CREATE );174}175176// Let user know if the library loaded properly177if( errors )178importantMessage( "OpenAL did not initialize properly!" );179else180message( "OpenAL initialized." );181182// Listener is at the origin, facing along the z axis, no velocity:183listenerPositionAL = BufferUtils.createFloatBuffer( 3 ).put(184new float[] { listener.position.x,185listener.position.y,186listener.position.z } );187listenerOrientation = BufferUtils.createFloatBuffer( 6 ).put (188new float[] { listener.lookAt.x, listener.lookAt.y,189listener.lookAt.z, listener.up.x, listener.up.y,190listener.up.z } );191listenerVelocity = BufferUtils.createFloatBuffer( 3 ).put (192new float[] { 0.0f, 0.0f, 0.0f } );193194// Flip the buffers, so they can be used:195listenerPositionAL.flip();196listenerOrientation.flip();197listenerVelocity.flip();198199// Pass the buffers to the sound system, and check for potential errors:200AL10.alListener( AL10.AL_POSITION, listenerPositionAL );201errors = checkALError() || errors;202AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );203errors = checkALError() || errors;204AL10.alListener( AL10.AL_VELOCITY, listenerVelocity );205errors = checkALError() || errors;206207AL10.alDopplerFactor( SoundSystemConfig.getDopplerFactor() );208errors = checkALError() || errors;209210AL10.alDopplerVelocity( SoundSystemConfig.getDopplerVelocity() );211errors = checkALError() || errors;212213// Let user know what caused the above error messages:214if( errors )215{216importantMessage( "OpenAL did not initialize properly!" );217throw new LibraryLWJGLOpenAL.Exception( "Problem encountered " +218"while loading OpenAL or " +219"creating the listener. " +220"Probable cause: OpenAL not " +221"supported",222LibraryLWJGLOpenAL.Exception.CREATE );223}224225super.init();226227// Check if we can use the AL_PITCH control:228ChannelLWJGLOpenAL channel = (ChannelLWJGLOpenAL)229normalChannels.get( 1 );230try231{232AL10.alSourcef( channel.ALSource.get( 0 ),233AL10.AL_PITCH, 1.0f );234if( checkALError() )235{236alPitchSupported( SET, false );237throw new LibraryLWJGLOpenAL.Exception( "OpenAL: AL_PITCH not " +238"supported.", LibraryLWJGLOpenAL.Exception.NO_AL_PITCH );239}240else241{242alPitchSupported( SET, true );243}244}245catch( java.lang.Exception e )246{247alPitchSupported( SET, false );248throw new LibraryLWJGLOpenAL.Exception( "OpenAL: AL_PITCH not " +249"supported.", LibraryLWJGLOpenAL.Exception.NO_AL_PITCH );250}251}252253/**254* Checks if the OpenAL library type is compatible.255* @return True or false.256*/257public static boolean libraryCompatible()258{259if( AL.isCreated() )260return true;261262try263{264AL.create();265}266catch( java.lang.Exception e )267{268return false;269}270271try272{273AL.destroy();274}275catch( java.lang.Exception e )276{}277278return true;279}280281/**282* Creates a new channel of the specified type (normal or streaming). Possible283* values for channel type can be found in the284* {@link paulscode.sound.SoundSystemConfig SoundSystemConfig} class.285* @param type Type of channel.286*/287@Override288protected Channel createChannel( int type )289{290ChannelLWJGLOpenAL channel;291IntBuffer ALSource;292293ALSource = BufferUtils.createIntBuffer( 1 );294try295{296AL10.alGenSources( ALSource );297}298catch( java.lang.Exception e )299{300AL10.alGetError();301return null; // no more voices left302}303304if( AL10.alGetError() != AL10.AL_NO_ERROR )305return null;306307channel = new ChannelLWJGLOpenAL( type, ALSource );308return channel;309}310311/**312* Stops all sources, shuts down OpenAL, and removes references to all313* instantiated objects.314*/315@Override316public void cleanup()317{318super.cleanup();319320Set<String> keys = bufferMap.keySet();321Iterator<String> iter = keys.iterator();322String filename;323IntBuffer buffer;324325// loop through and clear all sound buffers:326while( iter.hasNext() )327{328filename = iter.next();329buffer = ALBufferMap.get( filename );330if( buffer != null )331{332AL10.alDeleteBuffers( buffer );333checkALError();334buffer.clear();335}336}337338bufferMap.clear();339AL.destroy();340341bufferMap = null;342listenerPositionAL = null;343listenerOrientation = null;344listenerVelocity = null;345}346347/**348* Pre-loads a sound into memory.349* @param filenameURL Filename/URL of the sound file to load.350* @return True if the sound loaded properly.351*/352@Override353public boolean loadSound( FilenameURL filenameURL )354{355// Make sure the buffer map exists:356if( bufferMap == null )357{358bufferMap = new HashMap<String, SoundBuffer>();359importantMessage( "Buffer Map was null in method 'loadSound'" );360}361// Make sure the OpenAL buffer map exists:362if( ALBufferMap == null )363{364ALBufferMap = new HashMap<String, IntBuffer>();365importantMessage( "Open AL Buffer Map was null in method" +366"'loadSound'" );367}368369// make sure they gave us a filename:370if( errorCheck( filenameURL == null,371"Filename/URL not specified in method 'loadSound'" ) )372return false;373374// check if it is already loaded:375if( bufferMap.get( filenameURL.getFilename() ) != null )376return true;377378ICodec codec = SoundSystemConfig.getCodec( filenameURL.getFilename() );379if( errorCheck( codec == null, "No codec found for file '" +380filenameURL.getFilename() +381"' in method 'loadSound'" ) )382return false;383codec.reverseByteOrder( true );384385URL url = filenameURL.getURL();386if( errorCheck( url == null, "Unable to open file '" +387filenameURL.getFilename() +388"' in method 'loadSound'" ) )389return false;390391codec.initialize( url );392SoundBuffer buffer = codec.readAll();393codec.cleanup();394codec = null;395if( errorCheck( buffer == null,396"Sound buffer null in method 'loadSound'" ) )397return false;398399bufferMap.put( filenameURL.getFilename(), buffer );400401AudioFormat audioFormat = buffer.audioFormat;402int soundFormat = 0;403if( audioFormat.getChannels() == 1 )404{405if( audioFormat.getSampleSizeInBits() == 8 )406{407soundFormat = AL10.AL_FORMAT_MONO8;408}409else if( audioFormat.getSampleSizeInBits() == 16 )410{411soundFormat = AL10.AL_FORMAT_MONO16;412}413else414{415errorMessage( "Illegal sample size in method 'loadSound'" );416return false;417}418}419else if( audioFormat.getChannels() == 2 )420{421if( audioFormat.getSampleSizeInBits() == 8 )422{423soundFormat = AL10.AL_FORMAT_STEREO8;424}425else if( audioFormat.getSampleSizeInBits() == 16 )426{427soundFormat = AL10.AL_FORMAT_STEREO16;428}429else430{431errorMessage( "Illegal sample size in method 'loadSound'" );432return false;433}434}435else436{437errorMessage( "File neither mono nor stereo in method " +438"'loadSound'" );439return false;440}441442IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 );443AL10.alGenBuffers( intBuffer );444if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR,445"alGenBuffers error when loading " +446filenameURL.getFilename() ) )447return false;448449// AL10.alBufferData( intBuffer.get( 0 ), soundFormat,450// ByteBuffer.wrap( buffer.audioData ),451// (int) audioFormat.getSampleRate() );452AL10.alBufferData( intBuffer.get( 0 ), soundFormat,453(ByteBuffer) BufferUtils.createByteBuffer(454buffer.audioData.length ).put(455buffer.audioData ).flip(),456(int) audioFormat.getSampleRate() );457458if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR,459"alBufferData error when loading " +460filenameURL.getFilename() ) )461462463if( errorCheck( intBuffer == null,464"Sound buffer was not created for " +465filenameURL.getFilename() ) )466return false;467468ALBufferMap.put( filenameURL.getFilename(), intBuffer );469470return true;471}472473/**474* Saves the specified sample data, under the specified identifier. This475* identifier can be later used in place of 'filename' parameters to reference476* the sample data.477* @param buffer the sample data and audio format to save.478* @param identifier What to call the sample.479* @return True if there weren't any problems.480*/481@Override482public boolean loadSound( SoundBuffer buffer, String identifier )483{484// Make sure the buffer map exists:485if( bufferMap == null )486{487bufferMap = new HashMap<String, SoundBuffer>();488importantMessage( "Buffer Map was null in method 'loadSound'" );489}490// Make sure the OpenAL buffer map exists:491if( ALBufferMap == null )492{493ALBufferMap = new HashMap<String, IntBuffer>();494importantMessage( "Open AL Buffer Map was null in method" +495"'loadSound'" );496}497498// make sure they gave us an identifier:499if( errorCheck( identifier == null,500"Identifier not specified in method 'loadSound'" ) )501return false;502503// check if it is already loaded:504if( bufferMap.get( identifier ) != null )505return true;506507if( errorCheck( buffer == null,508"Sound buffer null in method 'loadSound'" ) )509return false;510511bufferMap.put( identifier, buffer );512513AudioFormat audioFormat = buffer.audioFormat;514int soundFormat = 0;515if( audioFormat.getChannels() == 1 )516{517if( audioFormat.getSampleSizeInBits() == 8 )518{519soundFormat = AL10.AL_FORMAT_MONO8;520}521else if( audioFormat.getSampleSizeInBits() == 16 )522{523soundFormat = AL10.AL_FORMAT_MONO16;524}525else526{527errorMessage( "Illegal sample size in method 'loadSound'" );528return false;529}530}531else if( audioFormat.getChannels() == 2 )532{533if( audioFormat.getSampleSizeInBits() == 8 )534{535soundFormat = AL10.AL_FORMAT_STEREO8;536}537else if( audioFormat.getSampleSizeInBits() == 16 )538{539soundFormat = AL10.AL_FORMAT_STEREO16;540}541else542{543errorMessage( "Illegal sample size in method 'loadSound'" );544return false;545}546}547else548{549errorMessage( "File neither mono nor stereo in method " +550"'loadSound'" );551return false;552}553554IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 );555AL10.alGenBuffers( intBuffer );556if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR,557"alGenBuffers error when saving " +558identifier ) )559return false;560561// AL10.alBufferData( intBuffer.get( 0 ), soundFormat,562// ByteBuffer.wrap( buffer.audioData ),563// (int) audioFormat.getSampleRate() );564AL10.alBufferData( intBuffer.get( 0 ), soundFormat,565(ByteBuffer) BufferUtils.createByteBuffer(566buffer.audioData.length ).put(567buffer.audioData ).flip(),568(int) audioFormat.getSampleRate() );569570if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR,571"alBufferData error when saving " +572identifier ) )573574575if( errorCheck( intBuffer == null,576"Sound buffer was not created for " +577identifier ) )578return false;579580ALBufferMap.put( identifier, intBuffer );581582return true;583}584585/**586* Removes a pre-loaded sound from memory. This is a good method to use for587* freeing up memory after a large sound file is no longer needed. NOTE: the588* source will remain in memory after this method has been called, for as long589* as the sound is attached to an existing source.590* @param filename Filename/identifier of the sound file to unload.591*/592@Override593public void unloadSound( String filename )594{595ALBufferMap.remove( filename );596super.unloadSound( filename );597}598599/**600* Sets the overall volume to the specified value, affecting all sources.601* @param value New volume, float value ( 0.0f - 1.0f ).602*/603@Override604public void setMasterVolume( float value )605{606super.setMasterVolume( value );607608AL10.alListenerf( AL10.AL_GAIN, value );609checkALError();610}611612/**613* Creates a new source and places it into the source map.614* @param priority Setting this to true will prevent other sounds from overriding this one.615* @param toStream Setting this to true will load the sound in pieces rather than all at once.616* @param toLoop Should this source loop, or play only once.617* @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.618* @param filenameURL Filename/URL of the sound file to play at this source.619* @param x X position for this source.620* @param y Y position for this source.621* @param z Z position for this source.622* @param attModel Attenuation model to use.623* @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel".624*/625@Override626public void newSource( boolean priority, boolean toStream, boolean toLoop,627String sourcename, FilenameURL filenameURL, float x,628float y, float z, int attModel, float distOrRoll )629{630IntBuffer myBuffer = null;631if( !toStream )632{633// Grab the sound buffer for this file:634myBuffer = ALBufferMap.get( filenameURL.getFilename() );635636// if not found, try loading it:637if( myBuffer == null )638{639if( !loadSound( filenameURL ) )640{641errorMessage( "Source '" + sourcename + "' was not created "642+ "because an error occurred while loading "643+ filenameURL.getFilename() );644return;645}646}647648// try and grab the sound buffer again:649myBuffer = ALBufferMap.get( filenameURL.getFilename() );650// see if it was there this time:651if( myBuffer == null )652{653errorMessage( "Source '" + sourcename + "' was not created "654+ "because a sound buffer was not found for "655+ filenameURL.getFilename() );656return;657}658}659SoundBuffer buffer = null;660661if( !toStream )662{663// Grab the audio data for this file:664buffer = bufferMap.get( filenameURL.getFilename() );665// if not found, try loading it:666if( buffer == null )667{668if( !loadSound( filenameURL ) )669{670errorMessage( "Source '" + sourcename + "' was not created "671+ "because an error occurred while loading "672+ filenameURL.getFilename() );673return;674}675}676// try and grab the sound buffer again:677buffer = bufferMap.get( filenameURL.getFilename() );678// see if it was there this time:679if( buffer == null )680{681errorMessage( "Source '" + sourcename + "' was not created "682+ "because audio data was not found for "683+ filenameURL.getFilename() );684return;685}686}687688sourceMap.put( sourcename,689new SourceLWJGLOpenAL( listenerPositionAL, myBuffer,690priority, toStream, toLoop,691sourcename, filenameURL, buffer, x,692y, z, attModel, distOrRoll,693false ) );694}695696/**697* Opens a direct line for streaming audio data.698* @param audioFormat Format that the data will be in.699* @param priority Setting this to true will prevent other sounds from overriding this one.700* @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.701* @param x X position for this source.702* @param y Y position for this source.703* @param z Z position for this source.704* @param attModel Attenuation model to use.705* @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel".706*/707@Override708public void rawDataStream( AudioFormat audioFormat, boolean priority,709String sourcename, float x, float y,710float z, int attModel, float distOrRoll )711{712sourceMap.put( sourcename,713new SourceLWJGLOpenAL( listenerPositionAL, audioFormat,714priority, sourcename, x, y, z,715attModel, distOrRoll ) );716}717718/**719* Creates and immediately plays a new source.720* @param priority Setting this to true will prevent other sounds from overriding this one.721* @param toStream Setting this to true will load the sound in pieces rather than all at once.722* @param toLoop Should this source loop, or play only once.723* @param sourcename A unique identifier for this source. Two sources may not use the same sourcename.724* @param filenameURL Filename/URL of the sound file to play at this source.725* @param x X position for this source.726* @param y Y position for this source.727* @param z Z position for this source.728* @param attModel Attenuation model to use.729* @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel".730* @param temporary Whether or not this source should be removed after it finishes playing.731*/732@Override733public void quickPlay( boolean priority, boolean toStream, boolean toLoop,734String sourcename, FilenameURL filenameURL, float x,735float y, float z, int attModel, float distOrRoll,736boolean temporary )737{738IntBuffer myBuffer = null;739if( !toStream )740{741// Grab the sound buffer for this file:742myBuffer = ALBufferMap.get( filenameURL.getFilename() );743// if not found, try loading it:744if( myBuffer == null )745loadSound( filenameURL );746// try and grab the sound buffer again:747myBuffer = ALBufferMap.get( filenameURL.getFilename() );748// see if it was there this time:749if( myBuffer == null )750{751errorMessage( "Sound buffer was not created for " +752filenameURL.getFilename() );753return;754}755}756757SoundBuffer buffer = null;758759if( !toStream )760{761// Grab the sound buffer for this file:762buffer = bufferMap.get( filenameURL.getFilename() );763// if not found, try loading it:764if( buffer == null )765{766if( !loadSound( filenameURL ) )767{768errorMessage( "Source '" + sourcename + "' was not created "769+ "because an error occurred while loading "770+ filenameURL.getFilename() );771return;772}773}774// try and grab the sound buffer again:775buffer = bufferMap.get( filenameURL.getFilename() );776// see if it was there this time:777if( buffer == null )778{779errorMessage( "Source '" + sourcename + "' was not created "780+ "because audio data was not found for "781+ filenameURL.getFilename() );782return;783}784}785SourceLWJGLOpenAL s = new SourceLWJGLOpenAL( listenerPositionAL,786myBuffer, priority,787toStream, toLoop,788sourcename, filenameURL,789buffer, x, y, z,790attModel, distOrRoll,791false );792793sourceMap.put( sourcename, s );794play( s );795if( temporary )796s.setTemporary( true );797}798799/**800* Creates sources based on the source map provided.801* @param srcMap Sources to copy.802*/803@Override804public void copySources( HashMap<String, Source> srcMap )805{806if( srcMap == null )807return;808Set<String> keys = srcMap.keySet();809Iterator<String> iter = keys.iterator();810String sourcename;811Source source;812813// Make sure the buffer map exists:814if( bufferMap == null )815{816bufferMap = new HashMap<String, SoundBuffer>();817importantMessage( "Buffer Map was null in method 'copySources'" );818}819// Make sure the OpenAL buffer map exists:820if( ALBufferMap == null )821{822ALBufferMap = new HashMap<String, IntBuffer>();823importantMessage( "Open AL Buffer Map was null in method" +824"'copySources'" );825}826827// remove any existing sources before starting:828sourceMap.clear();829830SoundBuffer buffer;831// loop through and copy all the sources:832while( iter.hasNext() )833{834sourcename = iter.next();835source = srcMap.get( sourcename );836if( source != null )837{838buffer = null;839if( !source.toStream )840{841loadSound( source.filenameURL );842buffer = bufferMap.get( source.filenameURL.getFilename() );843}844if( source.toStream || buffer != null )845sourceMap.put( sourcename, new SourceLWJGLOpenAL(846listenerPositionAL,847ALBufferMap.get(848source.filenameURL.getFilename() ),849source, buffer ) );850}851}852}853854/**855* Changes the listener's position.856* @param x Destination X coordinate.857* @param y Destination Y coordinate.858* @param z Destination Z coordinate.859*/860@Override861public void setListenerPosition( float x, float y, float z )862{863super.setListenerPosition( x, y, z );864865listenerPositionAL.put( 0, x );866listenerPositionAL.put( 1, y );867listenerPositionAL.put( 2, z );868869// Update OpenAL listener position:870AL10.alListener( AL10.AL_POSITION, listenerPositionAL );871// Check for errors:872checkALError();873}874875/**876* Changes the listeners orientation to the specified 'angle' radians877* counterclockwise around the y-Axis.878* @param angle Radians.879*/880@Override881public void setListenerAngle( float angle )882{883super.setListenerAngle( angle );884885listenerOrientation.put( 0, listener.lookAt.x );886listenerOrientation.put( 2, listener.lookAt.z );887888// Update OpenAL listener orientation:889AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );890// Check for errors:891checkALError();892}893894/**895* Changes the listeners orientation using the specified coordinates.896* @param lookX X element of the look-at direction.897* @param lookY Y element of the look-at direction.898* @param lookZ Z element of the look-at direction.899* @param upX X element of the up direction.900* @param upY Y element of the up direction.901* @param upZ Z element of the up direction.902*/903@Override904public void setListenerOrientation( float lookX, float lookY, float lookZ,905float upX, float upY, float upZ )906{907super.setListenerOrientation( lookX, lookY, lookZ, upX, upY, upZ );908listenerOrientation.put( 0, lookX );909listenerOrientation.put( 1, lookY );910listenerOrientation.put( 2, lookZ );911listenerOrientation.put( 3, upX );912listenerOrientation.put( 4, upY );913listenerOrientation.put( 5, upZ );914AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );915checkALError();916}917918/**919* Changes the listeners position and orientation using the specified listener920* data.921* @param l Listener data to use.922*/923@Override924public void setListenerData( ListenerData l )925{926super.setListenerData( l );927928listenerPositionAL.put( 0, l.position.x );929listenerPositionAL.put( 1, l.position.y );930listenerPositionAL.put( 2, l.position.z );931AL10.alListener( AL10.AL_POSITION, listenerPositionAL );932checkALError();933934listenerOrientation.put( 0, l.lookAt.x );935listenerOrientation.put( 1, l.lookAt.y );936listenerOrientation.put( 2, l.lookAt.z );937listenerOrientation.put( 3, l.up.x );938listenerOrientation.put( 4, l.up.y );939listenerOrientation.put( 5, l.up.z );940AL10.alListener( AL10.AL_ORIENTATION, listenerOrientation );941checkALError();942943listenerVelocity.put( 0, l.velocity.x );944listenerVelocity.put( 1, l.velocity.y );945listenerVelocity.put( 2, l.velocity.z );946AL10.alListener( AL10.AL_VELOCITY, listenerVelocity );947checkALError();948}949950/**951* Sets the listener's velocity, for use in Doppler effect.952* @param x Velocity along world x-axis.953* @param y Velocity along world y-axis.954* @param z Velocity along world z-axis.955*/956@Override957public void setListenerVelocity( float x, float y, float z )958{959super.setListenerVelocity( x, y, z );960961listenerVelocity.put( 0, listener.velocity.x );962listenerVelocity.put( 1, listener.velocity.y );963listenerVelocity.put( 2, listener.velocity.z );964AL10.alListener( AL10.AL_VELOCITY, listenerVelocity );965}966967/**968* The Doppler parameters have changed.969*/970@Override971public void dopplerChanged()972{973super.dopplerChanged();974975AL10.alDopplerFactor( SoundSystemConfig.getDopplerFactor() );976checkALError();977AL10.alDopplerVelocity( SoundSystemConfig.getDopplerVelocity() );978checkALError();979}980981/**982* Checks for OpenAL errors, and prints a message if there is an error.983* @return True if there was an error, False if not.984*/985private boolean checkALError()986{987switch( AL10.alGetError() )988{989case AL10.AL_NO_ERROR:990return false;991case AL10.AL_INVALID_NAME:992errorMessage( "Invalid name parameter." );993return true;994case AL10.AL_INVALID_ENUM:995errorMessage( "Invalid parameter." );996return true;997case AL10.AL_INVALID_VALUE:998errorMessage( "Invalid enumerated parameter value." );999return true;1000case AL10.AL_INVALID_OPERATION:1001errorMessage( "Illegal call." );1002return true;1003case AL10.AL_OUT_OF_MEMORY:1004errorMessage( "Unable to allocate memory." );1005return true;1006default:1007errorMessage( "An unrecognized error occurred." );1008return true;1009}1010}10111012/**1013* Whether or not the AL_PITCH control is supported.1014* @return True if AL_PITCH is supported.1015*/1016public static boolean alPitchSupported()1017{1018return alPitchSupported( GET, XXX );1019}1020/**1021* Sets or returns the value of boolean 'alPitchSupported'.1022* @param action Action to perform (GET or SET).1023* @param value New value if action is SET, otherwise XXX.1024* @return value of boolean 'alPitchSupported'.1025*/1026private static synchronized boolean alPitchSupported( boolean action,1027boolean value )1028{1029if( action == SET )1030alPitchSupported = value;1031return alPitchSupported;1032}10331034/**1035* Returns the short title of this library type.1036* @return A short title.1037*/1038public static String getTitle()1039{1040return "LWJGL OpenAL";1041}10421043/**1044* Returns a longer description of this library type.1045* @return A longer description.1046*/1047public static String getDescription()1048{1049return "The LWJGL binding of OpenAL. For more information, see " +1050"http://www.lwjgl.org";1051}10521053/**1054* Returns the name of the class.1055* @return "Library" + library title.1056*/1057@Override1058public String getClassName()1059{1060return "LibraryLWJGLOpenAL";1061}10621063/**1064* The LibraryLWJGLOpenAL.Exception class provides library-specific error1065* information.1066*/1067public static class Exception extends SoundSystemException1068{1069private static final long serialVersionUID = -7502452059037798035L;1070/**1071* Global identifier for an exception during AL.create(). Probably1072* means that OpenAL is not supported.1073*/1074public static final int CREATE = 101;1075/**1076* Global identifier for an invalid name parameter in OpenAL.1077*/1078public static final int INVALID_NAME = 102;1079/**1080* Global identifier for an invalid parameter in OpenAL.1081*/1082public static final int INVALID_ENUM = 103;1083/**1084* Global identifier for an invalid enumerated parameter value in OpenAL.1085*/1086public static final int INVALID_VALUE = 104;1087/**1088* Global identifier for an illegal call in OpenAL.1089*/1090public static final int INVALID_OPERATION = 105;1091/**1092* Global identifier for OpenAL out of memory.1093*/1094public static final int OUT_OF_MEMORY = 106;1095/**1096* Global identifier for an exception while creating the OpenAL Listener.1097*/1098public static final int LISTENER = 107;1099/**1100* Global identifier for OpenAL AL_PITCH not supported.1101*/1102public static final int NO_AL_PITCH = 108;11031104/**1105* Constructor: Generates a standard "unknown error" exception with the1106* specified message.1107* @param message A brief description of the problem that occurred.1108*/1109public Exception( String message )1110{1111super( message );1112}11131114/**1115* Constructor: Generates an exception of the specified type, with the1116* specified message.1117* @param message A brief description of the problem that occurred.1118* @param type Identifier indicating they type of error.1119*/1120public Exception( String message, int type )1121{1122super( message, type );1123}1124}1125}112611271128