Added support for looping sounds.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3371 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2005-02-28 19:29:44 +00:00
parent a784ce8a79
commit 9733fc2303
@@ -89,6 +89,31 @@ public class SoundManager
protected String _strname; protected String _strname;
} }
/**
* A control for sounds.
*/
public static interface Frob
{
/**
* Stop playing or looping the sound.
* At present, the granularity of this command is limited to the
* buffer size of the line spooler, or about 8k of data. Thus,
* if playing an 11khz sample, it could take 8/11ths of a second
* for the sound to actually stop playing.
*/
public void stop ();
/**
* Set the volume of the sound.
*/
public void setVolume (float vol);
/**
* Get the volume of this sound.
*/
public float getVolume ();
}
/** The default sound type. */ /** The default sound type. */
public static final SoundType DEFAULT = new SoundType("default"); public static final SoundType DEFAULT = new SoundType("default");
@@ -239,7 +264,8 @@ public class SoundManager
} }
if ((_clipVol != 0f) && isEnabled(type)) { if ((_clipVol != 0f) && isEnabled(type)) {
final SoundKey skey = new SoundKey(PLAY, pkgPath, key, delay); final SoundKey skey = new SoundKey(PLAY, pkgPath, key, delay,
_clipVol);
if (delay > 0) { if (delay > 0) {
new Interval() { new Interval() {
public void expired () { public void expired () {
@@ -252,6 +278,18 @@ public class SoundManager
} }
} }
/**
* Loop the specified sound.
*/
public Frob loop (SoundType type, String pkgPath, String key)
{
SoundKey skey = new SoundKey(LOOP, pkgPath, key, 0, _clipVol);
addToPlayQueue(skey);
return skey; // it is a frob
}
// ==== End of public methods ====
/** /**
* Add the sound clip key to the queue to be played. * Add the sound clip key to the queue to be played.
*/ */
@@ -344,6 +382,7 @@ public class SoundManager
{ {
switch (key.cmd) { switch (key.cmd) {
case PLAY: case PLAY:
case LOOP:
playSound(key); playSound(key);
break; break;
@@ -378,6 +417,10 @@ public class SoundManager
*/ */
protected void playSound (SoundKey key) protected void playSound (SoundKey key)
{ {
if (!key.running) {
return;
}
key.thread = Thread.currentThread();
SourceDataLine line = null; SourceDataLine line = null;
try { try {
// get the sound data from our LRU cache // get the sound data from our LRU cache
@@ -390,6 +433,7 @@ public class SoundManager
Log.info("Sound expired [key=" + key.key + "]."); Log.info("Sound expired [key=" + key.key + "].");
} }
return; return;
} }
AudioInputStream stream = AudioSystem.getAudioInputStream( AudioInputStream stream = AudioSystem.getAudioInputStream(
@@ -400,27 +444,39 @@ public class SoundManager
line = (SourceDataLine) AudioSystem.getLine( line = (SourceDataLine) AudioSystem.getLine(
new DataLine.Info(SourceDataLine.class, format)); new DataLine.Info(SourceDataLine.class, format));
line.open(format, LINEBUF_SIZE); line.open(format, LINEBUF_SIZE);
float setVolume = 1;
line.start(); line.start();
adjustVolume(line, _clipVol);
_soundSeemsToWork = true; _soundSeemsToWork = true;
// play the sound do {
byte[] buffer = new byte[LINEBUF_SIZE]; // play the sound
int count = 0; byte[] buffer = new byte[LINEBUF_SIZE];
while (count != -1) { int count = 0;
try { while (key.running && count != -1) {
count = stream.read(buffer, 0, buffer.length); float vol = key.volume;
} catch (IOException e) { if (vol != setVolume) {
// this shouldn't ever ever happen because the stream adjustVolume(line, vol);
// we're given is from a reliable source setVolume = vol;
Log.warning("Error reading clip data! [e=" + e + "]."); }
return; try {
count = stream.read(buffer, 0, buffer.length);
} catch (IOException e) {
// this shouldn't ever ever happen because the stream
// we're given is from a reliable source
Log.warning("Error reading clip data! [e=" + e + "].");
return;
}
if (count >= 0) {
line.write(buffer, 0, count);
}
} }
if (count >= 0) { // if we're going to loop, reset the stream to the beginning
line.write(buffer, 0, count); if (key.cmd == LOOP) {
stream.reset();
} }
} } while (key.cmd == LOOP && 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
@@ -468,6 +524,7 @@ public class SoundManager
if (line != null) { if (line != null) {
line.close(); line.close();
} }
key.thread = null;
} }
} }
@@ -696,6 +753,7 @@ public class SoundManager
* A key for tracking sounds. * A key for tracking sounds.
*/ */
protected static class SoundKey protected static class SoundKey
implements Frob
{ {
public byte cmd; public byte cmd;
@@ -705,6 +763,14 @@ public class SoundManager
public long stamp; public long stamp;
/** Should we still be running? */
public volatile boolean running = true;
public volatile float volume;
/** The player thread, if it's playing us. */
public Thread thread;
/** /**
* Create a SoundKey that just contains the specified command. * Create a SoundKey that just contains the specified command.
* DIE. * DIE.
@@ -727,11 +793,36 @@ public class SoundManager
/** /**
* Constructor for a sound effect soundkey. * Constructor for a sound effect soundkey.
*/ */
public SoundKey (byte cmd, String pkgPath, String key, int delay) public SoundKey (byte cmd, String pkgPath, String key, int delay,
float volume)
{ {
this(cmd, pkgPath, key); this(cmd, pkgPath, key);
stamp = System.currentTimeMillis() + delay; stamp = System.currentTimeMillis() + delay;
this.volume = volume;
}
// documentation inherited from interface Frob
public void stop ()
{
running = false;
Thread t = thread;
if (t != null) {
// doesn't actually ever seem to do much
t.interrupt();
}
}
// documentation inherited from interface Frob
public void setVolume (float vol)
{
volume = Math.max(0f, Math.min(1f, vol));
}
// documentation inherited from interface Frob
public float getVolume ()
{
return volume;
} }
/** /**
@@ -804,6 +895,7 @@ public class SoundManager
protected static final byte LOCK = 1; protected static final byte LOCK = 1;
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;
/** 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 =