[changed] SoundTest is fancier now, demonstrates new API.
}\r
\r
@Override\r
- public void play () {\r
- play(1);\r
+ public long play () {\r
+ return play(1);\r
}\r
\r
@Override\r
- public void play (float volume) {\r
+ public long play (float volume) {\r
if (streamIds.size == 8) streamIds.pop();\r
- streamIds.add(soundPool.play(soundId, volume, volume, 1, 0, 1));\r
+ int streamId = soundPool.play(soundId, volume, volume, 1, 0, 1);\r
+ streamIds.add(streamId);\r
+ return streamId;\r
}\r
\r
public void stop () {\r
for (int i = 0, n = streamIds.size; i < n; i++)\r
soundPool.stop(streamIds.get(i));\r
}\r
+\r
+ @Override\r
+ public void stop (long soundId) {\r
+ soundPool.stop((int)soundId);\r
+ }\r
+\r
+ @Override\r
+ public void setPitch (long soundId, float pitch) {\r
+ soundPool.setRate((int)soundId, pitch);\r
+ }\r
+\r
+ @Override\r
+ public void setVolume (long soundId, float volume) {\r
+ soundPool.setVolume((int)soundId, volume, volume);\r
+ }\r
+\r
+ @Override\r
+ public long loop () {\r
+ return loop(1);\r
+ }\r
+\r
+ @Override\r
+ public long loop (float volume) {\r
+ long streamId = play(volume);\r
+ soundPool.setLoop((int)streamId, -1);\r
+ return streamId;\r
+ }\r
+\r
+ @Override\r
+ public void setLooping (long soundId, boolean looping) {\r
+ soundPool.setLoop((int)soundId, looping?-1:0);\r
+ }\r
}\r
\r
package com.badlogic.gdx.backends.openal;\r
\r
+import static org.lwjgl.openal.AL10.AL_BUFFER;\r
+import static org.lwjgl.openal.AL10.AL_NO_ERROR;\r
+import static org.lwjgl.openal.AL10.AL_ORIENTATION;\r
+import static org.lwjgl.openal.AL10.AL_PAUSED;\r
+import static org.lwjgl.openal.AL10.AL_PLAYING;\r
+import static org.lwjgl.openal.AL10.AL_POSITION;\r
+import static org.lwjgl.openal.AL10.AL_SOURCE_STATE;\r
+import static org.lwjgl.openal.AL10.AL_STOPPED;\r
+import static org.lwjgl.openal.AL10.AL_VELOCITY;\r
+import static org.lwjgl.openal.AL10.alDeleteSources;\r
+import static org.lwjgl.openal.AL10.alGenSources;\r
+import static org.lwjgl.openal.AL10.alGetError;\r
+import static org.lwjgl.openal.AL10.alGetSourcei;\r
+import static org.lwjgl.openal.AL10.alListener;\r
+import static org.lwjgl.openal.AL10.alSourceStop;\r
+import static org.lwjgl.openal.AL10.alSourcei;\r
+\r
import java.nio.FloatBuffer;\r
\r
import org.lwjgl.BufferUtils;\r
import org.lwjgl.LWJGLException;\r
import org.lwjgl.openal.AL;\r
+import org.lwjgl.openal.AL10;\r
\r
import com.badlogic.gdx.Audio;\r
import com.badlogic.gdx.audio.AudioDevice;\r
import com.badlogic.gdx.utils.Array;\r
import com.badlogic.gdx.utils.GdxRuntimeException;\r
import com.badlogic.gdx.utils.IntArray;\r
+import com.badlogic.gdx.utils.IntMap;\r
+import com.badlogic.gdx.utils.LongMap;\r
import com.badlogic.gdx.utils.ObjectMap;\r
\r
-import static org.lwjgl.openal.AL10.*;\r
-\r
/** @author Nathan Sweet */\r
public class OpenALAudio implements Audio {\r
private IntArray idleSources, allSources;\r
+ /** sound id to source **/\r
+ private LongMap<Integer> soundIdToSource;\r
+ private IntMap<Long> sourceToSoundId;\r
+ private long nextSoundId = 0;\r
private ObjectMap<String, Class<? extends OpenALSound>> extensionToSoundClass = new ObjectMap();\r
private ObjectMap<String, Class<? extends OpenALMusic>> extensionToMusicClass = new ObjectMap();\r
\r
allSources.add(sourceID);\r
}\r
idleSources = new IntArray(allSources);\r
+ soundIdToSource = new LongMap<Integer>();\r
+ sourceToSoundId = new IntMap<Long>();\r
\r
FloatBuffer orientation = (FloatBuffer)BufferUtils.createFloatBuffer(6)\r
.put(new float[] {0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f}).flip();\r
\r
int obtainSource (boolean isMusic) {\r
for (int i = 0, n = idleSources.size; i < n; i++) {\r
- int sourceID = idleSources.get(i);\r
- int state = alGetSourcei(sourceID, AL_SOURCE_STATE);\r
+ int sourceId = idleSources.get(i);\r
+ int state = alGetSourcei(sourceId, AL_SOURCE_STATE);\r
if (state != AL_PLAYING && state != AL_PAUSED) {\r
- if (isMusic) idleSources.removeIndex(i);\r
- alSourceStop(sourceID);\r
- alSourcei(sourceID, AL_BUFFER, 0);\r
- return sourceID;\r
+ if (isMusic) {\r
+ idleSources.removeIndex(i);\r
+ } else {\r
+ if(sourceToSoundId.containsKey(sourceId)) {\r
+ long soundId = sourceToSoundId.get(sourceId);\r
+ sourceToSoundId.remove(sourceId);\r
+ soundIdToSource.remove(soundId);\r
+ }\r
+ \r
+ long soundId = nextSoundId++;\r
+ sourceToSoundId.put(sourceId, soundId);\r
+ soundIdToSource.put(soundId, sourceId);\r
+ }\r
+ alSourceStop(sourceId);\r
+ alSourcei(sourceId, AL_BUFFER, 0);\r
+ AL10.alSourcef(sourceId, AL10.AL_GAIN, 1);\r
+ AL10.alSourcef(sourceId, AL10.AL_PITCH, 1);\r
+ return sourceId;\r
}\r
}\r
return -1;\r
void freeSource (int sourceID) {\r
alSourceStop(sourceID);\r
alSourcei(sourceID, AL_BUFFER, 0);\r
+ if(sourceToSoundId.containsKey(sourceID)) {\r
+ long soundId = sourceToSoundId.remove(sourceID);\r
+ soundIdToSource.remove(soundId);\r
+ }\r
idleSources.add(sourceID);\r
}\r
\r
for (int i = 0, n = idleSources.size; i < n; i++) {\r
int sourceID = idleSources.get(i);\r
if (alGetSourcei(sourceID, AL_BUFFER) == bufferID) {\r
+ long soundId = sourceToSoundId.remove(sourceID);\r
+ soundIdToSource.remove(soundId);\r
+ \r
alSourceStop(sourceID);\r
alSourcei(sourceID, AL_BUFFER, 0);\r
}\r
void stopSourcesWithBuffer (int bufferID) {\r
for (int i = 0, n = idleSources.size; i < n; i++) {\r
int sourceID = idleSources.get(i);\r
- if (alGetSourcei(sourceID, AL_BUFFER) == bufferID) alSourceStop(sourceID);\r
+ if (alGetSourcei(sourceID, AL_BUFFER) == bufferID) {\r
+ long soundId = sourceToSoundId.remove(sourceID);\r
+ soundIdToSource.remove(soundId);\r
+ \r
+ alSourceStop(sourceID);\r
+ }\r
}\r
}\r
\r
music.items[i].update();\r
}\r
\r
+ public long getSoundId(int sourceId) {\r
+ if(!sourceToSoundId.containsKey(sourceId)) return -1;\r
+ return sourceToSoundId.get(sourceId);\r
+ }\r
+ \r
+ public void stopSound(long soundId) {\r
+ if(!soundIdToSource.containsKey(soundId)) return;\r
+ int sourceId = soundIdToSource.get(soundId);\r
+ alSourceStop(sourceId);\r
+ }\r
+ \r
+ public void setSoundGain(long soundId, float volume) {\r
+ if(!soundIdToSource.containsKey(soundId)) return;\r
+ int sourceId = soundIdToSource.get(soundId);\r
+ AL10.alSourcef(sourceId, AL10.AL_GAIN, volume);\r
+ }\r
+\r
+ public void setSoundLooping (long soundId, boolean looping) {\r
+ if(!soundIdToSource.containsKey(soundId)) return;\r
+ int sourceId = soundIdToSource.get(soundId);\r
+ alSourcei(sourceId, AL10.AL_LOOPING, looping?AL10.AL_TRUE:AL10.AL_FALSE);\r
+ }\r
+ \r
+ public void setSoundPitch(long soundId, float pitch) {\r
+ if(!soundIdToSource.containsKey(soundId)) return;\r
+ int sourceId = soundIdToSource.get(soundId);\r
+ AL10.alSourcef(sourceId, AL10.AL_PITCH, pitch);\r
+ }\r
+ \r
public void dispose () {\r
for (int i = 0, n = allSources.size; i < n; i++) {\r
int sourceID = allSources.get(i);\r
alDeleteSources(sourceID);\r
}\r
\r
+ sourceToSoundId.clear();\r
+ soundIdToSource.clear();\r
+ \r
AL.destroy();\r
while(AL.isCreated()) {\r
try {\r
public AudioRecorder newAudioRecoder (int samplingRate, boolean isMono) {\r
return new JavaSoundAudioRecorder(samplingRate, isMono);\r
}\r
+\r
}\r
}\r
}\r
\r
- public void play () {\r
- play(1);\r
+ public long play () {\r
+ return play(1);\r
}\r
\r
- public void play (float volume) {\r
+ public long play (float volume) {\r
int sourceID = audio.obtainSource(false);\r
- if (sourceID == -1) return;\r
+ if (sourceID == -1) return -1;\r
+ long soundId = audio.getSoundId(sourceID);\r
alSourcei(sourceID, AL_BUFFER, bufferID);\r
alSourcei(sourceID, AL_LOOPING, AL_FALSE);\r
alSourcef(sourceID, AL_GAIN, volume);\r
alSourcePlay(sourceID);\r
+ return soundId;\r
}\r
\r
- public void loop () {\r
+ public long loop () {\r
+ return loop(1);\r
+ }\r
+\r
+ @Override\r
+ public long loop (float volume) {\r
int sourceID = audio.obtainSource(false);\r
- if (sourceID == -1) return;\r
+ if (sourceID == -1) return -1;\r
+ long soundId = audio.getSoundId(sourceID);\r
alSourcei(sourceID, AL_BUFFER, bufferID);\r
alSourcei(sourceID, AL_LOOPING, AL_TRUE);\r
+ alSourcef(sourceID, AL_GAIN, volume);\r
alSourcePlay(sourceID);\r
+ return soundId;\r
}\r
\r
public void stop () {\r
alDeleteBuffers(bufferID);\r
bufferID = -1;\r
}\r
+\r
+ @Override\r
+ public void stop (long soundId) {\r
+ audio.stopSound(soundId);\r
+ }\r
+\r
+ @Override\r
+ public void setPitch (long soundId, float pitch) {\r
+ audio.setSoundPitch(soundId, pitch);\r
+ }\r
+\r
+ @Override\r
+ public void setVolume (long soundId, float volume) {\r
+ audio.setSoundGain(soundId, volume);\r
+ }\r
+\r
+\r
+ @Override\r
+ public void setLooping (long soundId, boolean looping) {\r
+ audio.setSoundLooping(soundId, looping);\r
+ }\r
}\r
* Sound instances are created via a call to {@link Audio#newSound(FileHandle)}.\r
* </p>\r
* \r
+ * <p>\r
+ * Calling the {@link #play()} or {@link #play(float)} method will return a long which is an id\r
+ * to that instance of the sound. You can use this id to modify the playback of that sound instance.\r
+ * </p>\r
+ * \r
* @author badlogicgames@gmail.com */\r
public interface Sound extends Disposable {\r
- /** Plays the sound. If the sound is already playing, it will be played again, concurrently. */\r
- public void play ();\r
+ /** Plays the sound. If the sound is already playing, it will be played again, concurrently. \r
+ * @return the id of the sound instance. */\r
+ public long play ();\r
\r
/** Plays the sound. If the sound is already playing, it will be played again, concurrently.\r
- * @param volume the volume in the range [0,1] */\r
- public void play (float volume);\r
+ * @param volume the volume in the range [0,1] \r
+ * @return the id of the sound instance \r
+ */\r
+ public long play (float volume);\r
+ \r
+ /**\r
+ * Plays the sound, looping. If the sound is already playing, it will be played again, concurrently.\r
+ * @return the id of the sound instance\r
+ */\r
+ public long loop();\r
+ \r
+ /**\r
+ * Plays the sound, looping. If the sound is already playing, it will be played again, concurrently.\r
+ * @param volume the volume in the range [0, 1]\r
+ * @return the id of the sound instance\r
+ */\r
+ public long loop(float volume);\r
\r
/** Stops playing all instances of this sound. */\r
public void stop ();\r
\r
/** Releases all the resources. */\r
public void dispose ();\r
+ \r
+ /**\r
+ * Stops the sound instance with the given id as returned by {@link #play()} or {@link #play(float)}. If\r
+ * the sound is no longer playing, this has no effect.\r
+ * @param soundId the sound id\r
+ */\r
+ public void stop(long soundId);\r
+ \r
+ /**\r
+ * Sets the sound instance with the given id to be looping. If the sound is no longer playing this has no effect.s\r
+ * @param soundId the sound id\r
+ * @param looping whether to loop or not.\r
+ */\r
+ public void setLooping(long soundId, boolean looping);\r
+\r
+ /**\r
+ * Changes the pitch multiplier of the sound instance with the given id as returned by {@link #play()} or {@link #play(float)}. If\r
+ * the sound is no longer playing, this has no effect.\r
+ * @param soundId the sound id\r
+ * @param pitch the pitch multiplier, 1 == default, >1 == faster, <1 == slower\r
+ */\r
+ public void setPitch(long soundId, float pitch);\r
+ \r
+ /**\r
+ * Changes the volume of the sound instance with the given id as returned by {@link #play()} or {@link #play(float)}. If\r
+ * the sound is no longer playing, this has no effect.\r
+ * @param soundId the sound id\r
+ * @param volume the volume in the range 0 (silent) to 1 (max volume).\r
+ */\r
+ public void setVolume(long soundId, float volume);\r
}\r
import com.badlogic.gdx.tests.Box2DTest;\r
import com.badlogic.gdx.tests.Box2DTestCollection;\r
import com.badlogic.gdx.tests.EdgeDetectionTest;\r
+import com.badlogic.gdx.tests.SoundTest;\r
import com.badlogic.gdx.tests.UITest;\r
import com.badlogic.gdx.tests.MD5Test;\r
import com.badlogic.gdx.tests.StbTrueTypeTest;\r
\r
public class LwjglDebugStarter {\r
public static void main (String[] argv) {\r
- GdxTest test = new UITest();\r
+ GdxTest test = new SoundTest();\r
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();\r
config.useGL20 = test.needsGL20();\r
config.vSyncEnabled = true;\r
\r
import com.badlogic.gdx.Files.FileType;\r
import com.badlogic.gdx.Gdx;\r
-import com.badlogic.gdx.Input;\r
-import com.badlogic.gdx.InputProcessor;\r
-import com.badlogic.gdx.audio.Music;\r
import com.badlogic.gdx.audio.Sound;\r
import com.badlogic.gdx.graphics.GL10;\r
import com.badlogic.gdx.graphics.g2d.BitmapFont;\r
import com.badlogic.gdx.graphics.g2d.SpriteBatch;\r
+import com.badlogic.gdx.scenes.scene2d.Actor;\r
+import com.badlogic.gdx.scenes.scene2d.Stage;\r
+import com.badlogic.gdx.scenes.scene2d.ui.Align;\r
+import com.badlogic.gdx.scenes.scene2d.ui.Button;\r
+import com.badlogic.gdx.scenes.scene2d.ui.ClickListener;\r
+import com.badlogic.gdx.scenes.scene2d.ui.Label;\r
+import com.badlogic.gdx.scenes.scene2d.ui.Skin;\r
+import com.badlogic.gdx.scenes.scene2d.ui.Slider;\r
+import com.badlogic.gdx.scenes.scene2d.ui.Slider.ValueChangedListener;\r
+import com.badlogic.gdx.scenes.scene2d.ui.tablelayout.Table;\r
import com.badlogic.gdx.tests.utils.GdxTest;\r
\r
-public class SoundTest extends GdxTest implements InputProcessor {\r
+public class SoundTest extends GdxTest {\r
Sound sound;\r
- Music music;\r
float volume = 0.5f;\r
+ long soundId = 0;\r
+ Stage ui;\r
\r
BitmapFont font;\r
SpriteBatch batch;\r
\r
@Override\r
- public void render () {\r
- Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);\r
- batch.begin();\r
- font.draw(batch, "Position: " + music.getPosition(), 30, 146);\r
- batch.end();\r
- }\r
-\r
- @Override\r
public void create () {\r
- // sound = Gdx.audio.newSound(Gdx.files.getFileHandle("data/shotgun.wav", FileType.Internal));\r
- sound = Gdx.audio.newSound(Gdx.files.getFileHandle("data/sell_buy_item.wav", FileType.Internal));\r
-\r
- // music = Gdx.audio.newMusic(Gdx.files.internal("data/cloudconnected.ogg"));\r
- music = Gdx.audio.newMusic(Gdx.files.getFileHandle("data/threeofaperfectpair.mp3", FileType.Internal));\r
- music.setVolume(volume);\r
- music.play();\r
- music.setLooping(true);\r
- Gdx.input.setInputProcessor(this);\r
-\r
- batch = new SpriteBatch();\r
- font = new BitmapFont(Gdx.files.internal("data/verdana39.fnt"), Gdx.files.internal("data/verdana39.png"), false);\r
- }\r
-\r
- @Override\r
- public boolean keyDown (int keycode) {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean keyTyped (char character) {\r
- if (character == '+') volume += 0.1f;\r
- if (character == '-') volume -= 0.1f;\r
- music.setVolume(volume);\r
-\r
- return false;\r
+ sound = Gdx.audio.newSound(Gdx.files.getFileHandle("data/shotgun.wav", FileType.Internal));\r
+ \r
+ Skin skin = new Skin(Gdx.files.internal("data/uiskin.json"), Gdx.files.internal("data/uiskin.png"));\r
+ ui = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);\r
+ Button play = new Button("Play", skin);\r
+ Button stop = new Button("Stop", skin);\r
+ final Slider pitch = new Slider(0.1f, 4, 0.1f, skin);\r
+ pitch.setValue(1);\r
+ final Label pitchValue = new Label("1.0", skin);\r
+ final Slider volume = new Slider(0.1f, 1, 0.1f, skin);\r
+ volume.setValue(1);\r
+ final Label volumeValue = new Label("1.0", skin);\r
+ Table table = new Table("ui");\r
+ table.width = Gdx.graphics.getWidth(); table.height = Gdx.graphics.getHeight();\r
+ \r
+ table.align(Align.CENTER | Align.TOP);\r
+ table.add(play);\r
+ table.add(stop);\r
+ table.row();\r
+ table.add(new Label("Pitch", skin));\r
+ table.add(pitch);\r
+ table.add(pitchValue);\r
+ table.row();\r
+ table.add(new Label("Volume", skin));\r
+ table.add(volume);\r
+ table.add(volumeValue);\r
+ ui.addActor(table);\r
+ \r
+ play.setClickListener(new ClickListener() {\r
+ @Override\r
+ public void click (Actor actor) {\r
+ soundId = sound.play(volume.getValue());\r
+ sound.setPitch(soundId, pitch.getValue());\r
+ }\r
+ });\r
+ \r
+ stop.setClickListener(new ClickListener() {\r
+ @Override\r
+ public void click (Actor actor) {\r
+ sound.stop(soundId);\r
+ }\r
+ });\r
+ pitch.setValueChangedListener(new ValueChangedListener() {\r
+ @Override\r
+ public void changed (Slider slider, float value) {\r
+ sound.setPitch(soundId, value);\r
+ pitchValue.setText("" + value);\r
+ }\r
+ });\r
+ volume.setValueChangedListener(new ValueChangedListener() {\r
+ @Override\r
+ public void changed (Slider slider, float value) {\r
+ sound.setVolume(soundId, value);\r
+ volumeValue.setText("" + value);\r
+ }\r
+ });\r
+ Gdx.input.setInputProcessor(ui);\r
}\r
-\r
+ \r
+ \r
@Override\r
- public boolean keyUp (int keycode) {\r
- if (keycode != Input.Keys.SPACE) return false;\r
- if (music.isPlaying())\r
- music.pause();\r
- else\r
- music.play();\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean touchDown (int x, int y, int pointer, int newParam) {\r
- sound.play(1f);\r
- if (music.isPlaying())\r
- music.stop();\r
- else\r
- music.play();\r
- return false;\r
+ public void render () {\r
+ Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);\r
+ ui.act(Gdx.graphics.getDeltaTime());\r
+ ui.draw();\r
}\r
\r
- @Override\r
- public boolean touchDragged (int x, int y, int pointer) {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean touchUp (int x, int y, int pointer, int button) {\r
- return false;\r
- }\r
\r
@Override\r
public boolean needsGL20 () {\r
return false;\r
}\r
-\r
- @Override\r
- public boolean touchMoved (int x, int y) {\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean scrolled (int amount) {\r
- return false;\r
- }\r
}\r