|
|
|
@ -58,442 +58,441 @@ import java.util.logging.Logger; |
|
|
|
|
* @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<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(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Pause the current playing sounds. Both from the {@link SoundPool} and the |
|
|
|
|
* active {@link MediaPlayer}s |
|
|
|
|
*/ |
|
|
|
|
public void pauseAll() { |
|
|
|
|
soundPool.autoPause(); |
|
|
|
|
for (MediaPlayer mp : musicPlaying.values()) { |
|
|
|
|
mp.pause(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Resume all paused sounds. |
|
|
|
|
*/ |
|
|
|
|
public void resumeAll() { |
|
|
|
|
soundPool.autoResume(); |
|
|
|
|
for (MediaPlayer mp : musicPlaying.values()) { |
|
|
|
|
mp.start(); //no resume -> api says call start to resume
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
} |
|
|
|
|
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(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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()) { |
|
|
|
|
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(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) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|