diff --git a/engine/src/android/com/jme3/app/AndroidHarness.java b/engine/src/android/com/jme3/app/AndroidHarness.java
index a7a116bb3..e0cf3ad96 100644
--- a/engine/src/android/com/jme3/app/AndroidHarness.java
+++ b/engine/src/android/com/jme3/app/AndroidHarness.java
@@ -15,7 +15,6 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.jme3.audio.AudioRenderer;
import com.jme3.audio.android.AndroidAudioRenderer;
-import com.jme3.audio.android.AndroidOpenALSoftAudioRenderer;
import com.jme3.input.JoyInput;
import com.jme3.input.TouchInput;
import com.jme3.input.android.AndroidSensorJoyInput;
@@ -72,6 +71,20 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
* set to 2, 4 to enable multisampling.
*/
protected int antiAliasingSamples = 0;
+
+ /**
+ * Sets the type of Audio Renderer to be used.
+ *
+ * Android MediaPlayer / SoundPool is the default and can be used on all
+ * supported Android platform versions (2.2+)
+ * OpenAL Soft uses an OpenSL backend and is only supported on Android
+ * versions 2.3+.
+ *
+ * Only use ANDROID_ static strings found in AppSettings
+ *
+ */
+ protected String audioRendererType = AppSettings.ANDROID_MEDIAPLAYER;
+
/**
* If true Android Sensors are used as simulated Joysticks Users can use the
* Android sensor feedback through the RawInputListener or by registering
@@ -110,7 +123,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
/**
* Set the screen window mode. If screenFullSize is true, then the
* notification bar and title bar are removed and the screen covers the
- * entire display. If screenFullSize is false, then the notification bar
+ * entire display. If screenFullSize is false, then the notification bar
* remains visible if screenShowTitle is true while screenFullScreen is
* false, then the title bar is also displayed under the notification bar.
*/
@@ -200,6 +213,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
settings.setSamples(antiAliasingSamples);
settings.setResolution(disp.getWidth(), disp.getHeight());
settings.put(AndroidConfigChooser.SETTINGS_CONFIG_TYPE, eglConfigType);
+ settings.setAudioRenderer(audioRendererType);
// Create application instance
@@ -487,10 +501,6 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
AndroidAudioRenderer renderer = (AndroidAudioRenderer) result;
renderer.resumeAll();
}
- if (result instanceof AndroidOpenALSoftAudioRenderer) {
- AndroidOpenALSoftAudioRenderer renderer = (AndroidOpenALSoftAudioRenderer) result;
- renderer.resumeAll();
- }
}
//resume the sensors (aka joysticks)
if (app.getContext() != null) {
@@ -530,10 +540,6 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt
AndroidAudioRenderer renderer = (AndroidAudioRenderer) result;
renderer.pauseAll();
}
- if (result instanceof AndroidOpenALSoftAudioRenderer) {
- AndroidOpenALSoftAudioRenderer renderer = (AndroidOpenALSoftAudioRenderer) result;
- renderer.pauseAll();
- }
}
//pause the sensors (aka joysticks)
if (app.getContext() != null) {
diff --git a/engine/src/android/com/jme3/asset/AndroidAssetManager.java b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
index 6b6e782b5..5d58dd421 100644
--- a/engine/src/android/com/jme3/asset/AndroidAssetManager.java
+++ b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
@@ -33,8 +33,11 @@ package com.jme3.asset;
import com.jme3.asset.plugins.AndroidLocator;
import com.jme3.asset.plugins.ClasspathLocator;
+import com.jme3.audio.android.AndroidAudioRenderer;
import com.jme3.audio.plugins.AndroidAudioLoader;
-import com.jme3.texture.Texture;
+import com.jme3.audio.plugins.WAVLoader;
+import com.jme3.system.AppSettings;
+import com.jme3.system.android.JmeAndroidSystem;
import com.jme3.texture.plugins.AndroidImageLoader;
import java.net.URL;
import java.util.logging.Level;
@@ -58,7 +61,7 @@ public class AndroidAssetManager extends DesktopAssetManager {
//this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg"));
this(null);
}
-
+
private void registerLoaderSafe(String loaderClass, String ... extensions) {
try {
Class extends AssetLoader> loader = (Class extends AssetLoader>) Class.forName(loaderClass);
@@ -73,22 +76,32 @@ public class AndroidAssetManager extends DesktopAssetManager {
* If URL == null then a default list of locators and loaders for android is set
* @param configFile
*/
- public AndroidAssetManager(URL configFile) {
+ public AndroidAssetManager(URL configFile) {
System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
- // Set Default Android config
+ // Set Default Android config
registerLocator("", AndroidLocator.class);
registerLocator("", ClasspathLocator.class);
-
+
registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg");
- registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav");
+ if (JmeAndroidSystem.getAudioRendererType().equals(AppSettings.ANDROID_MEDIAPLAYER)) {
+ registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav");
+ } else if (JmeAndroidSystem.getAudioRendererType().equals(AppSettings.ANDROID_OPENAL_SOFT)) {
+ registerLoader(WAVLoader.class, "wav");
+ // TODO jogg is not in core, need to add some other way to get around compile errors, or not.
+// registerLoader(com.jme3.audio.plugins.OGGLoader.class, "ogg");
+ registerLoaderSafe("com.jme3.audio.plugins.OGGLoader", "ogg");
+ } else {
+ throw new IllegalStateException("No Audio Renderer Type defined!");
+ }
+
registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m");
registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md");
registerLoader(com.jme3.material.plugins.ShaderNodeDefinitionLoader.class, "j3sn");
registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib");
registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o");
registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt");
-
+
// Less common loaders (especially on Android)
registerLoaderSafe("com.jme3.texture.plugins.DDSLoader", "dds");
registerLoaderSafe("com.jme3.texture.plugins.PFMLoader", "pfm");
@@ -100,9 +113,9 @@ public class AndroidAssetManager extends DesktopAssetManager {
registerLoaderSafe("com.jme3.scene.plugins.ogre.SkeletonLoader", "skeleton.xml");
registerLoaderSafe("com.jme3.scene.plugins.ogre.MaterialLoader", "material");
registerLoaderSafe("com.jme3.scene.plugins.ogre.SceneLoader", "scene");
-
+
logger.fine("AndroidAssetManager created.");
}
-
+
}
diff --git a/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java b/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
index ae7e309d5..0cde16aa5 100644
--- a/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
+++ b/engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
@@ -1,523 +1,24 @@
-/*
- * Copyright (c) 2009-2012 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.android;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.SoundPool;
-import com.jme3.asset.AssetKey;
-import com.jme3.audio.*;
-import com.jme3.audio.AudioSource.Status;
-import com.jme3.math.FastMath;
-import com.jme3.math.Vector3f;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import com.jme3.audio.AudioRenderer;
/**
- * This class is the android implementation for {@link AudioRenderer}
+ * Android specific AudioRenderer interface that supports pausing and resuming
+ * audio files when the app is minimized or placed in the background
*
- * @author larynx
- * @author plan_rich
+ * @author iwgeric
*/
-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 final HashMap musicPlaying = new HashMap();
- private SoundPool soundPool = null;
- private final Vector3f listenerPosition = new Vector3f();
- // For temp use
- private final Vector3f distanceVector = new Vector3f();
- private final AssetManager assetManager;
- private HashMap soundpoolStillLoading = new HashMap();
- private Listener listener;
- private boolean audioDisabled = false;
- private final AudioManager manager;
-
- public AndroidAudioRenderer(Activity context) {
- manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- context.setVolumeControlStream(AudioManager.STREAM_MUSIC);
- assetManager = context.getAssets();
- }
-
- @Override
- public void initialize() {
- soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC,
- 0);
- soundPool.setOnLoadCompleteListener(this);
- }
-
- @Override
- public void updateSourceParam(AudioSource src, AudioParam param) {
- if (audioDisabled) {
- return;
- }
-
- if (src.getChannel() < 0) {
- return;
- }
-
- switch (param) {
- case Position:
- if (!src.isPositional()) {
- return;
- }
-
- Vector3f pos = src.getPosition();
- 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()) {
- }
- break;
- case Volume:
- MediaPlayer mp = musicPlaying.get(src);
- if (mp != null) {
- mp.setVolume(src.getVolume(), src.getVolume());
- } else {
- soundPool.setVolume(src.getChannel(), src.getVolume(),
- src.getVolume());
- }
-
- break;
- case Pitch:
-
- break;
- }
-
- }
-
- @Override
- public void updateListenerParam(Listener listener, ListenerParam param) {
- if (audioDisabled) {
- return;
- }
-
- switch (param) {
- case Position:
- listenerPosition.set(listener.getLocation());
- break;
- case Rotation:
- Vector3f dir = listener.getDirection();
- Vector3f up = listener.getUp();
-
- break;
- case Velocity:
- Vector3f vel = listener.getVelocity();
-
- break;
- case Volume:
- // alListenerf(AL_GAIN, listener.getVolume());
- break;
- }
-
- }
-
- @Override
- public void update(float tpf) {
- float distance;
- float volume;
-
- // Loop over all mediaplayers
- for (AudioSource src : musicPlaying.keySet()) {
-
- MediaPlayer mp = musicPlaying.get(src);
-
- // Calc the distance to the listener
- distanceVector.set(listenerPosition);
- distanceVector.subtractLocal(src.getPosition());
- distance = FastMath.abs(distanceVector.length());
-
- if (distance < src.getRefDistance()) {
- distance = src.getRefDistance();
- }
- 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) {
- // 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;
- }
-
- 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 cleanup() {
- // Cleanup sound pool
- if (soundPool != null) {
- soundPool.release();
- soundPool = null;
- }
-
- // Cleanup media player
- for (AudioSource src : musicPlaying.keySet()) {
- MediaPlayer mp = musicPlaying.get(src);
- {
- mp.stop();
- mp.release();
- src.setStatus(Status.Stopped);
- }
- }
- musicPlaying.clear();
- }
-
- @Override
- public void onCompletion(MediaPlayer mp) {
- if (mp.isPlaying()) {
- mp.seekTo(0);
- mp.stop();
- }
- // XXX: This has bad performance -> maybe change overall structure of
- // mediaplayer in this audiorenderer?
- for (AudioSource src : musicPlaying.keySet()) {
- if (musicPlaying.get(src) == mp) {
- src.setStatus(Status.Stopped);
- break;
- }
- }
-
- }
+public interface AndroidAudioRenderer extends AudioRenderer {
/**
- * Plays using the {@link SoundPool} of Android. Due to hard limitation of
- * the SoundPool: After playing more instances of the sound you only have
- * the channel of the last played instance.
- *
- * It is not possible to get information about the state of the soundpool of
- * a specific streamid, so removing is not possilbe -> noone knows when
- * sound finished.
+ * Pauses all Playing audio. To be used when the app is placed in the
+ * background.
*/
- public void playSourceInstance(AudioSource src) {
- if (audioDisabled) {
- return;
- }
-
- AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
-
- if (!(audioData.getAssetKey() instanceof AudioKey)) {
- throw new IllegalArgumentException("Asset is not a AudioKey");
- }
-
- AudioKey assetKey = (AudioKey) audioData.getAssetKey();
-
- try {
-
- if (audioData.getId() < 0) { // found something to load
- int soundId = soundPool.load(
- assetManager.openFd(assetKey.getName()), 1);
- audioData.setId(soundId);
- }
-
- int channel = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
-
- if (channel == 0) {
- soundpoolStillLoading.put(audioData.getId(), src);
- } else {
- if (src.getStatus() != Status.Stopped) {
- soundPool.stop(channel);
- src.setStatus(Status.Stopped);
- }
- src.setChannel(channel); // receive a channel at the last
- setSourceParams(src);
- // playing at least
-
-
- }
- } catch (IOException e) {
- logger.log(Level.SEVERE,
- "Failed to load sound " + assetKey.getName(), e);
- audioData.setId(-1);
- }
- }
-
- @Override
- public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
- AudioSource src = soundpoolStillLoading.remove(sampleId);
-
- if (src == null) {
- logger.warning("Something went terribly wrong! onLoadComplete"
- + " had sampleId which was not in the HashMap of loading items");
- return;
- }
-
- AudioData audioData = src.getAudioData();
-
- // load was successfull
- if (status == 0) {
- int channelIndex;
- channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
- src.setChannel(channelIndex);
- setSourceParams(src);
- }
- }
-
- public void playSource(AudioSource src) {
- if (audioDisabled) {
- return;
- }
-
- AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
-
- MediaPlayer mp = musicPlaying.get(src);
- if (mp == null) {
- mp = new MediaPlayer();
- mp.setOnCompletionListener(this);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- }
-
- try {
- if (src.getStatus() == Status.Stopped) {
- mp.reset();
- AssetKey> key = audioData.getAssetKey();
-
- AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
- mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
- afd.getLength());
- mp.prepare();
- setSourceParams(src, mp);
- src.setChannel(0);
- src.setStatus(Status.Playing);
- musicPlaying.put(src, mp);
- mp.start();
- } else {
- mp.start();
- }
- } catch (IllegalStateException e) {
- e.printStackTrace();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void setSourceParams(AudioSource src, MediaPlayer mp) {
- mp.setLooping(src.isLooping());
- mp.setVolume(src.getVolume(), src.getVolume());
- //src.getDryFilter();
- }
-
- private void setSourceParams(AudioSource src) {
- soundPool.setLoop(src.getChannel(), src.isLooping() ? -1 : 0);
- soundPool.setVolume(src.getChannel(), src.getVolume(), src.getVolume());
- }
+ public void pauseAll();
/**
- * Pause the current playing sounds. Both from the {@link SoundPool} and the
- * active {@link MediaPlayer}s
+ * Resumes all Paused audio. To be used when the app is brought back to
+ * the foreground.
*/
- public void pauseAll() {
- if (soundPool != null) {
- soundPool.autoPause();
- for (MediaPlayer mp : musicPlaying.values()) {
- if(mp.isPlaying()){
- mp.pause();
- }
- }
- }
- }
-
- /**
- * Resume all paused sounds.
- */
- public void resumeAll() {
- if (soundPool != null) {
- soundPool.autoResume();
- for (MediaPlayer mp : musicPlaying.values()) {
- mp.start(); //no resume -> api says call start to resume
- }
- }
- }
-
- public void pauseSource(AudioSource src) {
- if (audioDisabled) {
- return;
- }
-
- MediaPlayer mp = musicPlaying.get(src);
- if (mp != null) {
- mp.pause();
- src.setStatus(Status.Paused);
- } else {
- int channel = src.getChannel();
- if (channel != -1) {
- soundPool.pause(channel); // is not very likley to make
- } // something useful :)
- }
- }
-
- public void stopSource(AudioSource src) {
- if (audioDisabled) {
- return;
- }
-
- // can be stream or buffer -> so try to get mediaplayer
- // if there is non try to stop soundpool
- MediaPlayer mp = musicPlaying.get(src);
- if (mp != null) {
- mp.stop();
- mp.reset();
- src.setStatus(Status.Stopped);
- } else {
- int channel = src.getChannel();
- if (channel != -1) {
- soundPool.pause(channel); // is not very likley to make
- // something useful :)
- }
- }
-
- }
-
- @Override
- public void deleteAudioData(AudioData ad) {
-
- for (AudioSource src : musicPlaying.keySet()) {
- if (src.getAudioData() == ad) {
- MediaPlayer mp = musicPlaying.remove(src);
- mp.stop();
- mp.release();
- src.setStatus(Status.Stopped);
- src.setChannel(-1);
- ad.setId(-1);
- break;
- }
- }
-
- if (ad.getId() > 0) {
- soundPool.unload(ad.getId());
- ad.setId(-1);
- }
- }
-
- @Override
- public void setEnvironment(Environment env) {
- // not yet supported
- }
-
- @Override
- public void deleteFilter(Filter filter) {
- }
+ public void resumeAll();
}
diff --git a/engine/src/android/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java b/engine/src/android/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java
new file mode 100644
index 000000000..df1bea3b6
--- /dev/null
+++ b/engine/src/android/com/jme3/audio/android/AndroidMediaPlayerAudioRenderer.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2009-2012 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.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.SoundPool;
+import com.jme3.asset.AssetKey;
+import com.jme3.audio.*;
+import com.jme3.audio.AudioSource.Status;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class is the android implementation for {@link AudioRenderer}
+ *
+ * @author larynx
+ * @author plan_rich
+ */
+public class AndroidMediaPlayerAudioRenderer implements AndroidAudioRenderer,
+ SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener {
+
+ private static final Logger logger = Logger.getLogger(AndroidMediaPlayerAudioRenderer.class.getName());
+ private final static int MAX_NUM_CHANNELS = 16;
+ private final HashMap musicPlaying = new HashMap();
+ private SoundPool soundPool = null;
+ private final Vector3f listenerPosition = new Vector3f();
+ // For temp use
+ private final Vector3f distanceVector = new Vector3f();
+ private final AssetManager assetManager;
+ private HashMap soundpoolStillLoading = new HashMap();
+ private Listener listener;
+ private boolean audioDisabled = false;
+ private final AudioManager manager;
+
+ public AndroidMediaPlayerAudioRenderer(Activity context) {
+ manager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ context.setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ assetManager = context.getAssets();
+ }
+
+ @Override
+ public void initialize() {
+ soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC,
+ 0);
+ soundPool.setOnLoadCompleteListener(this);
+ }
+
+ @Override
+ public void updateSourceParam(AudioSource src, AudioParam param) {
+ if (audioDisabled) {
+ return;
+ }
+
+ if (src.getChannel() < 0) {
+ return;
+ }
+
+ switch (param) {
+ case Position:
+ if (!src.isPositional()) {
+ return;
+ }
+
+ Vector3f pos = src.getPosition();
+ 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()) {
+ }
+ break;
+ case Volume:
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp != null) {
+ mp.setVolume(src.getVolume(), src.getVolume());
+ } else {
+ soundPool.setVolume(src.getChannel(), src.getVolume(),
+ src.getVolume());
+ }
+
+ break;
+ case Pitch:
+
+ break;
+ }
+
+ }
+
+ @Override
+ public void updateListenerParam(Listener listener, ListenerParam param) {
+ if (audioDisabled) {
+ return;
+ }
+
+ switch (param) {
+ case Position:
+ listenerPosition.set(listener.getLocation());
+ break;
+ case Rotation:
+ Vector3f dir = listener.getDirection();
+ Vector3f up = listener.getUp();
+
+ break;
+ case Velocity:
+ Vector3f vel = listener.getVelocity();
+
+ break;
+ case Volume:
+ // alListenerf(AL_GAIN, listener.getVolume());
+ break;
+ }
+
+ }
+
+ @Override
+ public void update(float tpf) {
+ float distance;
+ float volume;
+
+ // Loop over all mediaplayers
+ for (AudioSource src : musicPlaying.keySet()) {
+
+ MediaPlayer mp = musicPlaying.get(src);
+
+ // Calc the distance to the listener
+ distanceVector.set(listenerPosition);
+ distanceVector.subtractLocal(src.getPosition());
+ distance = FastMath.abs(distanceVector.length());
+
+ if (distance < src.getRefDistance()) {
+ distance = src.getRefDistance();
+ }
+ 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) {
+ // 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;
+ }
+
+ 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 cleanup() {
+ // Cleanup sound pool
+ if (soundPool != null) {
+ soundPool.release();
+ soundPool = null;
+ }
+
+ // Cleanup media player
+ for (AudioSource src : musicPlaying.keySet()) {
+ MediaPlayer mp = musicPlaying.get(src);
+ {
+ mp.stop();
+ mp.release();
+ src.setStatus(Status.Stopped);
+ }
+ }
+ musicPlaying.clear();
+ }
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ if (mp.isPlaying()) {
+ mp.seekTo(0);
+ mp.stop();
+ }
+ // XXX: This has bad performance -> maybe change overall structure of
+ // mediaplayer in this audiorenderer?
+ for (AudioSource src : musicPlaying.keySet()) {
+ if (musicPlaying.get(src) == mp) {
+ src.setStatus(Status.Stopped);
+ break;
+ }
+ }
+
+ }
+
+ /**
+ * Plays using the {@link SoundPool} of Android. Due to hard limitation of
+ * the SoundPool: After playing more instances of the sound you only have
+ * the channel of the last played instance.
+ *
+ * It is not possible to get information about the state of the soundpool of
+ * a specific streamid, so removing is not possilbe -> noone knows when
+ * sound finished.
+ */
+ public void playSourceInstance(AudioSource src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
+
+ if (!(audioData.getAssetKey() instanceof AudioKey)) {
+ throw new IllegalArgumentException("Asset is not a AudioKey");
+ }
+
+ AudioKey assetKey = (AudioKey) audioData.getAssetKey();
+
+ try {
+
+ if (audioData.getId() < 0) { // found something to load
+ int soundId = soundPool.load(
+ assetManager.openFd(assetKey.getName()), 1);
+ audioData.setId(soundId);
+ }
+
+ int channel = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
+
+ if (channel == 0) {
+ soundpoolStillLoading.put(audioData.getId(), src);
+ } else {
+ if (src.getStatus() != Status.Stopped) {
+ soundPool.stop(channel);
+ src.setStatus(Status.Stopped);
+ }
+ src.setChannel(channel); // receive a channel at the last
+ setSourceParams(src);
+ // playing at least
+
+
+ }
+ } catch (IOException e) {
+ logger.log(Level.SEVERE,
+ "Failed to load sound " + assetKey.getName(), e);
+ audioData.setId(-1);
+ }
+ }
+
+ @Override
+ public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
+ AudioSource src = soundpoolStillLoading.remove(sampleId);
+
+ if (src == null) {
+ logger.warning("Something went terribly wrong! onLoadComplete"
+ + " had sampleId which was not in the HashMap of loading items");
+ return;
+ }
+
+ AudioData audioData = src.getAudioData();
+
+ // load was successfull
+ if (status == 0) {
+ int channelIndex;
+ channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
+ src.setChannel(channelIndex);
+ setSourceParams(src);
+ }
+ }
+
+ public void playSource(AudioSource src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ AndroidAudioData audioData = (AndroidAudioData) src.getAudioData();
+
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp == null) {
+ mp = new MediaPlayer();
+ mp.setOnCompletionListener(this);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ }
+
+ try {
+ if (src.getStatus() == Status.Stopped) {
+ mp.reset();
+ AssetKey> key = audioData.getAssetKey();
+
+ AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
+ mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
+ afd.getLength());
+ mp.prepare();
+ setSourceParams(src, mp);
+ src.setChannel(0);
+ src.setStatus(Status.Playing);
+ musicPlaying.put(src, mp);
+ mp.start();
+ } else {
+ mp.start();
+ }
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void setSourceParams(AudioSource src, MediaPlayer mp) {
+ mp.setLooping(src.isLooping());
+ mp.setVolume(src.getVolume(), src.getVolume());
+ //src.getDryFilter();
+ }
+
+ private void setSourceParams(AudioSource src) {
+ soundPool.setLoop(src.getChannel(), src.isLooping() ? -1 : 0);
+ soundPool.setVolume(src.getChannel(), src.getVolume(), src.getVolume());
+ }
+
+ /**
+ * Pause the current playing sounds. Both from the {@link SoundPool} and the
+ * active {@link MediaPlayer}s
+ */
+ public void pauseAll() {
+ if (soundPool != null) {
+ soundPool.autoPause();
+ for (MediaPlayer mp : musicPlaying.values()) {
+ if(mp.isPlaying()){
+ mp.pause();
+ }
+ }
+ }
+ }
+
+ /**
+ * Resume all paused sounds.
+ */
+ public void resumeAll() {
+ if (soundPool != null) {
+ soundPool.autoResume();
+ for (MediaPlayer mp : musicPlaying.values()) {
+ mp.start(); //no resume -> api says call start to resume
+ }
+ }
+ }
+
+ public void pauseSource(AudioSource src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp != null) {
+ mp.pause();
+ src.setStatus(Status.Paused);
+ } else {
+ int channel = src.getChannel();
+ if (channel != -1) {
+ soundPool.pause(channel); // is not very likley to make
+ } // something useful :)
+ }
+ }
+
+ public void stopSource(AudioSource src) {
+ if (audioDisabled) {
+ return;
+ }
+
+ // can be stream or buffer -> so try to get mediaplayer
+ // if there is non try to stop soundpool
+ MediaPlayer mp = musicPlaying.get(src);
+ if (mp != null) {
+ mp.stop();
+ mp.reset();
+ src.setStatus(Status.Stopped);
+ } else {
+ int channel = src.getChannel();
+ if (channel != -1) {
+ soundPool.pause(channel); // is not very likley to make
+ // something useful :)
+ }
+ }
+
+ }
+
+ @Override
+ public void deleteAudioData(AudioData ad) {
+
+ for (AudioSource src : musicPlaying.keySet()) {
+ if (src.getAudioData() == ad) {
+ MediaPlayer mp = musicPlaying.remove(src);
+ mp.stop();
+ mp.release();
+ src.setStatus(Status.Stopped);
+ src.setChannel(-1);
+ ad.setId(-1);
+ break;
+ }
+ }
+
+ if (ad.getId() > 0) {
+ soundPool.unload(ad.getId());
+ ad.setId(-1);
+ }
+ }
+
+ @Override
+ public void setEnvironment(Environment env) {
+ // not yet supported
+ }
+
+ @Override
+ public void deleteFilter(Filter filter) {
+ }
+}
diff --git a/engine/src/android/com/jme3/audio/android/AndroidOpenALSoftAudioRenderer.java b/engine/src/android/com/jme3/audio/android/AndroidOpenALSoftAudioRenderer.java
index 6b880a7e9..1a0672033 100644
--- a/engine/src/android/com/jme3/audio/android/AndroidOpenALSoftAudioRenderer.java
+++ b/engine/src/android/com/jme3/audio/android/AndroidOpenALSoftAudioRenderer.java
@@ -44,7 +44,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
-public class AndroidOpenALSoftAudioRenderer implements AudioRenderer, Runnable {
+public class AndroidOpenALSoftAudioRenderer implements AndroidAudioRenderer, Runnable {
private static final Logger logger = Logger.getLogger(AndroidOpenALSoftAudioRenderer.class.getName());
private final NativeObjectManager objManager = new NativeObjectManager();
diff --git a/engine/src/android/com/jme3/system/android/JmeAndroidSystem.java b/engine/src/android/com/jme3/system/android/JmeAndroidSystem.java
index 40318040b..ab4f8665c 100644
--- a/engine/src/android/com/jme3/system/android/JmeAndroidSystem.java
+++ b/engine/src/android/com/jme3/system/android/JmeAndroidSystem.java
@@ -10,25 +10,25 @@ import com.jme3.asset.AndroidImageInfo;
import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioRenderer;
import com.jme3.audio.android.AndroidAudioRenderer;
+import com.jme3.audio.android.AndroidMediaPlayerAudioRenderer;
+import com.jme3.audio.android.AndroidOpenALSoftAudioRenderer;
import com.jme3.system.*;
import com.jme3.system.JmeContext.Type;
import com.jme3.texture.Image;
import com.jme3.texture.image.DefaultImageRaster;
import com.jme3.texture.image.ImageRaster;
import com.jme3.util.AndroidScreenshots;
-import com.jme3.util.JmeFormatter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.nio.ByteBuffer;
-import java.util.logging.Handler;
import java.util.logging.Level;
-import java.util.logging.Logger;
public class JmeAndroidSystem extends JmeSystemDelegate {
private static Activity activity;
+ private static String audioRendererType = AppSettings.ANDROID_MEDIAPLAYER;
static {
try {
@@ -97,6 +97,16 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
@Override
public JmeContext newContext(AppSettings settings, Type contextType) {
+ if (settings.getAudioRenderer().equals(AppSettings.ANDROID_MEDIAPLAYER)) {
+ logger.log(Level.INFO, "newContext settings set to Android MediaPlayer / SoundPool");
+ audioRendererType = AppSettings.ANDROID_MEDIAPLAYER;
+ } else if (settings.getAudioRenderer().equals(AppSettings.ANDROID_OPENAL_SOFT)) {
+ logger.log(Level.INFO, "newContext settings set to Android OpenAL Soft");
+ audioRendererType = AppSettings.ANDROID_OPENAL_SOFT;
+ } else {
+ logger.log(Level.INFO, "AudioRenderer not set. Defaulting to Android MediaPlayer / SoundPool");
+ audioRendererType = AppSettings.ANDROID_MEDIAPLAYER;
+ }
initialize(settings);
JmeContext ctx = new OGLESContext();
ctx.setSettings(settings);
@@ -105,7 +115,20 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
@Override
public AudioRenderer newAudioRenderer(AppSettings settings) {
- return new AndroidAudioRenderer(activity);
+
+ if (settings.getAudioRenderer().equals(AppSettings.ANDROID_MEDIAPLAYER)) {
+ logger.log(Level.INFO, "newAudioRenderer settings set to Android MediaPlayer / SoundPool");
+ audioRendererType = AppSettings.ANDROID_MEDIAPLAYER;
+ return new AndroidMediaPlayerAudioRenderer(activity);
+ } else if (settings.getAudioRenderer().equals(AppSettings.ANDROID_OPENAL_SOFT)) {
+ logger.log(Level.INFO, "newAudioRenderer settings set to Android OpenAL Soft");
+ audioRendererType = AppSettings.ANDROID_OPENAL_SOFT;
+ return new AndroidOpenALSoftAudioRenderer();
+ } else {
+ logger.log(Level.INFO, "AudioRenderer not set. Defaulting to Android MediaPlayer / SoundPool");
+ audioRendererType = AppSettings.ANDROID_MEDIAPLAYER;
+ return new AndroidMediaPlayerAudioRenderer(activity);
+ }
}
@Override
@@ -198,4 +221,8 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
public static Activity getActivity() {
return activity;
}
+
+ public static String getAudioRendererType() {
+ return audioRendererType;
+ }
}
diff --git a/engine/src/core/com/jme3/system/AppSettings.java b/engine/src/core/com/jme3/system/AppSettings.java
index 5699e749e..81ca315ee 100644
--- a/engine/src/core/com/jme3/system/AppSettings.java
+++ b/engine/src/core/com/jme3/system/AppSettings.java
@@ -42,69 +42,88 @@ import java.util.prefs.Preferences;
/**
* AppSettings
provides a store of configuration
- * to be used by the application.
+ * to be used by the application.
*
* By default only the {@link JmeContext context} uses the configuration,
- * however the user may set and retrieve the settings as well.
- * The settings can be stored either in the Java preferences
+ * however the user may set and retrieve the settings as well.
+ * The settings can be stored either in the Java preferences
* (using {@link #save(java.lang.String) } or
* a .properties file (using {@link #save(java.io.OutputStream) }.
- *
+ *
* @author Kirill Vainer
*/
public final class AppSettings extends HashMap {
private static final AppSettings defaults = new AppSettings(false);
-
+
/**
* Use LWJGL as the display system and force using the OpenGL1.1 renderer.
- *
- * @see AppSettings#setRenderer(java.lang.String)
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
*/
public static final String LWJGL_OPENGL1 = "LWJGL-OPENGL1";
-
+
/**
* Use LWJGL as the display system and force using the OpenGL2.0 renderer.
*
* If the underlying system does not support OpenGL2.0, then the context
* initialization will throw an exception.
- *
- * @see AppSettings#setRenderer(java.lang.String)
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
*/
public static final String LWJGL_OPENGL2 = "LWJGL-OpenGL2";
-
+
/**
* Use LWJGL as the display system and force using the core OpenGL3.3 renderer.
*
* If the underlying system does not support OpenGL3.2, then the context
* initialization will throw an exception. Note that currently jMonkeyEngine
- * does not have any shaders that support OpenGL3.2 therefore this
+ * does not have any shaders that support OpenGL3.2 therefore this
* option is not useful.
*
* Note: OpenGL 3.2 is used to give 3.x support to Mac users.
- *
- * @see AppSettings#setRenderer(java.lang.String)
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
*/
public static final String LWJGL_OPENGL3 = "LWJGL-OpenGL3";
-
+
/**
- * Use LWJGL as the display system and allow the context
+ * Use LWJGL as the display system and allow the context
* to choose an appropriate renderer based on system capabilities.
*
* If the GPU supports OpenGL2 or later, then the OpenGL2.0 renderer will
* be used, otherwise, the OpenGL1.1 renderer is used.
- *
- * @see AppSettings#setRenderer(java.lang.String)
+ *
+ * @see AppSettings#setRenderer(java.lang.String)
*/
public static final String LWJGL_OPENGL_ANY = "LWJGL-OpenGL-Any";
-
+
/**
* Use the LWJGL OpenAL based renderer for audio capabilities.
- *
- * @see AppSettings#setAudioRenderer(java.lang.String)
+ *
+ * @see AppSettings#setAudioRenderer(java.lang.String)
*/
public static final String LWJGL_OPENAL = "LWJGL";
+ /**
+ * Use the Android MediaPlayer / SoundPool based renderer for Android audio capabilities.
+ *
+ * NOTE: Supports Android 2.2+ platforms. This is the current default for
+ * Android platforms.
+ *
+ * @see AppSettings#setAudioRenderer(java.lang.String)
+ */
+ public static final String ANDROID_MEDIAPLAYER = "MediaPlayer";
+
+ /**
+ * Use the OpenAL Soft based renderer for Android audio capabilities.
+ *
+ * NOTE: Only to be used on Android 2.3+ platforms due to using OpenSL.
+ *
+ * @see AppSettings#setAudioRenderer(java.lang.String)
+ */
+ public static final String ANDROID_OPENAL_SOFT = "OpenAL_SOFT";
+
static {
defaults.put("Width", 640);
defaults.put("Height", 480);
@@ -131,10 +150,10 @@ public final class AppSettings extends HashMap {
* Create a new instance of AppSettings
.
*
* If loadDefaults
is true, then the default settings
- * will be set on the AppSettings.
+ * will be set on the AppSettings.
* Use false if you want to change some settings but you would like the
* application to load settings from previous launches.
- *
+ *
* @param loadDefaults If default settings are to be loaded.
*/
public AppSettings(boolean loadDefaults) {
@@ -145,11 +164,11 @@ public final class AppSettings extends HashMap {
/**
* Copies all settings from other
to this
- * AppSettings.
+ * AppSettings.
*
* Any settings that are specified in other will overwrite settings
* set on this AppSettings.
- *
+ *
* @param other The AppSettings to copy the settings from
*/
public void copyFrom(AppSettings other) {
@@ -159,7 +178,7 @@ public final class AppSettings extends HashMap {
/**
* Same as {@link #copyFrom(com.jme3.system.AppSettings) }, except
* doesn't overwrite settings that are already set.
- *
+ *
* @param other The AppSettings to merge the settings from
*/
public void mergeFrom(AppSettings other) {
@@ -172,11 +191,11 @@ public final class AppSettings extends HashMap {
/**
* Loads the settings from the given properties input stream.
- *
+ *
* @param in The InputStream to load from
* @throws IOException If an IOException occurs
- *
- * @see #save(java.io.OutputStream)
+ *
+ * @see #save(java.io.OutputStream)
*/
public void load(InputStream in) throws IOException {
Properties props = new Properties();
@@ -207,11 +226,11 @@ public final class AppSettings extends HashMap {
/**
* Saves all settings to the given properties output stream.
- *
+ *
* @param out The OutputStream to write to
* @throws IOException If an IOException occurs
- *
- * @see #load(java.io.InputStream)
+ *
+ * @see #load(java.io.InputStream)
*/
public void save(OutputStream out) throws IOException {
Properties props = new Properties();
@@ -238,11 +257,11 @@ public final class AppSettings extends HashMap {
/**
* Loads settings previously saved in the Java preferences.
- *
+ *
* @param preferencesKey The preferencesKey previously used to save the settings.
* @throws BackingStoreException If an exception occurs with the preferences
- *
- * @see #save(java.lang.String)
+ *
+ * @see #save(java.lang.String)
*/
public void load(String preferencesKey) throws BackingStoreException {
Preferences prefs = Preferences.userRoot().node(preferencesKey);
@@ -289,10 +308,10 @@ public final class AppSettings extends HashMap {
* On the Windows operating system, the preferences are saved in the registry
* at the following key:
* HKEY_CURRENT_USER\Software\JavaSoft\Prefs\[preferencesKey]
- *
- * @param preferencesKey The preferences key to save at. Generally the
- * application's unique name.
- *
+ *
+ * @param preferencesKey The preferences key to save at. Generally the
+ * application's unique name.
+ *
* @throws BackingStoreException If an exception occurs with the preferences
*/
public void save(String preferencesKey) throws BackingStoreException {
@@ -302,7 +321,7 @@ public final class AppSettings extends HashMap {
// purge any other parameters set in older versions of the app, so
// that they don't leak onto the AppSettings of newer versions.
prefs.clear();
-
+
for (String key : keySet()) {
Object val = get(key);
if (val instanceof Integer) {
@@ -314,12 +333,12 @@ public final class AppSettings extends HashMap {
} else if (val instanceof Boolean) {
prefs.putBoolean("B_" + key, (Boolean) val);
}
- // NOTE: Ignore any parameters of unsupported types instead
- // of throwing exception. This is specifically for handling
+ // NOTE: Ignore any parameters of unsupported types instead
+ // of throwing exception. This is specifically for handling
// BufferedImage which is used in setIcons(), as you do not
// want to export such data in the preferences.
}
-
+
// Ensure the data is properly written into preferences before
// continuing.
prefs.sync();
@@ -366,7 +385,7 @@ public final class AppSettings extends HashMap {
return s;
}
-
+
/**
* Get a float from the settings.
*
@@ -401,51 +420,51 @@ public final class AppSettings extends HashMap {
public void putString(String key, String value) {
put(key, value);
}
-
+
/**
* Set a float on the settings.
*/
public void putFloat(String key, float value) {
put(key, Float.valueOf(value));
}
-
+
/**
* Enable or disable mouse emulation on touchscreen based devices.
* This will convert taps on the touchscreen or movement of finger
* over touchscreen (only the first) into the appropriate mouse events.
- *
+ *
* @param emulateMouse If mouse emulation should be enabled.
*/
public void setEmulateMouse(boolean emulateMouse) {
putBoolean("TouchEmulateMouse", emulateMouse);
}
-
+
/**
* Returns true if mouse emulation is enabled, false otherwise.
- *
+ *
* @return Mouse emulation mode.
*/
public boolean isEmulateMouse() {
return getBoolean("TouchEmulateMouse");
}
-
+
/**
* Specify if the X or Y (or both) axes should be flipped for emulated mouse.
- *
+ *
* @param flipX Set to flip X axis
* @param flipY Set to flip Y axis
- *
- * @see #setEmulateMouse(boolean)
+ *
+ * @see #setEmulateMouse(boolean)
*/
public void setEmulateMouseFlipAxis(boolean flipX, boolean flipY) {
putBoolean("TouchEmulateMouseFlipX", flipX);
putBoolean("TouchEmulateMouseFlipY", flipY);
}
-
+
public boolean isEmulateMouseFlipX() {
return getBoolean("TouchEmulateMouseFlipX");
}
-
+
public boolean isEmulateMouseFlipY() {
return getBoolean("TouchEmulateMouseFlipY");
}
@@ -484,7 +503,7 @@ public final class AppSettings extends HashMap {
* AppSettings.LWJGL_OPENGL1 - Force OpenGL1.1 compatability
* AppSettings.LWJGL_OPENGL2 - Force OpenGL2 compatability
* AppSettings.LWJGL_OPENGL3 - Force OpenGL3.3 compatability
- * AppSettings.LWJGL_OPENGL_ANY - Choose an appropriate
+ * AppSettings.LWJGL_OPENGL_ANY - Choose an appropriate
* OpenGL version based on system capabilities
* null - Disable graphics rendering
*
@@ -496,7 +515,7 @@ public final class AppSettings extends HashMap {
}
/**
- * Set a custom graphics renderer to use. The class should implement
+ * Set a custom graphics renderer to use. The class should implement
* the {@link JmeContext} interface.
* @param clazz The custom context class.
* (Default: not set)
@@ -511,7 +530,7 @@ public final class AppSettings extends HashMap {
* AppSettings.LWJGL_OPENAL - Default for LWJGL
* null - Disable audio
*
- * @param audioRenderer
+ * @param audioRenderer
* (Default: LWJGL)
*/
public void setAudioRenderer(String audioRenderer) {
@@ -573,10 +592,10 @@ public final class AppSettings extends HashMap {
setMinHeight(height);
}
-
-
+
+
/**
- * Set the frequency, also known as refresh rate, for the
+ * Set the frequency, also known as refresh rate, for the
* rendering display.
* @param value The frequency
* (Default: 60)
@@ -593,13 +612,13 @@ public final class AppSettings extends HashMap {
* 16 bits. On some platforms 24 bits might not be supported, in that case,
* specify 16 bits.
* (Default: 24)
- *
+ *
* @param value The depth bits
*/
public void setDepthBits(int value){
putInteger("DepthBits", value);
}
-
+
/**
* Set the number of stencil bits.
*
@@ -608,17 +627,17 @@ public final class AppSettings extends HashMap {
* the stencil buffer.
*
* (Default: 0)
- *
+ *
* @param value Number of stencil bits
*/
public void setStencilBits(int value){
putInteger("StencilBits", value);
}
-
+
/**
* Set the bits per pixel for the display. Appropriate
* values are 16 for RGB565 color format, or 24 for RGB8 color format.
- *
+ *
* @param value The bits per pixel to set
* (Default: 24)
*/
@@ -630,7 +649,7 @@ public final class AppSettings extends HashMap {
* Set the number of samples per pixel. A value of 1 indicates
* each pixel should be single-sampled, higher values indicate
* a pixel should be multi-sampled.
- *
+ *
* @param value The number of samples
* (Default: 1)
*/
@@ -657,16 +676,16 @@ public final class AppSettings extends HashMap {
/**
* Set to true to enable vertical-synchronization, limiting and synchronizing
* every frame rendered to the monitor's refresh rate.
- * @param value
+ * @param value
* (Default: false)
*/
public void setVSync(boolean value) {
putBoolean("VSync", value);
}
-
+
/**
* Enable 3D stereo.
- * This feature requires hardware support from the GPU driver.
+ *
This feature requires hardware support from the GPU driver.
* @see http://en.wikipedia.org/wiki/Quad_buffering
* Once enabled, filters or scene processors that handle 3D stereo rendering
* could use this feature to render using hardware 3D stereo.
@@ -693,16 +712,16 @@ public final class AppSettings extends HashMap {
public void setIcons(Object[] value) {
put("Icons", value);
}
-
+
/**
* Sets the path of the settings dialog image to use.
*
- * The image will be displayed in the settings dialog when the
+ * The image will be displayed in the settings dialog when the
* application is started.
*
* (Default: /com/jme3/app/Monkey.png)
- *
- * @param path The path to the image in the classpath.
+ *
+ * @param path The path to the image in the classpath.
*/
public void setSettingsDialogImage(String path) {
putString("SettingsDialogImage", path);
@@ -710,7 +729,7 @@ public final class AppSettings extends HashMap {
/**
* Get the framerate.
- * @see #setFrameRate(int)
+ * @see #setFrameRate(int)
*/
public int getFrameRate() {
return getInteger("FrameRate");
@@ -718,7 +737,7 @@ public final class AppSettings extends HashMap {
/**
* Get the use input state.
- * @see #setUseInput(boolean)
+ * @see #setUseInput(boolean)
*/
public boolean useInput() {
return getBoolean("UseInput");
@@ -726,7 +745,7 @@ public final class AppSettings extends HashMap {
/**
* Get the renderer
- * @see #setRenderer(java.lang.String)
+ * @see #setRenderer(java.lang.String)
*/
public String getRenderer() {
return getString("Renderer");
@@ -734,7 +753,7 @@ public final class AppSettings extends HashMap {
/**
* Get the width
- * @see #setWidth(int)
+ * @see #setWidth(int)
*/
public int getWidth() {
return getInteger("Width");
@@ -742,7 +761,7 @@ public final class AppSettings extends HashMap {
/**
* Get the height
- * @see #setHeight(int)
+ * @see #setHeight(int)
*/
public int getHeight() {
return getInteger("Height");
@@ -750,7 +769,7 @@ public final class AppSettings extends HashMap {
/**
* Get the width
- * @see #setWidth(int)
+ * @see #setWidth(int)
*/
public int getMinWidth() {
return getInteger("MinWidth");
@@ -758,7 +777,7 @@ public final class AppSettings extends HashMap {
/**
* Get the height
- * @see #setHeight(int)
+ * @see #setHeight(int)
*/
public int getMinHeight() {
return getInteger("MinHeight");
@@ -766,7 +785,7 @@ public final class AppSettings extends HashMap {
/**
* Get the bits per pixel
- * @see #setBitsPerPixel(int)
+ * @see #setBitsPerPixel(int)
*/
public int getBitsPerPixel() {
return getInteger("BitsPerPixel");
@@ -774,7 +793,7 @@ public final class AppSettings extends HashMap {
/**
* Get the frequency
- * @see #setFrequency(int)
+ * @see #setFrequency(int)
*/
public int getFrequency() {
return getInteger("Frequency");
@@ -790,7 +809,7 @@ public final class AppSettings extends HashMap {
/**
* Get the number of stencil bits
- * @see #setStencilBits(int)
+ * @see #setStencilBits(int)
*/
public int getStencilBits() {
return getInteger("StencilBits");
@@ -798,7 +817,7 @@ public final class AppSettings extends HashMap {
/**
* Get the number of samples
- * @see #setSamples(int)
+ * @see #setSamples(int)
*/
public int getSamples() {
return getInteger("Samples");
@@ -806,7 +825,7 @@ public final class AppSettings extends HashMap {
/**
* Get the application title
- * @see #setTitle(java.lang.String)
+ * @see #setTitle(java.lang.String)
*/
public String getTitle() {
return getString("Title");
@@ -814,7 +833,7 @@ public final class AppSettings extends HashMap {
/**
* Get the vsync state
- * @see #setVSync(boolean)
+ * @see #setVSync(boolean)
*/
public boolean isVSync() {
return getBoolean("VSync");
@@ -822,7 +841,7 @@ public final class AppSettings extends HashMap {
/**
* Get the fullscreen state
- * @see #setFullscreen(boolean)
+ * @see #setFullscreen(boolean)
*/
public boolean isFullscreen() {
return getBoolean("Fullscreen");
@@ -830,7 +849,7 @@ public final class AppSettings extends HashMap {
/**
* Get the use joysticks state
- * @see #setUseJoysticks(boolean)
+ * @see #setUseJoysticks(boolean)
*/
public boolean useJoysticks() {
return !getBoolean("DisableJoysticks");
@@ -838,31 +857,31 @@ public final class AppSettings extends HashMap {
/**
* Get the audio renderer
- * @see #setAudioRenderer(java.lang.String)
+ * @see #setAudioRenderer(java.lang.String)
*/
public String getAudioRenderer() {
return getString("AudioRenderer");
}
-
+
/**
* Get the stereo 3D state
- * @see #setStereo3D(boolean)
+ * @see #setStereo3D(boolean)
*/
public boolean useStereo3D(){
- return getBoolean("Stereo3D");
+ return getBoolean("Stereo3D");
}
/**
* Get the icon array
- * @see #setIcons(java.lang.Object[])
+ * @see #setIcons(java.lang.Object[])
*/
public Object[] getIcons() {
return (Object[]) get("Icons");
}
-
+
/**
* Get the settings dialog image
- * @see #setSettingsDialogImage(java.lang.String)
+ * @see #setSettingsDialogImage(java.lang.String)
*/
public String getSettingsDialogImage() {
return getString("SettingsDialogImage");