genericized music playing, made transitioning from any music type to any

other clean and good.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1983 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2002-11-22 04:23:31 +00:00
parent e203c56483
commit 1953e5686c
5 changed files with 415 additions and 381 deletions
@@ -0,0 +1,168 @@
//
// $Id: MidiPlayer.java,v 1.1 2002/11/22 04:23:31 ray Exp $
package com.threerings.media;
import java.io.BufferedInputStream;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
/**
* Does something extraordinary.
*/
public class MidiPlayer extends MusicPlayer
implements MetaEventListener
{
// documentation inherited
public void init ()
{
try {
Sequencer seq = MidiSystem.getSequencer();
seq.open();
if (seq instanceof Synthesizer) {
_channels = ((Synthesizer) seq).getChannels();
//updateMusicVolume();
}
_sequencer = seq;
} catch (MidiUnavailableException mue) {
Log.warning("Midi unavailable! [e=" + mue + "].");
}
}
// documentation inherited
public void shutdown ()
{
_sequencer.close();
}
// documentation inherited
public void start (String set, String path)
throws Exception
{
_sequencer.setSequence(
new BufferedInputStream(_rmgr.getResource(set, path)));
_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;
}
@@ -1,5 +1,5 @@
// //
// $Id: ModPlayer.java,v 1.2 2002/11/20 23:06:33 ray Exp $ // $Id: ModPlayer.java,v 1.3 2002/11/22 04:23:31 ray Exp $
package com.threerings.media; package com.threerings.media;
@@ -20,36 +20,32 @@ import com.threerings.resource.ResourceManager;
/** /**
* Does something extraordinary. * Does something extraordinary.
*/ */
public class ModPlayer public class ModPlayer extends MusicPlayer
{ {
public ModPlayer (ResourceManager rmgr, SoundManager smgr) // documentation inherited
public void init ()
{ {
_rmgr = rmgr;
_smgr = smgr;
try { try {
_device = new NaryaSoundDevice(); _device = new NaryaSoundDevice();
_device.start();
} catch (OutputDeviceException ode) { } catch (OutputDeviceException ode) {
Log.warning("Unable to allocate sound channel for mod playing " + Log.warning("Unable to allocate sound channel for mod playing " +
"[e=" + ode + "]."); "[e=" + ode + "].");
} }
} }
public void stop () // documentation inherited
public void shutdown ()
{ {
_player = null; _device.stop();
} }
// documentation inherited
public void start (String set, String path) public void start (String set, String path)
throws Exception
{ {
Module module = null; Module module = ModuleLoader.read(
try { new DataInputStream(_rmgr.getResource(set, path)));
module = ModuleLoader.read(
new DataInputStream(_rmgr.getResource(set, path)));
} catch (IOException ioe) {
Log.warning("error loading oh shit oh shit: [e=" + ioe + "].");
return;
}
final MicroMod mod = new MicroMod( final MicroMod mod = new MicroMod(
module, _device, new LinearResampler()); module, _device, new LinearResampler());
@@ -57,9 +53,7 @@ public class ModPlayer
_player = new Thread("narya mod player") { _player = new Thread("narya mod player") {
public void run () { public void run () {
_device.start(); while (mod.getSequenceLoopCount() == 0) {
while ((_player == Thread.currentThread()) &&
(mod.getSequenceLoopCount() == 0)) {
mod.doRealTimePlayback(); mod.doRealTimePlayback();
try { try {
@@ -67,9 +61,14 @@ public class ModPlayer
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
// WFCares // WFCares
} }
if (_player != Thread.currentThread()) {
// we were stopped!
return;
}
} }
_device.stop(); _device.drain();
_smgr.songStopEvent(); _musicListener.musicStopped();
} }
}; };
@@ -77,10 +76,13 @@ public class ModPlayer
_player.start(); _player.start();
} }
/** // documentation inherited
* Set the volume of the midiplayer. public void stop ()
* @param vol 0f - 1f (inclusive). {
*/ _player = null;
}
// documentation inherited
public void setVolume (float vol) public void setVolume (float vol)
{ {
_device.setVolume(vol); _device.setVolume(vol);
@@ -105,11 +107,16 @@ public class ModPlayer
{ {
SoundManager.adjustVolume(sourceDataLine, vol); SoundManager.adjustVolume(sourceDataLine, vol);
} }
/**
* Access the drain method of the line.
*/
public void drain ()
{
sourceDataLine.drain();
}
} }
protected Thread _player; protected Thread _player;
protected NaryaSoundDevice _device; protected NaryaSoundDevice _device;
protected ResourceManager _rmgr;
protected SoundManager _smgr;
} }
@@ -1,5 +1,5 @@
// //
// $Id: MP3Manager.java,v 1.2 2002/11/20 04:03:09 ray Exp $ // $Id: Mp3Player.java,v 1.1 2002/11/22 04:23:31 ray Exp $
package com.threerings.media; package com.threerings.media;
@@ -24,21 +24,24 @@ import com.threerings.resource.ResourceManager;
/** /**
* Does something extraordinary. * Does something extraordinary.
*/ */
public class MP3Manager public class Mp3Player extends MusicPlayer
{ {
public MP3Manager (ResourceManager rmgr, SoundManager smgr) // documentation inherited
public void init ()
{ {
_rmgr = rmgr;
_smgr = smgr;
} }
public void stop () // documentation inherited
public void shutdown ()
{ {
_player = null;
} }
// documentation inherited
public void start (final String set, final String path) public void start (final String set, final String path)
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") { _player = new Thread("narya mp3 relay") {
public void run () { public void run () {
AudioInputStream inStream = null; AudioInputStream inStream = null;
@@ -79,7 +82,7 @@ public class MP3Manager
byte[] data = new byte[BUFFER_SIZE]; byte[] data = new byte[BUFFER_SIZE];
int count = 0; int count = 0;
while (_player == Thread.currentThread()) { while (count >= 0) {
try { try {
count = inStream.read(data, 0, data.length); count = inStream.read(data, 0, data.length);
} catch (IOException ioe) { } catch (IOException ioe) {
@@ -88,13 +91,15 @@ public class MP3Manager
} }
if (count >= 0) { if (count >= 0) {
line.write(data, 0, count); line.write(data, 0, count);
} else { }
break; if (_player != Thread.currentThread()) {
return;
} }
} }
line.drain();
line.close(); line.close();
_smgr.songStopEvent(); _musicListener.musicStopped();
} }
}; };
@@ -103,10 +108,19 @@ public class MP3Manager
_player.start(); _player.start();
} }
// documentation inherited
public void stop ()
{
_player = null;
}
// documentation inherited
public void setVolume (float volume)
{
// TODO
}
protected Thread _player; protected Thread _player;
protected ResourceManager _rmgr; protected static final int BUFFER_SIZE = 8192;
protected SoundManager _smgr;
protected static final int BUFFER_SIZE = 128000;
} }
@@ -0,0 +1,72 @@
//
// $Id: MusicPlayer.java,v 1.1 2002/11/22 04:23:31 ray Exp $
package com.threerings.media;
import com.threerings.resource.ResourceManager;
/**
* Does something extraordinary.
*/
public abstract class MusicPlayer
{
/**
*/
public interface MusicEventListener
{
/**
* A callback that all players should use when the song they are
* playing has finished playing (completely).
*/
public void musicStopped();
}
/**
*/
public final void init (
ResourceManager rmgr, MusicEventListener musicListener)
{
_rmgr = rmgr;
_musicListener = musicListener;
init();
}
/**
* Do your init here.
*/
public void init ()
{
}
/**
* Shutdown and free all resources.
*/
public void shutdown ()
{
}
/**
* Start playing the specified song.
*/
public abstract void start (String set, String path)
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);
/** The place we load data from. */
protected ResourceManager _rmgr;
/** Tell this guy about it when a song stops. */
protected MusicEventListener _musicListener;
}
@@ -1,5 +1,5 @@
// //
// $Id: SoundManager.java,v 1.23 2002/11/20 23:06:33 ray Exp $ // $Id: SoundManager.java,v 1.24 2002/11/22 04:23:31 ray Exp $
package com.threerings.media; package com.threerings.media;
@@ -16,17 +16,6 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioSystem;
@@ -64,7 +53,7 @@ import com.threerings.media.Log;
// -- CLEANUP is NEEDED (lots of references to Sequences, when we're now // -- CLEANUP is NEEDED (lots of references to Sequences, when we're now
// playing mods and mp3s as well. ) // playing mods and mp3s as well. )
public class SoundManager public class SoundManager
implements MetaEventListener implements MusicPlayer.MusicEventListener
{ {
/** /**
* Create instances of this for your application to differentiate * Create instances of this for your application to differentiate
@@ -109,7 +98,7 @@ public class SoundManager
public void run () { public void run () {
Object command = null; Object command = null;
SoundKey key = null; SoundKey key = null;
MidiInfo midiInfo = null; MusicInfo musicInfo = null;
while (amRunning()) { while (amRunning()) {
try { try {
@@ -126,7 +115,7 @@ public class SoundManager
key = (SoundKey) _queue.get(); key = (SoundKey) _queue.get();
} else if (PLAYMUSIC == command) { } else if (PLAYMUSIC == command) {
midiInfo = (MidiInfo) _queue.get(); musicInfo = (MusicInfo) _queue.get();
} }
} }
@@ -135,10 +124,10 @@ public class SoundManager
playSound(key); playSound(key);
} else if (PLAYMUSIC == command) { } else if (PLAYMUSIC == command) {
playSequence(midiInfo); playMusic(musicInfo);
} else if (STOPMUSIC == command) { } else if (STOPMUSIC == command) {
stopSequence(key); stopMusic(key);
} else if (LOCK == command) { } else if (LOCK == command) {
_clipCache.lock(key); _clipCache.lock(key);
@@ -159,7 +148,7 @@ public class SoundManager
} else if (DIE == command) { } else if (DIE == command) {
_resourceFreer.stop(); _resourceFreer.stop();
flushResources(true); flushResources(true);
shutdownMidi(); shutdownMusic();
} }
} catch (Exception e) { } catch (Exception e) {
Log.warning("Captured exception in SoundManager loop."); Log.warning("Captured exception in SoundManager loop.");
@@ -320,7 +309,7 @@ public class SoundManager
{ {
synchronized (_queue) { synchronized (_queue) {
_queue.append(PLAYMUSIC); _queue.append(PLAYMUSIC);
_queue.append(new MidiInfo(set, path, numloops)); _queue.append(new MusicInfo(set, path, numloops));
} }
} }
@@ -336,78 +325,6 @@ public class SoundManager
} }
} }
/**
* 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;
}
}
/** /**
* On the SoundManager thread, * On the SoundManager thread,
* plays the sound file with the given pathname. * plays the sound file with the given pathname.
@@ -468,88 +385,107 @@ public class SoundManager
} }
/** /**
* Play a sequence from the specified path. * Play a song from the specified path.
*/ */
protected void playSequence (MidiInfo info) protected void playMusic (MusicInfo info)
{ {
if (_sequencer == null) { Log.info("Playing: " + info);
initMidi(); // stop whatever's currently playing
if (_musicPlayer != null) {
_musicPlayer.stop();
handleMusicStopped();
} }
stopCurrentSong(false); // add the new song
_midiStack.addFirst(info); _musicStack.addFirst(info);
playTopSong();
// and play it
playTopMusic();
} }
/** /**
* Start the specified sequence. * Start the specified sequence.
*/ */
protected void playTopSong () protected void playTopMusic ()
{ {
if (_midiStack.isEmpty()) { if (_musicStack.isEmpty()) {
return; return;
} }
MidiInfo info = (MidiInfo) _midiStack.getFirst(); MusicInfo info = (MusicInfo) _musicStack.getFirst();
Class playerClass = getMusicPlayerClass(info.path);
// start the new one // shutdown the old player if we're switching music types
try { if (! playerClass.isInstance(_musicPlayer)) {
if (info.path.endsWith(".mp3")) { if (_musicPlayer != null) {
_mp3player.start(info.set, info.path); _musicPlayer.shutdown();
} else if (info.path.endsWith(".mod")) {
_modplayer.start(info.set, info.path);
} else {
_sequencer.setSequence(getMidiData(info));
if (info.msPosition != -1) {
// TODO: this doesn't work correctly
_sequencer.setTickPosition(info.tickPosition);
_sequencer.setMicrosecondPosition(info.msPosition);
info.msPosition = -1;
}
_sequencer.start();
//updateMusicVolume();
//Log.info("Now playing : " + info.path);
} }
} catch (InvalidMidiDataException imda) { // set up the new player
Log.warning("Invalid midi data, not playing [path=" + try {
info.path + "]."); _musicPlayer = (MusicPlayer) playerClass.newInstance();
} catch (Exception e) {
Log.warning("Unable to instantiate music player [class=" +
playerClass + "].");
} catch (IOException ioe) { // scrap it, try again with the next song
Log.warning("ioe=" + ioe); _musicPlayer = null;
_musicStack.removeFirst();
playTopMusic();
return;
}
_musicPlayer.init(_rmgr, this);
_musicPlayer.setVolume(_musicVol);
}
// play!
try {
_musicPlayer.start(info.set, info.path);
} catch (Exception e) {
Log.warning("Error playing music, skipping [e=" + e +
", set=" + info.set + ", path=" + info.path + "].");
_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 {
return null;
} }
} }
/** /**
* Stop whatever song is currently playing and deal with the * Stop whatever song is currently playing and deal with the
* MidiInfo associated with it. * MusicInfo associated with it.
*/ */
protected void stopCurrentSong (boolean wasStopped) protected void handleMusicStopped ()
{ {
if (_midiStack.isEmpty()) { if (_musicStack.isEmpty()) {
return; return;
} }
// see what was playing // see what was playing
MidiInfo current = (MidiInfo) _midiStack.getFirst(); MusicInfo current = (MusicInfo) _musicStack.getFirst();
// stop the existing song
if (!wasStopped) {
// TODO: fade?
_stoppingSong = true;
if (current.path.endsWith(".mp3")) {
_mp3player.stop();
} else if (current.path.endsWith(".mod")) {
_modplayer.stop();
} else {
_sequencer.stop();
}
}
// see how many times the song was to loop and act accordingly
switch (current.loops) { switch (current.loops) {
default: default:
current.loops--; current.loops--;
@@ -557,15 +493,7 @@ public class SoundManager
case 1: case 1:
// sorry charlie // sorry charlie
_midiStack.removeFirst(); _musicStack.removeFirst();
break;
case -1:
// save info...
// TODO: this doesn't seem to work, and is out-n-out broken
// if we use the tickPos
current.msPosition = _sequencer.getMicrosecondPosition();
current.tickPosition = _sequencer.getTickPosition();
break; break;
} }
} }
@@ -573,34 +501,27 @@ public class SoundManager
/** /**
* Stop the sequence at the specified path. * Stop the sequence at the specified path.
*/ */
protected void stopSequence (SoundKey key) protected void stopMusic (SoundKey key)
{ {
if (! _midiStack.isEmpty()) { Log.info("Stopping: " + key);
MidiInfo current = (MidiInfo) _midiStack.getFirst(); if (! _musicStack.isEmpty()) {
MusicInfo current = (MusicInfo) _musicStack.getFirst();
// if we're currently playing this song.. // if we're currently playing this song..
if (key.equals(current)) { if (key.equals(current)) {
Log.info("is top song!");
// stop it // stop it
_stoppingSong = true; _musicPlayer.stop();
if (key.path.endsWith(".mp3")) {
_mp3player.stop();
} else if (key.path.endsWith(".mod")) {
_modplayer.stop();
} else {
_sequencer.stop();
}
// remove it from the stack // remove it from the stack
_midiStack.removeFirst(); _musicStack.removeFirst();
// start playing the next.. // start playing the next..
playTopSong(); playTopMusic();
} else { } else {
// we aren't currently playing this song. Simply remove. // we aren't currently playing this song. Simply remove.
for (Iterator iter=_midiStack.iterator(); iter.hasNext(); ) { for (Iterator iter=_musicStack.iterator(); iter.hasNext(); ) {
if (key.equals(iter.next())) { if (key.equals(iter.next())) {
iter.remove(); iter.remove();
return; return;
@@ -614,119 +535,28 @@ public class SoundManager
"[key=" + key + "]."); "[key=" + key + "].");
} }
/**
* Initialize the midi system.
*/
protected void initMidi ()
{
_mp3player = new MP3Manager(_rmgr, this);
_modplayer = new ModPlayer(_rmgr, this);
_modplayer.setVolume(_musicVol);
try {
Sequencer seq = MidiSystem.getSequencer();
// Sequencer seq = getTritonusSequencer();
seq.open();
if (seq instanceof Synthesizer) {
_midiChannels = ((Synthesizer) seq).getChannels();
//updateMusicVolume();
}
_sequencer = seq;
_sequencer.addMetaEventListener(this);
Log.info("Seq class: " + _sequencer.getClass());
// _sequencer.getTransmitter().setReceiver(new Receiver() {
// public void close ()
// {
// }
//
// public void send (javax.sound.midi.MidiMessage msg, long stamp)
// {
// Log.info("msg : " + msg);
// }
// });
} catch (MidiUnavailableException mue) {
Log.warning("Midi unavailable. Can't play music.");
return;
}
}
protected Sequencer getTritonusSequencer ()
throws MidiUnavailableException
{
MidiDevice.Info[] info = MidiSystem.getMidiDeviceInfo();
for (int ii=0; ii < info.length; ii++) {
if (info[ii].toString().indexOf("Tritonus") != -1) {
MidiDevice dev = MidiSystem.getMidiDevice(info[ii]);
if (dev instanceof Sequencer) {
return (Sequencer) dev;
}
}
}
Log.info("ACK SPUTTER!");
return MidiSystem.getSequencer();
}
/**
* Stop playing and shutdown the midi system.
*/
protected void shutdownMidi ()
{
_sequencer.removeMetaEventListener(this);
stopCurrentSong(false);
_sequencer.close();
if (_receiver != null) {
_receiver.close();
}
_sequencer = null;
_midiChannels = null;
_midiStack.clear();
}
/** /**
* Attempt to modify the music volume for any playing tracks. * Attempt to modify the music volume for any playing tracks.
*/ */
protected void updateMusicVolume () protected void updateMusicVolume ()
{ {
if (_modplayer != null) { if (_musicPlayer != null) {
_modplayer.setVolume(_musicVol); _musicPlayer.setVolume(_musicVol);
}
if (_midiChannels == null) {
Log.warning("Cannot modify music volume!");
} else {
int setting = (int) (_musicVol * 127.0);
for (int ii=0; ii < _midiChannels.length; ii++) {
_midiChannels[ii].controlChange(MIDI_VOLUME_CONTROL, setting);
}
} }
} }
// documentation inherited from interface MetaEventListener // documentation inherited from interface MusicPlayer.MusicEventListener
public void meta (MetaMessage msg) public void musicStopped ()
{ {
// Log.info("meta message: " + msg.getType() + ", msg=" + handleMusicStopped();
// new String(msg.getData())); playTopMusic();
if (msg.getType() == MIDI_END_OF_TRACK) {
songStopEvent();
}
} }
/** protected void shutdownMusic ()
* Submitted by external players like the MP3Manager.
*/
public void songStopEvent ()
{ {
if (_stoppingSong) { if (_musicPlayer != null) {
_stoppingSong = false; _musicPlayer.stop();
} else { _musicPlayer.shutdown();
stopCurrentSong(true);
playTopSong();
} }
} }
@@ -764,7 +594,8 @@ public class SoundManager
} else { } else {
// set it up and put it in the cache // set it up and put it in the cache
AudioInputStream stream = AudioSystem.getAudioInputStream( AudioInputStream stream = AudioSystem.getAudioInputStream(
getResource(key)); new ByteArrayInputStream(StreamUtils.streamAsBytes(
_rmgr.getResource(key.set, key.path), BUFFER_SIZE)));
DataLine.Info dinfo = new DataLine.Info( DataLine.Info dinfo = new DataLine.Info(
Clip.class, stream.getFormat()); Clip.class, stream.getFormat());
info = new ClipInfo(dinfo, stream); info = new ClipInfo(dinfo, stream);
@@ -774,37 +605,6 @@ public class SoundManager
return info; return info;
} }
/**
* Get the midi data for the specified path.
*/
protected InputStream getMidiData (MidiInfo info)
throws IOException
{
InputStream stream = (InputStream) _midiCache.get(info);
if (stream != null) {
// reset the stream for the new user
stream.reset();
} else {
stream = getResource(info);
_midiCache.put(info, stream);
}
return stream;
}
/**
* Get the data specified by the path from the resource bundle.
* No caching is done.
*/
protected InputStream getResource (SoundKey key)
throws IOException
{
return new ByteArrayInputStream(StreamUtils.streamAsBytes(
_rmgr.getResource(key.set, key.path), BUFFER_SIZE));
}
// /** // /**
// * Adjust the volume of this clip. // * Adjust the volume of this clip.
// */ // */
@@ -1042,7 +842,8 @@ public class SoundManager
{ {
if (o instanceof SoundKey) { if (o instanceof SoundKey) {
SoundKey that = (SoundKey) o; SoundKey that = (SoundKey) o;
return (this.path == that.path) && (this.set == that.set); return this.path.equals(that.path) &&
this.set.equals(that.set);
} }
return false; return false;
} }
@@ -1067,18 +868,14 @@ public class SoundManager
} }
/** /**
* A class that tracks the information about our playing midi files. * A class that tracks the information about our playing music files.
*/ */
protected static class MidiInfo extends SoundKey protected static class MusicInfo extends SoundKey
{ {
/** How many times to loop, or -1 for forever. */ /** How many times to loop, or -1 for forever. */
public int loops; public int loops;
/** The position of big loopers, or -1 if none. */ public MusicInfo (String set, String path, int loops)
public long tickPosition = -1;
public long msPosition = -1;
public MidiInfo (String set, String path, int loops)
{ {
super(set, path); super(set, path);
this.loops = loops; this.loops = loops;
@@ -1106,10 +903,6 @@ public class SoundManager
/** The queue of sound clips to be played. */ /** The queue of sound clips to be played. */
protected Queue _queue = new Queue(); protected Queue _queue = new Queue();
/** So that we ignore the stopped meta event when we stop a song
* ourselves. */
protected boolean _stoppingSong = false;
/** Volume levels for both sound clips and music. */ /** Volume levels for both sound clips and music. */
protected float _clipVol = 1f, _musicVol = 1f; protected float _clipVol = 1f, _musicVol = 1f;
@@ -1117,28 +910,14 @@ public class SoundManager
/** The cache of recent audio clips . */ /** The cache of recent audio clips . */
protected LockableLRUHashMap _clipCache = new LockableLRUHashMap(10); protected LockableLRUHashMap _clipCache = new LockableLRUHashMap(10);
/** The cache of recent midi sequences. */
protected LRUHashMap _midiCache = new LRUHashMap(4);
/** The clips that are currently active. */ /** The clips that are currently active. */
protected ArrayList _activeClips = new ArrayList(); protected ArrayList _activeClips = new ArrayList();
/** The sequencer that plays midi music. */
protected Sequencer _sequencer;
protected MP3Manager _mp3player;
protected ModPlayer _modplayer;
/** The receiver used to send midi from the sequencer to an alternate
* midi device. */
protected Receiver _receiver;
/** The channels in the sequencer, which we'll use to fuxor volumes. */
protected MidiChannel[] _midiChannels;
/** The stack of songs that we're playing. */ /** The stack of songs that we're playing. */
protected LinkedList _midiStack = new LinkedList(); protected LinkedList _musicStack = new LinkedList();
/** The current music player, if any. */
protected MusicPlayer _musicPlayer;
/** A set of soundTypes for which sound is enabled. */ /** A set of soundTypes for which sound is enabled. */
protected HashSet _disabledTypes = new HashSet(); protected HashSet _disabledTypes = new HashSet();
@@ -1153,12 +932,6 @@ public class SoundManager
protected Object FLUSH = new Object(); protected Object FLUSH = new Object();
protected Object DIE = new Object(); protected Object DIE = new Object();
/** This is apparently the midi code for end of track. Wack. */
protected static final int MIDI_END_OF_TRACK = 47;
/** The midi control for volume is 7. Ooooooo. */
protected static final int MIDI_VOLUME_CONTROL = 7;
/** The queue size at which we start to ignore requests to play sounds. */ /** The queue size at which we start to ignore requests to play sounds. */
protected static final int MAX_QUEUE_SIZE = 100; protected static final int MAX_QUEUE_SIZE = 100;