Android: Added streaming music to AndroidAudioRenderer

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7753 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
kim..ng 14 years ago
parent 09137fef25
commit 354ff46ccf
  1. 2
      engine/src/android/com/jme3/app/android/AndroidApplication.java
  2. 1
      engine/src/android/com/jme3/asset/AndroidAssetManager.java
  3. 375
      engine/src/android/com/jme3/audio/android/AndroidAudioRenderer.java
  4. 101
      engine/src/android/com/jme3/system/android/AndroidConfigChooser.java

@ -296,7 +296,7 @@ public abstract class AndroidApplication extends Application implements DialogIn
{ {
if (whichButton != -2) if (whichButton != -2)
{ {
this.stop(); this.stop(true);
activity.finish(); activity.finish();
} }
} }

@ -70,6 +70,7 @@ public class AndroidAssetManager extends DesktopAssetManager {
System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver"); System.setProperty("org.xml.sax.driver","org.xmlpull.v1.sax2.Driver");
// Set Default Android config // Set Default Android config
this.registerLocator("", AndroidLocator.class); this.registerLocator("", AndroidLocator.class);
this.registerLocator("", ClasspathLocator.class); this.registerLocator("", ClasspathLocator.class);

@ -33,31 +33,31 @@ package com.jme3.audio.android;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool; import android.media.SoundPool;
import com.jme3.audio.AudioKey;
import com.jme3.audio.ListenerParam; import com.jme3.audio.ListenerParam;
import com.jme3.audio.AudioParam; import com.jme3.audio.AudioParam;
import com.jme3.audio.AudioBuffer;
import com.jme3.audio.AudioData; import com.jme3.audio.AudioData;
import com.jme3.audio.AudioRenderer; import com.jme3.audio.AudioRenderer;
import com.jme3.audio.AudioNode; import com.jme3.audio.AudioNode;
import com.jme3.audio.AudioNode.Status; import com.jme3.audio.AudioNode.Status;
import com.jme3.audio.AudioStream;
import com.jme3.audio.Environment; import com.jme3.audio.Environment;
import com.jme3.audio.Filter; import com.jme3.audio.Filter;
import com.jme3.audio.Listener; import com.jme3.audio.Listener;
import com.jme3.audio.LowPassFilter;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.util.BufferUtils;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
@ -69,13 +69,15 @@ import java.util.logging.Logger;
* @author larynx * @author larynx
* *
*/ */
public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadCompleteListener public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadCompleteListener, MediaPlayer.OnCompletionListener
{ {
private static final Logger logger = Logger.getLogger(AndroidAudioRenderer.class.getName()); private static final Logger logger = Logger.getLogger(AndroidAudioRenderer.class.getName());
private final static int MAX_NUM_CHANNELS = 16; private final static int MAX_NUM_CHANNELS = 16;
private SoundPool soundPool = null; private SoundPool soundPool = null;
private HashMap<AudioNode, MediaPlayer> musicPlaying = new HashMap<AudioNode, MediaPlayer>();
private final AudioManager manager; private final AudioManager manager;
private final Context context; private final Context context;
private final AssetManager am; private final AssetManager am;
@ -104,12 +106,6 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC, 0); soundPool = new SoundPool(MAX_NUM_CHANNELS, AudioManager.STREAM_MUSIC, 0);
soundPool.setOnLoadCompleteListener(this); soundPool.setOnLoadCompleteListener(this);
} }
private void updateFilter(Filter f)
{
throw new UnsupportedOperationException("Filter type unsupported: " + f.getClass().getName());
}
@Override @Override
public void updateSourceParam(AudioNode src, AudioParam param) public void updateSourceParam(AudioNode src, AudioParam param)
@ -184,7 +180,7 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
if (src.getDryFilter() != null){ if (src.getDryFilter() != null){
Filter f = src.getDryFilter(); Filter f = src.getDryFilter();
if (f.isUpdateNeeded()){ if (f.isUpdateNeeded()){
updateFilter(f); //updateFilter(f);
} }
} }
@ -232,6 +228,7 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
} }
/*
public void update(float tpf) public void update(float tpf)
{ {
@ -246,6 +243,7 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
return; return;
} }
*/
public void setListener(Listener listener) public void setListener(Listener listener)
{ {
@ -266,15 +264,13 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
@Override @Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) public void onLoadComplete(SoundPool soundPool, int sampleId, int status)
{ {
//lastLoadCompleted.set(true); AudioNode src = mapLoadingAudioNodes.get(sampleId);
if (src.getAudioData() instanceof AndroidAudioData)
if (status == 0)
{ {
AudioNode src = mapLoadingAudioNodes.get(sampleId); AndroidAudioData audioData = (AndroidAudioData)src.getAudioData();
if (src.getAudioData() instanceof AndroidAudioData)
if (status == 0) // load was successfull
{ {
AndroidAudioData audioData = (AndroidAudioData)src.getAudioData();
int channelIndex; int channelIndex;
channelIndex = soundPool.play(audioData.getSoundId(), 1f, 1f, 1, -1, 1f); channelIndex = soundPool.play(audioData.getSoundId(), 1f, 1f, 1, -1, 1f);
src.setChannel(channelIndex); src.setChannel(channelIndex);
@ -286,14 +282,19 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
} }
else else
{ {
throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString()); src.setChannel(-1);
} }
} }
else
{
throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString());
}
} }
@Override @Override
public void cleanup() public void cleanup()
{ {
// Cleanup sound pool
if (soundPool != null) if (soundPool != null)
{ {
for (AudioNode src: mapLoadingAudioNodes.values()) for (AudioNode src: mapLoadingAudioNodes.values())
@ -316,41 +317,142 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
soundPool.release(); soundPool.release();
soundPool = null; soundPool = null;
} }
// Cleanup media player
for (AudioNode src : musicPlaying.keySet())
{
MediaPlayer mp = musicPlaying.get(src);
{
mp.stop();
mp.release();
src.setStatus(Status.Stopped);
}
}
musicPlaying.clear();
} }
@Override
public void onCompletion(MediaPlayer mp)
{
for (AudioNode src : musicPlaying.keySet())
{
if (musicPlaying.get(src) == mp)
{
mp.seekTo(0);
mp.stop();
src.setStatus(Status.Stopped);
}
}
}
public void playSourceInstance(AudioNode src) public void playSourceInstance(AudioNode src)
{ {
if (audioDisabled) if (audioDisabled)
return; return;
AndroidAudioData audioData; AndroidAudioData audioData;
int soundId = 0; int soundId = 0;
if (src.getAudioData() instanceof AndroidAudioData) if (src.getAudioData() instanceof AndroidAudioData)
{ {
audioData = (AndroidAudioData)src.getAudioData(); audioData = (AndroidAudioData)src.getAudioData();
if (audioData.isUpdateNeeded() || (audioData.getSoundId() == 0))
{ if (audioData.getAssetKey() instanceof AudioKey)
if (audioData.getSoundId() > 0) {
AudioKey assetKey = (AudioKey) audioData.getAssetKey();
if (assetKey.isStream())
{ {
if (src.getChannel() > 0) MediaPlayer mp;
if (musicPlaying.containsKey(src))
{ {
soundPool.stop(src.getChannel()); mp = musicPlaying.get(src);
src.setChannel(-1);
} }
soundPool.unload(audioData.getSoundId()); 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.setStatus(Status.Playing);
src.setChannel(1);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }
else
try
{
soundId = soundPool.load(am.openFd(audioData.getAssetKey().getName()), 1);
}
catch (IOException e)
{ {
logger.log(Level.SEVERE, "Failed to load sound " + audioData.getAssetKey().getName(), e); // Low latency Sound effect using SoundPool
soundId = -1; 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(audioData.getAssetKey().getName()), 1);
}
catch (IOException e)
{
logger.log(Level.SEVERE, "Failed to load sound " + audioData.getAssetKey().getName(), e);
soundId = -1;
}
audioData.setSoundId(soundId);
}
// Sound failed to load ?
if (audioData.getSoundId() <= 0)
{
throw new IllegalArgumentException("Failed to load: " + audioData.getAssetKey().getName());
}
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);
}
// Playing started ?
if (src.getChannel() > 0)
{
src.setStatus(Status.Playing);
}
} }
audioData.setSoundId(soundId);
} }
} }
else else
@ -358,29 +460,7 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString()); throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString());
} }
// Sound failed to load ?
if (audioData.getSoundId() <= 0)
{
throw new IllegalArgumentException("Failed to load: " + audioData.getAssetKey().getName());
}
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);
}
// Playing started ?
if (src.getChannel() > 0)
{
src.setStatus(Status.Playing);
}
} }
@ -411,14 +491,37 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
return; return;
if (src.getStatus() == Status.Playing) if (src.getStatus() == Status.Playing)
{ {
assert src.getChannel() != -1; if (src.getAudioData() instanceof AndroidAudioData)
if (src.getChannel() > 0)
{ {
soundPool.pause(src.getChannel()); 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);
}
}
}
} }
src.setStatus(Status.Paused);
} }
} }
@ -429,59 +532,55 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
if (audioDisabled) if (audioDisabled)
return; return;
if (src.getStatus() != Status.Stopped){
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);
}
AndroidAudioData audioData; if (src.getStatus() != Status.Stopped)
if (src.getAudioData() instanceof AndroidAudioData) {
{ if (src.getAudioData() instanceof AndroidAudioData)
audioData = (AndroidAudioData)src.getAudioData();
if (audioData.getSoundId() > 0)
{ {
soundPool.unload(audioData.getSoundId()); 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);
}
src.setStatus(Status.Stopped);
if (audioData.getSoundId() > 0)
{
soundPool.unload(audioData.getSoundId());
}
audioData.setSoundId(-1);
}
}
} }
audioData.setSoundId(0);
} }
else
{
throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString());
}
} }
private int convertFormat(AudioData ad)
{
/*
switch (ad.getBitsPerSample()){
case 8:
if (ad.getChannels() == 1)
return AL_FORMAT_MONO8;
else if (ad.getChannels() == 2)
return AL_FORMAT_STEREO8;
break;
case 16:
if (ad.getChannels() == 1)
return AL_FORMAT_MONO16;
else
return AL_FORMAT_STEREO16;
}
*/
throw new UnsupportedOperationException("Unsupported channels/bits combination: "+
"bits="+ad.getBitsPerSample()+", channels="+ad.getChannels());
}
public void updateAudioData(AndroidAudioData data) public void updateAudioData(AndroidAudioData data)
{ {
@ -490,15 +589,39 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
@Override @Override
public void deleteAudioData(AudioData ad) public void deleteAudioData(AudioData ad)
{ {
if (ad instanceof AndroidAudioData) if (ad instanceof AndroidAudioData)
{ {
if (((AndroidAudioData)ad).getSoundId() > 0) AndroidAudioData audioData = (AndroidAudioData)ad;
{ if (audioData.getAssetKey() instanceof AudioKey)
soundPool.unload(((AndroidAudioData)ad).getSoundId()); {
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;
}
}
}
else
{
if (audioData.getSoundId() > 0)
{
soundPool.unload(audioData.getSoundId());
}
audioData.setSoundId(0);
}
} }
((AndroidAudioData)ad).setSoundId(0);
} }
else else
{ {
@ -512,7 +635,11 @@ public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadComp
} }
@Override
public void update(float tpf) {
// TODO Auto-generated method stub
}
} }

