Give OpenALSoundPlayer its own thread and queue to keep it from sharing with the EDT and possibly blocking

git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@639 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Charlie Groves
2008-08-20 23:31:21 +00:00
parent 1bd35bd926
commit 0abacd42d3
2 changed files with 128 additions and 26 deletions
@@ -125,6 +125,15 @@ public abstract class FrameManager
* @see #newInstance(ManagedRoot, MediaTimer)
*/
public static FrameManager newInstance (ManagedRoot root)
{
return newInstance(root, createTimer());
}
/**
* Attempts to create a high resolution timer, but if that isn't possible, uses a
* System.currentTimeMillis based timer.
*/
public static MediaTimer createTimer ()
{
MediaTimer timer = null;
for (String timerClass : PERF_TIMERS) {
@@ -140,7 +149,7 @@ public abstract class FrameManager
"System.currentTimeMillis() based timer.");
timer = new MillisTimer();
}
return newInstance(root, timer);
return timer;
}
/**
@@ -3,6 +3,8 @@
package com.threerings.openal;
import static com.threerings.media.Log.log;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Map;
@@ -11,12 +13,13 @@ import org.lwjgl.util.WaveData;
import com.google.common.collect.Maps;
import com.samskivert.util.BasicRunQueue;
import com.samskivert.util.RunQueue;
import com.threerings.media.FrameManager;
import com.threerings.media.sound.SoundLoader;
import com.threerings.media.sound.SoundPlayer;
import static com.threerings.media.Log.log;
import com.threerings.media.timer.MediaTimer;
/**
* Implements the abstract pieces of {@link SoundPlayer} via OpenAL.
@@ -30,6 +33,7 @@ public class OpenALSoundPlayer extends SoundPlayer
_loader = loader;
_alSoundManager = new MediaALSoundManager();
_group = _alSoundManager.createGroup(this, SOURCE_COUNT);
_ticker.start();
}
public Clip loadClip (String path)
@@ -43,42 +47,56 @@ public class OpenALSoundPlayer extends SoundPlayer
@Override
public RunQueue getSoundQueue ()
{
return RunQueue.AWT;
return _ticker;
}
@Override
public void lock (final String pkgPath, String... keys)
public void lock (String pkgPath, String... keys)
{
for (final String key : keys) {
for (String key : keys) {
final String path = pkgPath + key;
if (_locked.containsKey(path)) {
continue;
}
_alSoundManager.loadClip(this, path, new ClipBuffer.Observer() {
public void clipFailed (ClipBuffer buffer) {
log.warning("Unable to load sound", "path", path);
}
getSoundQueue().postRunnable(new Runnable() {
public void run () {
if (_locked.containsKey(path)) {
return;
}
_alSoundManager.loadClip(OpenALSoundPlayer.this, path,
new ClipBuffer.Observer() {
public void clipFailed (ClipBuffer buffer) {
log.warning("Unable to load sound", "path", path);
}
public void clipLoaded (ClipBuffer buffer) {
_locked.put(path, buffer);
}});
public void clipLoaded (ClipBuffer buffer) {
_locked.put(path, buffer);
}
});
}
});
}
}
@Override
public void unlock (String pkgPath, String... keys)
public void unlock (final String pkgPath, String... keys)
{
for (String key : keys) {
_locked.remove(pkgPath + key);
for (final String key : keys) {
getSoundQueue().postRunnable(new Runnable() {
public void run () {
_locked.remove(pkgPath + key);
}
});
}
}
@Override
protected Frob loop (String pkgPath, String key, float pan)
{
final Sound sound = _group.getSound(pkgPath + key);
sound.loop(true);
return new Frob(){
final SoundGrabber loader = new SoundGrabber(pkgPath, key) {
@Override
protected void soundLoaded () {
sound.loop(true);
}};
getSoundQueue().postRunnable(loader);
return new Frob() {
public float getPan () {
return 0;
}
@@ -89,18 +107,26 @@ public class OpenALSoundPlayer extends SoundPlayer
public void setPan (float pan) {}
public void setVolume (float vol) { }
public void setVolume (float vol) {}
public void stop () {
sound.stop();
getSoundQueue().postRunnable(new Runnable(){
public void run () {
if (loader.sound != null) {
loader.sound.stop();
}
}});
}};
}
@Override
public void play (String pkgPath, String key, float pan)
{
Sound sound = _group.getSound(pkgPath + key);
sound.play(true);
getSoundQueue().postRunnable(new SoundGrabber(pkgPath, key) {
@Override
protected void soundLoaded () {
sound.play(true);
}});
}
@Override
@@ -113,6 +139,9 @@ public class OpenALSoundPlayer extends SoundPlayer
}
}
/**
* Extends sound manager to allow sounds to be pulled out of the locked map.
*/
protected class MediaALSoundManager extends SoundManager {
protected MediaALSoundManager () {
super(getSoundQueue());
@@ -127,6 +156,70 @@ public class OpenALSoundPlayer extends SoundPlayer
}
}
/**
* Updates the sound manager's streams every STREAM_UPDATE_INTERVAL and processes sound
* runnables added to its queue.
*/
protected class TickingQueue extends BasicRunQueue
{
@Override
protected void iterate ()
{
long elapsed = _timer.getElapsedMillis() - _lastTick;
Runnable r;
if (elapsed >= STREAM_UPDATE_INTERVAL) {
r = _queue.getNonBlocking();
} else {
r = _queue.get(STREAM_UPDATE_INTERVAL - elapsed);
}
if (r != null) {
try {
r.run();
} catch (Throwable t) {
log.warning("Runnable posted to SoundPlayerQueue barfed.", t);
}
}
long newTime = _timer.getElapsedMillis();
_alSoundManager.updateStreams((newTime - _lastTick)/ 1000F);
_lastTick = newTime;
}
protected MediaTimer _timer = FrameManager.createTimer();
protected long _lastTick;
};
/**
* Loads a sound in its run method and calls subclasses with soundLoaded to let them know it's
* ready.
*/
protected abstract class SoundGrabber
implements Runnable
{
public String path;
public Sound sound;
public SoundGrabber (String pkgPath, String key)
{
path = pkgPath + key;
}
public void run ()
{
sound = _group.getSound(path);
soundLoaded();
}
protected abstract void soundLoaded ();
}
/** Number of milliseconds to wait between stream updates. */
protected static final int STREAM_UPDATE_INTERVAL = 100;
protected TickingQueue _ticker = new TickingQueue();
protected Map<String, ClipBuffer> _locked = Maps.newHashMap();
protected final SoundLoader _loader;