diff --git a/core/src/gdx/diablo/Audio.java b/core/src/gdx/diablo/Audio.java index e62924f4..c3d7fadc 100644 --- a/core/src/gdx/diablo/Audio.java +++ b/core/src/gdx/diablo/Audio.java @@ -1,12 +1,12 @@ package gdx.diablo; -import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.audio.Sound; -import com.badlogic.gdx.utils.ObjectFloatMap; import com.badlogic.gdx.utils.ObjectMap; +import com.badlogic.gdx.utils.Pool; +import com.badlogic.gdx.utils.Pools; import gdx.diablo.codec.excel.Sounds; @@ -19,21 +19,18 @@ public class Audio { private final AssetManager assets; private final ObjectMap> descriptors = new ObjectMap<>(); - private final ObjectFloatMap playing = new ObjectFloatMap<>(); public Audio(AssetManager assets) { this.assets = assets; } - public synchronized void play(final Sounds.Entry sound, boolean global) { - if (sound.FileName.isEmpty()) return; - // TODO: Fix memory leak and dispose sound after playing - // TODO: Support group size - // TODO: global vs local sounds - - + // TODO: Fix memory leak and dispose sound after playing + // TODO: Support group size + // TODO: global vs local sounds + public synchronized Instance play(Sounds.Entry sound, boolean global) { + if (sound.FileName.isEmpty()) return null; if (sound.Stream) { - Music s; + Music stream; AssetDescriptor descriptor = (AssetDescriptor) descriptors.get(sound); if (descriptor == null) { descriptor = new AssetDescriptor<>((global ? GLOBAL : LOCAL) + sound.FileName, Music.class); @@ -41,55 +38,79 @@ public class Audio { assets.load(descriptor); assets.finishLoadingAsset(descriptor); - s = assets.get(descriptor); - s.setVolume(sound.Volume / 255f); - s.play(); + stream = assets.get(descriptor); + stream.setVolume(sound.Volume / 255f); } else { - s = assets.get(descriptor); + stream = assets.get(descriptor); } - if (sound.Defer_Inst && s.isPlaying()) { - return; + if (sound.Defer_Inst && stream.isPlaying()) { + return null; } - s.play(); + stream.play(); + Instance instance = Instance.obtain(stream, -1); + return instance; } else { - final Sound s; AssetDescriptor descriptor = (AssetDescriptor) descriptors.get(sound); if (descriptor == null) { descriptor = new AssetDescriptor<>((global ? GLOBAL : LOCAL) + sound.FileName, Sound.class); descriptors.put(sound, descriptor); assets.load(descriptor); assets.finishLoadingAsset(descriptor); - - s = assets.get(descriptor); - } else { - s = assets.get(descriptor); } - long id = s.play(sound.Volume / 255f); - if (id == -1) { - Gdx.app.postRunnable(new Runnable() { - @Override - public void run() { - s.play(sound.Volume / 255f); - } - }); + Sound sfx = assets.get(descriptor); + return Instance.obtain(sfx, sfx.play(sound.Volume / 255f)); + } + } + + public static class Instance implements Pool.Poolable { + + boolean stream; + Object delegate; + long id; + + static Instance obtain(Object delegate, long id) { + Instance instance = Pools.obtain(Instance.class); + instance.stream = delegate instanceof Music; + instance.delegate = delegate; + instance.id = id; + return instance; + } + + @Override + public void reset() { + delegate = null; + id = -1; + } + + public void stop() { + if (stream) { + ((Music) delegate).stop(); + } else { + ((Sound) delegate).stop(id); + } + } + + public void setVolume(float volume) { + if (stream) { + ((Music) delegate).setVolume(volume); + } else { + ((Sound) delegate).setVolume(id, volume); } } } - public void play(int id, boolean global) { + public Instance play(int id, boolean global) { Sounds.Entry sound = Diablo.files.Sounds.get(id); - play(sound, global); + return play(sound, global); } - public int play(String id, boolean global) { - if (id.isEmpty()) return 0; + public Instance play(String id, boolean global) { + if (id.isEmpty()) return null; Sounds.Entry sound = Diablo.files.Sounds.get(id); - if (sound == null) return 0; - play(sound, global); - return sound.Index; + if (sound == null) return null; + return play(sound, global); } - } diff --git a/core/src/gdx/diablo/ai/Npc.java b/core/src/gdx/diablo/ai/Npc.java index 716b6afc..a56734b4 100644 --- a/core/src/gdx/diablo/ai/Npc.java +++ b/core/src/gdx/diablo/ai/Npc.java @@ -9,6 +9,7 @@ import com.badlogic.gdx.utils.IntSet; import org.apache.commons.lang3.ArrayUtils; +import gdx.diablo.Audio; import gdx.diablo.Diablo; import gdx.diablo.entity.Monster; import gdx.diablo.map.DS1; @@ -36,7 +37,6 @@ public class Npc extends AI { float actionTimer = 0; boolean actionPerformed = false; NpcMenu menu; - int activeAudio; public Npc(Monster entity) { super(entity); @@ -49,8 +49,8 @@ public class Npc extends AI { // I.e., akara_act1_intro -> akara_act1_intro_sor automatically if it exists String name = entity.getName().toLowerCase(); String id = name + "_greeting_1"; - int index = Diablo.audio.play(id, false); - if (index == 0) { + Audio.Instance instance = Diablo.audio.play(id, false); + if (instance == null) { id = name + "_greeting_inactive_1"; Diablo.audio.play(id, false); } @@ -74,10 +74,7 @@ public class Npc extends AI { public void clicked(InputEvent event, float x, float y) { String name = entity.getName().toLowerCase(); String id = name + "_act1_intro"; - Diablo.audio.play(id, false); - - gameScreen.setDialog(new NpcDialogBox(Diablo.files.speech.get(id).soundstr, new NpcDialogBox.DialogCompletionListener() { - + gameScreen.setDialog(new NpcDialogBox(id, new NpcDialogBox.DialogCompletionListener() { @Override public void onCompleted(NpcDialogBox d) { gameScreen.setDialog(null); @@ -91,9 +88,7 @@ public class Npc extends AI { public void clicked(InputEvent event, float x, float y) { String name = entity.getName().toLowerCase(); String id = name + "_act1_gossip_1"; - Diablo.audio.play(id, false); - - gameScreen.setDialog(new NpcDialogBox(Diablo.files.speech.get(id).soundstr, new NpcDialogBox.DialogCompletionListener() { + gameScreen.setDialog(new NpcDialogBox(id, new NpcDialogBox.DialogCompletionListener() { @Override public void onCompleted(NpcDialogBox d) { gameScreen.setDialog(null); @@ -101,12 +96,7 @@ public class Npc extends AI { })); } }) - .addCancel(new NpcMenu.CancellationListener() { - @Override - public void onCancelled() { - // TODO: stop audio - } - }) + .addCancel(null) .build()); } diff --git a/core/src/gdx/diablo/screen/GameScreen.java b/core/src/gdx/diablo/screen/GameScreen.java index f7a2577f..d3890670 100644 --- a/core/src/gdx/diablo/screen/GameScreen.java +++ b/core/src/gdx/diablo/screen/GameScreen.java @@ -682,6 +682,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable if (this.dialog != dialog) { if (this.dialog != null) { this.dialog.remove(); + this.dialog.dispose(); if (menu != null) menu.setVisible(true); } diff --git a/core/src/gdx/diablo/widget/NpcDialogBox.java b/core/src/gdx/diablo/widget/NpcDialogBox.java index d342101a..aa0ba298 100644 --- a/core/src/gdx/diablo/widget/NpcDialogBox.java +++ b/core/src/gdx/diablo/widget/NpcDialogBox.java @@ -2,23 +2,25 @@ package gdx.diablo.widget; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.scenes.scene2d.ui.Table; +import com.badlogic.gdx.utils.Disposable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; +import gdx.diablo.Audio; import gdx.diablo.Diablo; import gdx.diablo.codec.FontTBL; import gdx.diablo.graphics.BorderedPaletteIndexedDrawable; -public class NpcDialogBox extends Table { +public class NpcDialogBox extends Table implements Disposable { DialogCompletionListener listener; TextArea textArea; ScrollPane scrollPane; float scrollSpeed; - float position; + Audio.Instance audio; - public NpcDialogBox(String key, DialogCompletionListener listener) { + public NpcDialogBox(String sound, DialogCompletionListener listener) { this.listener = listener; setBackground(new BorderedPaletteIndexedDrawable()); setTouchable(Touchable.disabled); @@ -28,6 +30,7 @@ public class NpcDialogBox extends Table { // problem seems to be with fontformat11 metrics, applying scalar to line height final float lineScalar = 0.85f; final FontTBL.BitmapFont FONT = Diablo.fonts.fontformal11; + String key = Diablo.files.speech.get(sound).soundstr; String text = Diablo.string.lookup(key); String[] parts = text.split("\n", 2); scrollSpeed = NumberUtils.toFloat(parts[0]) / 60 * FONT.getLineHeight() * lineScalar; @@ -55,6 +58,7 @@ public class NpcDialogBox extends Table { pack(); scrollPane.setScrollY(-scrollPane.getScrollHeight() + textArea.getStyle().font.getLineHeight() / 2); + audio = Diablo.audio.play(sound, false); } @Override @@ -66,6 +70,11 @@ public class NpcDialogBox extends Table { } } + @Override + public void dispose() { + audio.stop(); + } + public interface DialogCompletionListener { void onCompleted(NpcDialogBox d); }