diff --git a/engine/lib/lwjgl/CREDITS b/engine/lib/lwjgl/CREDITS index 8f1a038fb..d03bbf510 100644 --- a/engine/lib/lwjgl/CREDITS +++ b/engine/lib/lwjgl/CREDITS @@ -3,7 +3,7 @@ The following people have helped to make this project what it is today: - Brian Matzon - Elias Naur - Ioannis Tsakpinis - - Niels Jørgensen + - Niels J�rgensen - Tristan Campbell - Gregory Pierce - Luke Holden @@ -12,12 +12,14 @@ The following people have helped to make this project what it is today: - Jos Hirth - Kevin Glass - Atsuya Takagi - - kappaOne + - kappaOne - Simon Felix - Ryan McNally - Ciardhubh - Jens von Pilgrim - Ruben Garat + - Pelle Johnsen + - Jae Kwon additional credits goes to: - Joseph I. Valenzuela [OpenAL stuff] diff --git a/engine/lib/lwjgl/README b/engine/lib/lwjgl/README index 89908f421..977ae0214 100644 --- a/engine/lib/lwjgl/README +++ b/engine/lib/lwjgl/README @@ -1,4 +1,4 @@ -This is the official readme file for lwjgl! +This is the official readme file for lwjgl. Unless otherwise stated, all files distributed or in SVN are covered by the license as stated in the LICENSE file. If you have not received this diff --git a/engine/lib/lwjgl/jME3-lwjgl-natives.jar b/engine/lib/lwjgl/jME3-lwjgl-natives.jar index 3cd6ff91c..02c178f9c 100644 Binary files a/engine/lib/lwjgl/jME3-lwjgl-natives.jar and b/engine/lib/lwjgl/jME3-lwjgl-natives.jar differ diff --git a/engine/lib/lwjgl/lwjgl-debug.jar b/engine/lib/lwjgl/lwjgl-debug.jar index 20d1fbf21..8e2e285e3 100644 Binary files a/engine/lib/lwjgl/lwjgl-debug.jar and b/engine/lib/lwjgl/lwjgl-debug.jar differ diff --git a/engine/lib/lwjgl/lwjgl.jar b/engine/lib/lwjgl/lwjgl.jar index 465fac4f0..8fbf723c8 100644 Binary files a/engine/lib/lwjgl/lwjgl.jar and b/engine/lib/lwjgl/lwjgl.jar differ diff --git a/engine/src/android/com/jme3/audio/android/AndroidAudioData.java b/engine/src/android/com/jme3/audio/android/AndroidAudioData.java index 15335d646..a01e7e262 100644 --- a/engine/src/android/com/jme3/audio/android/AndroidAudioData.java +++ b/engine/src/android/com/jme3/audio/android/AndroidAudioData.java @@ -3,12 +3,20 @@ package com.jme3.audio.android; import com.jme3.asset.AssetKey; import com.jme3.audio.AudioData; import com.jme3.audio.AudioRenderer; +import com.jme3.util.NativeObject; + +public class AndroidAudioData extends AudioData { -public class AndroidAudioData extends AudioData -{ protected AssetKey assetKey; - protected int soundId = 0; protected float currentVolume = 0f; + + public AndroidAudioData(){ + super(); + } + + protected AndroidAudioData(int id){ + super(id); + } public AssetKey getAssetKey() { return assetKey; @@ -18,35 +26,25 @@ public class AndroidAudioData extends AudioData this.assetKey = assetKey; } - public int getSoundId() { - return soundId; - } - - public void setSoundId(int soundId) { - this.soundId = soundId; - } - @Override public DataType getDataType() { - // TODO Auto-generated method stub - return null; + return DataType.Buffer; } @Override public float getDuration() { - // TODO Auto-generated method stub - return 0; + return 0; // TODO: ??? } @Override public void resetObject() { - // TODO Auto-generated method stub - + this.id = -1; + setUpdateNeeded(); } @Override - public void deleteObject(AudioRenderer r) { - r.deleteAudioData(this); + public void deleteObject(Object rendererObject) { + ((AudioRenderer)rendererObject).deleteAudioData(this); } public float getCurrentVolume() { @@ -56,7 +54,9 @@ public class AndroidAudioData extends AudioData public void setCurrentVolume(float currentVolume) { this.currentVolume = currentVolume; } - - + @Override + public NativeObject createDestructableClone() { + return new AndroidAudioData(id); + } } diff --git a/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java b/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java index 0426b68e0..b978e418a 100644 --- a/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java +++ b/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java @@ -64,165 +64,161 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; - /** * This class is the android implementation for {@link AudioRenderer} * @author larynx * */ -public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener -{ +public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener { private static final Logger logger = Logger.getLogger(AndroidAudioRenderer.class.getName()); private final static int MAX_NUM_CHANNELS = 16; - private SoundPool soundPool = null; - private HashMap musicPlaying = new HashMap(); - + private HashMap musicPlaying = new HashMap(); private final Vector3f listenerPosition = new Vector3f(); // For temp use private final Vector3f distanceVector = new Vector3f(); - private final AudioManager manager; private final Context context; private final AssetManager am; - private HashMap mapLoadingAudioNodes = new HashMap(); - private final AtomicBoolean lastLoadCompleted = new AtomicBoolean(); - - private Listener listener; private boolean audioDisabled = false; - - - public AndroidAudioRenderer(Activity context) - { + public AndroidAudioRenderer(Activity context) { this.context = context; - manager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); + manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); context.setVolumeControlStream(AudioManager.STREAM_MUSIC); am = context.getAssets(); } - + @Override - public void initialize() - { - soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC, 0); + public void initialize() { + soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC, 0); soundPool.setOnLoadCompleteListener(this); } @Override - public void updateSourceParam(AudioNode src, AudioParam param) - { + public void updateSourceParam(AudioNode src, AudioParam param) { //logger.log(Level.INFO, "updateSourceParam " + param); - - if (audioDisabled) - return; - - if (src.getChannel() < 0) - return; - - assert src.getChannel() >= 0; - - - switch (param){ - case Position: - if (!src.isPositional()) - return; - - Vector3f pos = src.getWorldTranslation(); - break; - case Velocity: - if (!src.isPositional()) - return; - - Vector3f vel = src.getVelocity(); - break; - case MaxDistance: - if (!src.isPositional()) - return; - break; - case RefDistance: - if (!src.isPositional()) - return; - break; - case ReverbFilter: - if (!src.isPositional() || !src.isReverbEnabled()) - return; - break; - case ReverbEnabled: - if (!src.isPositional()) - return; - - if (src.isReverbEnabled()){ - updateSourceParam(src, AudioParam.ReverbFilter); - } - break; - case IsPositional: - break; - case Direction: - if (!src.isDirectional()) - return; - - Vector3f dir = src.getDirection(); - break; - case InnerAngle: - if (!src.isDirectional()) - return; - break; - case OuterAngle: - if (!src.isDirectional()) - return; - break; - case IsDirectional: - if (src.isDirectional()){ - updateSourceParam(src, AudioParam.Direction); - updateSourceParam(src, AudioParam.InnerAngle); - updateSourceParam(src, AudioParam.OuterAngle); - }else{ - } - break; - case DryFilter: - if (src.getDryFilter() != null){ - Filter f = src.getDryFilter(); - if (f.isUpdateNeeded()){ - //updateFilter(f); - } - } - break; - case Looping: - if (src.isLooping()){ + if (audioDisabled) { + return; + } + + if (src.getChannel() < 0) { + return; + } + + assert src.getChannel() >= 0; + + + switch (param) { + case Position: + if (!src.isPositional()) { + return; + } + + Vector3f pos = src.getWorldTranslation(); + break; + case Velocity: + if (!src.isPositional()) { + return; + } + + Vector3f vel = src.getVelocity(); + break; + case MaxDistance: + if (!src.isPositional()) { + return; + } + break; + case RefDistance: + if (!src.isPositional()) { + return; + } + break; + case ReverbFilter: + if (!src.isPositional() || !src.isReverbEnabled()) { + return; + } + break; + case ReverbEnabled: + if (!src.isPositional()) { + return; + } + + if (src.isReverbEnabled()) { + updateSourceParam(src, AudioParam.ReverbFilter); + } + break; + case IsPositional: + break; + case Direction: + if (!src.isDirectional()) { + return; + } + + Vector3f dir = src.getDirection(); + break; + case InnerAngle: + if (!src.isDirectional()) { + return; + } + break; + case OuterAngle: + if (!src.isDirectional()) { + return; + } + break; + case IsDirectional: + if (src.isDirectional()) { + updateSourceParam(src, AudioParam.Direction); + updateSourceParam(src, AudioParam.InnerAngle); + updateSourceParam(src, AudioParam.OuterAngle); + } else { + } + break; + case DryFilter: + if (src.getDryFilter() != null) { + Filter f = src.getDryFilter(); + if (f.isUpdateNeeded()) { + //updateFilter(f); } - break; - case Volume: - - soundPool.setVolume(src.getChannel(), src.getVolume(), src.getVolume()); + } + break; + case Looping: + if (src.isLooping()) { + } + break; + case Volume: - break; - case Pitch: + soundPool.setVolume(src.getChannel(), src.getVolume(), src.getVolume()); + + break; + case Pitch: + + break; + } - break; - } - } - + @Override - public void updateListenerParam(Listener listener, ListenerParam param) - { + public void updateListenerParam(Listener listener, ListenerParam param) { //logger.log(Level.INFO, "updateListenerParam " + param); - if (audioDisabled) + if (audioDisabled) { return; - - switch (param){ + } + + switch (param) { case Position: listenerPosition.set(listener.getLocation()); break; case Rotation: Vector3f dir = listener.getDirection(); - Vector3f up = listener.getUp(); + Vector3f up = listener.getUp(); break; case Velocity: @@ -237,436 +233,358 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp } @Override - public void update(float tpf) - { + public void update(float tpf) { float distance; float volume; - + // Loop over all mediaplayers - for (AudioNode src : musicPlaying.keySet()) - { - + for (AudioNode src : musicPlaying.keySet()) { + MediaPlayer mp = musicPlaying.get(src); { // Calc the distance to the listener distanceVector.set(listenerPosition); distanceVector.subtractLocal(src.getLocalTranslation()); distance = FastMath.abs(distanceVector.length()); - - if (distance < src.getRefDistance()) + + if (distance < src.getRefDistance()) { distance = src.getRefDistance(); - if (distance > src.getMaxDistance()) - distance = src.getMaxDistance(); + } + if (distance > src.getMaxDistance()) { + distance = src.getMaxDistance(); + } volume = src.getRefDistance() / distance; - - AndroidAudioData audioData = (AndroidAudioData)src.getAudioData(); - - if (FastMath.abs(audioData.getCurrentVolume() - volume) > FastMath.FLT_EPSILON) - { + + AndroidAudioData audioData = (AndroidAudioData) src.getAudioData(); + + if (FastMath.abs(audioData.getCurrentVolume() - volume) > FastMath.FLT_EPSILON) { // Left / Right channel get the same volume by now, only positional mp.setVolume(volume, volume); - + audioData.setCurrentVolume(volume); } - - + + } } } - - public void setListener(Listener listener) - { - if (audioDisabled) - return; + public void setListener(Listener listener) { + if (audioDisabled) { + return; + } - if (this.listener != null){ - // previous listener no longer associated with current - // renderer - this.listener.setRenderer(null); - } - - this.listener = listener; - this.listener.setRenderer(this); + if (this.listener != null) { + // previous listener no longer associated with current + // renderer + this.listener.setRenderer(null); + } + + this.listener = listener; + this.listener.setRenderer(this); } - + @Override - public void onLoadComplete(SoundPool soundPool, int sampleId, int status) - { + public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { AudioNode src = mapLoadingAudioNodes.get(sampleId); - if (src.getAudioData() instanceof AndroidAudioData) - { - AndroidAudioData audioData = (AndroidAudioData)src.getAudioData(); - - if (status == 0) // load was successfull + if (src.getAudioData() instanceof AndroidAudioData) { + AndroidAudioData audioData = (AndroidAudioData) src.getAudioData(); + + if (status == 0) // load was successfull { int channelIndex; - channelIndex = soundPool.play(audioData.getSoundId(), 1f, 1f, 1, -1, 1f); + channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, -1, 1f); src.setChannel(channelIndex); // Playing started ? - if (src.getChannel() > 0) - { + if (src.getChannel() > 0) { src.setStatus(Status.Playing); } + } else { + src.setChannel(-1); } - else - { - src.setChannel(-1); - } - } - else - { + } else { throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString()); } } - + @Override - public void cleanup() - { + public void cleanup() { // Cleanup sound pool - if (soundPool != null) - { - for (AudioNode src: mapLoadingAudioNodes.values()) - { - if ((src.getStatus() == Status.Playing) && (src.getChannel() > 0)) - { + if (soundPool != null) { + for (AudioNode src : mapLoadingAudioNodes.values()) { + if ((src.getStatus() == Status.Playing) && (src.getChannel() > 0)) { soundPool.stop(src.getChannel()); } - - if (src.getAudioData() instanceof AndroidAudioData) - { - AndroidAudioData audioData = (AndroidAudioData)src.getAudioData(); - if (audioData.getSoundId() > 0) - { - soundPool.unload(audioData.getSoundId()); - } - } + + if (src.getAudioData() instanceof AndroidAudioData) { + AndroidAudioData audioData = (AndroidAudioData) src.getAudioData(); + if (audioData.getId() > 0) { + soundPool.unload(audioData.getId()); + } + } } - + soundPool.release(); soundPool = null; } - + // Cleanup media player - for (AudioNode src : musicPlaying.keySet()) - { + for (AudioNode src : musicPlaying.keySet()) { MediaPlayer mp = musicPlaying.get(src); - { + { mp.stop(); mp.release(); src.setStatus(Status.Stopped); } } - musicPlaying.clear(); + musicPlaying.clear(); } @Override - public void onCompletion(MediaPlayer mp) - { - for (AudioNode src : musicPlaying.keySet()) - { - if (musicPlaying.get(src) == mp) - { - mp.seekTo(0); + public void onCompletion(MediaPlayer mp) { + for (AudioNode src : musicPlaying.keySet()) { + if (musicPlaying.get(src) == mp) { + mp.seekTo(0); mp.stop(); src.setStatus(Status.Stopped); break; } } - + } - - public void playSourceInstance(AudioNode src) - { - if (audioDisabled) - return; - - AndroidAudioData audioData; - int soundId = 0; - - if (src.getAudioData() instanceof AndroidAudioData) - { - audioData = (AndroidAudioData)src.getAudioData(); - - if (audioData.getAssetKey() instanceof AudioKey) - { - AudioKey assetKey = (AudioKey) audioData.getAssetKey(); - - // streaming audionodes get played using android mediaplayer, non streaming uses SoundPool - if (assetKey.isStream()) - { - MediaPlayer mp; - if (musicPlaying.containsKey(src)) - { - mp = musicPlaying.get(src); - } - else - { - mp = new MediaPlayer(); - mp.setOnCompletionListener(this); - //mp = MediaPlayer.create(context, new Ur ); - musicPlaying.put(src, mp); - } - if (!mp.isPlaying()) - { - try { - AssetFileDescriptor afd = am.openFd(assetKey.getName()); - mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); - - mp.setAudioStreamType(AudioManager.STREAM_MUSIC); - mp.prepare(); - mp.setLooping(src.isLooping()); - mp.start(); - src.setChannel(1); - src.setStatus(Status.Playing); - } catch (IllegalArgumentException e) - { - logger.log(Level.SEVERE, "Failed to play " + assetKey.getName(), e); - } catch (IllegalStateException e) { - // TODO Auto-generated catch block - logger.log(Level.SEVERE, "Failed to play " + assetKey.getName(), e); - } catch (IOException e) { - // TODO Auto-generated catch block - logger.log(Level.SEVERE, "Failed to play " + assetKey.getName(), e); - } - + + public void playSourceInstance(AudioNode src) { + if (audioDisabled) { + return; + } + + AndroidAudioData audioData; + int soundId = 0; + + if (src.getAudioData() instanceof AndroidAudioData) { + audioData = (AndroidAudioData) src.getAudioData(); + + if (audioData.getAssetKey() instanceof AudioKey) { + AudioKey assetKey = (AudioKey) audioData.getAssetKey(); + + // streaming audionodes get played using android mediaplayer, non streaming uses SoundPool + if (assetKey.isStream()) { + MediaPlayer mp; + if (musicPlaying.containsKey(src)) { + mp = musicPlaying.get(src); + } else { + mp = new MediaPlayer(); + mp.setOnCompletionListener(this); + //mp = MediaPlayer.create(context, new Ur ); + musicPlaying.put(src, mp); + } + if (!mp.isPlaying()) { + try { + AssetFileDescriptor afd = am.openFd(assetKey.getName()); + mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + mp.prepare(); + mp.setLooping(src.isLooping()); + mp.start(); + src.setChannel(1); + src.setStatus(Status.Playing); + } catch (IllegalArgumentException e) { + logger.log(Level.SEVERE, "Failed to play " + assetKey.getName(), e); + } catch (IllegalStateException e) { + // TODO Auto-generated catch block + logger.log(Level.SEVERE, "Failed to play " + assetKey.getName(), e); + } catch (IOException e) { + // TODO Auto-generated catch block + logger.log(Level.SEVERE, "Failed to play " + assetKey.getName(), e); } - + } - else - { - // Low latency Sound effect using SoundPool - if (audioData.isUpdateNeeded() || (audioData.getSoundId() <= 0)) - { - if (audioData.getSoundId() > 0) - { - if (src.getChannel() > 0) - { - soundPool.stop(src.getChannel()); - src.setChannel(-1); - } - soundPool.unload(audioData.getSoundId()); - } - - try - { - soundId = soundPool.load(am.openFd(assetKey.getName()), 1); - } - catch (IOException e) - { - logger.log(Level.SEVERE, "Failed to load sound " + assetKey.getName(), e); - soundId = -1; + + } else { + // Low latency Sound effect using SoundPool + if (audioData.isUpdateNeeded() || (audioData.getId() <= 0)) { + if (audioData.getId() > 0) { + if (src.getChannel() > 0) { + soundPool.stop(src.getChannel()); + src.setChannel(-1); } - audioData.setSoundId(soundId); - } - - // Sound failed to load ? - if (audioData.getSoundId() <= 0) - { - throw new IllegalArgumentException("Failed to load: " + assetKey.getName()); + soundPool.unload(audioData.getId()); } - else - { - int channelIndex; - channelIndex = soundPool.play(audioData.getSoundId(), 1f, 1f, 1, -1, 1f); - if (channelIndex == 0) - { - // Loading is not finished - // Store the soundId and the AudioNode for async loading and later play start - mapLoadingAudioNodes.put(audioData.getSoundId(), src); - } - src.setChannel(channelIndex); + + try { + soundId = soundPool.load(am.openFd(assetKey.getName()), 1); + } catch (IOException e) { + logger.log(Level.SEVERE, "Failed to load sound " + assetKey.getName(), e); + soundId = -1; } - - // Playing started ? - if (src.getChannel() > 0) - { - src.setStatus(Status.Playing); + audioData.setId(soundId); + } + + // Sound failed to load ? + if (audioData.getId() <= 0) { + throw new IllegalArgumentException("Failed to load: " + assetKey.getName()); + } else { + int channelIndex; + channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, -1, 1f); + if (channelIndex == 0) { + // Loading is not finished + // Store the soundId and the AudioNode for async loading and later play start + mapLoadingAudioNodes.put(audioData.getId(), src); } + src.setChannel(channelIndex); + } + + // Playing started ? + if (src.getChannel() > 0) { + src.setStatus(Status.Playing); } - } + } - else - { - throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString()); - } - + } else { + throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString()); + } + + - } - - public void playSource(AudioNode src) - { - if (audioDisabled) - return; + public void playSource(AudioNode src) { + if (audioDisabled) { + return; + } + + //assert src.getStatus() == Status.Stopped || src.getChannel() == -1; + + if (src.getStatus() == Status.Playing) { + return; + } else if (src.getStatus() == Status.Stopped) { + playSourceInstance(src); + } - //assert src.getStatus() == Status.Stopped || src.getChannel() == -1; - if (src.getStatus() == Status.Playing) - { - return; - } - else if (src.getStatus() == Status.Stopped) - { - playSourceInstance(src); - } - - } - - public void pauseSource(AudioNode src) - { - if (audioDisabled) - return; - - if (src.getStatus() == Status.Playing) - { - if (src.getAudioData() instanceof AndroidAudioData) - { - AndroidAudioData audioData = (AndroidAudioData)src.getAudioData(); - if (audioData.getAssetKey() instanceof AudioKey) - { - AudioKey assetKey = (AudioKey) audioData.getAssetKey(); - - if (assetKey.isStream()) - { - MediaPlayer mp; - if (musicPlaying.containsKey(src)) - { - mp = musicPlaying.get(src); - mp.pause(); - src.setStatus(Status.Paused); - } + public void pauseSource(AudioNode src) { + if (audioDisabled) { + return; + } + + if (src.getStatus() == Status.Playing) { + if (src.getAudioData() instanceof AndroidAudioData) { + AndroidAudioData audioData = (AndroidAudioData) src.getAudioData(); + if (audioData.getAssetKey() instanceof AudioKey) { + AudioKey assetKey = (AudioKey) audioData.getAssetKey(); + + if (assetKey.isStream()) { + MediaPlayer mp; + if (musicPlaying.containsKey(src)) { + mp = musicPlaying.get(src); + mp.pause(); + src.setStatus(Status.Paused); } - else - { - assert src.getChannel() != -1; - - if (src.getChannel() > 0) - { - soundPool.pause(src.getChannel()); - src.setStatus(Status.Paused); - } + } else { + assert src.getChannel() != -1; + + if (src.getChannel() > 0) { + soundPool.pause(src.getChannel()); + src.setStatus(Status.Paused); } } } - } + } + } - - public void stopSource(AudioNode src) - { - if (audioDisabled) - return; - - - if (src.getStatus() != Status.Stopped) - { - if (src.getAudioData() instanceof AndroidAudioData) - { - AndroidAudioData audioData = (AndroidAudioData)src.getAudioData(); - if (audioData.getAssetKey() instanceof AudioKey) - { - AudioKey assetKey = (AudioKey) audioData.getAssetKey(); - if (assetKey.isStream()) - { - MediaPlayer mp; - if (musicPlaying.containsKey(src)) - { - mp = musicPlaying.get(src); - mp.stop(); - src.setStatus(Status.Stopped); - src.setChannel(-1); - } - } - else - { - int chan = src.getChannel(); - assert chan != -1; // if it's not stopped, must have id - - if (src.getChannel() > 0) - { - soundPool.stop(src.getChannel()); - src.setChannel(-1); - } - + public void stopSource(AudioNode src) { + if (audioDisabled) { + return; + } + + + if (src.getStatus() != Status.Stopped) { + if (src.getAudioData() instanceof AndroidAudioData) { + AndroidAudioData audioData = (AndroidAudioData) src.getAudioData(); + if (audioData.getAssetKey() instanceof AudioKey) { + AudioKey assetKey = (AudioKey) audioData.getAssetKey(); + if (assetKey.isStream()) { + MediaPlayer mp; + if (musicPlaying.containsKey(src)) { + mp = musicPlaying.get(src); + mp.stop(); src.setStatus(Status.Stopped); - - if (audioData.getSoundId() > 0) - { - soundPool.unload(audioData.getSoundId()); - } - audioData.setSoundId(-1); - - - + src.setChannel(-1); + } + } else { + int chan = src.getChannel(); + assert chan != -1; // if it's not stopped, must have id + + if (src.getChannel() > 0) { + soundPool.stop(src.getChannel()); + src.setChannel(-1); + } + + src.setStatus(Status.Stopped); + + if (audioData.getId() > 0) { + soundPool.unload(audioData.getId()); } + audioData.setId(-1); + + + } } - - } - - } + } + } + + } - public void updateAudioData(AndroidAudioData data) - { + public void updateAudioData(AndroidAudioData data) { throw new UnsupportedOperationException("updateAudioData"); } + public void deleteFilter(Filter filter) { + } + @Override - public void deleteAudioData(AudioData ad) - { - if (ad instanceof AndroidAudioData) - { - AndroidAudioData audioData = (AndroidAudioData)ad; - if (audioData.getAssetKey() instanceof AudioKey) - { - AudioKey assetKey = (AudioKey) audioData.getAssetKey(); - if (assetKey.isStream()) - { - for (AudioNode src : musicPlaying.keySet()) - { - if (src.getAudioData() == ad) - { + public void deleteAudioData(AudioData ad) { + if (ad instanceof AndroidAudioData) { + AndroidAudioData audioData = (AndroidAudioData) ad; + if (audioData.getAssetKey() instanceof AudioKey) { + AudioKey assetKey = (AudioKey) audioData.getAssetKey(); + if (assetKey.isStream()) { + for (AudioNode src : musicPlaying.keySet()) { + if (src.getAudioData() == ad) { MediaPlayer mp = musicPlaying.get(src); mp.stop(); mp.release(); musicPlaying.remove(src); src.setStatus(Status.Stopped); src.setChannel(-1); - break; + break; } } - } - else - { - if (audioData.getSoundId() > 0) - { - soundPool.unload(audioData.getSoundId()); + } else { + if (audioData.getId() > 0) { + soundPool.unload(audioData.getId()); } - audioData.setSoundId(0); + audioData.setId(0); } - + } - } - else - { + } else { throw new IllegalArgumentException("AudioData is not of type AndroidAudioData in deleteAudioData"); - } + } } @Override public void setEnvironment(Environment env) { // TODO Auto-generated method stub - } - - } diff --git a/engine/src/core/com/jme3/audio/ALObject.java b/engine/src/core/com/jme3/audio/ALObject.java deleted file mode 100644 index a801fd6d9..000000000 --- a/engine/src/core/com/jme3/audio/ALObject.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.audio; - -/** - * Used for managing AL (Audio Library) objects. - * - * @author Kirill Vainer - */ -public abstract class ALObject { - - protected int id = -1; - protected Object handleRef = null; - protected boolean updateNeeded = true; - - public void setId(int id){ - if (this.id != -1) - throw new IllegalStateException("ID has already been set for this AL object."); - - this.id = id; - } - - public int getId(){ - return id; - } - - public void setUpdateNeeded(){ - updateNeeded = true; - } - - public void clearUpdateNeeded(){ - updateNeeded = false; - } - - public boolean isUpdateNeeded(){ - return updateNeeded; - } - - @Override - public String toString(){ - return getClass().getSimpleName() + " " + Integer.toHexString(hashCode()); - } - - public abstract void resetObject(); - - public abstract void deleteObject(AudioRenderer r); - -} diff --git a/engine/src/core/com/jme3/audio/AudioBuffer.java b/engine/src/core/com/jme3/audio/AudioBuffer.java index 1b4d4abd9..7d29c496d 100644 --- a/engine/src/core/com/jme3/audio/AudioBuffer.java +++ b/engine/src/core/com/jme3/audio/AudioBuffer.java @@ -33,6 +33,7 @@ package com.jme3.audio; import com.jme3.audio.AudioData.DataType; +import com.jme3.util.NativeObject; import java.nio.ByteBuffer; /** @@ -51,6 +52,11 @@ public class AudioBuffer extends AudioData { protected ByteBuffer audioData; public AudioBuffer(){ + super(); + } + + protected AudioBuffer(int id){ + super(id); } public DataType getDataType() { @@ -98,7 +104,17 @@ public class AudioBuffer extends AudioData { } public void deleteObject(AudioRenderer ar) { - ar.deleteAudioData(this); + + } + + @Override + public void deleteObject(Object rendererObject) { + ((AudioRenderer)rendererObject).deleteAudioData(this); + } + + @Override + public NativeObject createDestructableClone() { + return new AudioBuffer(id); } } diff --git a/engine/src/core/com/jme3/audio/AudioData.java b/engine/src/core/com/jme3/audio/AudioData.java index ab9675c5d..a984018e3 100644 --- a/engine/src/core/com/jme3/audio/AudioData.java +++ b/engine/src/core/com/jme3/audio/AudioData.java @@ -32,6 +32,8 @@ package com.jme3.audio; +import com.jme3.util.NativeObject; + /** * AudioData is an abstract representation * of audio data. There are two ways to handle audio data, short audio files @@ -40,7 +42,7 @@ package com.jme3.audio; * * @author Kirill Vainer */ -public abstract class AudioData extends ALObject { +public abstract class AudioData extends NativeObject { protected int sampleRate; protected int channels; @@ -50,7 +52,15 @@ public abstract class AudioData extends ALObject { Buffer, Stream } + + public AudioData(){ + super(AudioData.class); + } + protected AudioData(int id){ + super(AudioData.class, id); + } + /** * @return The data type, either Buffer or Stream. */ diff --git a/engine/src/core/com/jme3/audio/AudioNode.java b/engine/src/core/com/jme3/audio/AudioNode.java index 6562936bd..aff2f756d 100644 --- a/engine/src/core/com/jme3/audio/AudioNode.java +++ b/engine/src/core/com/jme3/audio/AudioNode.java @@ -64,7 +64,7 @@ public class AudioNode extends Node { protected float pitch = 1; protected float timeOffset = 0; protected Filter dryFilter; - protected AudioKey key; + protected AudioKey audioKey; protected transient AudioData data = null; protected transient volatile Status status = Status.Stopped; protected transient volatile int channel = -1; @@ -123,22 +123,22 @@ public class AudioNode extends Node { * * @param audioRenderer The audio renderer to use for playing. Cannot be null. * @param audioData The audio data contains the audio track to play. - * @param key The audio key that was used to load the AudioData + * @param audioKey The audio key that was used to load the AudioData * * @deprecated AudioRenderer parameter is ignored. */ - public AudioNode(AudioRenderer audioRenderer, AudioData audioData, AudioKey key) { - setAudioData(audioData, key); + public AudioNode(AudioRenderer audioRenderer, AudioData audioData, AudioKey audioKey) { + setAudioData(audioData, audioKey); } /** * Creates a new AudioNode with the given data and key. * * @param audioData The audio data contains the audio track to play. - * @param key The audio key that was used to load the AudioData + * @param audioKey The audio key that was used to load the AudioData */ - public AudioNode(AudioData audioData, AudioKey key) { - setAudioData(audioData, key); + public AudioNode(AudioData audioData, AudioKey audioKey) { + setAudioData(audioData, audioKey); } /** @@ -157,8 +157,8 @@ public class AudioNode extends Node { * @deprecated AudioRenderer parameter is ignored. */ public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name, boolean stream, boolean streamCache) { - this.key = new AudioKey(name, stream, streamCache); - this.data = (AudioData) assetManager.loadAsset(key); + this.audioKey = new AudioKey(name, stream, streamCache); + this.data = (AudioData) assetManager.loadAsset(audioKey); } /** @@ -174,8 +174,8 @@ public class AudioNode extends Node { * seeking, looping and determining duration. */ public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) { - this.key = new AudioKey(name, stream, streamCache); - this.data = (AudioData) assetManager.loadAsset(key); + this.audioKey = new AudioKey(name, stream, streamCache); + this.data = (AudioData) assetManager.loadAsset(audioKey); } /** @@ -309,15 +309,15 @@ public class AudioNode extends Node { * without an {@link AudioData}. * * @param audioData The audio data contains the audio track to play. - * @param key The audio key that was used to load the AudioData + * @param audioKey The audio key that was used to load the AudioData */ - public void setAudioData(AudioData audioData, AudioKey key) { + public void setAudioData(AudioData audioData, AudioKey audioKey) { if (data != null) { throw new IllegalStateException("Cannot change data once its set"); } data = audioData; - this.key = key; + this.audioKey = audioKey; } /** @@ -713,7 +713,7 @@ public class AudioNode extends Node { public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule oc = ex.getCapsule(this); - oc.write(key, "key", null); + oc.write(audioKey, "audio_key", null); oc.write(loop, "looping", false); oc.write(volume, "volume", 1); oc.write(pitch, "pitch", 1); @@ -738,7 +738,16 @@ public class AudioNode extends Node { public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule ic = im.getCapsule(this); - key = (AudioKey) ic.readSavable("key", null); + + // NOTE: In previous versions of jME3, audioKey was actually + // written with the name "key". This has been changed + // to "audio_key" in case Spatial's key will be written as "key". + if (ic.getSavableVersion(AudioNode.class) == 0){ + audioKey = (AudioKey) ic.readSavable("key", null); + }else{ + audioKey = (AudioKey) ic.readSavable("audio_key", null); + } + loop = ic.readBoolean("looping", false); volume = ic.readFloat("volume", 1); pitch = ic.readFloat("pitch", 1); @@ -758,8 +767,9 @@ public class AudioNode extends Node { positional = ic.readBoolean("positional", false); - if (key != null) - data = im.getAssetManager().loadAudio(key); + if (audioKey != null) { + data = im.getAssetManager().loadAudio(audioKey); + } } @Override diff --git a/engine/src/core/com/jme3/audio/AudioRenderer.java b/engine/src/core/com/jme3/audio/AudioRenderer.java index 20fa2be27..15ad9c90d 100644 --- a/engine/src/core/com/jme3/audio/AudioRenderer.java +++ b/engine/src/core/com/jme3/audio/AudioRenderer.java @@ -61,6 +61,7 @@ public interface AudioRenderer { public void updateSourceParam(AudioNode src, AudioParam param); public void updateListenerParam(Listener listener, ListenerParam param); + public void deleteFilter(Filter filter); public void deleteAudioData(AudioData ad); /** diff --git a/engine/src/core/com/jme3/audio/AudioStream.java b/engine/src/core/com/jme3/audio/AudioStream.java index 4c58b8a60..658e858a1 100644 --- a/engine/src/core/com/jme3/audio/AudioStream.java +++ b/engine/src/core/com/jme3/audio/AudioStream.java @@ -32,6 +32,7 @@ package com.jme3.audio; +import com.jme3.util.NativeObject; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; @@ -52,6 +53,16 @@ public class AudioStream extends AudioData implements Closeable{ protected int[] ids; public AudioStream(){ + super(); + } + + protected AudioStream(int[] ids){ + // Pass some dummy ID so handle + // doesn't get created. + super(-1); + + // This is what gets destroyed in reality + this.ids = ids; } public void updateData(InputStream in, float duration){ @@ -142,10 +153,17 @@ public class AudioStream extends AudioData implements Closeable{ } @Override - public void deleteObject(AudioRenderer r) { - r.deleteAudioData(this); + public void deleteObject(Object rendererObject) { + // It seems that the audio renderer is already doing a good + // job at deleting audio streams when they finish playing. +// ((AudioRenderer)rendererObject).deleteAudioData(this); } + @Override + public NativeObject createDestructableClone() { + return new AudioStream(ids); + } + /** * @return Whether the stream is open or not. Reading from a closed * stream will always return eof. diff --git a/engine/src/core/com/jme3/audio/Filter.java b/engine/src/core/com/jme3/audio/Filter.java index d930d9e4f..da231d6b8 100644 --- a/engine/src/core/com/jme3/audio/Filter.java +++ b/engine/src/core/com/jme3/audio/Filter.java @@ -35,29 +35,19 @@ package com.jme3.audio; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.Savable; +import com.jme3.util.NativeObject; import java.io.IOException; -public class Filter implements Savable { +public abstract class Filter extends NativeObject implements Savable { - protected int id = -1; - protected boolean updateNeeded = true; - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; + public Filter(){ + super(Filter.class); } - - public void clearUpdateNeeded(){ - this.updateNeeded = false; - } - - public boolean isUpdateNeeded() { - return updateNeeded; + + protected Filter(int id){ + super(Filter.class, id); } - + public void write(JmeExporter ex) throws IOException { // nothing to save } @@ -66,4 +56,18 @@ public class Filter implements Savable { // nothing to read } + @Override + public void resetObject() { + this.id = -1; + setUpdateNeeded(); + } + + @Override + public void deleteObject(Object rendererObject) { + ((AudioRenderer)rendererObject).deleteFilter(this); + } + + @Override + public abstract NativeObject createDestructableClone(); + } diff --git a/engine/src/core/com/jme3/audio/LowPassFilter.java b/engine/src/core/com/jme3/audio/LowPassFilter.java index d2b633239..64f7a9283 100644 --- a/engine/src/core/com/jme3/audio/LowPassFilter.java +++ b/engine/src/core/com/jme3/audio/LowPassFilter.java @@ -36,6 +36,7 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.InputCapsule; import com.jme3.export.OutputCapsule; +import com.jme3.util.NativeObject; import java.io.IOException; public class LowPassFilter extends Filter { @@ -43,9 +44,14 @@ public class LowPassFilter extends Filter { protected float volume, highFreqVolume; public LowPassFilter(float volume, float highFreqVolume) { + super(); setVolume(volume); setHighFreqVolume(highFreqVolume); } + + protected LowPassFilter(int id){ + super(id); + } public float getHighFreqVolume() { return highFreqVolume; @@ -86,4 +92,9 @@ public class LowPassFilter extends Filter { highFreqVolume = ic.readFloat("hf_volume", 0); } + @Override + public NativeObject createDestructableClone() { + return new LowPassFilter(id); + } + } diff --git a/engine/src/lwjgl-oal/com/jme3/audio/lwjgl/LwjglAudioRenderer.java b/engine/src/lwjgl-oal/com/jme3/audio/lwjgl/LwjglAudioRenderer.java index 494987d10..bb8e0d719 100644 --- a/engine/src/lwjgl-oal/com/jme3/audio/lwjgl/LwjglAudioRenderer.java +++ b/engine/src/lwjgl-oal/com/jme3/audio/lwjgl/LwjglAudioRenderer.java @@ -46,6 +46,7 @@ import com.jme3.audio.Listener; import com.jme3.audio.LowPassFilter; import com.jme3.math.Vector3f; import com.jme3.util.BufferUtils; +import com.jme3.util.NativeObjectManager; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; @@ -68,6 +69,8 @@ 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; @@ -228,7 +231,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { EFX10.alEffecti(reverbFx, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_REVERB); // attach reverb effect to effect slot -// EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx); + EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx); }else{ logger.log(Level.WARNING, "OpenAL EFX not available! Audio effects won't work."); } @@ -252,19 +255,22 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { ib.put(channels); ib.flip(); alDeleteSources(ib); + + // delete audio buffers and filters + objManager.deleteAllObjects(this); if (supportEfx){ ib.position(0).limit(1); ib.put(0, reverbFx); EFX10.alDeleteEffects(ib); + // If this is not allocated, why is it deleted? + // Commented out to fix native crash in OpenAL. ib.position(0).limit(1); ib.put(0, reverbFxSlot); EFX10.alDeleteAuxiliaryEffectSlots(ib); } - // TODO: Cleanup buffers allocated for audio buffers and streams - AL.destroy(); } @@ -282,6 +288,8 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { EFX10.alGenFilters(ib); id = ib.get(0); f.setId(id); + + objManager.registerForCleanup(f); } if (f instanceof LowPassFilter){ @@ -296,7 +304,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { f.clearUpdateNeeded(); } - + public void updateSourceParam(AudioNode src, AudioParam param){ checkDead(); synchronized (threadLock){ @@ -792,6 +800,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { } } } + + // Delete any unused objects. + objManager.deleteUnused(this); } public void setListener(Listener listener) { @@ -983,6 +994,8 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { alGenBuffers(ib); id = ib.get(0); ab.setId(id); + + objManager.registerForCleanup(ab); } ab.getData().clear(); @@ -1001,6 +1014,10 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { ib.position(0).limit(STREAMING_BUFFER_COUNT); ib.get(ids); + // Not registered with object manager. + // AudioStreams can be handled without object manager + // since their lifecycle is known to the audio renderer. + as.setIds(ids); as.clearUpdateNeeded(); } @@ -1012,6 +1029,13 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { updateAudioStream((AudioStream) ad); } } + + public void deleteFilter(Filter filter) { + int id = filter.getId(); + if (id != -1){ + EFX10.alDeleteFilters(id); + } + } public void deleteAudioData(AudioData ad){ synchronized (threadLock){