@ -29,11 +29,10 @@
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
package com.jme3.audio.lwjgl ;
import com.jme3.audio.* ;
import com.jme3.audio.AudioNode.Status ;
import com.jme3.audio.* ;
import com.jme3.math.Vector3f ;
import com.jme3.util.BufferUtils ;
import com.jme3.util.NativeObjectManager ;
@ -45,42 +44,34 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level ;
import java.util.logging.Logger ;
import org.lwjgl.LWJGLException ;
import org.lwjgl.openal.* ;
import static org.lwjgl.openal.AL10.* ;
import org.lwjgl.openal.* ;
public class LwjglAudioRenderer implements AudioRenderer , Runnable {
private static final Logger logger = Logger . getLogger ( LwjglAudioRenderer . class . getName ( ) ) ;
private final NativeObjectManager objManager = new NativeObjectManager ( ) ;
// When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2
// which is exactly 1 second of audio.
private static final int BUFFER_SIZE = 35280 ;
private static final int STREAMING_BUFFER_COUNT = 5 ;
private final static int MAX_NUM_CHANNELS = 64 ;
private IntBuffer ib = BufferUtils . createIntBuffer ( 1 ) ;
private final FloatBuffer fb = BufferUtils . createVector3Buffer ( 2 ) ;
private final ByteBuffer nativeBuf = BufferUtils . createByteBuffer ( BUFFER_SIZE ) ;
private final byte [ ] arrayBuf = new byte [ BUFFER_SIZE ] ;
private int [ ] channels ;
private AudioNode [ ] chanSrcs ;
private int nextChan = 0 ;
private ArrayList < Integer > freeChans = new ArrayList < Integer > ( ) ;
private Listener listener ;
private boolean audioDisabled = false ;
private boolean supportEfx = false ;
private int auxSends = 0 ;
private int reverbFx = - 1 ;
private int reverbFxSlot = - 1 ;
// Update audio 20 times per second
private static final float UPDATE_RATE = 0 . 05f ;
private final Thread audioThread = new Thread ( this , "jME3 Audio Thread" ) ;
private final AtomicBoolean threadLock = new AtomicBoolean ( false ) ;
@ -98,9 +89,10 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
private void checkDead ( ) {
if ( audioThread . getState ( ) = = Thread . State . TERMINATED )
if ( audioThread . getState ( ) = = Thread . State . TERMINATED ) {
throw new IllegalStateException ( "Audio thread is terminated" ) ;
}
}
public void run ( ) {
initInThread ( ) ;
@ -110,11 +102,13 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
long updateRateNanos = ( long ) ( UPDATE_RATE * 1000000000 ) ;
mainloop : while ( true ) {
mainloop :
while ( true ) {
long startTime = System . nanoTime ( ) ;
if ( Thread . interrupted ( ) )
if ( Thread . interrupted ( ) ) {
break ;
}
synchronized ( threadLock ) {
updateInThread ( UPDATE_RATE ) ;
@ -281,8 +275,8 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
EFX10 . alFilterf ( id , EFX10 . AL_LOWPASS_GAIN , lpf . getVolume ( ) ) ;
EFX10 . alFilterf ( id , EFX10 . AL_LOWPASS_GAINHF , lpf . getHighFreqVolume ( ) ) ;
} else {
throw new UnsupportedOperationException ( "Filter type unsupported: " +
f . getClass ( ) . getName ( ) ) ;
throw new UnsupportedOperationException ( "Filter type unsupported: "
+ f . getClass ( ) . getName ( ) ) ;
}
f . clearUpdateNeeded ( ) ;
@ -297,8 +291,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
// There is a race condition in AudioNode that can
// cause this to be called for a node that has been
@ -309,42 +304,48 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
// updateSourceParam() (because the audio stopped playing
// on its own right as the volume was set). In this case,
// it should be safe to just ignore the update
if ( src . getChannel ( ) < 0 )
if ( src . getChannel ( ) < 0 ) {
return ;
}
assert src . getChannel ( ) > = 0 ;
int id = channels [ src . getChannel ( ) ] ;
switch ( param ) {
case Position :
if ( ! src . isPositional ( ) )
if ( ! src . isPositional ( ) ) {
return ;
}
Vector3f pos = src . getWorldTranslation ( ) ;
alSource3f ( id , AL_POSITION , pos . x , pos . y , pos . z ) ;
break ;
case Velocity :
if ( ! src . isPositional ( ) )
if ( ! src . isPositional ( ) ) {
return ;
}
Vector3f vel = src . getVelocity ( ) ;
alSource3f ( id , AL_VELOCITY , vel . x , vel . y , vel . z ) ;
break ;
case MaxDistance :
if ( ! src . isPositional ( ) )
if ( ! src . isPositional ( ) ) {
return ;
}
alSourcef ( id , AL_MAX_DISTANCE , src . getMaxDistance ( ) ) ;
break ;
case RefDistance :
if ( ! src . isPositional ( ) )
if ( ! src . isPositional ( ) ) {
return ;
}
alSourcef ( id , AL_REFERENCE_DISTANCE , src . getRefDistance ( ) ) ;
break ;
case ReverbFilter :
if ( ! supportEfx | | ! src . isPositional ( ) | | ! src . isReverbEnabled ( ) )
if ( ! supportEfx | | ! src . isPositional ( ) | | ! src . isReverbEnabled ( ) ) {
return ;
}
int filter = EFX10 . AL_FILTER_NULL ;
if ( src . getReverbFilter ( ) ! = null ) {
@ -357,8 +358,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
AL11 . alSource3i ( id , EFX10 . AL_AUXILIARY_SEND_FILTER , reverbFxSlot , 0 , filter ) ;
break ;
case ReverbEnabled :
if ( ! supportEfx | | ! src . isPositional ( ) )
if ( ! supportEfx | | ! src . isPositional ( ) ) {
return ;
}
if ( src . isReverbEnabled ( ) ) {
updateSourceParam ( src , AudioParam . ReverbFilter ) ;
@ -368,10 +370,13 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
break ;
case IsPositional :
if ( ! src . isPositional ( ) ) {
// p lay in headspace
// P lay in headspace
alSourcei ( id , AL_SOURCE_RELATIVE , AL_TRUE ) ;
alSource3f ( id , AL_POSITION , 0 , 0 , 0 ) ;
alSource3f ( id , AL_VELOCITY , 0 , 0 , 0 ) ;
// Disable reverb
AL11 . alSource3i ( id , EFX10 . AL_AUXILIARY_SEND_FILTER , 0 , 0 , EFX10 . AL_FILTER_NULL ) ;
} else {
alSourcei ( id , AL_SOURCE_RELATIVE , AL_FALSE ) ;
updateSourceParam ( src , AudioParam . Position ) ;
@ -382,21 +387,24 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
break ;
case Direction :
if ( ! src . isDirectional ( ) )
if ( ! src . isDirectional ( ) ) {
return ;
}
Vector3f dir = src . getDirection ( ) ;
alSource3f ( id , AL_DIRECTION , dir . x , dir . y , dir . z ) ;
break ;
case InnerAngle :
if ( ! src . isDirectional ( ) )
if ( ! src . isDirectional ( ) ) {
return ;
}
alSourcef ( id , AL_CONE_INNER_ANGLE , src . getInnerAngle ( ) ) ;
break ;
case OuterAngle :
if ( ! src . isDirectional ( ) )
if ( ! src . isDirectional ( ) ) {
return ;
}
alSourcef ( id , AL_CONE_OUTER_ANGLE , src . getOuterAngle ( ) ) ;
break ;
@ -413,8 +421,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
break ;
case DryFilter :
if ( ! supportEfx )
if ( ! supportEfx ) {
return ;
}
if ( src . getDryFilter ( ) ! = null ) {
Filter f = src . getDryFilter ( ) ;
@ -516,8 +525,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
switch ( param ) {
case Position :
@ -561,9 +571,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
private int newChannel ( ) {
if ( freeChans . size ( ) > 0 )
if ( freeChans . size ( ) > 0 ) {
return freeChans . remove ( 0 ) ;
else if ( nextChan < channels . length ) {
} else if ( nextChan < channels . length ) {
return nextChan + + ;
} else {
return - 1 ;
@ -587,8 +597,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled | | ! supportEfx )
if ( audioDisabled | | ! supportEfx ) {
return ;
}
EFX10 . alEffectf ( reverbFx , EFX10 . AL_REVERB_DENSITY , env . getDensity ( ) ) ;
EFX10 . alEffectf ( reverbFx , EFX10 . AL_REVERB_DIFFUSION , env . getDiffusion ( ) ) ;
@ -622,8 +633,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
}
if ( size = = 0 )
if ( size = = 0 ) {
return false ;
}
nativeBuf . clear ( ) ;
nativeBuf . put ( arrayBuf , 0 , size ) ;
@ -635,8 +647,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
private boolean fillStreamingSource ( int sourceId , AudioStream stream ) {
if ( ! stream . isOpen ( ) )
if ( ! stream . isOpen ( ) ) {
return false ;
}
boolean active = true ;
int processed = alGetSourcei ( sourceId , AL_BUFFERS_PROCESSED ) ;
@ -656,8 +669,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
alSourceQueueBuffers ( sourceId , ib ) ;
}
if ( ! active & & stream . isOpen ( ) )
if ( ! active & & stream . isOpen ( ) ) {
stream . close ( ) ;
}
return active ;
}
@ -724,13 +738,15 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
public void updateInThread ( float tpf ) {
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
for ( int i = 0 ; i < channels . length ; i + + ) {
AudioNode src = chanSrcs [ i ] ;
if ( src = = null )
if ( src = = null ) {
continue ;
}
int sourceId = channels [ i ] ;
@ -752,8 +768,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
AudioStream stream = ( AudioStream ) src . getAudioData ( ) ;
if ( stream . isOpen ( ) ) {
fillStreamingSource ( sourceId , stream ) ;
if ( stopped )
if ( stopped ) {
alSourcePlay ( sourceId ) ;
}
} else {
if ( stopped ) {
// became inactive
@ -797,8 +814,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
if ( this . listener ! = null ) {
// previous listener no longer associated with current
@ -821,13 +839,15 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
if ( src . getAudioData ( ) instanceof AudioStream )
if ( src . getAudioData ( ) instanceof AudioStream ) {
throw new UnsupportedOperationException (
"Cannot play instances " +
"of audio streams. Use playSource() instead." ) ;
"Cannot play instances "
+ "of audio streams. Use playSource() instead." ) ;
}
if ( src . getAudioData ( ) . isUpdateNeeded ( ) ) {
updateAudioData ( src . getAudioData ( ) ) ;
@ -835,8 +855,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
// create a new index for an audio-channel
int index = newChannel ( ) ;
if ( index = = - 1 )
if ( index = = - 1 ) {
return ;
}
int sourceId = channels [ index ] ;
@ -852,7 +873,6 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
}
public void playSource ( AudioNode src ) {
checkDead ( ) ;
synchronized ( threadLock ) {
@ -862,8 +882,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
//assert src.getStatus() == Status.Stopped || src.getChannel() == -1;
@ -881,8 +902,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
src . setChannel ( index ) ;
AudioData data = src . getAudioData ( ) ;
if ( data . isUpdateNeeded ( ) )
if ( data . isUpdateNeeded ( ) ) {
updateAudioData ( data ) ;
}
chanSrcs [ index ] = src ;
setSourceParams ( channels [ index ] , src , false ) ;
@ -894,7 +916,6 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
}
public void pauseSource ( AudioNode src ) {
checkDead ( ) ;
synchronized ( threadLock ) {
@ -904,8 +925,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
if ( src . getStatus ( ) = = Status . Playing ) {
assert src . getChannel ( ) ! = - 1 ;
@ -916,7 +938,6 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
}
public void stopSource ( AudioNode src ) {
synchronized ( threadLock ) {
while ( ! threadLock . get ( ) ) {
@ -925,8 +946,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
if ( src . getStatus ( ) ! = Status . Stopped ) {
int chan = src . getChannel ( ) ;
@ -954,20 +976,22 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
private int convertFormat ( AudioData ad ) {
switch ( ad . getBitsPerSample ( ) ) {
case 8 :
if ( ad . getChannels ( ) = = 1 )
if ( ad . getChannels ( ) = = 1 ) {
return AL_FORMAT_MONO8 ;
else if ( ad . getChannels ( ) = = 2 )
} else if ( ad . getChannels ( ) = = 2 ) {
return AL_FORMAT_STEREO8 ;
}
break ;
case 16 :
if ( ad . getChannels ( ) = = 1 )
if ( ad . getChannels ( ) = = 1 ) {
return AL_FORMAT_MONO16 ;
else
} else {
return AL_FORMAT_STEREO16 ;
}
throw new UnsupportedOperationException ( "Unsupported channels/bits combination: " +
"bits=" + ad . getBitsPerSample ( ) + ", channels=" + ad . getChannels ( ) ) ;
}
throw new UnsupportedOperationException ( "Unsupported channels/bits combination: "
+ "bits=" + ad . getBitsPerSample ( ) + ", channels=" + ad . getChannels ( ) ) ;
}
private void updateAudioBuffer ( AudioBuffer ab ) {
@ -1028,8 +1052,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
} catch ( InterruptedException ex ) {
}
}
if ( audioDisabled )
if ( audioDisabled ) {
return ;
}
if ( ad instanceof AudioBuffer ) {
AudioBuffer ab = ( AudioBuffer ) ad ;
@ -1052,5 +1077,4 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
}
}
}
}