mirror of
https://github.com/collinsmith/riiablo.git
synced 2025-02-21 20:18:14 +07:00
Added support for stoppable audio streams
Changed audio system design to support audio instances backed by either a Sound or Music instance Made NpcDialogBox the owner of the audio it's playing to ensure proper disposal
This commit is contained in:
parent
2569fbb185
commit
9386ba4e8f
@ -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<Sounds.Entry, AssetDescriptor<?>> descriptors = new ObjectMap<>();
|
||||
private final ObjectFloatMap<Sounds.Entry> 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<Music> descriptor = (AssetDescriptor<Music>) 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<Sound> descriptor = (AssetDescriptor<Sound>) 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user