Android: Refactor AndroidAudioRenderer into an interface with 2 implementations (current MediaPlayer/SoundPool and new OpenAL Soft). Added AppSetting that allows AndroidHarness to switch the audio renderer (default is still MediaPlayer/SoundPool).
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10615 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
96f7b179b5
commit
e727928731
@ -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; |
||||
|
||||
/** |
||||
* 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 final HashMap<AudioSource, MediaPlayer> musicPlaying = new HashMap<AudioSource, MediaPlayer>(); |
||||
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<Integer, AudioSource> soundpoolStillLoading = new HashMap<Integer, AudioSource>(); |
||||
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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
import com.jme3.audio.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. |
||||
* Android specific AudioRenderer interface that supports pausing and resuming |
||||
* audio files when the app is minimized or placed in the background |
||||
* |
||||
* 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. |
||||
* @author iwgeric |
||||
*/ |
||||
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 interface AndroidAudioRenderer extends AudioRenderer { |
||||
|
||||
/** |
||||
* Pause the current playing sounds. Both from the {@link SoundPool} and the |
||||
* active {@link MediaPlayer}s |
||||
* Pauses all Playing audio. To be used when the app is placed in the |
||||
* background. |
||||
*/ |
||||
public void pauseAll() { |
||||
if (soundPool != null) { |
||||
soundPool.autoPause(); |
||||
for (MediaPlayer mp : musicPlaying.values()) { |
||||
if(mp.isPlaying()){ |
||||
mp.pause(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
public void pauseAll(); |
||||
|
||||
/** |
||||
* Resume all paused sounds. |
||||
* Resumes all Paused audio. To be used when the app is brought back to |
||||
* the foreground. |
||||
*/ |
||||
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(); |
||||
} |
||||
|
@ -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<AudioSource, MediaPlayer> musicPlaying = new HashMap<AudioSource, MediaPlayer>(); |
||||
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<Integer, AudioSource> soundpoolStillLoading = new HashMap<Integer, AudioSource>(); |
||||
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) { |
||||
} |
||||
} |
Loading…
Reference in new issue