/*
* JFugue, an Application Programming Interface (API) for Music Programming
* http://www.jfugue.org
*
* Copyright (C) 2003-2014 David Koelle
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jfugue.realtime;
import org.jfugue.midi.MidiDictionary;
import org.jfugue.midi.MidiTools;
import org.jfugue.pattern.PatternProducer;
import org.jfugue.player.SynthesizerManager;
import org.jfugue.theory.Chord;
import org.jfugue.theory.Note;
import org.staccato.StaccatoParser;
import jp.kshoji.javax.sound.midi.MidiChannel;
import jp.kshoji.javax.sound.midi.MidiUnavailableException;
import jp.kshoji.javax.sound.midi.Synthesizer;
/**
* This player sends messages directly to the MIDI Synthesizer, rather than creating a
* sequence with the MIDI Sequencer.
*
* There are two ways that you can send messages to RealTimePlayer, and you can freely intermix these:
* 1. Pass any Staccato string to the play() method. In this case, start notes should be indicated as the start of a tie
* (e.g., "C4s-") and stop notes should be indicated as the end of a tie (e.g., "C4-s")
* 2. Call specific methods, like startNote or changeInstrument
*/
public class RealtimePlayer
{
private Synthesizer synth;
private MidiChannel[] channels;
private int currentChannel;
private StaccatoParser staccatoParser;
private RealtimeMidiParserListener rtMidiParserListener;
public RealtimePlayer() throws MidiUnavailableException {
this.synth = SynthesizerManager.getInstance().getSynthesizer();
this.synth.open();
this.channels = this.synth.getChannels();
staccatoParser = new StaccatoParser();
rtMidiParserListener = new RealtimeMidiParserListener(this);
staccatoParser.addParserListener(rtMidiParserListener);
}
public void play(PatternProducer pattern) {
staccatoParser.parse(pattern);
}
public void play(String pattern) {
staccatoParser.parse(pattern);
}
protected MidiChannel getCurrentChannel() {
return this.channels[this.currentChannel];
}
public long getCurrentTime() {
return rtMidiParserListener.getCurrentTime();
}
public void schedule(long timeInMillis, ScheduledEvent event) {
rtMidiParserListener.onEventScheduled(timeInMillis, event);
}
public void unschedule(long timeInMillis, ScheduledEvent event) {
rtMidiParserListener.onEventUnscheduled(timeInMillis, event);
}
public void startNote(Note note) {
getCurrentChannel().noteOn(note.getValue(), note.getOnVelocity());
}
public void stopNote(Note note) {
getCurrentChannel().noteOff(note.getValue(), note.getOffVelocity());
}
public void startChord(Chord chord) {
for (Note note : chord.getNotes()) {
startNote(note);
}
}
public void stopChord(Chord chord) {
for (Note note : chord.getNotes()) {
stopNote(note);
}
}
public void startInterpolator(RealtimeInterpolator interpolator, long durationInMillis) {
rtMidiParserListener.onInterpolatorStarted(interpolator, durationInMillis);
}
public void stopInterpolator(RealtimeInterpolator interpolator) {
rtMidiParserListener.onInterpolatorStopping(interpolator);
}
public void changeInstrument(int newInstrument) {
getCurrentChannel().programChange(newInstrument);
}
public void changeInstrument(String newInstrument) {
getCurrentChannel().programChange(MidiDictionary.INSTRUMENT_STRING_TO_BYTE.get(newInstrument.toUpperCase()));
}
public void changeTrack(int newTrack) {
this.currentChannel = newTrack;
}
public void setPitchBend(int pitch) {
setPitchBend(MidiTools.getLSB(pitch), MidiTools.getMSB(pitch));
}
public void setPitchBend(byte lsb, byte msb) {
getCurrentChannel().setPitchBend(lsb + (msb << 7));
}
public void changeChannelPressure(byte pressure) {
getCurrentChannel().setChannelPressure(pressure);
}
public void changePolyphonicPressure(byte key, byte pressure) {
getCurrentChannel().setPolyPressure(key, pressure);
}
public void changeController(byte controller, byte value) {
getCurrentChannel().controlChange(controller, value);
}
public void close() {
for (MidiChannel channel : channels) {
channel.allNotesOff();
}
rtMidiParserListener.finish();
}
}