/*
* JFugue - API for Music Programming
* Copyright (C) 2003-2008 David Koelle
*
* http://www.jfugue.org
*
* 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.jfugue;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
/**
* Provides a player that can be given new MusicStrings at runtime. The original
* Player class requires that a MusicString be fully formed before sending to
* Player.play(). This class lets you add new bits of a MusicString with the
* add() method. The newly-added patterns are played immediately.
*
* @see Pattern
* @see Player
* @author David Koelle
* @version 3.2
*/
public final class StreamingPlayer {
private Sequencer sequencer;
private StreamingMidiRenderer renderer;
private MusicStringParser parser;
private DurationPatternTool durationPatternTool;
/**
* Instantiates a new Player object, which is used for playing music.
*/
public StreamingPlayer() {
try {
init(MidiSystem.getSequencer());
} catch (MidiUnavailableException e) {
throw new JFugueException(
JFugueException.SEQUENCER_DEVICE_NOT_SUPPORTED_WITH_EXCEPTION
+ e.getMessage());
}
}
/**
* Creates a new StreamingPlayer instance using a Sequencer that you have
* provided.
*
* @param sequencer
* The Sequencer to send the MIDI events
*/
public StreamingPlayer(Sequencer sequencer) {
init(sequencer);
}
/**
* Creates a new StreamingPlayer instance using a Sequencer obtained from
* the Synthesizer that you have provided.
*
* @param synth
* The Synthesizer you want to use for this Player.
*/
public StreamingPlayer(Synthesizer synth) throws MidiUnavailableException {
this(Player.getSequencerConnectedToSynthesizer(synth));
}
private void init(Sequencer sequencer) {
setSequencer(sequencer);
parser = new MusicStringParser();
renderer = new StreamingMidiRenderer();
parser.addParserListener(renderer);
durationPatternTool = new DurationPatternTool();
parser.addParserListener(durationPatternTool);
}
/**
* Closes MIDI resources - be sure to call this after play() has returned.
*/
public void close() {
getSequencer().close();
try {
if (MidiSystem.getSynthesizer() != null) {
MidiSystem.getSynthesizer().close();
}
} catch (MidiUnavailableException e) {
throw new JFugueException(
JFugueException.GENERAL_ERROR + e.getMessage());
}
renderer.close();
}
private void setSequencer(Sequencer sequencer) {
this.sequencer = sequencer;
}
/**
* Returns the sequencer containing the MIDI data from a pattern that has
* been parsed.
*
* @return the Sequencer from the pattern that was recently parsed
*/
public Sequencer getSequencer() {
return this.sequencer;
}
/**
* Streams a Pattern containing a single token. Does not wait for the music
* to stop playing - the user will need to throttle calls to stream().
*
* @param singleToken
*/
public void stream(Pattern singleToken) {
parser.parse(singleToken);
}
/**
* Streams a MusicString containing a single token. Does not wait for the
* music to stop playing - the user will need to throttle calls to stream().
*
* @param singleToken
*/
public void stream(String singleToken) {
stream(new Pattern(singleToken));
}
/**
* Streams a Pattern containing one or more tokens, and waits for each token
* to finish playing before returning. NOTE: The timing logic is a bit off
* TODO: The timing logic in streamAndWait() needs to be fixed
*
* @param fragment
*/
public void streamAndWait(Pattern fragment) {
String[] tokens = fragment.getTokens();
for (String token : tokens) {
durationPatternTool.reset();
Pattern pattern = new Pattern(token);
parser.parse(pattern);
long duration = durationPatternTool.getDuration();
try {
System.out.println(duration);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* Streams a MusicString containing one or more tokens, and waits for each
* token to finish playing before returning. NOTE: The timing logic is a bit
* off
*
* @param fragment
*/
public void streamAndWait(String fragment) {
streamAndWait(new Pattern(fragment));
}
}