git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7723 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
68bac95cd5
commit
a7974ac659
@ -0,0 +1,54 @@ |
|||||||
|
package com.jme3.audio.android; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetKey; |
||||||
|
import com.jme3.audio.AudioData; |
||||||
|
import com.jme3.audio.AudioRenderer; |
||||||
|
|
||||||
|
public class AndroidAudioData extends AudioData |
||||||
|
{ |
||||||
|
protected AssetKey assetKey; |
||||||
|
protected int soundId = 0; |
||||||
|
|
||||||
|
public AssetKey getAssetKey() { |
||||||
|
return assetKey; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAssetKey(AssetKey assetKey) { |
||||||
|
this.assetKey = assetKey; |
||||||
|
} |
||||||
|
|
||||||
|
public int getSoundId() { |
||||||
|
return soundId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSoundId(int soundId) { |
||||||
|
this.soundId = soundId; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public DataType getDataType() { |
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public float getDuration() { |
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void resetObject() { |
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void deleteObject(AudioRenderer r) { |
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,518 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2010 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.AssetManager; |
||||||
|
import android.media.AudioManager; |
||||||
|
import android.media.SoundPool; |
||||||
|
|
||||||
|
import com.jme3.audio.ListenerParam; |
||||||
|
import com.jme3.audio.AudioParam; |
||||||
|
import com.jme3.audio.AudioBuffer; |
||||||
|
import com.jme3.audio.AudioData; |
||||||
|
import com.jme3.audio.AudioRenderer; |
||||||
|
import com.jme3.audio.AudioNode; |
||||||
|
import com.jme3.audio.AudioNode.Status; |
||||||
|
import com.jme3.audio.AudioStream; |
||||||
|
import com.jme3.audio.Environment; |
||||||
|
import com.jme3.audio.Filter; |
||||||
|
import com.jme3.audio.Listener; |
||||||
|
import com.jme3.audio.LowPassFilter; |
||||||
|
import com.jme3.math.Vector3f; |
||||||
|
import com.jme3.util.BufferUtils; |
||||||
|
|
||||||
|
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.concurrent.atomic.AtomicBoolean; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* This class is the android implementation for {@link AudioRenderer} |
||||||
|
* @author larynx |
||||||
|
* |
||||||
|
*/ |
||||||
|
public class AndroidAudioRenderer implements AudioRenderer, SoundPool.OnLoadCompleteListener |
||||||
|
{ |
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(AndroidAudioRenderer.class.getName()); |
||||||
|
private final static int MAX_NUM_CHANNELS = 16; |
||||||
|
|
||||||
|
private SoundPool soundPool = null; |
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void updateFilter(Filter f) |
||||||
|
{ |
||||||
|
throw new UnsupportedOperationException("Filter type unsupported: " + f.getClass().getName()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateSourceParam(AudioNode src, AudioParam 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) |
||||||
|
{ |
||||||
|
if (audioDisabled) |
||||||
|
return; |
||||||
|
|
||||||
|
switch (param){ |
||||||
|
case Position: |
||||||
|
Vector3f pos = 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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void update(float tpf) |
||||||
|
{ |
||||||
|
// does nothing
|
||||||
|
} |
||||||
|
|
||||||
|
public void updateInThread(float tpf) |
||||||
|
{ |
||||||
|
if (audioDisabled) |
||||||
|
return; |
||||||
|
if (!audioDisabled) |
||||||
|
return; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
{ |
||||||
|
//lastLoadCompleted.set(true);
|
||||||
|
|
||||||
|
if (status == 0) |
||||||
|
{ |
||||||
|
AudioNode src = mapLoadingAudioNodes.get(sampleId); |
||||||
|
if (src.getAudioData() instanceof AndroidAudioData) |
||||||
|
{ |
||||||
|
AndroidAudioData audioData = (AndroidAudioData)src.getAudioData(); |
||||||
|
|
||||||
|
int channelIndex; |
||||||
|
channelIndex = soundPool.play(audioData.getSoundId(), 1f, 1f, 1, -1, 1f); |
||||||
|
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()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void cleanup() |
||||||
|
{ |
||||||
|
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.getSoundId() > 0) |
||||||
|
{ |
||||||
|
soundPool.unload(audioData.getSoundId()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
soundPool.release(); |
||||||
|
soundPool = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void playSourceInstance(AudioNode src) |
||||||
|
{ |
||||||
|
if (audioDisabled) |
||||||
|
return; |
||||||
|
|
||||||
|
AndroidAudioData audioData; |
||||||
|
int soundId = 0; |
||||||
|
|
||||||
|
if (src.getAudioData() instanceof AndroidAudioData) |
||||||
|
{ |
||||||
|
audioData = (AndroidAudioData)src.getAudioData(); |
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
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) |
||||||
|
{ |
||||||
|
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){ |
||||||
|
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.getAudioData() instanceof AndroidAudioData) |
||||||
|
{ |
||||||
|
audioData = (AndroidAudioData)src.getAudioData(); |
||||||
|
if (audioData.getSoundId() > 0) |
||||||
|
{ |
||||||
|
soundPool.unload(audioData.getSoundId()); |
||||||
|
} |
||||||
|
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) |
||||||
|
{ |
||||||
|
throw new UnsupportedOperationException("updateAudioData"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void deleteAudioData(AudioData ad) |
||||||
|
{ |
||||||
|
if (ad instanceof AndroidAudioData) |
||||||
|
{ |
||||||
|
if (((AndroidAudioData)ad).getSoundId() > 0) |
||||||
|
{ |
||||||
|
soundPool.unload(((AndroidAudioData)ad).getSoundId()); |
||||||
|
} |
||||||
|
((AndroidAudioData)ad).setSoundId(0); |
||||||
|
|
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException("AudioData is not of type AndroidAudioData in deleteAudioData"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setEnvironment(Environment env) { |
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.jme3.audio.plugins; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetInfo; |
||||||
|
import com.jme3.asset.AssetLoader; |
||||||
|
import com.jme3.audio.android.AndroidAudioData; |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue