Behold, Nenya, Ring of Water and repository for our media and animation related

goodies, both Java 2D and LWJGL/JME 3D.


git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Michael Bayne
2006-06-23 18:07:28 +00:00
commit c2117ee86d
570 changed files with 61913 additions and 0 deletions
@@ -0,0 +1,178 @@
//
// $Id: MidiPlayer.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sound;
import java.io.BufferedInputStream;
import java.io.InputStream;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
/**
* Plays midi/rmf sounds using Java's sequencer, which is susceptible
* to the accuracy of System.currentTimeMillis() and so currently sounds
* like "ass" under Windows.
*/
public class MidiPlayer extends MusicPlayer
implements MetaEventListener
{
// documentation inherited
public void init ()
throws Exception
{
_sequencer = MidiSystem.getSequencer();
_sequencer.open();
if (_sequencer instanceof Synthesizer) {
_channels = ((Synthesizer) _sequencer).getChannels();
}
}
// documentation inherited
public void shutdown ()
{
_sequencer.close();
}
// documentation inherited
public void start (InputStream stream)
throws Exception
{
_sequencer.setSequence(new BufferedInputStream(stream));
_sequencer.start();
_sequencer.addMetaEventListener(this);
}
// documentation inherited
public void stop ()
{
_sequencer.removeMetaEventListener(this);
_sequencer.stop();
}
// documentation inherited
public void setVolume (float volume)
{
if (_channels != null) {
int setting = (int) (volume * 127.0);
for (int ii=0; ii < _channels.length; ii++) {
_channels[ii].controlChange(VOLUME_CONTROL, setting);
}
}
}
// documentation inherited from interface MetaEventListener
public void meta (MetaMessage msg)
{
if (msg.getType() == END_OF_TRACK) {
_musicListener.musicStopped();
}
}
// STUFF FROM ANOTHER TIME
// /**
// * Get a list of alternate midi devices.
// */
// public MidiDevice.Info[] getAlternateMidiDevices ()
// {
// ArrayList infos = new ArrayList();
// CollectionUtil.addAll(infos, MidiSystem.getMidiDeviceInfo());
//
// // remove the synth/seqs, leaving only hardware midi thingies
// for (Iterator iter=infos.iterator(); iter.hasNext(); ) {
// try {
// MidiDevice dev = MidiSystem.getMidiDevice(
// (MidiDevice.Info) iter.next());
// if ((dev instanceof Sequencer) ||
// (dev instanceof Synthesizer)) {
// iter.remove();
// }
// } catch (MidiUnavailableException mue) {
// iter.remove();
// }
// }
//
// return (MidiDevice.Info[]) infos.toArray(
// new MidiDevice.Info[infos.size()]);
// }
//
// /**
// * Attempt to use the alternate midi device for output.
// * Return true if we're using it.
// */
// public boolean useAlternateDevice (MidiDevice.Info devinfo)
// {
// Log.info("Trying alternate device: " + devinfo);
// try {
// MidiDevice dev = MidiSystem.getMidiDevice(devinfo);
// Receiver rec = dev.getReceiver();
// if (rec == null) {
// Log.info("Got no device!");
// return false;
// }
// _stoppingSong = true;
// _sequencer.stop();
// _sequencer.close();
//
// Receiver old = _sequencer.getTransmitter().getReceiver();
// Log.info("Old receiver: " + old);
// if (old != null) {
// old.close();
// }
// _sequencer.open();
//
// // THIS DOESN'T WORK.
// // See bug #4347135, specifically notes on the bottom.
// _sequencer.getTransmitter().setReceiver(rec);
// playTopSong();
//
// // possibly shut down an old receiver
// if (_receiver != null) {
// _receiver.close();
// }
// // set the new receiver
// _receiver = rec;
//
// return true;
//
// } catch (MidiUnavailableException mue) {
// Log.warning("Use of alternate device failed [e=" + mue +
// ", device=" + devinfo + "].");
// return false;
// }
// }
/** This is apparently the midi code for end of track. Wack. */
protected static final int END_OF_TRACK = 47;
/** The midi control for volume is 7. Ooooooo. */
protected static final int VOLUME_CONTROL = 7;
/** The sequencer. */
protected Sequencer _sequencer;
/** The channels in the sequencer, which we'll use to fuxor volumes. */
protected MidiChannel[] _channels;
}
@@ -0,0 +1,135 @@
//
// $Id: ModPlayer.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sound;
import java.io.DataInputStream;
import java.io.InputStream;
import micromod.MicroMod;
import micromod.Module;
import micromod.ModuleLoader;
import micromod.output.JavaSoundOutputDevice;
import micromod.output.OutputDeviceException;
import micromod.output.converters.SS16LEAudioFormatConverter;
import micromod.resamplers.LinearResampler;
/**
* A player that plays .mod format music.
*/
public class ModPlayer extends MusicPlayer
{
// documentation inherited
public void init ()
throws Exception
{
_device = new NaryaSoundDevice();
_device.start();
}
// documentation inherited
public void shutdown ()
{
_device.stop();
}
// documentation inherited
public void start (InputStream stream)
throws Exception
{
Module module = ModuleLoader.read(new DataInputStream(stream));
final MicroMod mod = new MicroMod(
module, _device, new LinearResampler());
_player = new Thread("narya mod player") {
public void run () {
while (mod.getSequenceLoopCount() == 0) {
mod.doRealTimePlayback();
try {
Thread.sleep(20);
} catch (InterruptedException ie) {
// WFCares
}
if (_player != Thread.currentThread()) {
// we were stopped!
return;
}
}
_device.drain();
_musicListener.musicStopped();
}
};
_player.setDaemon(true);
_player.start();
}
// documentation inherited
public void stop ()
{
_player = null;
}
// documentation inherited
public void setVolume (float vol)
{
_device.setVolume(vol);
}
/**
* A class that allows us to access the dataline so we can adjust
* the volume.
*/
protected static class NaryaSoundDevice extends JavaSoundOutputDevice
{
public NaryaSoundDevice ()
throws OutputDeviceException
{
super(new SS16LEAudioFormatConverter(), 44100, 1000);
}
/**
* Adjust the volume of the line that we're sending our mod data to.
*/
public void setVolume (float vol)
{
SoundManager.adjustVolume(sourceDataLine, vol);
}
/**
* Access the drain method of the line.
*/
public void drain ()
{
sourceDataLine.drain();
}
}
/** The thread that does the work. */
protected Thread _player;
/** The sound output device. */
protected NaryaSoundDevice _device;
}
@@ -0,0 +1,148 @@
//
// $Id: Mp3Player.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sound;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import com.threerings.media.Log;
/**
* Plays mp3 files. Depends on three external jar files that aren't even
* imported here:
* tritonus_share.jar
* tritonus_mp3.jar
* javalayer.jar
*/
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
public void shutdown ()
{
}
// documentation inherited
public void start (final InputStream stream)
throws Exception
{
// TODO: some stuff needs to come out of here and into init/shutdown
// but we'll deal with all that later, d00d.
_player = new Thread("narya mp3 relay") {
public void run () {
AudioInputStream inStream = null;
try {
inStream = AudioSystem.getAudioInputStream(
new BufferedInputStream(stream, BUFFER_SIZE));
} catch (Exception e) {
Log.warning("MP3 fuckola. [e=" + e + "].");
return;
}
AudioFormat sourceFormat = inStream.getFormat();
AudioFormat.Encoding targetEnc =
AudioFormat.Encoding.PCM_SIGNED;
inStream = AudioSystem.getAudioInputStream(
targetEnc, inStream);
AudioFormat format = inStream.getFormat();
DataLine.Info info = new DataLine.Info(
SourceDataLine.class, format);
try {
_line = (SourceDataLine) AudioSystem.getLine(info);
_line.open(format);
} catch (LineUnavailableException lue) {
Log.warning("MP3 line unavailable: " + lue);
return;
}
_line.start();
byte[] data = new byte[BUFFER_SIZE];
int count = 0;
while (count >= 0) {
try {
count = inStream.read(data, 0, data.length);
} catch (IOException ioe) {
Log.warning("Error reading MP3: " + ioe);
break;
}
if (count >= 0) {
_line.write(data, 0, count);
}
if (_player != Thread.currentThread()) {
return;
}
}
_line.drain();
_line.close();
_musicListener.musicStopped();
}
};
_player.setDaemon(true);
//_player.setPriority(_player.getPriority() + 1);
_player.start();
}
// documentation inherited
public void stop ()
{
_player = null;
}
// documentation inherited
public void setVolume (float volume)
{
// 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;
}
@@ -0,0 +1,353 @@
//
// $Id: SoundManager.java 3290 2004-12-29 21:56:58Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sound;
import java.util.Iterator;
import java.util.LinkedList;
import com.samskivert.util.Config;
import com.samskivert.util.RandomUtil;
import com.samskivert.util.RunQueue;
import com.threerings.media.Log;
/**
* Manages the playing of audio files.
*/
// TODO:
// - fade music out when stopped?
// - be able to pause music
public class MusicManager
{
/**
* Constructs a music manager.
*
* @param smgr The soundManager we work with.
* @param runQueue the client event run queue.
*
*/
public MusicManager (SoundManager smgr, RunQueue runQueue)
{
_smgr = smgr;
_runQueue = runQueue;
}
/**
* Shut the damn thing off.
*/
public void shutdown ()
{
_musicStack.clear();
stopMusicPlayer();
}
/**
* Returns a string summarizing our volume settings and disabled sound
* types.
*/
public String summarizeState ()
{
StringBuilder buf = new StringBuilder();
buf.append("musicVol=").append(_musicVol);
return buf.append("]").toString();
}
/**
* Sets the volume for music.
*
* @param vol a volume parameter between 0f and 1f, inclusive.
*/
public void setMusicVolume (float vol)
{
float oldvol = _musicVol;
_musicVol = Math.max(0f, Math.min(1f, vol));
if (_musicPlayer != null) {
_musicPlayer.setVolume(_musicVol);
}
if ((oldvol == 0f) && (_musicVol != 0f)) {
playTopMusic();
} else if ((oldvol != 0f) && (_musicVol == 0f)) {
stopMusicPlayer();
}
}
/**
* Get the music volume.
*/
public float getMusicVolume ()
{
return _musicVol;
}
/**
* Start playing the specified music repeatedly.
*/
public void pushMusic (String pkgPath, String key)
{
pushMusic(pkgPath, key, -1);
}
/**
* Start playing music for the specified number of loops. If no other
* music is pushed, the specified music will play for the number of loops
* specified, and will then be popped off the stack.
*/
public void pushMusic (String pkgPath, String key, int numloops)
{
MusicKey mkey = new MusicKey(pkgPath, key, numloops);
// stop any existing playing music
if (_musicPlayer != null) {
_musicPlayer.stop();
handleMusicStopped();
}
// add the new song
_musicStack.addFirst(mkey);
// and play it
playTopMusic();
}
/**
* 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 pkgPath, String key)
{
MusicKey mkey = new MusicKey(pkgPath, key, -1);
if (!_musicStack.isEmpty()) {
MusicKey current = (MusicKey) _musicStack.getFirst();
// if we're currently playing this song..
if (mkey.equals(current)) {
// stop it
if (_musicPlayer != null) {
_musicPlayer.stop();
}
// remove it from the stack
_musicStack.removeFirst();
// start playing the next..
playTopMusic();
return;
} else {
// we aren't currently playing this song. Simply remove.
for (Iterator iter=_musicStack.iterator(); iter.hasNext(); ) {
if (key.equals(iter.next())) {
iter.remove();
return;
}
}
}
}
Log.debug("Sequence stopped that wasn't in the stack anymore " +
"[key=" + mkey + "].");
}
/**
* Start the specified sequence.
*/
protected void playTopMusic ()
{
if (_musicStack.isEmpty()) {
return;
}
// if the volume is off, we don't actually want to play anything
// but we want to at least decrement any loopers by one
// and keep them on the top of the queue
if (_musicVol == 0f) {
handleMusicStopped();
return;
}
MusicKey info = (MusicKey) _musicStack.getFirst();
Config c = _smgr.getConfig(info);
String[] names = c.getValue(info.key, (String[])null);
if ((names == null) || (names.length == 0)) {
Log.warning("No such music [key=" + info + "].");
_musicStack.removeFirst();
playTopMusic();
return;
}
String music = names[RandomUtil.getInt(names.length)];
Class playerClass = getMusicPlayerClass(music);
// if we don't have a player for this song, play the next song
if (playerClass == null) {
_musicStack.removeFirst();
playTopMusic();
return;
}
// shutdown the old player if we're switching music types
if (! playerClass.isInstance(_musicPlayer)) {
if (_musicPlayer != null) {
_musicPlayer.shutdown();
}
// set up the new player
try {
_musicPlayer = (MusicPlayer) playerClass.newInstance();
_musicPlayer.init(_playerListener);
} catch (Exception e) {
Log.warning("Unable to instantiate music player [class=" +
playerClass + ", e=" + e + "].");
// scrap it, try again with the next song
_musicPlayer = null;
_musicStack.removeFirst();
playTopMusic();
return;
}
_musicPlayer.setVolume(_musicVol);
}
// play!
String bundle = c.getValue("bundle", (String)null);
try {
// TODO: buffer for the music player?
_musicPlayer.start(_smgr._rmgr.getResource(bundle, music));
} catch (Exception e) {
Log.warning("Error playing music, skipping [e=" + e +
", bundle=" + bundle + ", music=" + music + "].");
_musicStack.removeFirst();
playTopMusic();
return;
}
}
/**
* Get the appropriate music player for the specified music file.
*/
protected static Class getMusicPlayerClass (String path)
{
path = path.toLowerCase();
// if (path.endsWith(".mid") || path.endsWith(".rmf")) {
// return MidiPlayer.class;
// } else if (path.endsWith(".mod")) {
// return ModPlayer.class;
// } else if (path.endsWith(".mp3")) {
// return Mp3Player.class;
// } else if (path.endsWith(".ogg")) {
// return OggPlayer.class;
// } else {
return null;
// }
}
/**
* Stop whatever song is currently playing and deal with the
* MusicKey associated with it.
*/
protected void handleMusicStopped ()
{
if (_musicStack.isEmpty()) {
return;
}
// see what was playing
MusicKey current = (MusicKey) _musicStack.getFirst();
// see how many times the song was to loop and act accordingly
switch (current.loops) {
default:
current.loops--;
break;
case 1:
// sorry charlie
_musicStack.removeFirst();
break;
}
}
/**
* Stop the current music player.
*/
protected void stopMusicPlayer ()
{
if (_musicPlayer != null) {
_musicPlayer.stop();
_musicPlayer.shutdown();
_musicPlayer = null;
}
}
/**
* A class that tracks the information about our playing music files.
*/
protected static class MusicKey extends SoundManager.SoundKey
{
/** How many times to loop, or -1 for forever. */
public int loops;
public MusicKey (String set, String path, int loops)
{
super((byte) -1, set, path);
this.loops = loops;
}
}
/** The sound manager we work with. */
protected SoundManager _smgr;
/** The client event run queue. */
protected RunQueue _runQueue;
/** Volume level for music. */
protected float _musicVol = 1f;
/** The stack of songs that we're playing. */
protected LinkedList _musicStack = new LinkedList();
/** The current music player, if any. */
protected MusicPlayer _musicPlayer;
/** Event listener for receiving information about a song ending. */
protected MusicPlayer.MusicEventListener _playerListener =
new MusicPlayer.MusicEventListener() {
public void musicStopped () {
_runQueue.postRunnable(new Runnable() {
public void run() {
handleMusicStopped();
playTopMusic();
}
});
}
};
}
@@ -0,0 +1,89 @@
//
// $Id: MusicPlayer.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sound;
import java.io.InputStream;
/**
* Abstract music player.
*/
public abstract class MusicPlayer
{
/**
* A watcher interested in music events.
*/
public interface MusicEventListener
{
/**
* A callback that all players should use when the song they are
* playing has finished playing (completely).
*/
public void musicStopped();
}
/**
* Initialize the music player.
*/
public final void init (MusicEventListener musicListener)
throws Exception
{
_musicListener = musicListener;
init();
}
/**
* Do your init here.
*/
public void init ()
throws Exception
{
}
/**
* Shutdown and free all resources.
*/
public void shutdown ()
{
}
/**
* Start playing song data from the specified stream.
*/
public abstract void start (InputStream stream)
throws Exception;
/**
* Stop playing the specified song.
*/
public abstract void stop ();
/**
* Set the volume.
*
* @param volume 0f - 1f, inclusive.
*/
public abstract void setVolume (float volume);
/** Tell this guy about it when a song stops. */
protected MusicEventListener _musicListener;
}
@@ -0,0 +1,401 @@
//
// $Id: OggPlayer.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sound;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import com.jcraft.jorbis.*;
import com.jcraft.jogg.*;
/**
* Plays Ogg Vorbis streams.
*
* Hacked together from NASTY code from JOrbis.
*/
// TODO- this would need to be greatly cleaned up if we were serious about
// using it.
public class OggPlayer extends MusicPlayer
{
// documentation inherited
public void start (final InputStream stream)
{
_player = new Thread("narya ogg player") {
public void run () {
playStream(stream);
}
};
_player.setDaemon(true);
_player.start();
}
// documentation inherited
public void stop ()
{
_player = null;
}
static final int BUFSIZE=4096*2;
static int convsize=BUFSIZE*2;
static byte[] convbuffer=new byte[convsize];
SyncState oy;
StreamState os;
Page og;
Packet op;
Info vi;
Comment vc;
DspState vd;
Block vb;
byte[] buffer=null;
int bytes=0;
int format;
int rate=0;
int channels=0;
SourceDataLine outputLine=null;
int frameSizeInBytes;
int bufferLengthInBytes;
void init_jorbis(){
oy=new SyncState();
os=new StreamState();
og=new Page();
op=new Packet();
vi=new Info();
vc=new Comment();
vd=new DspState();
vb=new Block(vd);
buffer=null;
bytes=0;
oy.init();
}
SourceDataLine getOutputLine(int channels, int rate){
if(outputLine!=null || this.rate!=rate || this.channels!=channels){
if(outputLine!=null){
outputLine.drain();
outputLine.stop();
outputLine.close();
}
init_audio(channels, rate);
outputLine.start();
}
return outputLine;
}
void init_audio(int channels, int rate){
try {
//ClassLoader originalClassLoader=null;
//try{
// originalClassLoader=Thread.currentThread().getContextClassLoader();
// Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
//}
//catch(Exception ee){
// System.out.println(ee);
//}
AudioFormat audioFormat =
new AudioFormat((float)rate,
16,
channels,
true, // PCM_Signed
false // littleEndian
);
DataLine.Info info =
new DataLine.Info(SourceDataLine.class,
audioFormat,
AudioSystem.NOT_SPECIFIED);
if (!AudioSystem.isLineSupported(info)) {
//System.out.println("Line " + info + " not supported.");
return;
}
try{
outputLine = (SourceDataLine) AudioSystem.getLine(info);
//outputLine.addLineListener(this);
outputLine.open(audioFormat);
}
catch (LineUnavailableException ex) {
System.out.println("Unable to open the sourceDataLine: " + ex);
return;
}
catch (IllegalArgumentException ex) {
System.out.println("Illegal Argument: " + ex);
return;
}
frameSizeInBytes = audioFormat.getFrameSize();
int bufferLengthInFrames = outputLine.getBufferSize()/frameSizeInBytes/2;
bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes;
//buffer = new byte[bufferLengthInBytes];
//if(originalClassLoader!=null)
// Thread.currentThread().setContextClassLoader(originalClassLoader);
this.rate=rate;
this.channels=channels;
}
catch(Exception ee){
System.out.println(ee);
}
}
protected void playStream (InputStream stream)
{
init_jorbis();
loop:
while (true) {
int eos = 0;
int index = oy.buffer(BUFSIZE);
buffer = oy.data;
try {
bytes = stream.read(buffer, index, BUFSIZE);
} catch (Exception e) {
System.err.println(e);
return;
}
oy.wrote(bytes);
if (oy.pageout(og) != 1) {
if (bytes < BUFSIZE) {
break;
}
System.err.println("Input does not appear to be an Ogg bitstream.");
return;
}
os.init(og.serialno());
os.reset();
vi.init();
vc.init();
if (os.pagein(og) < 0) {
// error; stream version mismatch perhaps
System.err.println("Error reading first page of Ogg bitstream data.");
return;
}
if (os.packetout(op) != 1) {
// no page? must not be vorbis
System.err.println("Error reading initial header packet.");
break;
// return;
}
if (vi.synthesis_headerin(vc, op) < 0) {
// error case; not a vorbis header
System.err.println("This Ogg bitstream does not contain Vorbis audio data.");
return;
}
int i=0;
while (i < 2) {
while (i < 2) {
int result = oy.pageout(og);
if (result==0) {
break; // Need more data
}
if (result==1) {
os.pagein(og);
while (i < 2) {
result = os.packetout(op);
if (result == 0) {
break;
}
if (result == -1) {
System.err.println("Corrupt secondary header. Exiting.");
//return;
break loop;
}
vi.synthesis_headerin(vc, op);
i++;
}
}
}
index = oy.buffer(BUFSIZE);
buffer = oy.data;
try {
bytes = stream.read(buffer, index, BUFSIZE);
}
catch(Exception e){
System.err.println(e);
return;
}
if (bytes == 0 && i < 2) {
System.err.println("End of file before finding all Vorbis headers!");
return;
}
oy.wrote(bytes);
}
convsize=BUFSIZE/vi.channels;
vd.synthesis_init(vi);
vb.init(vd);
double[][][] _pcm=new double[1][][];
float[][][] _pcmf=new float[1][][];
int[] _index=new int[vi.channels];
getOutputLine(vi.channels, vi.rate);
while (eos == 0) {
while (eos == 0) {
if (_player != Thread.currentThread()) {
//System.err.println("bye.");
try {
//outputLine.drain();
//outputLine.stop();
//outputLine.close();
stream.close();
} catch(Exception ee) {
}
return;
}
int result = oy.pageout(og);
if (result == 0) {
break; // need more data
}
if (result == -1) { // missing or corrupt data at this page position
// System.err.println("Corrupt or missing data in bitstream; continuing...");
} else {
os.pagein(og);
while (true) {
result = os.packetout(op);
if (result == 0) break; // need more data
if (result == -1) { // missing or corrupt data at this page position
// no reason to complain; already complained above
} else {
// we have a packet. Decode it
int samples;
if (vb.synthesis(op) == 0) { // test for success!
vd.synthesis_blockin(vb);
}
while ((samples =
vd.synthesis_pcmout(_pcmf, _index)) > 0) {
double[][] pcm = _pcm[0];
float[][] pcmf = _pcmf[0];
boolean clipflag = false;
int bout = Math.min(samples, convsize);
// convert doubles to 16 bit signed ints (host order) and
// interleave
for (i = 0; i < vi.channels; i++) {
int ptr = i*2;
//int ptr=i;
int mono = _index[i];
for (int j = 0; j < bout; j++) {
int val = (int)
(pcmf[i][mono+j] * 32767.);
if (val > 32767){
val = 32767;
clipflag = true;
} else if (val < -32768) {
val = -32768;
clipflag = true;
}
if (val < 0) {
val = val | 0x8000;
}
convbuffer[ptr] = (byte)(val);
convbuffer[ptr+1] = (byte)(val>>>8);
ptr += 2 * (vi.channels);
}
}
outputLine.write(convbuffer, 0,
2 * vi.channels * bout);
vd.synthesis_read(bout);
}
}
}
if (og.eos() != 0) {
eos=1;
}
}
}
if (eos==0) {
index = oy.buffer(BUFSIZE);
buffer = oy.data;
try {
bytes = stream.read(buffer,index,BUFSIZE);
} catch (Exception e) {
System.err.println(e);
return;
}
if (bytes == -1) {
break;
}
oy.wrote(bytes);
if (bytes==0) {
eos=1;
}
}
}
os.clear();
vb.clear();
vd.clear();
vi.clear();
}
oy.clear();
//System.err.println("Done.");
try {
if (stream != null) {
stream.close();
}
} catch (Exception e) {
}
}
public void setVolume (float volume)
{
// TODO
}
protected Thread _player;
}
@@ -0,0 +1,57 @@
//
// $Id: SoundCodes.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sound;
import com.threerings.media.sound.SoundManager.SoundType;
/**
* A basic set of sound types.
*/
public interface SoundCodes
{
/**
* Alert sounds are the type of sounds a player would hear when
* getting a puzzle challenge.
*/
public static final SoundType ALERT = new SoundType("alert");
/**
* Feedback sounds are the type of sounds a player would here when
* clicking on buttons or performing an action.
*/
public static final SoundType FEEDBACK = new SoundType("feedback");
/**
* Ambient sounds are birds chirping, waves lapping, boats creaking.
*/
public static final SoundType AMBIENT = new SoundType("ambient");
/**
* Game alert sounds are used to indicate that it's a player's turn.
*/
public static final SoundType GAME_ALERT = new SoundType("game_alert");
/**
* General game sound effects.
*/
public static final SoundType GAME_FX =new SoundType("game_fx");
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
//
// $Id: Sounds.java 4191 2006-06-13 22:42:20Z ray $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.sound;
/**
* A base class for sound repository classes. These would extend this
* class and define keys for the various sounds that are mapped in the
* properties file associated with that sound repository.
*/
public class Sounds
{
/** The name of the sound repository configuration file. */
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
* <code>com.threerings.happy.fun.GameSounds</code> were supplied to
* this method, it would return
* <code>com/threerings/happy/fun/sounds/</code> which would reference
* a <code>sounds.properties</code> file in the
* <code>com.threerings.happy.fun</code> package.
*/
protected static String getPackagePath (Class clazz)
{
return clazz.getPackage().getName().replace('.', '/') + "/";
}
}