- now only two volume levels: music and clips.

- clip types are turned on or off, individually.
- wired up volume for music, and it worked, but then I angered some norse god
  and now it doesn't work anymore despite repeated poking.
- fixed up clip volume formula (copied simplified/better code from Sun example)


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1951 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2002-11-15 20:52:31 +00:00
parent 275d35ce07
commit 9e881810a9
@@ -1,5 +1,5 @@
//
// $Id: SoundManager.java,v 1.13 2002/11/15 05:34:43 ray Exp $
// $Id: SoundManager.java,v 1.14 2002/11/15 20:52:31 ray Exp $
package com.threerings.media;
@@ -105,7 +105,6 @@ public class SoundManager
_player = new Thread() {
public void run () {
Object command = null;
SoundType type = null;
String path = null;
while (amRunning()) {
@@ -115,21 +114,20 @@ public class SoundManager
synchronized (_queue) {
command = _queue.get();
if (PLAY == command) {
type = (SoundType) _queue.get();
path = (String) _queue.get();
// some commands have an additional argument.
if ((PLAY == command) ||
(PLAYMUSIC == command) ||
(STOPMUSIC == command) ||
(LOCK == command) ||
(UNLOCK == command)) {
} else if ((PLAYMUSIC == command) ||
(STOPMUSIC == command) ||
(LOCK == command) ||
(UNLOCK == command)) {
path = (String) _queue.get();
}
}
// execute the command outside of the queue synch
if (PLAY == command) {
playSound(type, path);
playSound(path);
} else if (PLAYMUSIC == command) {
playSequence(path);
@@ -150,6 +148,9 @@ public class SoundManager
// to do it again in 3 seconds
_resourceFreer.restart();
} else if (UPDATE_MUSIC_VOL == command) {
updateMusicVolume();
} else if (DIE == command) {
_resourceFreer.stop();
flushResources(true);
@@ -192,36 +193,61 @@ public class SoundManager
}
/**
* Sets the volume of a particular type of sound.
*
* @param val a volume parameter between 0f and 1f, inclusive.
* Is the specified soundtype enabled?
*/
public void setVolume (SoundType type, float val)
public boolean isEnabled (SoundType type)
{
// bound it in
val = Math.max(0f, Math.min(1f, val));
_volumes.put(type, new Float(val));
// by default, types are enabled..
return (!_disabledTypes.contains(type));
}
/**
* Is the specified soundtype enabled?
* Turns on or off the specified sound type.
*/
public float getVolume (SoundType type)
public void setEnabled (SoundType type, boolean enabled)
{
Float vol = (Float) _volumes.get(type);
// now we're nice to unregistered sounds..
return (vol != null) ? vol.floatValue() : 1f;
if (enabled) {
_disabledTypes.remove(type);
} else {
_disabledTypes.add(type);
}
}
/**
* Sets the volume for all sound clips.
*
* @param val a volume parameter between 0f and 1f, inclusive.
*/
public void setClipVolume (float vol)
{
_clipVol = Math.max(0f, Math.min(1f, vol));
}
/**
* Get the volume for all sound clips.
*/
public float getClipVolume ()
{
return _clipVol;
}
/**
* Sets the volume for music.
*
* @param val a volume parameter between 0f and 1f, inclusive.
*/
public void setMusicVolume (float vol)
{
// TODO
_musicVol = Math.max(0f, Math.min(1f, vol));
_queue.append(UPDATE_MUSIC_VOL);
}
/**
* Get the music volume.
*/
public float getMusicVolume ()
{
return 1f; // TODO
return _musicVol;
}
/**
@@ -258,11 +284,10 @@ public class SoundManager
type = DEFAULT;
}
if (_player != null && (0f != getVolume(type))) {
if (_player != null && isEnabled(type)) {
synchronized (_queue) {
if (_queue.size() < MAX_QUEUE_SIZE) {
_queue.append(PLAY);
_queue.append(type);
_queue.append(path);
} else {
@@ -274,6 +299,10 @@ public class SoundManager
}
}
/**
* Start playing the specified music, stopping any currently played
* music.
*/
public void pushMusic (String path)
{
synchronized (_queue) {
@@ -282,6 +311,10 @@ public class SoundManager
}
}
/**
* Remove the specified music from the playlist. If it is currently
* playing, it will be stopped and the previous song will be started.
*/
public void removeMusic (String path)
{
synchronized (_queue) {
@@ -294,11 +327,11 @@ public class SoundManager
* On the SoundManager thread,
* plays the sound file with the given pathname.
*/
protected void playSound (SoundType type, String path)
protected void playSound (String path)
{
// see if we can restart a previously used sound that's still
// hanging out.
if (restartSound(type, path)) {
if (restartSound(path)) {
return;
}
@@ -316,7 +349,7 @@ public class SoundManager
clip.open(stream);
SoundRecord rec = new SoundRecord(path, clip);
rec.start(type);
rec.start(_clipVol);
_activeClips.add(rec);
// and rewind the stream to the beginning for next time
@@ -335,7 +368,7 @@ public class SoundManager
/**
* Attempt to reuse a clip that's already been loaded.
*/
protected boolean restartSound (SoundType type, String path)
protected boolean restartSound (String path)
{
long now = System.currentTimeMillis();
@@ -343,7 +376,7 @@ public class SoundManager
for (int ii=0, nn=_activeClips.size(); ii < nn; ii++) {
SoundRecord rec = (SoundRecord) _activeClips.get(ii);
if (rec.path.equals(path) && rec.isStoppedSince(now)) {
rec.restart(type);
rec.restart(_clipVol);
return true;
}
}
@@ -361,7 +394,8 @@ public class SoundManager
Sequencer seq = MidiSystem.getSequencer();
seq.open();
if (seq instanceof Synthesizer) {
_midiChannels = ((Synthesizer) seq).getChannels();
_midiChannels = ((Synthesizer) seq).getChannels();
//updateMusicVolume();
}
_sequencer = seq;
_sequencer.addMetaEventListener(this);
@@ -379,6 +413,7 @@ public class SoundManager
try {
_sequencer.setSequence(getResource(path));
_sequencer.start();
//updateMusicVolume();
_midiStack.addFirst(path);
} catch (InvalidMidiDataException imda) {
@@ -418,6 +453,24 @@ public class SoundManager
}
}
/**
* Attempt to modify the music volume for any playing tracks.
*/
protected void updateMusicVolume ()
{
if (_midiChannels == null) {
Log.warning("Cannot modify music volume!");
} else {
int setting = (int) (_musicVol * 127.0);
Log.info("setting musicvol: " + setting +
", chans=" + _midiChannels.length);
for (int ii=0; ii < _midiChannels.length; ii++) {
_midiChannels[ii].controlChange(7, (int) (_musicVol * 127));
}
}
}
// documentation inherited from interface MetaEventListener
public void meta (MetaMessage msg)
{
@@ -498,7 +551,7 @@ public class SoundManager
* We don't free the resources associated with a clip immediately, because
* it may be played again shortly.
*/
protected class SoundRecord
protected static class SoundRecord
implements LineListener
{
public String path;
@@ -546,9 +599,9 @@ public class SoundManager
/**
* Start playing the sound.
*/
public void start (SoundType type)
public void start (float volume)
{
adjustVolume(_clip, type);
adjustVolume(volume);
_clip.start();
didStart();
}
@@ -556,15 +609,12 @@ public class SoundManager
/**
* Restart the sound from the beginning.
*/
public void restart (SoundType type)
public void restart (float volume)
{
// this seems to be unneeded
// if (_clip.isRunning()) {
// Log.info("Restarted a sound and sent it a stop..");
// _clip.stop();
// }
// this only gets called after the sound has stopped, so
// simply rewind and replay.
_clip.setFramePosition(0);
start(type);
start(volume);
}
/**
@@ -644,6 +694,52 @@ public class SoundManager
return _length == AudioSystem.NOT_SPECIFIED;
}
// /**
// * Adjust the volume of this clip.
// */
// protected void adjustVolume (float volume)
// {
// if (_clip.isControlSupported(FloatControl.Type.VOLUME)) {
// FloatControl vol = (FloatControl)
// _clip.getControl(FloatControl.Type.VOLUME);
//
// float min = vol.getMinimum();
// float max = vol.getMaximum();
//
// float ourval = (volume * (max - min)) + min;
// Log.debug("adjust vol: [min=" + min + ", ourval=" + ourval +
// ", max=" + max + "].");
// vol.setValue(ourval);
//
// } else {
// // fall back
// adjustVolumeFallback(vol);
// }
// }
/**
* Use the gain control to implement volume.
*/
protected void adjustVolume (float volume)
{
FloatControl vol = (FloatControl)
_clip.getControl(FloatControl.Type.MASTER_GAIN);
// the only problem is that gain is specified in decibals,
// which is a logarithmic scale.
// Since we want max volume to leave the sample unchanged, our
// maximum volume translates into a 0db gain.
float gain;
if (volume == 0f) {
gain = vol.getMinimum();
} else {
gain = (float) ((Math.log(volume) / Math.log(10.0)) * 20.0);
}
vol.setValue(gain);
Log.info("Set gain: " + gain);
}
/** The timestamp of the moment this clip last stopped playing. */
protected long _stamp;
@@ -655,68 +751,6 @@ public class SoundManager
protected Clip _clip;
}
/**
* Adjust the volume of this clip.
*/
protected void adjustVolume (Line c, SoundType type)
{
if (c.isControlSupported(FloatControl.Type.VOLUME)) {
try {
FloatControl vol = (FloatControl)
c.getControl(FloatControl.Type.VOLUME);
float min = vol.getMinimum();
float max = vol.getMaximum();
float ourval = (getVolume(type) * (max - min)) + min;
Log.debug("adjust vol: [min=" + min + ", ourval=" + ourval +
", max=" + max + "].");
vol.setValue(ourval);
return;
} catch (Exception e) {
Log.logStackTrace(e);
}
}
// fall back
adjustVolumeFallback(c, type);
}
/**
* Use the gain control to implement volume.
*/
protected void adjustVolumeFallback (Line c, SoundType type)
{
FloatControl vol = (FloatControl)
c.getControl(FloatControl.Type.MASTER_GAIN);
// the only problem is that gain is specified in decibals,
// which is a logarithmic scale.
// Since we want max volume to leave the sample unchanged, our
// maximum volume translates into a 0db gain.
float ourSetting = getVolume(type);
float gain;
if (ourSetting == 1f) {
// because our max is a 0db gain, and we're short-circuiting
// divide-by-zero problems down below
gain = 0f;
} else {
// if our setting isn't 1, we need to scale it by the
// linear value of the minimum gain.
// And we have to be very careful with the sign, since
// it fucks up these log/pow methods.
double absmin = Math.abs(vol.getMinimum());
double minlin = Math.pow(10.0, absmin / 20.0);
double ourval = ((1f - ourSetting) * absmin);
gain = (float) ((Math.log(ourval) / Math.log(10)) * -20d);
}
vol.setValue(gain);
//Log.info("Set gain: " + gain);
}
/**
* Every 3 seconds we look for sounds that haven't been used for 4 and
* free them up.
@@ -738,6 +772,9 @@ public class SoundManager
/** The queue of sound clips to be played. */
protected Queue _queue = new Queue();
/** Volume levels for both sound clips and music. */
protected float _clipVol = 1f, _musicVol = 1f;
/** The cache of recent audio clips . */
protected LockableLRUHashMap _dataCache = new LockableLRUHashMap(10);
@@ -755,12 +792,13 @@ public class SoundManager
protected LinkedList _midiStack = new LinkedList();
/** A set of soundTypes for which sound is enabled. */
protected HashMap _volumes = new HashMap();
protected HashSet _disabledTypes = new HashSet();
/** Signals to the queue to do different things. */
protected Object PLAY = new Object();
protected Object PLAYMUSIC = new Object();
protected Object STOPMUSIC = new Object();
protected Object UPDATE_MUSIC_VOL = new Object();
protected Object LOCK = new Object();
protected Object UNLOCK = new Object();
protected Object FLUSH = new Object();
@@ -769,9 +807,6 @@ public class SoundManager
/** This is apparently the midi code for end of track. Wack. */
protected static final int MIDI_END_OF_TRACK = 47;
/** Default volume float object if no others found for a sound type. */
protected static final Float DEFAULT_VOLUME = new Float(1f);
/** The queue size at which we start to ignore requests to play sounds. */
protected static final int MAX_QUEUE_SIZE = 100;