@ -91,8 +91,8 @@ public class AndroidConfigChooser implements EGLConfigChooser
if ((value[0] & EGL10.EGL_WINDOW_BIT) != 0) if ((value[0] & EGL10.EGL_WINDOW_BIT) != 0)
{ {
egl.eglGetConfigAttrib(display, conf[i], EGL10.EGL_DEPTH_SIZE, value); egl.eglGetConfigAttrib(display, conf[i], EGL10.EGL_DEPTH_SIZE, value);
// check if conf has a depth of 16 // check if conf has a minimum depth of 16
if (value[0] == 16) if (value[0] >= 16)
{ {
egl.eglGetConfigAttrib(display, conf[i], EGL10.EGL_RENDERABLE_TYPE, value); egl.eglGetConfigAttrib(display, conf[i], EGL10.EGL_RENDERABLE_TYPE, value);
// Check if conf is OpenGL ES 2.0 // Check if conf is OpenGL ES 2.0
@ -121,7 +121,7 @@ public class AndroidConfigChooser implements EGLConfigChooser
{ {
if (verbose) if (verbose)
{ {
logger.info("NOT Supported EGL Configuration #" + i + " EGL_DEPTH_SIZE != 16"); logger.info("NOT Supported EGL Configuration #" + i + " EGL_DEPTH_SIZE < 16");
logEGLConfig(conf[i], display, egl); logEGLConfig(conf[i], display, egl);
} }
} }
@ -199,30 +199,45 @@ public class AndroidConfigChooser implements EGLConfigChooser
result = b; result = b;
else // red size is equal else // red size is equal
{ {
// Choose lowest alpha size // Choose highest depth size
egl.eglGetConfigAttrib(display, a, EGL10.EGL_ALPHA_SIZE, value); egl.eglGetConfigAttrib(display, a, EGL10.EGL_DEPTH_SIZE, value);
int alphaA = value[0]; int depthA = value[0];
egl.eglGetConfigAttrib(display, b, EGL10.EGL_ALPHA_SIZE, value); egl.eglGetConfigAttrib(display, b, EGL10.EGL_DEPTH_SIZE, value);
int alphaB = value[0]; int depthB = value[0];
if (alphaA < alphaB) if (depthA > depthB)
result = a; result = a;
else if (alphaA > alphaB) else if (depthA < depthB)
result = b; result = b;
else // alpha is equal else // depth is equal
{ {
// Choose lowest stencil size
egl.eglGetConfigAttrib(display, a, EGL10.EGL_STENCIL_SIZE, value); // Choose lowest alpha size
int stencilA = value[0]; egl.eglGetConfigAttrib(display, a, EGL10.EGL_ALPHA_SIZE, value);
int alphaA = value[0];
egl.eglGetConfigAttrib(display, b, EGL10.EGL_STENCIL_SIZE, value); egl.eglGetConfigAttrib(display, b, EGL10.EGL_ALPHA_SIZE, value);
int stencilB = value[0]; int alphaB = value[0];
if (stencilA < stencilB) if (alphaA < alphaB)
result = a; result = a;
else else if (alphaA > alphaB)
result = b; result = b;
else // alpha is equal
{
// Choose lowest stencil size
egl.eglGetConfigAttrib(display, a, EGL10.EGL_STENCIL_SIZE, value);
int stencilA = value[0];
egl.eglGetConfigAttrib(display, b, EGL10.EGL_STENCIL_SIZE, value);
int stencilB = value[0];
if (stencilA < stencilB)
result = a;
else
result = b;
}
} }
} }
return result; return result;
@ -242,7 +257,7 @@ public class AndroidConfigChooser implements EGLConfigChooser
int[] value = new int[1]; int[] value = new int[1];
// Choose highest color size // Choose 565 color size
egl.eglGetConfigAttrib(display, a, EGL10.EGL_RED_SIZE, value); egl.eglGetConfigAttrib(display, a, EGL10.EGL_RED_SIZE, value);
int redA = value[0]; int redA = value[0];
@ -255,30 +270,44 @@ public class AndroidConfigChooser implements EGLConfigChooser
result = b; result = b;
else // red size is equal else // red size is equal
{ {
// Choose lowest alpha size // Choose lowest depth size
egl.eglGetConfigAttrib(display, a, EGL10.EGL_ALPHA_SIZE, value); egl.eglGetConfigAttrib(display, a, EGL10.EGL_DEPTH_SIZE, value);
int alphaA = value[0]; int depthA = value[0];
egl.eglGetConfigAttrib(display, b, EGL10.EGL_ALPHA_SIZE, value); egl.eglGetConfigAttrib(display, b, EGL10.EGL_DEPTH_SIZE, value);
int alphaB = value[0]; int depthB = value[0];
if (alphaA < alphaB) if (depthA < depthB)
result = a; result = a;
else if (alphaA > alphaB) else if (depthA > depthB)
result = b; result = b;
else // alpha is equal else // depth is equal
{ {
// Choose lowest stencil size // Choose lowest alpha size
egl.eglGetConfigAttrib(display, a, EGL10.EGL_STENCIL_SIZE, value); egl.eglGetConfigAttrib(display, a, EGL10.EGL_ALPHA_SIZE, value);
int stencilA = value[0]; int alphaA = value[0];
egl.eglGetConfigAttrib(display, b, EGL10.EGL_STENCIL_SIZE, value); egl.eglGetConfigAttrib(display, b, EGL10.EGL_ALPHA_SIZE, value);
int stencilB = value[0]; int alphaB = value[0];
if (stencilA < stencilB) if (alphaA < alphaB)
result = a; result = a;
else else if (alphaA > alphaB)
result = b; result = b;
else // alpha is equal
{
// Choose lowest stencil size
egl.eglGetConfigAttrib(display, a, EGL10.EGL_STENCIL_SIZE, value);
int stencilA = value[0];
egl.eglGetConfigAttrib(display, b, EGL10.EGL_STENCIL_SIZE, value);
int stencilB = value[0];
if (stencilA < stencilB)
result = a;
else
result = b;
}
} }
} }
return result; return result;

Loading…
Cancel
Save