Add a second looping mode, LOOP_TO_COMPLETION, that finishes playing its

current run through the sound when stop is called before actually stopping.

Add a runnable to the lock method that will be called on the spooler thread
when locking is complete.



git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@597 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Charlie Groves
2008-08-01 23:46:17 +00:00
parent d1de5f411d
commit 4432a9d6f8
2 changed files with 79 additions and 44 deletions
@@ -314,7 +314,7 @@ public class MusicManager
public MusicKey (String set, String path, int loops) public MusicKey (String set, String path, int loops)
{ {
super((byte) -1, set, path); super((byte) -1, set, path, null);
this.loops = loops; this.loops = loops;
} }
} }
@@ -31,7 +31,6 @@ import java.io.InputStream;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioFormat;
@@ -213,11 +212,11 @@ public class SoundManager
buf.append("clipVol=").append(_clipVol); buf.append("clipVol=").append(_clipVol);
buf.append(", disabled=["); buf.append(", disabled=[");
int ii = 0; int ii = 0;
for (Iterator<SoundType> iter = _disabledTypes.iterator(); iter.hasNext(); ) { for (SoundType soundType : _disabledTypes) {
if (ii++ > 0) { if (ii++ > 0) {
buf.append(", "); buf.append(", ");
} }
buf.append(iter.next()); buf.append(soundType);
} }
return buf.append("]").toString(); return buf.append("]").toString();
} }
@@ -262,39 +261,33 @@ public class SoundManager
} }
/** /**
* Optionally lock the sound data prior to playing, to guarantee * Optionally lock each of these keys prior to playing, to guarantee that it will be quickly
* that it will be quickly available for playing. * available for playing.
*/ */
public void lock (String pkgPath, String key) public void lock (String pkgPath, String... keys)
{ {
enqueue(new SoundKey(LOCK, pkgPath, key), true); lock(pkgPath, null, keys);
} }
/** /**
* Unlock the specified sound so that its resources can be freed. * Optionally lock each of these keys prior to playing, to guarantee that it will be quickly
* available for playing. <code>onLock</code> will be called on a spooler thread when locking
* is complete.
*/ */
public void unlock (String pkgPath, String key) public void lock (String pkgPath, Runnable onLock, String... keys)
{
enqueue(new SoundKey(UNLOCK, pkgPath, key), true);
}
/**
* Batch lock a list of sounds.
*/
public void lock (String pkgPath, String[] keys)
{ {
for (int ii=0; ii < keys.length; ii++) { for (int ii=0; ii < keys.length; ii++) {
enqueue(new SoundKey(LOCK, pkgPath, keys[ii]), (ii == 0)); enqueue(new SoundKey(LOCK, pkgPath, keys[ii], onLock), (ii == 0));
} }
} }
/** /**
* Batch unlock a list of sounds. *Unlock the specified sounds so that its resources can be freed.
*/ */
public void unlock (String pkgPath, String[] keys) public void unlock (String pkgPath, String... keys)
{ {
for (int ii=0; ii < keys.length; ii++) { for (int ii=0; ii < keys.length; ii++) {
enqueue(new SoundKey(UNLOCK, pkgPath, keys[ii]), (ii == 0)); enqueue(new SoundKey(UNLOCK, pkgPath, keys[ii], null), (ii == 0));
} }
} }
@@ -355,28 +348,29 @@ public class SoundManager
} }
/** /**
* Loop the specified sound. * Loop the specified sound, stopping as quickly as possible when stop is called.
*/ */
public Frob loop (SoundType type, String pkgPath, String key) public Frob loop (SoundType type, String pkgPath, String key)
{ {
return loop(type, pkgPath, key, PAN_CENTER); return loop(type, pkgPath, key, PAN_CENTER, LOOP_TO_COMPLETION);
} }
/** /**
* Loop the specified sound. * Loop the specified sound, stopping as quickly as possible when stop is called.
*/ */
public Frob loop (SoundType type, String pkgPath, String key, float pan) public Frob loop (SoundType type, String pkgPath, String key, float pan)
{ {
if (type == null) { return loop(type, pkgPath, key, pan, LOOP);
type = DEFAULT;
}
if (!isEnabled(type)) { }
return null;
} /**
SoundKey skey = new SoundKey(LOOP, pkgPath, key, 0, _clipVol, pan); * Loop the specified sound, stopping after the current iteration completes when stop is
addToPlayQueue(skey); * called.
return skey; // it is a frob */
public Frob loopToCompletion (SoundType type, String pkgPath, String key)
{
return loop(type, pkgPath, key, PAN_CENTER, LOOP_TO_COMPLETION);
} }
// ==== End of public methods ==== // ==== End of public methods ====
@@ -474,6 +468,7 @@ public class SoundManager
switch (key.cmd) { switch (key.cmd) {
case PLAY: case PLAY:
case LOOP: case LOOP:
case LOOP_TO_COMPLETION:
playSound(key); playSound(key);
break; break;
@@ -500,6 +495,7 @@ public class SoundManager
} }
break; break;
} }
key.processed();
} }
/** /**
@@ -551,7 +547,7 @@ public class SoundManager
AudioInputStream stream = setupAudioStream(data); AudioInputStream stream = setupAudioStream(data);
if (key.cmd == LOOP && stream.markSupported()) { if (key.isLoop() && stream.markSupported()) {
stream.mark(data.length); stream.mark(data.length);
} }
@@ -572,7 +568,7 @@ public class SoundManager
do { do {
// play the sound // play the sound
int count = 0; int count = 0;
while (key.running && count != -1) { while ((key.running || key.cmd == LOOP_TO_COMPLETION) && count != -1) {
float vol = key.volume; float vol = key.volume;
if (vol != setVolume) { if (vol != setVolume) {
adjustVolume(line, vol); adjustVolume(line, vol);
@@ -599,7 +595,7 @@ public class SoundManager
} }
} }
if (key.cmd == LOOP) { if (key.isLoop()) {
// if we're going to loop, reset the stream to the beginning if we can, // if we're going to loop, reset the stream to the beginning if we can,
// otherwise just remake the stream // otherwise just remake the stream
if (stream.markSupported()) { if (stream.markSupported()) {
@@ -608,7 +604,7 @@ public class SoundManager
stream = setupAudioStream(data); stream = setupAudioStream(data);
} }
} }
} while (key.cmd == LOOP && key.running); } while (key.isLoop() && key.running);
// sleep the drain time. We never trust line.drain() because // sleep the drain time. We never trust line.drain() because
// it is buggy and locks up on natively multithreaded systems // it is buggy and locks up on natively multithreaded systems
@@ -838,6 +834,23 @@ public class SoundManager
return c; return c;
} }
/**
* Loop the specified sound.
*/
protected Frob loop (SoundType type, String pkgPath, String key, float pan, byte cmd)
{
if (type == null) {
type = DEFAULT;
}
if (!isEnabled(type)) {
return null;
}
SoundKey skey = new SoundKey(cmd, pkgPath, key, 0, _clipVol, pan);
addToPlayQueue(skey);
return skey; // it is a frob
}
// /** // /**
// * Adjust the volume of this clip. // * Adjust the volume of this clip.
// */ // */
@@ -920,9 +933,11 @@ public class SoundManager
/** The player thread, if it's playing us. */ /** The player thread, if it's playing us. */
public Thread thread; public Thread thread;
/** Run when the processed method is called. */
public Runnable onProcessed;
/** /**
* Create a SoundKey that just contains the specified command. * Create a SoundKey that just contains the specified command.
* DIE.
*/ */
public SoundKey (byte cmd) public SoundKey (byte cmd)
{ {
@@ -932,19 +947,21 @@ public class SoundManager
/** /**
* Quicky constructor for music keys and lock operations. * Quicky constructor for music keys and lock operations.
*/ */
public SoundKey (byte cmd, String pkgPath, String key) public SoundKey (byte cmd, String pkgPath, String key, Runnable onProcessed)
{ {
this(cmd); this(cmd);
this.pkgPath = pkgPath; this.pkgPath = pkgPath;
this.key = key; this.key = key;
this.onProcessed = onProcessed;
} }
/** /**
* Constructor for a sound effect soundkey. * Constructor for a sound effect soundkey.
*/ */
public SoundKey (byte cmd, String pkgPath, String key, int delay, float volume, float pan) public SoundKey (byte cmd, String pkgPath, String key, int delay, float volume,
float pan)
{ {
this(cmd, pkgPath, key); this(cmd, pkgPath, key, null);
stamp = System.currentTimeMillis() + delay; stamp = System.currentTimeMillis() + delay;
setVolume(volume); setVolume(volume);
@@ -994,6 +1011,23 @@ public class SoundManager
return (stamp + MAX_SOUND_DELAY < System.currentTimeMillis()); return (stamp + MAX_SOUND_DELAY < System.currentTimeMillis());
} }
/**
* If this key is one of the two loop types.
*/
protected boolean isLoop() {
return cmd == LOOP || cmd == LOOP_TO_COMPLETION;
}
/**
* Called when the manager is done processing this key.
*/
public void processed ()
{
if (onProcessed != null) {
onProcessed.run();
}
}
@Override @Override
public String toString () public String toString ()
{ {
@@ -1055,6 +1089,7 @@ public class SoundManager
protected static final byte UNLOCK = 2; protected static final byte UNLOCK = 2;
protected static final byte DIE = 3; protected static final byte DIE = 3;
protected static final byte LOOP = 4; protected static final byte LOOP = 4;
protected static final byte LOOP_TO_COMPLETION = 5;
/** A pref that specifies a directory for us to get test sounds from. */ /** A pref that specifies a directory for us to get test sounds from. */
protected static RuntimeAdjust.FileAdjust _testDir = protected static RuntimeAdjust.FileAdjust _testDir =