diff --git a/src/java/com/threerings/media/sound/MidiPlayer.java b/src/java/com/threerings/media/sound/MidiPlayer.java index 978dddaa5..bf218c2b1 100644 --- a/src/java/com/threerings/media/sound/MidiPlayer.java +++ b/src/java/com/threerings/media/sound/MidiPlayer.java @@ -1,9 +1,10 @@ // -// $Id: MidiPlayer.java,v 1.2 2002/11/22 19:21:12 ray Exp $ +// $Id: MidiPlayer.java,v 1.3 2002/11/26 02:39:40 ray Exp $ package com.threerings.media; import java.io.BufferedInputStream; +import java.io.InputStream; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MetaEventListener; @@ -42,11 +43,10 @@ public class MidiPlayer extends MusicPlayer } // documentation inherited - public void start (String set, String path) + public void start (InputStream stream) throws Exception { - _sequencer.setSequence( - new BufferedInputStream(_rmgr.getResource(set, path))); + _sequencer.setSequence(new BufferedInputStream(stream)); _sequencer.start(); _sequencer.addMetaEventListener(this); } diff --git a/src/java/com/threerings/media/sound/ModPlayer.java b/src/java/com/threerings/media/sound/ModPlayer.java index 669580e5d..d813a4755 100644 --- a/src/java/com/threerings/media/sound/ModPlayer.java +++ b/src/java/com/threerings/media/sound/ModPlayer.java @@ -1,9 +1,10 @@ // -// $Id: ModPlayer.java,v 1.4 2002/11/22 19:21:12 ray Exp $ +// $Id: ModPlayer.java,v 1.5 2002/11/26 02:39:40 ray Exp $ package com.threerings.media; import java.io.DataInputStream; +import java.io.InputStream; import java.io.IOException; import micromod.MicroMod; @@ -37,11 +38,10 @@ public class ModPlayer extends MusicPlayer } // documentation inherited - public void start (String set, String path) + public void start (InputStream stream) throws Exception { - Module module = ModuleLoader.read( - new DataInputStream(_rmgr.getResource(set, path))); + Module module = ModuleLoader.read(new DataInputStream(stream)); final MicroMod mod = new MicroMod( module, _device, new LinearResampler()); diff --git a/src/java/com/threerings/media/sound/Mp3Player.java b/src/java/com/threerings/media/sound/Mp3Player.java index 77431f497..dcbe5e882 100644 --- a/src/java/com/threerings/media/sound/Mp3Player.java +++ b/src/java/com/threerings/media/sound/Mp3Player.java @@ -1,5 +1,5 @@ // -// $Id: Mp3Player.java,v 1.2 2002/11/22 19:21:12 ray Exp $ +// $Id: Mp3Player.java,v 1.3 2002/11/26 02:39:40 ray Exp $ package com.threerings.media; @@ -33,6 +33,9 @@ public class Mp3Player extends MusicPlayer // documentation inherited public void init () { + // TODO: some stuff needs to move here, like setting up the line + // but we don't yet know the audio format, so I need to figure that + // out (the format might always be known..). } // documentation inherited @@ -41,7 +44,7 @@ public class Mp3Player extends MusicPlayer } // documentation inherited - public void start (final String set, final String path) + public void start (final InputStream stream) throws Exception { // TODO: some stuff needs to come out of here and into init/shutdown @@ -51,14 +54,9 @@ public class Mp3Player extends MusicPlayer AudioInputStream inStream = null; try { inStream = AudioSystem.getAudioInputStream( -// new ByteArrayInputStream(StreamUtils.streamAsBytes( -// _rmgr.getResource(path), 8192))); - // TODO: use the fucking resource sets - new BufferedInputStream( - _rmgr.getResource(set, path), 4096)); + new BufferedInputStream(stream, BUFFER_SIZE)); } catch (Exception e) { - Log.warning("MP3 fuckola [path=" + path + - ", e=" + e + "]."); + Log.warning("MP3 fuckola. [e=" + e + "]."); return; } @@ -70,19 +68,18 @@ public class Mp3Player extends MusicPlayer targetEnc, inStream); AudioFormat format = inStream.getFormat(); - SourceDataLine line = null; DataLine.Info info = new DataLine.Info( SourceDataLine.class, format); try { - line = (SourceDataLine) AudioSystem.getLine(info); - line.open(format); + _line = (SourceDataLine) AudioSystem.getLine(info); + _line.open(format); } catch (LineUnavailableException lue) { Log.warning("MP3 line unavailable: " + lue); return; } - line.start(); + _line.start(); byte[] data = new byte[BUFFER_SIZE]; int count = 0; @@ -94,15 +91,15 @@ public class Mp3Player extends MusicPlayer break; } if (count >= 0) { - line.write(data, 0, count); + _line.write(data, 0, count); } if (_player != Thread.currentThread()) { return; } } - line.drain(); - line.close(); + _line.drain(); + _line.close(); _musicListener.musicStopped(); } }; @@ -121,12 +118,18 @@ public class Mp3Player extends MusicPlayer // documentation inherited public void setVolume (float volume) { - // TODO + // TODO : line won't be null when we initialize it in the right place + if (_line != null) { + SoundManager.adjustVolume(_line, volume); + } } /** The thread that transfers data to the line. */ protected Thread _player; + /** The line that we play through. */ + protected SourceDataLine _line; + /** The size of our buffer. */ protected static final int BUFFER_SIZE = 8192; } diff --git a/src/java/com/threerings/media/sound/MusicPlayer.java b/src/java/com/threerings/media/sound/MusicPlayer.java index e92bd3d7e..d5ca3bf91 100644 --- a/src/java/com/threerings/media/sound/MusicPlayer.java +++ b/src/java/com/threerings/media/sound/MusicPlayer.java @@ -1,9 +1,9 @@ // -// $Id: MusicPlayer.java,v 1.2 2002/11/22 19:21:12 ray Exp $ +// $Id: MusicPlayer.java,v 1.3 2002/11/26 02:39:40 ray Exp $ package com.threerings.media; -import com.threerings.resource.ResourceManager; +import java.io.InputStream; /** * Abstract music player. @@ -25,11 +25,9 @@ public abstract class MusicPlayer /** * Initialize the music player. */ - public final void init ( - ResourceManager rmgr, MusicEventListener musicListener) + public final void init (MusicEventListener musicListener) throws Exception { - _rmgr = rmgr; _musicListener = musicListener; init(); @@ -51,9 +49,9 @@ public abstract class MusicPlayer } /** - * Start playing the specified song. + * Start playing song data from the specified stream. */ - public abstract void start (String set, String path) + public abstract void start (InputStream stream) throws Exception; /** @@ -68,9 +66,6 @@ public abstract class MusicPlayer */ public abstract void setVolume (float volume); - /** The place we load data from. */ - protected ResourceManager _rmgr; - /** Tell this guy about it when a song stops. */ protected MusicEventListener _musicListener; } diff --git a/src/java/com/threerings/media/sound/SoundManager.java b/src/java/com/threerings/media/sound/SoundManager.java index 911dbbe52..7f83b9a27 100644 --- a/src/java/com/threerings/media/sound/SoundManager.java +++ b/src/java/com/threerings/media/sound/SoundManager.java @@ -1,11 +1,14 @@ // -// $Id: SoundManager.java,v 1.28 2002/11/25 20:02:31 ray Exp $ +// $Id: SoundManager.java,v 1.29 2002/11/26 02:39:40 ray Exp $ package com.threerings.media; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; import java.util.ArrayList; import java.util.HashMap; @@ -40,6 +43,7 @@ import com.samskivert.util.LRUHashMap; import com.samskivert.util.Queue; import com.threerings.resource.ResourceManager; +import com.threerings.util.RandomUtil; import com.threerings.media.Log; @@ -194,6 +198,14 @@ public class SoundManager } } + /** + * Set the test sound clip directory. + */ + public void setTestDir (String testy) + { + _testDir = testy; + } + /** * Sets the volume for all sound clips. * @@ -244,22 +256,22 @@ public class SoundManager * Optionally lock the sound data prior to playing, to guarantee * that it will be quickly available for playing. */ - public void lock (String set, String path) + public void lock (String pkgPath, String key) { synchronized (_queue) { _queue.append(LOCK); - _queue.append(new SoundKey(set, path)); + _queue.append(new SoundKey(pkgPath, key)); } } /** * Unlock the specified sound so that its resources can be freed. */ - public void unlock (String set, String path) + public void unlock (String pkgPath, String key) { synchronized (_queue) { _queue.append(UNLOCK); - _queue.append(new SoundKey(set, path)); + _queue.append(new SoundKey(pkgPath, key)); } } @@ -267,7 +279,7 @@ public class SoundManager * Play the specified sound of as the specified type of sound. * Note that a sound need not be locked prior to playing. */ - public void play (SoundType type, String set, String path) + public void play (SoundType type, String pkgPath, String key) { if (type == null) { // let the lazy kids play too @@ -278,12 +290,12 @@ public class SoundManager synchronized (_queue) { if (_queue.size() < MAX_QUEUE_SIZE) { _queue.append(PLAY); - _queue.append(new SoundKey(set, path)); + _queue.append(new SoundKey(pkgPath, key)); } else { Log.warning("SoundManager not playing sound because " + - "too many sounds in queue [path=" + path + - ", type=" + type + "]."); + "too many sounds in queue [pkgPath=" + pkgPath + + ", key=" + key + ", type=" + type + "]."); } } } @@ -292,19 +304,19 @@ public class SoundManager /** * Start playing the specified music repeatedly. */ - public void pushMusic (String set, String path) + public void pushMusic (String pkgPath, String key) { - pushMusic(set, path, -1); + pushMusic(pkgPath, key, -1); } /** * Start playing music for the specified number of loops. */ - public void pushMusic (String set, String path, int numloops) + public void pushMusic (String pkgPath, String key, int numloops) { synchronized (_queue) { _queue.append(PLAYMUSIC); - _queue.append(new MusicInfo(set, path, numloops)); + _queue.append(new MusicInfo(pkgPath, key, numloops)); } } @@ -312,11 +324,11 @@ 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 set, String path) + public void removeMusic (String pkgPath, String key) { synchronized (_queue) { _queue.append(STOPMUSIC); - _queue.append(new SoundKey(set, path)); + _queue.append(new SoundKey(pkgPath, key)); } } @@ -388,7 +400,18 @@ public class SoundManager } MusicInfo info = (MusicInfo) _musicStack.getFirst(); - Class playerClass = getMusicPlayerClass(info.path); + + Config c = getConfig(info); + String[] names = c.getValue(info.key, (String[])null); + if (names == null) { + Log.warning("No such music [key=" + info + "]."); + _musicStack.removeFirst(); + playTopMusic(); + return; + } + String music = names[RandomUtil.getInt(names.length)]; + + Class playerClass = getMusicPlayerClass(music); // shutdown the old player if we're switching music types if (! playerClass.isInstance(_musicPlayer)) { @@ -399,7 +422,7 @@ public class SoundManager // set up the new player try { _musicPlayer = (MusicPlayer) playerClass.newInstance(); - _musicPlayer.init(_rmgr, this); + _musicPlayer.init(this); } catch (Exception e) { Log.warning("Unable to instantiate music player [class=" + @@ -416,11 +439,13 @@ public class SoundManager } // play! + String bundle = c.getValue("bundle", (String)null); try { - _musicPlayer.start(info.set, info.path); + // TODO: buffer for the music player? + _musicPlayer.start(_rmgr.getResource(bundle, music)); } catch (Exception e) { Log.warning("Error playing music, skipping [e=" + e + - ", set=" + info.set + ", path=" + info.path + "]."); + ", bundle=" + bundle + ", music=" + music + "]."); _musicStack.removeFirst(); playTopMusic(); return; @@ -556,14 +581,77 @@ public class SoundManager protected byte[] getClipData (SoundKey key) throws IOException, UnsupportedAudioFileException { - byte[] data = (byte[]) _clipCache.get(key); + byte[][] data = (byte[][]) _clipCache.get(key); if (data == null) { - data = StreamUtils.streamAsBytes( - _rmgr.getResource(key.set, key.path), BUFFER_SIZE); - _clipCache.put(key, data); + + // if there is a test sound, JUST use the test sound. + InputStream stream = getTestClip(key); + if (stream != null) { + data = new byte[1][]; + data[0] = StreamUtils.streamAsBytes(stream, BUFFER_SIZE); + + } else { + // otherwise, randomize between all available sounds + Config c = getConfig(key); + String[] names = c.getValue(key.key, (String[])null); + if (names == null) { + Log.warning("No such sound [key=" + key + "]."); + return null; + } + + data = new byte[names.length][]; + String bundle = c.getValue("bundle", (String)null); + for (int ii=0; ii < names.length; ii++) { + data[ii] = StreamUtils.streamAsBytes( + _rmgr.getResource(bundle, names[ii]), BUFFER_SIZE); + } + } + + // TODO: be more sophisticated and allow locked sounds (as long + // as they're locked) to be put in the cache + // for now- nothing is cached when we're testing + if (_testDir == null) { + _clipCache.put(key, data); + } } - return data; + return data[RandomUtil.getInt(data.length)]; + } + + protected InputStream getTestClip (SoundKey key) + { + if (_testDir == null) { + return null; + } + + final String namePrefix = key.key + "."; + File f = new File(_testDir); + File[] list = f.listFiles(new FilenameFilter() { + public boolean accept (File f, String name) + { + return name.startsWith(namePrefix); + } + }); + if (list != null) { + try { + return new FileInputStream(list[0]); + } catch (Exception e) { + Log.warning("Error reading test sound [e=" + e + ", file=" + + list[0] + "]."); + } + } + return null; + } + + protected Config getConfig (SoundKey key) + { + Config c = (Config) _configs.get(key.pkgPath); + if (c == null) { + String propPath = key.pkgPath + Sounds.PROP_NAME; + c = new Config(propPath); + _configs.put(key.pkgPath, c); + } + return c; } // /** @@ -832,26 +920,26 @@ public class SoundManager */ protected static class SoundKey { - public String path; + public String pkgPath; - public String set; + public String key; - public SoundKey (String set, String path) + public SoundKey (String pkgPath, String key) { - this.set = set; - this.path = path; + this.pkgPath = pkgPath; + this.key = key; } // documentation inherited public String toString () { - return "SoundKey{set=" + set + ", path=" + path + "}"; + return "SoundKey{pkgPath=" + pkgPath + ", key=" + key + "}"; } // documentation inherited public int hashCode () { - return path.hashCode() ^ set.hashCode(); + return pkgPath.hashCode() ^ key.hashCode(); } // documentation inherited @@ -859,8 +947,8 @@ public class SoundManager { if (o instanceof SoundKey) { SoundKey that = (SoundKey) o; - return this.path.equals(that.path) && - this.set.equals(that.set); + return this.pkgPath.equals(that.pkgPath) && + this.key.equals(that.key); } return false; } @@ -874,6 +962,7 @@ public class SoundManager /** How many times to loop, or -1 for forever. */ public int loops; + // TODO rename public MusicInfo (String set, String path, int loops) { super(set, path); @@ -881,6 +970,9 @@ public class SoundManager } } + /** Directory from which we load test sounds. */ + protected String _testDir; + /** The resource manager from which we obtain audio files. */ protected ResourceManager _rmgr; @@ -911,6 +1003,9 @@ public class SoundManager /** A set of soundTypes for which sound is enabled. */ protected HashSet _disabledTypes = new HashSet(); + /** A cache of config objects we've created. */ + protected HashMap _configs = new HashMap(); + /** Signals to the queue to do different things. */ protected Object PLAY = new Object(); protected Object PLAYMUSIC = new Object(); diff --git a/src/java/com/threerings/media/sound/Sounds.java b/src/java/com/threerings/media/sound/Sounds.java index 8580494e4..c25d5e492 100644 --- a/src/java/com/threerings/media/sound/Sounds.java +++ b/src/java/com/threerings/media/sound/Sounds.java @@ -1,8 +1,10 @@ // -// $Id: Sounds.java,v 1.1 2002/11/22 21:54:49 mdb Exp $ +// $Id: Sounds.java,v 1.2 2002/11/26 02:39:40 ray Exp $ package com.threerings.media; +import java.io.File; + /** * A base class for sound repository classes. These would extend this * class and define keys for the various sounds that are mapped in the @@ -14,18 +16,19 @@ public class Sounds public static final String PROP_NAME = "sounds"; /** + * Return the package path prefix of the supplied class. + * * Generates the key for the sound repository configuration file in * the package associated with the class. For example, if a the class * com.threerings.happy.fun.GameSounds were supplied to * this method, it would return - * com.threerings.happy.fun.sounds which would reference + * com/threerings/happy/fun/sounds/ which would reference * a sounds.properties file in the * com.threerings.happy.fun package. */ - protected static String makeKey (Class clazz) + protected static String getPackagePath (Class clazz) { - String cname = clazz.getName(); - int didx = cname.lastIndexOf("."); - return (didx != -1) ? cname.substring(0, didx) : ""; + return clazz.getPackage().getName().replace('.', File.separatorChar) + + File.separator; } }