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:
@@ -125,6 +125,15 @@ public abstract class FrameManager
|
|||||||
* @see #newInstance(ManagedRoot, MediaTimer)
|
* @see #newInstance(ManagedRoot, MediaTimer)
|
||||||
*/
|
*/
|
||||||
public static FrameManager newInstance (ManagedRoot root)
|
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;
|
MediaTimer timer = null;
|
||||||
for (String timerClass : PERF_TIMERS) {
|
for (String timerClass : PERF_TIMERS) {
|
||||||
@@ -140,7 +149,7 @@ public abstract class FrameManager
|
|||||||
"System.currentTimeMillis() based timer.");
|
"System.currentTimeMillis() based timer.");
|
||||||
timer = new MillisTimer();
|
timer = new MillisTimer();
|
||||||
}
|
}
|
||||||
return newInstance(root, timer);
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
package com.threerings.openal;
|
package com.threerings.openal;
|
||||||
|
|
||||||
|
import static com.threerings.media.Log.log;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -11,12 +13,13 @@ import org.lwjgl.util.WaveData;
|
|||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import com.samskivert.util.BasicRunQueue;
|
||||||
import com.samskivert.util.RunQueue;
|
import com.samskivert.util.RunQueue;
|
||||||
|
|
||||||
|
import com.threerings.media.FrameManager;
|
||||||
import com.threerings.media.sound.SoundLoader;
|
import com.threerings.media.sound.SoundLoader;
|
||||||
import com.threerings.media.sound.SoundPlayer;
|
import com.threerings.media.sound.SoundPlayer;
|
||||||
|
import com.threerings.media.timer.MediaTimer;
|
||||||
import static com.threerings.media.Log.log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the abstract pieces of {@link SoundPlayer} via OpenAL.
|
* Implements the abstract pieces of {@link SoundPlayer} via OpenAL.
|
||||||
@@ -30,6 +33,7 @@ public class OpenALSoundPlayer extends SoundPlayer
|
|||||||
_loader = loader;
|
_loader = loader;
|
||||||
_alSoundManager = new MediaALSoundManager();
|
_alSoundManager = new MediaALSoundManager();
|
||||||
_group = _alSoundManager.createGroup(this, SOURCE_COUNT);
|
_group = _alSoundManager.createGroup(this, SOURCE_COUNT);
|
||||||
|
_ticker.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Clip loadClip (String path)
|
public Clip loadClip (String path)
|
||||||
@@ -43,42 +47,56 @@ public class OpenALSoundPlayer extends SoundPlayer
|
|||||||
@Override
|
@Override
|
||||||
public RunQueue getSoundQueue ()
|
public RunQueue getSoundQueue ()
|
||||||
{
|
{
|
||||||
return RunQueue.AWT;
|
return _ticker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
final String path = pkgPath + key;
|
||||||
if (_locked.containsKey(path)) {
|
getSoundQueue().postRunnable(new Runnable() {
|
||||||
continue;
|
public void run () {
|
||||||
}
|
if (_locked.containsKey(path)) {
|
||||||
_alSoundManager.loadClip(this, path, new ClipBuffer.Observer() {
|
return;
|
||||||
public void clipFailed (ClipBuffer buffer) {
|
}
|
||||||
log.warning("Unable to load sound", "path", path);
|
_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) {
|
public void clipLoaded (ClipBuffer buffer) {
|
||||||
_locked.put(path, buffer);
|
_locked.put(path, buffer);
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unlock (String pkgPath, String... keys)
|
public void unlock (final String pkgPath, String... keys)
|
||||||
{
|
{
|
||||||
for (String key : keys) {
|
for (final String key : keys) {
|
||||||
_locked.remove(pkgPath + key);
|
getSoundQueue().postRunnable(new Runnable() {
|
||||||
|
public void run () {
|
||||||
|
_locked.remove(pkgPath + key);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Frob loop (String pkgPath, String key, float pan)
|
protected Frob loop (String pkgPath, String key, float pan)
|
||||||
{
|
{
|
||||||
final Sound sound = _group.getSound(pkgPath + key);
|
final SoundGrabber loader = new SoundGrabber(pkgPath, key) {
|
||||||
sound.loop(true);
|
@Override
|
||||||
return new Frob(){
|
protected void soundLoaded () {
|
||||||
|
sound.loop(true);
|
||||||
|
}};
|
||||||
|
getSoundQueue().postRunnable(loader);
|
||||||
|
return new Frob() {
|
||||||
public float getPan () {
|
public float getPan () {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -89,18 +107,26 @@ public class OpenALSoundPlayer extends SoundPlayer
|
|||||||
|
|
||||||
public void setPan (float pan) {}
|
public void setPan (float pan) {}
|
||||||
|
|
||||||
public void setVolume (float vol) { }
|
public void setVolume (float vol) {}
|
||||||
|
|
||||||
public void stop () {
|
public void stop () {
|
||||||
sound.stop();
|
getSoundQueue().postRunnable(new Runnable(){
|
||||||
|
public void run () {
|
||||||
|
if (loader.sound != null) {
|
||||||
|
loader.sound.stop();
|
||||||
|
}
|
||||||
|
}});
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void play (String pkgPath, String key, float pan)
|
public void play (String pkgPath, String key, float pan)
|
||||||
{
|
{
|
||||||
Sound sound = _group.getSound(pkgPath + key);
|
getSoundQueue().postRunnable(new SoundGrabber(pkgPath, key) {
|
||||||
sound.play(true);
|
@Override
|
||||||
|
protected void soundLoaded () {
|
||||||
|
sound.play(true);
|
||||||
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 class MediaALSoundManager extends SoundManager {
|
||||||
protected MediaALSoundManager () {
|
protected MediaALSoundManager () {
|
||||||
super(getSoundQueue());
|
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 Map<String, ClipBuffer> _locked = Maps.newHashMap();
|
||||||
|
|
||||||
protected final SoundLoader _loader;
|
protected final SoundLoader _loader;
|
||||||
|
|||||||
Reference in New Issue
Block a user