* Applied android audio renderer patch (thanks to prich on the forum)
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9147 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
f357a7e89f
commit
381a3365ef
@ -1,62 +1,62 @@
|
||||
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 {
|
||||
|
||||
protected AssetKey assetKey;
|
||||
protected float currentVolume = 0f;
|
||||
|
||||
public AndroidAudioData(){
|
||||
super();
|
||||
}
|
||||
|
||||
protected AndroidAudioData(int id){
|
||||
super(id);
|
||||
}
|
||||
|
||||
public AssetKey getAssetKey() {
|
||||
return assetKey;
|
||||
}
|
||||
|
||||
public void setAssetKey(AssetKey assetKey) {
|
||||
this.assetKey = assetKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType() {
|
||||
return DataType.Buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDuration() {
|
||||
return 0; // TODO: ???
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetObject() {
|
||||
this.id = -1;
|
||||
setUpdateNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(Object rendererObject) {
|
||||
((AudioRenderer)rendererObject).deleteAudioData(this);
|
||||
}
|
||||
|
||||
public float getCurrentVolume() {
|
||||
return currentVolume;
|
||||
}
|
||||
|
||||
public void setCurrentVolume(float currentVolume) {
|
||||
this.currentVolume = currentVolume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeObject createDestructableClone() {
|
||||
return new AndroidAudioData(id);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
|
||||
protected AssetKey<?> assetKey;
|
||||
protected float currentVolume = 0f;
|
||||
|
||||
public AndroidAudioData(){
|
||||
super();
|
||||
}
|
||||
|
||||
protected AndroidAudioData(int id){
|
||||
super(id);
|
||||
}
|
||||
|
||||
public AssetKey<?> getAssetKey() {
|
||||
return assetKey;
|
||||
}
|
||||
|
||||
public void setAssetKey(AssetKey<?> assetKey) {
|
||||
this.assetKey = assetKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType() {
|
||||
return DataType.Buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDuration() {
|
||||
return 0; // TODO: ???
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetObject() {
|
||||
this.id = -1;
|
||||
setUpdateNeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(Object rendererObject) {
|
||||
((AudioRenderer)rendererObject).deleteAudioData(this);
|
||||
}
|
||||
|
||||
public float getCurrentVolume() {
|
||||
return currentVolume;
|
||||
}
|
||||
|
||||
public void setCurrentVolume(float currentVolume) {
|
||||
this.currentVolume = currentVolume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeObject createDestructableClone() {
|
||||
return new AndroidAudioData(id);
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,9 @@ import android.content.res.AssetManager;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.SoundPool;
|
||||
import android.util.Log;
|
||||
|
||||
import com.jme3.asset.AssetKey;
|
||||
import com.jme3.audio.AudioNode.Status;
|
||||
import com.jme3.audio.*;
|
||||
import com.jme3.math.FastMath;
|
||||
@ -50,525 +53,426 @@ import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This class is the android implementation for {@link AudioRenderer}
|
||||
*
|
||||
* @author larynx
|
||||
*
|
||||
* @author plan_rich
|
||||
*/
|
||||
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<AudioNode, MediaPlayer> musicPlaying = new HashMap<AudioNode, MediaPlayer>();
|
||||
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<Integer, AudioNode> mapLoadingAudioNodes = new HashMap<Integer, AudioNode>();
|
||||
private final AtomicBoolean lastLoadCompleted = new AtomicBoolean();
|
||||
private Listener listener;
|
||||
private boolean audioDisabled = false;
|
||||
|
||||
public AndroidAudioRenderer(Activity context) {
|
||||
this.context = context;
|
||||
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);
|
||||
soundPool.setOnLoadCompleteListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
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()) {
|
||||
}
|
||||
break;
|
||||
case Volume:
|
||||
|
||||
soundPool.setVolume(src.getChannel(), src.getVolume(), src.getVolume());
|
||||
|
||||
break;
|
||||
case Pitch:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateListenerParam(Listener listener, ListenerParam param) {
|
||||
//logger.log(Level.INFO, "updateListenerParam " + 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 (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()) {
|
||||
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 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
|
||||
{
|
||||
int channelIndex;
|
||||
channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, -1, 1f);
|
||||
src.setChannel(channelIndex);
|
||||
// Playing started ?
|
||||
if (src.getChannel() > 0) {
|
||||
src.setStatus(Status.Playing);
|
||||
}
|
||||
} else {
|
||||
src.setChannel(-1);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("AudioData is not of type AndroidAudioData for AudioNode " + src.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
// Cleanup sound pool
|
||||
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.getId() > 0) {
|
||||
soundPool.unload(audioData.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
soundPool.release();
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} 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);
|
||||
}
|
||||
soundPool.unload(audioData.getId());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
src.setStatus(Status.Stopped);
|
||||
|
||||
if (audioData.getId() > 0) {
|
||||
soundPool.unload(audioData.getId());
|
||||
}
|
||||
audioData.setId(-1);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
MediaPlayer mp = musicPlaying.get(src);
|
||||
mp.stop();
|
||||
mp.release();
|
||||
musicPlaying.remove(src);
|
||||
src.setStatus(Status.Stopped);
|
||||
src.setChannel(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (audioData.getId() > 0) {
|
||||
soundPool.unload(audioData.getId());
|
||||
}
|
||||
audioData.setId(0);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("AudioData is not of type AndroidAudioData in deleteAudioData");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment env) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
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<AudioNode, MediaPlayer> musicPlaying = new HashMap<AudioNode, MediaPlayer>();
|
||||
private SoundPool soundPool = null;
|
||||
|
||||
private final Vector3f listenerPosition = new Vector3f();
|
||||
// For temp use
|
||||
private final Vector3f distanceVector = new Vector3f();
|
||||
private final Context context;
|
||||
private final AssetManager assetManager;
|
||||
private HashMap<Integer, AudioNode> soundpoolStillLoading = new HashMap<Integer, AudioNode>();
|
||||
private Listener listener;
|
||||
private boolean audioDisabled = false;
|
||||
|
||||
private final AudioManager manager;
|
||||
|
||||
public AndroidAudioRenderer(Activity context) {
|
||||
this.context = 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(AudioNode src, AudioParam param) {
|
||||
// logger.log(Level.INFO, "updateSourceParam " + param);
|
||||
|
||||
if (audioDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (src.getChannel() < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
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()) {
|
||||
}
|
||||
break;
|
||||
case Volume:
|
||||
|
||||
soundPool.setVolume(src.getChannel(), src.getVolume(),
|
||||
src.getVolume());
|
||||
|
||||
break;
|
||||
case Pitch:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateListenerParam(Listener listener, ListenerParam param) {
|
||||
// logger.log(Level.INFO, "updateListenerParam " + 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 (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()) {
|
||||
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 (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) {
|
||||
mp.seekTo(0);
|
||||
mp.stop();
|
||||
// XXX: This has bad performance -> maybe change overall structure of
|
||||
// mediaplayer in this audiorenderer?
|
||||
for (AudioNode 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(AudioNode 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 {
|
||||
src.setChannel(channel); // receive a channel at the last
|
||||
// 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) {
|
||||
AudioNode 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();
|
||||
|
||||
if (status == 0) // load was successfull
|
||||
{
|
||||
int channelIndex;
|
||||
channelIndex = soundPool.play(audioData.getId(), 1f, 1f, 1, 0, 1f);
|
||||
src.setChannel(channelIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void playSource(AudioNode 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 {
|
||||
AssetKey<?> key = audioData.getAssetKey();
|
||||
|
||||
AssetFileDescriptor afd = assetManager.openFd(key.getName()); // assetKey.getName()
|
||||
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
|
||||
afd.getLength());
|
||||
mp.prepare();
|
||||
mp.setLooping(src.isLooping());
|
||||
mp.start();
|
||||
src.setChannel(0);
|
||||
src.setStatus(Status.Playing);
|
||||
musicPlaying.put(src, mp);
|
||||
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void pauseSource(AudioNode 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(AudioNode 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();
|
||||
src.setStatus(Status.Paused);
|
||||
} 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 (AudioNode 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) {
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,19 @@
|
||||
package com.jme3.audio.plugins;
|
||||
|
||||
import com.jme3.asset.AssetInfo;
|
||||
import com.jme3.asset.AssetLoader;
|
||||
import com.jme3.audio.android.AndroidAudioData;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class AndroidAudioLoader implements AssetLoader
|
||||
{
|
||||
|
||||
@Override
|
||||
public Object load(AssetInfo assetInfo) throws IOException
|
||||
{
|
||||
|
||||
InputStream in = assetInfo.openStream();
|
||||
if (in != null)
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
AndroidAudioData result = new AndroidAudioData();
|
||||
result.setAssetKey( assetInfo.getKey() );
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
package com.jme3.audio.plugins;
|
||||
|
||||
import com.jme3.asset.AssetInfo;
|
||||
import com.jme3.asset.AssetLoader;
|
||||
import com.jme3.audio.android.AndroidAudioData;
|
||||
import java.io.IOException;
|
||||
|
||||
public class AndroidAudioLoader implements AssetLoader
|
||||
{
|
||||
|
||||
@Override
|
||||
public Object load(AssetInfo assetInfo) throws IOException
|
||||
{
|
||||
AndroidAudioData result = new AndroidAudioData();
|
||||
result.setAssetKey( assetInfo.getKey() );
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user