jfugue/0000755000175000017500000000000011644146261012556 5ustar giovannigiovannijfugue/org/0000755000175000017500000000000010724373466013354 5ustar giovannigiovannijfugue/org/jfugue/0000755000175000017500000000000010776766040014642 5ustar giovannigiovannijfugue/org/jfugue/Layer.java0000644000175000017500000000521711005706240016544 0ustar giovannigiovanni/* * 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; /** * Represents layer changes. A Layer allows multiple sounds to be played at the same * time on a single track (also known as a voice), without those notes being specified * as a chord. This is particularly helpful when sing Track 9, the percussion track, * so multiple percussion sounds can occur at the same time. * *@author David Koelle *@version 3.0 */ public final class Layer implements JFugueElement { private byte layer; /** * Creates a new Layer object, with the specified layer number. * @param layer the number of the layer to use */ public Layer(byte layer) { setLayer(layer); } /** * Sets the value of the layer for this object. * @param layer the number of the layer to use */ public void setLayer(byte layer) { this.layer = layer; } /** * Returns the layer used in this object * @return the layer used in this object */ public byte getLayer() { return layer; } /** * Returns the Music String representing this element and all of its settings. * For a Layer object, the Music String is Llayer-number * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("L"); buffy.append(getLayer()); return buffy.toString(); } /** * Returns verification string in this format: * Layer: layer={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("Layer: layer="); buffy.append(getLayer()); return buffy.toString(); } }jfugue/org/jfugue/MusicStringRenderer.java0000644000175000017500000000626511067530674021450 0ustar giovannigiovanni/* * 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; /** * This class is used to build a Pattern (i.e., a MusicString) given a MIDI Sequence. * *@author David Koelle *@version 3.0 */ public final class MusicStringRenderer implements ParserListener { private Pattern pattern; public MusicStringRenderer() { pattern = new Pattern(); } public Pattern getPattern() { return this.pattern; } public void voiceEvent(Voice voice) { pattern.add(voice.getMusicString()); } public void instrumentEvent(Instrument instrument) { pattern.add(instrument.getMusicString()); } public void tempoEvent(Tempo tempo) { pattern.add(tempo.getMusicString()); } public void layerEvent(Layer layer) { pattern.add(layer.getMusicString()); } public void timeEvent(Time time) { pattern.add(time.getMusicString()); } public void keySignatureEvent(KeySignature keySig) { pattern.add(keySig.getMusicString()); } public void measureEvent(Measure measure) { pattern.add(measure.getMusicString()); } public void controllerEvent(Controller controller) { pattern.add(controller.getMusicString()); } public void channelPressureEvent(ChannelPressure channelPressure) { pattern.add(channelPressure.getMusicString()); } public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { pattern.add(polyphonicPressure.getMusicString()); } public void pitchBendEvent(PitchBend pitchBend) { pattern.add(pitchBend.getMusicString()); } public void noteEvent(Note note) { // Don't use add(note.getMusicString(), because that will incorrectly // add a space between sequential or parallel notes. // Don't add notes that have 0 duration - these indicate a note that // is triggered, but has no duration (TODO: Don't special-case this // in the future... maybe add a new noteEvent for notePressed?) if (note.getDuration() > 0) { pattern.addElement(note); } } public void sequentialNoteEvent(Note note) { // We won't get these events from a MIDI parser } public void parallelNoteEvent(Note note) { // We won't get these events from a MIDI parser } } jfugue/org/jfugue/MidiRenderer.java0000644000175000017500000001636411067761542020064 0ustar giovannigiovanni/* * 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.MetaMessage; import javax.sound.midi.Sequence; import javax.sound.midi.ShortMessage; /** * This class takes a Pattern, and turns it into wonderful music. * *

* Playing music is only one thing that can be done by rendering a pattern. * You could also create your own renderer that draws sheet music based on * a pattern. Or, you could create a graphical light show based on the musical * notes in the pattern. *

* *

* This was named Renderer in previous versions of JFugue. The name has been * changed to differentiate it from other types of renderers. *

* *@author David Koelle *@version 2.0 *@version 3.0 - Renderer renamed to MidiRenderer */ public final class MidiRenderer extends ParserListenerAdapter { private MidiEventManager eventManager; long initialNoteTime = 0; private float sequenceTiming; private int resolution; /** * Instantiates a Renderer */ public MidiRenderer(float sequenceTiming, int resolution) { reset(sequenceTiming, resolution); } /** * Creates a new MidiEventManager. If this isn't called, * events from multiple calls to render() will be added * to the same eventManager, which means that the second * time render() is called, it will contain music left over * from the first time it was called. (This wasn't a problem * with Java 1.4) * @since 3.0 */ public void reset(float sequenceTiming, int resolution) { this.sequenceTiming = sequenceTiming; this.resolution = resolution; this.eventManager = new MidiEventManager(sequenceTiming, resolution); } /** * Creates a new MidiEventManager using the sequenceTiming and * resolution already used to create this MidiRenderer. If this * isn't called, events from multiple calls to render() will be * added to the same eventManager, which means that the second * time render() is called, it will contain music left over * from the first time it was called. (This wasn't a problem * with Java 1.4) * @since 3.2 */ public void reset() { this.eventManager = new MidiEventManager(this.sequenceTiming, this.resolution); } /** * Returns the last sequence generated by this renderer */ public Sequence getSequence() { return this.eventManager.getSequence(); } // ParserListener methods //////////////////////////// public void voiceEvent(Voice voice) { this.eventManager.setCurrentTrack(voice.getVoice()); } public void tempoEvent(Tempo tempo) { byte[] threeTempoBytes = TimeFactor.convertToThreeTempoBytes(tempo.getTempo()); this.eventManager.addMetaMessage(0x51, threeTempoBytes); } public void instrumentEvent(Instrument instrument) { this.eventManager.addEvent(ShortMessage.PROGRAM_CHANGE, instrument.getInstrument(), 0); } public void layerEvent(Layer layer) { this.eventManager.setCurrentLayer(layer.getLayer()); } public void timeEvent(Time time) { this.eventManager.setTrackTimer(time.getTime()); } public void measureEvent(Measure measure) { // No MIDI is generated when a measure indicator is identified. } public void keySignatureEvent(KeySignature keySig) { this.eventManager.addMetaMessage(0x59, new byte[] { keySig.getKeySig(), keySig.getScale() }); } public void controllerEvent(Controller controller) { this.eventManager.addEvent(ShortMessage.CONTROL_CHANGE, controller.getIndex(), controller.getValue()); } public void channelPressureEvent(ChannelPressure channelPressure) { this.eventManager.addEvent(ShortMessage.CHANNEL_PRESSURE, channelPressure.getPressure()); } public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { this.eventManager.addEvent(ShortMessage.POLY_PRESSURE, polyphonicPressure.getKey(), polyphonicPressure.getPressure()); } public void pitchBendEvent(PitchBend pitchBend) { this.eventManager.addEvent(ShortMessage.PITCH_BEND, pitchBend.getBend()[0], pitchBend.getBend()[1]); } public void noteEvent(Note note) { // Remember the current track time, so we can flip back to it // if there are other notes to play in parallel this.initialNoteTime = this.eventManager.getTrackTimer(); long duration = note.getDuration(); // If there is no duration, don't add this note to the event manager // TODO: This is a special case as of v4.0.3 that should be re-thought if a new noteEvent callback is created in v5.0 if (duration == 0) { return; } // Add messages to the track if (note.isRest()) { this.eventManager.advanceTrackTimer(duration); } else { initialNoteTime = eventManager.getTrackTimer(); byte attackVelocity = note.getAttackVelocity(); byte decayVelocity = note.getDecayVelocity(); this.eventManager.addNoteEvent(note.getValue(), attackVelocity, decayVelocity, duration, !note.isEndOfTie(), !note.isStartOfTie()); } } public void sequentialNoteEvent(Note note) { long duration = note.getDuration(); if (note.isRest()) { this.eventManager.advanceTrackTimer(duration); } else { byte attackVelocity = note.getAttackVelocity(); byte decayVelocity = note.getDecayVelocity(); this.eventManager.addNoteEvent(note.getValue(), attackVelocity, decayVelocity, duration, !note.isEndOfTie(), !note.isStartOfTie()); } } public void parallelNoteEvent(Note note) { long duration = note.getDuration(); this.eventManager.setTrackTimer(this.initialNoteTime); if (note.isRest()) { this.eventManager.advanceTrackTimer(duration); } else { byte attackVelocity = note.getAttackVelocity(); byte decayVelocity = note.getDecayVelocity(); this.eventManager.addNoteEvent(note.getValue(), attackVelocity, decayVelocity, duration, !note.isEndOfTie(), !note.isStartOfTie()); } } } jfugue/org/jfugue/StreamingMidiEventManager.java0000644000175000017500000002306211001736656022531 0ustar giovannigiovanni/* * JFugue - API for Music Programming * Copyright (C) 2003-2007 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 java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.sound.midi.MidiChannel; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.ShortMessage; import javax.sound.midi.Synthesizer; /** * Assists the StreamingMidiRenderer in converting Patterns to MIDI. * *@see StreamingPlayer *@author David Koelle *@version 3.2 */ public class StreamingMidiEventManager { private final int CHANNELS = 16; private final int LAYERS = 16; private byte currentTrack = 0; private byte[] currentLayer = new byte[CHANNELS]; private long time[][] = new long[CHANNELS][LAYERS]; private MidiChannel channels[] = new MidiChannel[CHANNELS]; private Map> timerMap; private long currentTime; private boolean isActive; public StreamingMidiEventManager() { timerMap = new HashMap>(); isActive = true; currentTime = System.currentTimeMillis(); Thread timerThread = new Thread( new Runnable() { public void run() { while (isActive) { long checkTime = System.currentTimeMillis(); if (checkTime != currentTime) { long tempBackTime = currentTime; currentTime = System.currentTimeMillis(); // Do this again to get the most up-to-date time // Get any TimerEvents that may have happened in the intervening time, and execute them for (long time = tempBackTime; time < currentTime; time++) { List timerEvents = timerMap.get(time); if (null != timerEvents) { for (NoteOffTimerEvent event : timerEvents) { channels[event.track].noteOff(event.noteValue, event.decayVelocity); } } timerMap.put(time, null); } } try { Thread.sleep(20); // Don't hog the CPU } catch (InterruptedException e) { throw new JFugueException(JFugueException.ERROR_SLEEP); } } } } ); timerThread.start(); try { Synthesizer synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); channels = synthesizer.getChannels(); } catch (MidiUnavailableException e) { throw new JFugueException(JFugueException.ERROR_PLAYING_MUSIC); } for (int i=0; i < CHANNELS; i++) { for (int u=0; u < LAYERS; u++) { time[i][u] = 0; } currentLayer[i] = 0; } currentTrack = 0; } public void close() { isActive = false; } /** * Sets the current track, or channel, to which new events will be added. * @param track the track to select */ public void setCurrentTrack(byte track) { currentTrack = track; } /** * Sets the current layer within the track to which new events will be added. * @param track the track to select */ public void setCurrentLayer(byte layer) { currentLayer[currentTrack] = layer; } /** * Advances the timer for the current track by the specified duration, * which is specified in Pulses Per Quarter (PPQ) * @param duration the duration to increase the track timer */ public void advanceTrackTimer(long duration) { time[currentTrack][currentLayer[currentTrack]] += duration; } /** * Sets the timer for the current track by the given time, * which is specified in Pulses Per Quarter (PPQ) * @param newTime the time at which to set the track timer */ public void setTrackTimer(long newTime) { time[currentTrack][currentLayer[currentTrack]] = newTime; } /** * Returns the timer for the current track. * @return the timer value for the current track, specified in Pulses Per Quarter (PPQ) */ public long getTrackTimer() { return time[currentTrack][currentLayer[currentTrack]]; } /** * Adds a MetaMessage to the current track. * * @param command the MIDI command represented by this message * @param data1 the first data byte * @param data2 the second data byte */ public void addMetaMessage(int type, byte[] bytes) { // NOP } /** * Adds a MIDI event to the current track. * * @param command the MIDI command represented by this message * @param data1 the first data byte */ public void addEvent(int command, int data1) { addEvent(command, data1, 0); } /** * Adds a MIDI event to the current track. * * @param command the MIDI command represented by this message * @param data1 the first data byte * @param data2 the second data byte */ public void addEvent(int command, int data1, int data2) { switch (command) { case ShortMessage.PROGRAM_CHANGE: channels[currentTrack].programChange(data1); break; case ShortMessage.CONTROL_CHANGE: channels[currentTrack].controlChange(data1, data2); break; case ShortMessage.CHANNEL_PRESSURE: channels[currentTrack].setChannelPressure(data1); break; case ShortMessage.POLY_PRESSURE: channels[currentTrack].setPolyPressure(data1, data2); break; case ShortMessage.PITCH_BEND: channels[currentTrack].setPitchBend(data1); break; default: break; } } /** * Adds a ShortMessage.NOTE_ON event to the current track, using attack and * decay velocity values. Also adds a ShortMessage.NOTE_OFF command for * the note, using the duration parameter to space the NOTE_OFF command properly. * * Both the NOTE_ON and NOTE_OFF events can be suppressed. This is useful * when notes are tied to other notes. * * @param data1 the first data byte, which contains the note value * @param data2 the second data byte for the NOTE_ON event, which contains the attack velocity * @param data3 the second data byte for the NOTE_OFF event, which contains the decay velocity * @param duration the duration of the note * @param addNoteOn whether a ShortMessage.NOTE_ON event should be created for for this event. For the end of a tied note, this should be false; otherwise it should be true. * @param addNoteOff whether a ShortMessage.NOTE_OFF event should be created for for this event. For the start of a tied note, this should be false; otherwise it should be true. */ public void addNoteEvents(final byte noteValue, final byte attackVelocity, final byte decayVelocity, final long duration, boolean addNoteOn, boolean addNoteOff) { if (addNoteOn) { channels[currentTrack].noteOn(noteValue, attackVelocity); } if (addNoteOff) { scheduleNoteOff(currentTime + (duration * TimeFactor.QUARTER_DURATIONS_IN_WHOLE), currentTrack, noteValue, decayVelocity); } } private void scheduleNoteOff(long when, byte track, byte noteValue, byte theWaxTadpole) { List timerEvents = timerMap.get(when*5); if (null == timerEvents) { timerEvents = new ArrayList(); } timerEvents.add(new NoteOffTimerEvent(track, noteValue, theWaxTadpole)); timerMap.put(when, timerEvents); } class NoteOffTimerEvent { public byte track; public byte noteValue; public byte decayVelocity; public NoteOffTimerEvent(byte track, byte noteValue, byte decayVelocity) { this.track = track; this.noteValue = noteValue; this.decayVelocity = decayVelocity; } } } jfugue/org/jfugue/PatternListener.java0000644000175000017500000000302410776766112020627 0ustar giovannigiovanni/* * 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 java.util.EventListener; /** * Classes that implement PatternListener and add themselves as listeners * to a Pattern object will receive events when new * fragments are added to a Pattern. This is mostly intended * to be used by the Player for handling streaming music. * @see Pattern * @see Player * *@author David Koelle *@version 3.0 */ public interface PatternListener extends EventListener { /** * Called when a new fragment has been added to a pattern * @param pattern the fragment that has been added */ public void fragmentAdded(Pattern fragment); } jfugue/org/jfugue/MusicXmlParser.java0000644000175000017500000007256011000430556020412 0ustar giovannigiovanni/* * 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 java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Reader; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Map; import nu.xom.Attribute; import nu.xom.Builder; import nu.xom.DocType; import nu.xom.Document; import nu.xom.Element; import nu.xom.Elements; import nu.xom.Node; import nu.xom.ParsingException; import nu.xom.ValidityException; // helper class class XMLpart extends Object { public String ID; public String part_name; public String score_instruments; public String midi_instruments; // channel1|name1~channel2|name2 public XMLpart() { ID = ""; part_name = ""; score_instruments = ""; midi_instruments = ""; } }; /** * voiceDef * MusicString voice can be a combination of part and voice */ class voiceDef { int part; int voice; } /** * Parses a MusicXML file, and fires events for ParserListener interfaces * when tokens are interpreted. The ParserListener does intelligent things * with the resulting events, such as create music, draw sheet music, or * transform the data. * * As of Version 3.0, the Parser supports turning MIDI Sequences into JFugue Patterns with the parse(Sequence) * method. In this case, the ParserListeners established by a ParserBuilder use the parsed * events to construct the Pattern string. * * MusicXmlParser.parse can be called with a file name, File, InputStream, or Reader * * @author E.Philip Sobolik * */ public final class MusicXmlParser extends Parser { private Map dictionaryMap; private Builder xomBuilder; private Document xomDoc; private String[] volumes = {"pppppp", "ppppp", "pppp", "ppp", "pp", "p", "mp", "mf", "f", "ff", "fff", "ffff", "fffff", "ffffff"}; // note difference between maxVolume and minVolume should be divisible by 13 private byte minVelocity = 10; private byte maxVelocity = 127; private byte curVelocity = Note.DEFAULT_VELOCITY; private byte beats; // beats per measure private byte divisions; // divisions per beat private int curVoice; // current voice private int nextVoice; // next available voice # for a new voice private voiceDef[] voices; // private double totalMeasurePct; // private double lastNoteInMeasureDuration; // adjusted duration of the // last note in the measure public MusicXmlParser() { xomBuilder = new Builder(); dictionaryMap = new HashMap(); JFugueDefinitions.populateDictionary(dictionaryMap); beats = 1; divisions = 1; curVoice = -1; nextVoice = 0; voices = new voiceDef[15]; // totalMeasurePct = 0.; // lastNoteInMeasureDuration = 0.f; } public void parse(String musicXmlString) { try { xomDoc = xomBuilder.build(musicXmlString, (String)null); // URI is null } catch (ValidityException e) { e.printStackTrace(); } catch (ParsingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } parse(); } public void parse(File fileXMLin) { try { xomDoc = xomBuilder.build(fileXMLin); } catch (ValidityException e) { e.printStackTrace(); } catch (ParsingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } parse(); } public void parse(FileInputStream fisXMLin) { try { xomDoc = xomBuilder.build(fisXMLin); } catch (ValidityException e) { e.printStackTrace(); } catch (ParsingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } parse(); } public void parse(Reader rXMLin) { try { xomDoc = xomBuilder.build(rXMLin); } catch (ValidityException e) { e.printStackTrace(); } catch (ParsingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } parse(); } ///////////////////////////////////////////////////////////////////////// // Tempo methods // /** The default value for the Tempo. */ private int tempo = 120; /** * Sets the tempo for the current song. Tempo is measured in "pulses per quarter". * The parser uses this value to convert note durations, which are relative values and * not directly related to time measurements, into actual times. For example, a whole * note has the same duration as four quarter notes, but neither a whole note nor a * quarter note equates to any real-life time delay until it's multplied by the tempo. * * The default value for Tempo is 120 pulses per quarter. * * @param tempo the tempo for the current song, in pulses per quarter. */ protected void setTempo(int tempo) { this.tempo = tempo; } /** * Returns the tempo for the current song. */ protected int getTempo() { return this.tempo; } // // End Tempo methods ///////////////////////////////////////////////////////////////////////// /** * Parses a MusicXML file and fires events to subscribed ParserListener * interfaces. As the file is parsed, events are sent * to ParserLisener interfaces, which are responsible for doing * something interesting with the music data, such as playing the music, * displaying it as sheet music, or transforming the pattern. * * the input is a XOM Document, which has been built previously * @throws Exception if there is an error parsing the pattern */ public void parse() throws JFugueException { DocType docType = xomDoc.getDocType(); Element root = xomDoc.getRootElement(); if (docType.getRootElementName().compareToIgnoreCase("score-partwise") == 0) { Element partlist = root.getFirstChildElement("part-list"); Elements parts = partlist.getChildElements(); XMLpart[] partHeaders = new XMLpart[parts.size()]; for (int p = 0; p < parts.size(); ++p) { partHeaders[p] = new XMLpart(); parsePartHeader(parts.get(p), partHeaders[p]); } parts = root.getChildElements("part"); for (int p = 0; p < parts.size(); ++p) { parsePart(p, parts.get(p), partHeaders); } } } /** * Parses a part element in the part-list section * @param part is the part element * @param partHeader is the array of XMLpart classes that stores * the part-list elements */ private void parsePartHeader(Element part, XMLpart partHeader) { // ID Attribute ID = part.getAttribute("id"); // may be changed by midi-instrument below partHeader.ID = ID.getValue(); // part-name Element partName = part.getFirstChildElement("part-name"); partHeader.part_name = partName.getValue(); // may or may not have 1 or more score-instrument and // midi-instrument elements // score-instruments int x; Elements scoreInsts = part.getChildElements("score-instrument"); for (x = 0; x < scoreInsts.size(); ++x ) { partHeader.score_instruments += scoreInsts.get(x).getValue(); if (x < scoreInsts.size()-1) partHeader.score_instruments += "~"; } // midi-instruments Elements midiInsts = part.getChildElements("midi-instrument"); for (x = 0; x < midiInsts.size(); ++x ) { Element midi_instrument = midiInsts.get(x); Element midi_channel = midi_instrument.getFirstChildElement("midi-channel"); String midiChannel = (midi_channel == null) ? "" : midi_channel.getValue(); if (midiChannel.length() > 0) { partHeader.midi_instruments += midiChannel; partHeader.midi_instruments += "|"; } Element midi_inst = midi_instrument.getFirstChildElement("midi-name"); String midiInst = (midi_inst == null) ? "" : midi_inst.getValue(); if (midiInst.length() < 1) { Element midi_bank = midi_instrument.getFirstChildElement("midi-bank"); midiInst = (midi_bank == null) ? "" : midi_bank.getValue(); if (midiInst.length() < 1) { Element midi_program = midi_instrument.getFirstChildElement("program"); midiInst = (midi_program == null) ? "" : midi_program.getValue(); } } partHeader.midi_instruments += midiInst; if (x < midiInsts.size()-1) partHeader.midi_instruments += "~"; } } /** * Parses a part and fires all the appropriate note events * @param part is the entire part * @param partHeaders is the array of XMLpart classes that contains * instrument info for the parts */ private void parsePart(int p, Element part, XMLpart[] partHeaders) { for (int x = 0; x < partHeaders.length; ++x) { if (part.getAttribute("id").getValue().equals(partHeaders[x].ID)) { if (partHeaders[x].midi_instruments.length() < 1) { parseVoice(p, x); parsePartElementInstruments(p, partHeaders[x].part_name); } else { parsePartElementInstruments(p, partHeaders[x].midi_instruments); } } } Elements measures = part.getChildElements("measure"); for (int m = 0; m < measures.size(); ++m) { Element measure = measures.get(m); Element attributes = measure.getFirstChildElement("attributes"); if (attributes != null) { // default key = Cmaj byte key = 0, scale = 0; // scale 0 = minor, 1 = major Element attr = attributes.getFirstChildElement("key"); if (attr != null) { Element eKey = attr.getFirstChildElement("fifths"); if (eKey != null) key = Byte.parseByte(eKey.getValue()); Element eMode = attr.getFirstChildElement("mode"); if (eMode != null) { String mode = eMode.getValue(); if (mode.compareToIgnoreCase("major") == 0) scale = 0; else if (mode.compareToIgnoreCase("minor") == 0) scale = 1; else throw new JFugueException(JFugueException.KEYSIG_EXC, mode); } } else scale = 0; // default = major fireKeySignatureEvent(new KeySignature(key, scale)); // divisions and beats used to calculate duration when note type not present Element element_divisions = attributes.getFirstChildElement("divisions"); if (element_divisions != null) this.divisions = Byte.valueOf(element_divisions.getValue()); Element element_time = attributes.getFirstChildElement("time"); if (element_time != null) { Element element_beats = element_time.getFirstChildElement("beats"); if (element_beats != null) this.beats = Byte.valueOf(element_beats.getValue()); } } // tempo Element direction = measure.getFirstChildElement("direction"); if (direction != null) { Element directionType = direction.getFirstChildElement("direction-type"); if (directionType != null) { Element metronome = directionType.getFirstChildElement("metronome"); if (metronome != null) { Element beatUnit = metronome.getFirstChildElement("beat-unit"); String sBeatUnit = beatUnit.getValue(); if (sBeatUnit.compareToIgnoreCase("quarter") != 0) throw new JFugueException(JFugueException.BEAT_UNIT_MUST_BE_QUARTER, sBeatUnit); Element bpm = metronome.getFirstChildElement("per-minute"); if (bpm != null) { this.setTempo(BPMtoPPM(Float.parseFloat(bpm.getValue()))); fireTempoEvent(new Tempo(this.getTempo())); } } } } // notes Elements notes = measure.getChildElements("note"); // totalMeasurePct = 0.f; for (int n = 0; n < notes.size(); ++n) parseNote(p, notes.get(n)); /* attempt to adjust for rounding errors with un-supported durations // if the total length of all the notes doesn't equal a full measure, // add a pad rest float minDif = (1.f / (beats * divisions)); double padDur = (1. - totalMeasurePct); if (padDur > minDif) { Note pad = new Note(); pad.setDecimalDuration(padDur); pad.setRest(true); fireNoteEvent(pad); } */ fireMeasureEvent(new Measure()); } // end of measure } /** * parses MusicXML note Element * @param note is the note Element to parse */ private void parseNote(int p, Element note) { Note newNote = new Note(); boolean isRest = false; boolean isStartOfTie = false; boolean isEndOfTie = false; byte noteNumber = 0; byte octaveNumber = 0; // long durationNumber = 0; double decimalDuration; // skip grace notes if (note.getFirstChildElement("grace") != null) return; Element voice = note.getFirstChildElement("voice"); if (voice != null) parseVoice(p, Integer.parseInt(voice.getValue())); Element pitch = note.getFirstChildElement("pitch"); if (pitch != null) { String sStep = pitch.getFirstChildElement("step").getValue(); switch(sStep.charAt(0)) { case 'C': noteNumber = 0; break; case 'D': noteNumber = 2; break; case 'E': noteNumber = 4; break; case 'F': noteNumber = 5; break; case 'G': noteNumber = 7; break; case 'A': noteNumber = 9; break; case 'B': noteNumber = 11; break; } Element Alter = pitch.getFirstChildElement("alter"); if (Alter != null) { String sAlter = Alter.getValue(); if (sAlter != null) { noteNumber += Integer.parseInt(sAlter); if (noteNumber > 11) noteNumber = 0; else if (noteNumber < 0) noteNumber = 11; } } Element Octave = pitch.getFirstChildElement("octave"); if (Octave != null) { String sOctave = Octave.getValue(); if (sOctave != null) octaveNumber = Byte.parseByte(sOctave); } // Compute the actual note number, based on octave and note int intNoteNumber = (octaveNumber * 12) + noteNumber; if ( intNoteNumber > 127) { throw new JFugueException(JFugueException.NOTE_OCTAVE_EXC,"", Integer.toString(intNoteNumber)); } noteNumber = (byte)intNoteNumber; } else isRest = true; // duration // Element type = note.getFirstChildElement("type"); // if (type == null) { // get duration from duration element rather than type element Element element_duration = note.getFirstChildElement("duration"); decimalDuration = (element_duration == null) ? beats * divisions : Double.parseDouble(element_duration.getValue()) / (beats * divisions); } /* else { String sDuration = type.getValue(); if (sDuration.compareToIgnoreCase("whole") == 0) durationNumber = 1; else if (sDuration.compareToIgnoreCase("half") == 0 durationNumber = 2; else if (sDuration.compareToIgnoreCase("quarter") == 0) durationNumber = 4; else if (sDuration.compareToIgnoreCase("eighth") == 0) durationNumber = 8; else if (sDuration.compareToIgnoreCase("16th") == 0) durationNumber = 16; else if (sDuration.compareToIgnoreCase("32nd") == 0) durationNumber = 32; else if (sDuration.compareToIgnoreCase("64th") == 0) durationNumber = 64; else throw new JFugueException(JFugueException.NOTE_DURATION_EXC, "", sDuration); decimalDuration = 1.0 / durationNumber; Element element_dot = note.getFirstChildElement("dot"); if (element_dot != null) decimalDuration *= 1.5; } */ // Tempo is in PPQ (Pulses Per Quarter). Turn that into // "PPW", then multiply that by durationNumber for WHQITXN notes double PPW = (double)this.getTempo() * 4.0; // 4 quarter notes in a whole note long duration = (long)(PPW * decimalDuration); Element notations = note.getFirstChildElement("notations"); if (notations != null) { // ties Element tied = notations.getFirstChildElement("tied"); if (tied != null) { Attribute tiedType = tied.getAttribute("type"); { String sTiedType = tiedType.getValue(); if (sTiedType.compareToIgnoreCase("start") == 0) isStartOfTie = true; else if (sTiedType.compareToIgnoreCase("end") == 0) isEndOfTie = true; } } // velocity Element dynamics = notations.getFirstChildElement("dynamics"); if (dynamics != null) { Node dynamic = dynamics.getChild(0); if (dynamic != null) { for (int x = 0; x < this.volumes.length; ++x) { if (dynamic.getValue().compareToIgnoreCase(this.volumes[x]) == 0) { this.curVelocity = (byte)(((this.maxVelocity - this.minVelocity) / (this.volumes.length - 1)) * x); } } } } } byte attackVelocity = this.curVelocity; byte decayVelocity = this.curVelocity; // Set up the note if (isRest) { newNote.setRest(true); newNote.setDuration(duration); newNote.setAttackVelocity( (byte)0 ); // turn off sound for rest notes newNote.setDecayVelocity( (byte)0 ); } else { newNote.setValue(noteNumber); newNote.setDuration(duration); newNote.setStartOfTie(isStartOfTie); newNote.setEndOfTie(isEndOfTie); newNote.setAttackVelocity(attackVelocity); newNote.setDecayVelocity(decayVelocity); } // ToDo - SEQUENTIAL Element element_chord = note.getFirstChildElement("chord"); newNote.setType( (element_chord == null) ? Note.FIRST : Note.PARALLEL); /* attempt to adjust for rounding errors in non-supported durations if (newNote.getType() == Note.FIRST) { if ((totalMeasurePct + decimalDuration) > 1.) { decimalDuration = 1. - totalMeasurePct; lastNoteInMeasureDuration = decimalDuration; totalMeasurePct = 1.; } else { float minDif = (1.f / (beats * divisions)); if (1. - (totalMeasurePct + decimalDuration) < minDif) { decimalDuration = (1. - totalMeasurePct); totalMeasurePct = 1.; } else totalMeasurePct += decimalDuration; } } else if (totalMeasurePct == 1.) // just did a last note in measure decimalDuration = lastNoteInMeasureDuration; */ newNote.setDecimalDuration(decimalDuration); fireNoteEvent(newNote); } /** * Looks up a string's value in the dictionary. The dictionary is used to * keep memorable names of obscure numbers - for example, the string FLUTE * is set to a value of 73, so when users want to play music with a flute, * they can say "I[Flute]" instead of "I[73]". * *

* The Dictionary feature also lets users define constants so that if the * value of something were to change, it only needs to be changed in one * place. For example, MY_FAVORITE_INSTRUMENT could be set to 73, then you * can say "I[My_Favorite_Instrument]" when you want to play with that * instrument. If your favorite instrument were ever to change, you only * have to make the change in one place, instead of every place where you * give the Instrument command. *

* * @param bracketedString the string to look up in the dictionary * @returns the definition of the string * @throws JFugueException if there is a problem looking up bracketedString */ private String dictionaryLookup(String bracketedString) throws JFugueException { int indexOfOpeningBracket = bracketedString.indexOf("["); int indexOfClosingBracket = bracketedString.indexOf("]"); String word = null; if ((indexOfOpeningBracket != -1) && (indexOfClosingBracket != -1)) { word = bracketedString.substring(indexOfOpeningBracket+1,indexOfClosingBracket); } else { // It appears that "bracketedString" wasn't bracketed. word = bracketedString; } word = word.toUpperCase(); String definition = (String)dictionaryMap.get(word); while ((definition != null) && (dictionaryMap.containsKey(definition.toUpperCase()))) { definition = (String)dictionaryMap.get(definition.toUpperCase()); } // If there is no definition for this word, see if the word is actually a number. if (null == definition) { char ch = 0; boolean isNumber = true; for (int i=0; i < word.length(); i++) { ch = word.charAt(i); if ((!Character.isDigit(ch) && (ch != '.'))) { isNumber = false; } } if (isNumber) { trace("Dictionary lookup returning the number ",word); return word; } else { //throw new JFugueException(JFugueException.WORD_NOT_DEFINED_EXC,word,bracketedString); definition = ""; } } trace("Word ",word," is defined as ",definition); return definition; } /** * Look up a byte from the dictionary * @param bracketedString the string to look up * @returns the byte value of the definition * @throws JFugueException if there is a problem getting a byte from the dictionary look-up */ private byte getByteFromDictionary(String bracketedString) throws JFugueException { String definition = dictionaryLookup(bracketedString); Byte newbyte = null; if (definition.length() > 0) { try { newbyte = new Byte(definition); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_BYTE,definition,bracketedString); } } else newbyte = new Byte("-1"); return newbyte.byteValue(); } /** * Look up a long from the dictionary * @param bracketedString the string to look up * @returns the long value of the definition * @throws JFugueException if there is a problem getting a long from the dictionary look-up */ private long getLongFromDictionary(String bracketedString) throws JFugueException { String definition = dictionaryLookup(bracketedString); Long newlong = null; try { newlong = new Long(definition); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_LONG,definition,bracketedString); } return newlong.longValue(); } /** * Look up an int from the dictionary * @param bracketedString the string to look up * @returns the int value of the definition * @throws JFugueException if there is a problem getting a int from the dictionary look-up */ private int getIntFromDictionary(String bracketedString) throws JFugueException { String definition = dictionaryLookup(bracketedString); Integer newint = null; try { newint = new Integer(definition); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_INT,definition,bracketedString); } return newint.intValue(); } /** * Look up a double from the dictionary * @param bracketedString the string to look up * @returns the double value of the definition * @throws JFugueException if there is a problem getting a double from the dictionary look-up */ private double getDoubleFromDictionary(String bracketedString) throws JFugueException { String definition = dictionaryLookup(bracketedString); Double newdouble = null; try { newdouble = new Double(definition); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_DOUBLE,definition,bracketedString); } return newdouble.doubleValue(); } /** * Parses a voice and fires a voice element * @param v is the voice number 1 - 16 * @throws JFugueException if there is a problem parsing the element */ private void parseVoice(int p, int v) throws JFugueException { // XML part ID's are 1-based, JFugue voice numbers are 0-based int voiceNumber = -1; for (int x = 0; x < this.nextVoice; ++x) if (p == voices[x].part && v == voices[x].voice) voiceNumber = x; // if not found, add it to the array if (voiceNumber == -1) { voiceNumber = nextVoice; voices[voiceNumber] = new voiceDef(); voices[voiceNumber].part = p; voices[voiceNumber].voice = v; ++nextVoice; } if (voiceNumber != this.curVoice) fireVoiceEvent(new Voice((byte)voiceNumber)); curVoice = voiceNumber; } /** * Parses a XMLpart.midi_instruments and fires a voice or * instrument events * @param instruments is the XMLpart.midiinstruments string to parse * Can be a list of ~ separated pairs - midi-channel|InstName where InstName * can be a midi-name, midi-bank, or program Element */ private void parsePartElementInstruments(int p, String instruments) { if(instruments.indexOf('~') > -1) { String[] instArray = instruments.split("~"); // just do the first in the array String[] midiArray = instArray[0].split("|"); if (midiArray.length > 0 && midiArray[0].length() > 0) parseVoice(p, Integer.parseInt(midiArray[0])-1); if (midiArray.length != 1) parseInstrument(midiArray[1]); } else parseInstrument(instruments); } /** * parses inst and fires an Instrument Event * @param inst is a String that represents the instrument. If it is a numeric * value, it is interpreted as a midi-bank or program. If it is an instrument * name, it is looked up in the Dictionary as an instrument name. */ private void parseInstrument(String inst) { byte instrumentNumber; try { instrumentNumber = Byte.parseByte(inst); } catch (NumberFormatException e) { instrumentNumber = getByteFromDictionary(inst); } trace("Instrument element: inst = ",inst); if (instrumentNumber > -1) fireInstrumentEvent(new Instrument(instrumentNumber)); } /** * converts beats per minute (BPM) to pulses per minute (PPM) assuming 240 pulses per second * In MusicXML, BPM can be fractional, so BPMtoPPM takes a float argument * @param bpm * @return */ public static int BPMtoPPM(float bpm) { // convert BPM to PPM assuming 240 pulses per second return( new Float((60.f * 240.f) / bpm).intValue() ); } /** ** Used for diagnostic purposes. main() makes calls to test the Pattern-to-MusicXML ** parser. ** If you make any changes to this parser, run ** this method ("java org.jfugue.MusicStringParser"), and make sure everything ** works correctly. ** @param args not used **/ public static void main(String[] args) { testMusicXmlParser(); } private static void testMusicXmlParser() { //File fileXML = new File("C:\\Documents and Settings\\Philip Sobolik\\My Documents\\" // + "Visual Studio 2005\\WebSites\\NYSSMA3\\" // + "NYSSMA-Flute-2.xml"); File fileXML = new File("/users/epsobolik/documents/binchois.xml"); //File fileXML = new File("/users/epsobolik/documents/SchbAvMaSample.xml"); try { FileInputStream fisXML = new FileInputStream(fileXML); // test the XML file by displaying the first 1024 characters FileChannel fc = fisXML.getChannel(); ByteBuffer buf = ByteBuffer.allocate((int)fc.size()); fc.read(buf); buf.flip(); // while(buf.hasRemaining()) // System.out.print((char)buf.get()); // fisXML.close(); // System.out.print('\n'); // set up the source MusicXML file (parser) MusicXmlParser MusicXMLIn = new MusicXmlParser(); // MusicXmlParser.setTracing(Parser.TRACING_ON); // set up the target MusicString (renderer) MusicStringRenderer MusicStringOut = new MusicStringRenderer(); // attach the render to the parser MusicXMLIn.addParserListener(MusicStringOut); // start the parser MusicXMLIn.parse(fileXML); // display the MusicString Pattern p = MusicStringOut.getPattern(); p.insert("T60"); System.out.println(p.toString()); System.out.print('\n'); // File fileMS = new File("/users/epsobolik/documents/SchbAvMaSample.jFugue"); // FileOutputStream fosMS = new FileOutputStream(fileMS); // String ps = p.toString(); // for (int c = 0; c < ps.length(); ++c) // fosMS.write(ps.charAt(c)); // fosMS.close(); // play the pattern Player player = new Player(); player.play(p); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }jfugue/org/jfugue/Parser.java0000644000175000017500000002746311002212656016733 0ustar giovannigiovanni/* * 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 java.util.EventListener; import javax.swing.event.EventListenerList; /** * You may notice that there is no parse() method in the Parser class! * That's because the parse() method may take any type of parameter, as * well as any number of parameters, so it isn't something that can * declared ahead of time. * * @author David Koelle * */ public class Parser { public Parser() { progressListenerList = new EventListenerList(); listenerList = new EventListenerList (); // The Parser could add itself as a ParserProgressListener. } // Logging methods /////////////////////////////////////////// /** Pass this value to setTracing( ) to turn tracing off. Tracing is off by default. */ public static final int TRACING_OFF = 0; /** Pass this value to setTracing( ) to turn tracing on. Tracing is off by default. */ public static final int TRACING_ON = 1; private int tracing = TRACING_OFF; /** * Turns tracing on or off. If you're having trouble with your music string, * or if you've added new tokens to the parser, turn tracing on to make sure * that your new tokens are parsed correctly. * @param tracing the state of tracing - on or off */ public void setTracing(int tracing) { this.tracing = tracing; } /** * Returns the current state of tracing. * @return the state of tracing */ public int getTracing() { return this.tracing; } /** * Displays the passed String. * @param s the String to display */ protected void trace(Object... sentenceFragments) { if (TRACING_ON == getTracing()) { StringBuilder buddy = new StringBuilder(); for (int i=0; i < sentenceFragments.length; i++) { buddy.append(sentenceFragments[i]); } System.out.println(buddy.toString()); } } // // ParserProgressListener methods ///////////////////////////////////////////////////////////////////////// /** List of ParserProgressListeners */ protected EventListenerList progressListenerList; /** * Adds a ParserListener. The listener will receive events when the parser * interprets music string tokens. * * @param listener the listener that is to be notified of parser events */ public void addParserProgressListener(ParserProgressListener l) { progressListenerList.add (ParserProgressListener.class, l); } /** * Removes a ParserListener. * * @param listener the listener to remove */ public void removeParserProgressListener(ParserProgressListener l) { progressListenerList.remove (ParserProgressListener.class, l); } protected void clearParserProgressListeners() { EventListener[] l = progressListenerList.getListeners (ParserProgressListener.class); int numListeners = l.length; for (int i = 0; i < numListeners; i++) { progressListenerList.remove (ParserProgressListener.class, (ParserProgressListener)l[i]); } } /** Tells all ParserProgressListener interfaces that progress has occurred. */ protected void fireProgressReported(String description, long partComplete, long whole) { Object[] listeners = progressListenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserProgressListener.class) { ((ParserProgressListener)listeners[i + 1]).progressReported(description, partComplete, whole); } } } // // ParserListener methods ///////////////////////////////////////////////////////////////////////// /** List of ParserListeners */ protected EventListenerList listenerList; /** * Adds a ParserListener. The listener will receive events when the parser * interprets music string tokens. * * @param listener the listener that is to be notified of parser events */ public void addParserListener(ParserListener l) { listenerList.add (ParserListener.class, l); } /** * Removes a ParserListener. * * @param listener the listener to remove */ public void removeParserListener(ParserListener l) { listenerList.remove (ParserListener.class, l); } protected void clearParserListeners() { EventListener[] l = listenerList.getListeners (ParserListener.class); int numListeners = l.length; for (int i = 0; i < numListeners; i++) { listenerList.remove (ParserListener.class, (ParserListener)l[i]); } } /** Tells all ParserListeners that a voice event has been parsed. */ protected void fireVoiceEvent(Voice event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).voiceEvent(event); } } } /** Tells all ParserListeners that a tempo event has been parsed. */ protected void fireTempoEvent(Tempo event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).tempoEvent(event); } } } /** Tells all ParserListeners that an instrument event has been parsed. */ protected void fireInstrumentEvent(Instrument event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).instrumentEvent(event); } } } /** Tells all ParserListeners that a layer event has been parsed. */ protected void fireLayerEvent(Layer event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).layerEvent(event); } } } /** Tells all ParserListeners that a time event has been parsed. */ protected void fireTimeEvent(Time event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).timeEvent(event); } } } /** Tells all ParserListeners that a key signature event has been parsed. */ protected void fireKeySignatureEvent(KeySignature event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).keySignatureEvent(event); } } } /** Tells all ParserListeners that a measure event has been parsed. */ protected void fireMeasureEvent(Measure event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).measureEvent(event); } } } /** Tells all ParserListeners that a controller event has been parsed. */ protected void fireControllerEvent(Controller event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).controllerEvent(event); } } } /** Tells all ParserListeners that a controller event has been parsed. */ protected void fireChannelPressureEvent(ChannelPressure event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).channelPressureEvent(event); } } } /** Tells all ParserListeners that a controller event has been parsed. */ protected void firePolyphonicPressureEvent(PolyphonicPressure event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).polyphonicPressureEvent(event); } } } /** Tells all ParserListeners that a controller event has been parsed. */ protected void firePitchBendEvent(PitchBend event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).pitchBendEvent(event); } } } /** Tells all ParserListeners that a note event has been parsed. */ protected void fireNoteEvent(Note event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).noteEvent(event); } } } /** Tells all ParserListeners that a sequential note event has been parsed. */ protected void fireSequentialNoteEvent(Note event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).sequentialNoteEvent(event); } } } /** Tells all ParserListeners that a parallel note event has been parsed. */ protected void fireParallelNoteEvent(Note event) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ParserListener.class) { ((ParserListener)listeners[i + 1]).parallelNoteEvent(event); } } } // // End ParserListener methods ///////////////////////////////////////////////////////////////////////// } jfugue/org/jfugue/PolyphonicPressure.java0000644000175000017500000000624511005706454021356 0ustar giovannigiovanni/* * 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; /** * Represents tempo changes. Tempo is kept for the whole * song, and is independent of tracks. You may change the * tempo during a song. * *@author David Koelle *@version 3.0 */ public final class PolyphonicPressure implements JFugueElement { private byte key; private byte pressure; /** * Creates a new polyphonic pressure object, with the specified key and pressure values. * @param key the key to apply pressure to * @param pressure the pressure to apply */ public PolyphonicPressure(byte key, byte pressure) { setKey(key); setPressure(pressure); } /** * Sets the key value of this object. * @param key the key for this object */ public void setKey(byte key) { this.key = key; } /** * Sets the pressure value of this object. * @param pressure the pressure for this object */ public void setPressure(byte pressure) { this.pressure = pressure; } /** * Returns the key for this object. * @return the key for this object */ public byte getKey() { return this.key; } /** * Returns the pressure for this object. * @return the pressure for this object */ public byte getPressure() { return this.pressure; } /** * Returns the Music String representing this element and all of its settings. * For a polyphonic pressure object, the Music String is *key,pressure * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("*"); buffy.append(getKey()); buffy.append(","); buffy.append(getPressure()); return buffy.toString(); } /** * Returns verification string in this format: * PolyphonicPressure: key={#}, pressure={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("PolyphonicPressure: key="); buffy.append(getKey()); buffy.append(", pressure="); buffy.append(getPressure()); return buffy.toString(); } }jfugue/org/jfugue/ParserListenerAdapter.java0000644000175000017500000001024710776766112021754 0ustar giovannigiovanni/* * 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; /** * This Adapter class implements all of the methods of * ParserListener, but the implementations are blank. * If you want something to be a ParserListener, but you don't * want to implement all of the ParserListener methods, extend * this class. * *@author David Koelle *@version 3.0 */ public class ParserListenerAdapter implements ParserListener { /** * Called when the parser encounters a voice event. * @param voice the event that has been parsed * @see Voice */ public void voiceEvent(Voice voice) { } /** * Called when the parser encounters a tempo event. * @param tempo the event that has been parsed * @see Tempo */ public void tempoEvent(Tempo tempo) { } /** * Called when the parser encounters an instrument event. * @param instrument the event that has been parsed * @see Instrument */ public void instrumentEvent(Instrument instrument) { } /** * Called when the parser encounters a layer event. * @param layer the event that has been parsed * @see Layer */ public void layerEvent(Layer layer) { } /** * Called when the parser encounters a measure event. * @param measure the event that has been parsed * @see Measure */ public void measureEvent(Measure measure) { } /** * Called when the parser encounters a time event. * @param time the event that has been parsed * @see Time */ public void timeEvent(Time time) { } /** * Called when the parser encounters a key signature event. * @param time the event that has been parsed * @see KeySignature */ public void keySignatureEvent(KeySignature keySig) { } /** * Called when the parser encounters a controller event. * @param controller the event that has been parsed */ public void controllerEvent(Controller controller) { } /** * Called when the parser encounters a channel pressure event. * @param channelPressure the event that has been parsed * @see ChannelPressure */ public void channelPressureEvent(ChannelPressure channelPressure) { } /** * Called when the parser encounters a polyphonic pressure event. * @param polyphonicPressure the event that has been parsed * @see PolyphonicPressure */ public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { } /** * Called when the parser encounters a pitch bend event. * @param pitchBend the event that has been parsed * @see PitchBend */ public void pitchBendEvent(PitchBend pitchBend) { } /** * Called when the parser encounters an initial note event. * @param note the event that has been parsed * @see Note */ public void noteEvent(Note note) { } /** * Called when the parser encounters a sequential note event. * @param note the event that has been parsed * @see Note */ public void sequentialNoteEvent(Note note) { } /** * Called when the parser encounters a parallel note event. * @param note the event that has been parsed * @see Note */ public void parallelNoteEvent(Note note) { } } jfugue/org/jfugue/MusicXmlRenderer.java0000644000175000017500000005701310776766112020743 0ustar giovannigiovanni/* * 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 java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; import nu.xom.Attribute; import nu.xom.DocType; import nu.xom.Document; import nu.xom.Element; import nu.xom.Elements; import nu.xom.Serializer; /** * This class is used to build a MusicXML file) given a * MIDI Sequence, Music String, etc. * *@author E.Philip Sobolik */ public class MusicXmlRenderer implements ParserListener { private Element root; // top-level node of entire MusicXML Document private Element elCurMeasure; // notes, etc. are added to this measure private Element elPartList; // may need to add score-parts to this private Element elCurScorePart; // may need to add instruments to this private Element elCurPart; // current 'voice' add measures to this private static final int MUSICXMLDIVISIONS = 4; // 4 divisions per quarter note private static final double WHOLE = 1024.0; private static final double QUARTER = 256.0; public MusicXmlRenderer() { root = new Element("score-partwise"); Element elID = new Element("identification"); Element elCreator = new Element("creator"); elCreator.addAttribute(new Attribute("type", "software")); elCreator.appendChild("JFugue MusicXMLRenderer"); elID.appendChild(elCreator); root.appendChild(elID); // add an empty score-part list here (before any parts are added) // score-parts are added to this as they are generated elPartList = new Element("part-list"); root.appendChild(elPartList); } // MusicXmlRenderer /** * creates the internal Document with the top-level * Element and then creates the MusicXML file (as a * string) from the internal Document * @return the completed MusicXML file as a String */ public String getMusicXMLString() { Document xomDoc = getMusicXMLDoc(); return xomDoc.toXML(); } /** * creates the internal Document with the top-level * Element. * @return the completed MusicXML file as a Document */ public Document getMusicXMLDoc() { finishCurrentVoice(); // remove empty measures Elements elDocParts = root.getChildElements("part"); for (int xP = 0; xP < elDocParts.size(); ++xP) { Element elDocPart = elDocParts.get(xP); Elements elPartMeasures = elDocPart.getChildElements("measure"); for (int xM = 0; xM < elPartMeasures.size(); ++xM) if (elPartMeasures.get(xM).getChildCount() < 1) elDocPart.removeChild(xM); } // create the Document Document xomDoc = new Document(root); DocType docType = new DocType("score-partwise", "-//Recordare//DTD MusicXML 1.1 Partwise//EN", "http://www.musicxml.org/dtds/partwise.dtd"); xomDoc.insertChild(docType, 0); return xomDoc; } // GetMusicXMLDoc public void doFirstMeasure(boolean bAddDefaults) { if (elCurPart == null) newVoice(new Voice((byte)0)); if (elCurMeasure == null) { elCurMeasure = new Element("measure"); elCurMeasure.addAttribute(new Attribute("number", Integer.toString(1))); // assemble attributes element Element elAttributes = new Element("attributes"); if (bAddDefaults) { // divisions - 4 per beat Element elDivisions = new Element("divisions"); elDivisions.appendChild(Integer.toString(MUSICXMLDIVISIONS)); elAttributes.appendChild(elDivisions); // beats - 1 beat per measure Element elTime = new Element("time"); Element elBeats = new Element("beats"); elBeats.appendChild(Integer.toString(4)); elTime.appendChild(elBeats); Element elBeatType = new Element("beat-type"); elBeatType.appendChild(Integer.toString(4)); elTime.appendChild(elBeatType); elAttributes.appendChild(elTime); } if (bAddDefaults) { // Clef - assumed to be treble clef Element elClef = new Element("clef"); Element elSign = new Element("sign"); elSign.appendChild("G"); Element elLine = new Element("line"); elLine.appendChild("2"); elClef.appendChild(elSign); elClef.appendChild(elLine); elAttributes.appendChild(elClef); } // add the attributes to the measure if (elAttributes.getChildCount() > 0) elCurMeasure.appendChild(elAttributes); // key signature if (bAddDefaults) doKeySig(new KeySignature((byte)0, (byte)0)); // C major if (bAddDefaults) doTempo(new Tempo(120)); // 120 BMP default } } // doFirstMeasure public void voiceEvent(Voice voice) { String sReqVoice = voice.getMusicString(); String sCurPartID = (elCurPart == null) ? null : elCurPart.getAttribute("id").getValue(); // if current voice is the same as the requested one, do nothing if (sCurPartID != null) if (sReqVoice.compareTo(sCurPartID) == 0) return; // check if the requested voice already exists boolean bNewVoiceExists = false; Elements elParts = root.getChildElements("part"); Element elExistingNewPart = null; for (int x = 0; x < elParts.size(); ++x) { Element elP = elParts.get(x); String sPID = elP.getAttribute("id").getValue(); if (sPID.compareTo(sReqVoice) == 0) { bNewVoiceExists = true; elExistingNewPart = elP; } } finishCurrentVoice(); // start the new part // if the new part exists, set the working part to the existing one // otherwise, start a new part if (bNewVoiceExists) elCurPart = elExistingNewPart; else newVoice(voice); // start the first/next measure of the working part // note: doesn't start a new measure if there // aren't any notes in the current measure newMeasure(); } // voiceEvent private void finishCurrentVoice() { String sCurPartID = (elCurPart == null) ? null : elCurPart.getAttribute("id").getValue(); boolean bCurVoiceExists = false; Elements elParts = root.getChildElements("part"); Element elExistingCurPart = null; for (int x = 0; x < elParts.size(); ++x) { Element elP = elParts.get(x); String sPID = elP.getAttribute("id").getValue(); if (sPID.compareTo(sCurPartID) == 0) { bCurVoiceExists = true; elExistingCurPart = elP; } } // finish the current measure if (elCurPart != null) { finishCurrentMeasure(); if (bCurVoiceExists == true) root.replaceChild(elExistingCurPart, elCurPart); else root.appendChild(elCurPart); } } // finishCurrentVoice private void newVoice(Voice voice) {// add a part to the part list elCurScorePart = new Element("score-part"); Attribute atPart = new Attribute("id", voice.getMusicString()); elCurScorePart.addAttribute(atPart); // empty part name - Finale ignores it and Sibelius gets it wrong elCurScorePart.appendChild(new Element("part-name")); Element elPL = root.getFirstChildElement("part-list"); elPL.appendChild(elCurScorePart); // start a new part - note that the score-part and the part have the // same id attribute elCurPart = new Element("part"); Attribute atPart2 = new Attribute(atPart); elCurPart.addAttribute(atPart2); elCurMeasure = null; doFirstMeasure(true); } // newVoice public void instrumentEvent(Instrument instrument) { Element elInstrName = new Element("instrument-name"); elInstrName.appendChild(instrument.getInstrumentName()); Element elInstrument = new Element("score-instrument"); elInstrument.addAttribute(new Attribute("id", Byte.toString(instrument.getInstrument()))); elInstrument.appendChild(elInstrName); } public void tempoEvent(Tempo tempo) { doTempo(tempo); } private void doTempo(Tempo tempo) { Element elDirection = new Element("direction"); elDirection.addAttribute(new Attribute("placement", "above")); Element elDirectionType = new Element("direction-type"); Element elMetronome = new Element("metronome"); Element elBeatUnit = new Element("beat-unit"); // assume quarter note beat unit elBeatUnit.appendChild("quarter"); Element elPerMinute = new Element("per-minute"); Integer iBPM = new Float(PPMtoBPM(tempo.getTempo())).intValue(); elPerMinute.appendChild(iBPM.toString()); // assemble all the pieces elMetronome.appendChild(elBeatUnit); elMetronome.appendChild(elPerMinute); elDirectionType.appendChild(elMetronome); elDirection.appendChild(elDirectionType); // attach the whole thing to the current measure if (elCurMeasure == null) doFirstMeasure(true); elCurMeasure.appendChild(elDirection); } // doTempo public void layerEvent(Layer layer) { // pattern.add(layer.getMusicString()); } public void timeEvent(Time time) { // pattern.add(time.getMusicString()); } public void keySignatureEvent(KeySignature keySig) { doKeySig(keySig); } private void doKeySig(KeySignature keySig) { Element elKey = new Element("key"); // build the key element Element elFifths = new Element("fifths"); elFifths.appendChild(Byte.toString(keySig.getKeySig())); elKey.appendChild(elFifths); Element elMode = new Element("mode"); elMode.appendChild((keySig.getScale() == 1 ? "minor" : "major")); elKey.appendChild(elMode); // add the key to the attributes element of the current measure if (elCurMeasure == null) doFirstMeasure(true); Element elAttributes = elCurMeasure.getFirstChildElement("attributes"); boolean bNewAttributes = (elAttributes == null); if (bNewAttributes == true) elAttributes = new Element("attributes"); elAttributes.appendChild(elKey); if (bNewAttributes == true) elCurMeasure.appendChild(elAttributes); } // doKeySig public void measureEvent(Measure measure) { // first measure stuff if (elCurMeasure == null) doFirstMeasure(false); else { // add the current measure to the part finishCurrentMeasure(); newMeasure(); } } // measureEvent private void finishCurrentMeasure() { // if the part exists, replace it with the new one // otherwise, add the new one { if (elCurMeasure.getParent() == null) elCurPart.appendChild(elCurMeasure); else { int sCurMNum = Integer.parseInt(elCurMeasure.getAttributeValue("number")); Elements elMeasures = elCurPart.getChildElements("measure"); for (int x = 0; x < elMeasures.size(); ++x) { Element elM = elMeasures.get(x); int sMNum = Integer.parseInt(elM.getAttributeValue("number")); if (sMNum == sCurMNum) elCurPart.replaceChild(elM, elCurMeasure); } } } } // finishCurrentMeasure private void newMeasure() { Integer nextNumber = 1; boolean bNewMeasure = true; // if there aren't any notes in the measure, // continue to use the current measure Elements elMeasures = elCurPart.getChildElements("measure"); Element elLastMeasure = null; if (elMeasures.size() > 0) { elLastMeasure = elMeasures.get(elMeasures.size()-1); // get the new measure number from the last one Attribute elNumber = elLastMeasure.getAttribute("number"); if (elLastMeasure.getChildElements("note").size() < 1) bNewMeasure = false; else nextNumber = Integer.parseInt(elNumber.getValue()) + 1; } else { // first measure may not have been added yet bNewMeasure = (elCurMeasure.getChildElements("note").size() > 0); } if (bNewMeasure) { // start the new measure elCurMeasure = new Element("measure"); // add the new measure number elCurMeasure.addAttribute(new Attribute("number", Integer.toString(nextNumber))); } // else continue using the same elCurMeasure } // newMeasure public void controllerEvent(Controller controller) { // pattern.add(controller.getMusicString()); } public void channelPressureEvent(ChannelPressure channelPressure) { // pattern.add(channelPressure.getMusicString()); } public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { // pattern.add(polyphonicPressure.getMusicString()); } public void pitchBendEvent(PitchBend pitchBend) { // pattern.add(pitchBend.getMusicString()); } public void noteEvent(Note note) { doNote(note, false); } private void doNote(Note note, boolean bChord) { Element elNote = new Element("note"); if (bChord) elNote.appendChild(new Element("chord")); // rest if (note.isRest()) { Element elRest = new Element("rest"); elNote.appendChild(elRest); } else { // pitch Element elPitch = new Element("pitch"); // step - note letter name without sharp or flat Element elStep = new Element("step"); String sPitch = Note.NOTES[note.getValue() % 12]; int iAlter = 0; if (sPitch.length() > 1) { iAlter = sPitch.contains("#") ? 1 : -1; sPitch = sPitch.substring(0,1); } elStep.appendChild(sPitch); elPitch.appendChild(elStep); // alter - -1 = flat, 1 = sharp if (iAlter != 0) { Element elAlter = new Element("alter"); elAlter.appendChild(Integer.toString(iAlter)); elPitch.appendChild(elAlter); } // octave Element elOctave = new Element("octave"); elOctave.appendChild(Integer.toString(note.getValue() / 12)); elPitch.appendChild(elOctave); elNote.appendChild(elPitch); } // duration Element elDuration = new Element("duration"); double dDuration = note.getDecimalDuration(); int iXMLDuration = (int) ((dDuration * WHOLE * MUSICXMLDIVISIONS) / QUARTER); elDuration.appendChild(Integer.toString(iXMLDuration)); elNote.appendChild(elDuration); // tie start/stop boolean bDoNotation = false; if (note.isStartOfTie()) { Element elTie = new Element("tie"); Attribute atType = new Attribute("type", "start"); elTie.addAttribute(atType); elNote.appendChild(elTie); bDoNotation = true; } else if (note.isEndOfTie()) { Element elTie = new Element("tie"); Attribute atType = new Attribute("type", "stop"); elTie.addAttribute(atType); elNote.appendChild(elTie); bDoNotation = true; } // duration type String sDuration; boolean bDotted = false; if (dDuration == 1.0) sDuration = "whole"; else if (dDuration == 0.75) { sDuration = "half"; bDotted = true; } else if (dDuration == 0.5) sDuration = "half"; else if (dDuration == 0.375) { sDuration = "quarter"; bDotted = true; } else if (dDuration == 0.25) sDuration = "quarter"; else if (dDuration == 0.1875) { sDuration = "eighth"; bDotted = true; } else if (dDuration == 0.125) sDuration = "eighth"; else if (dDuration == 0.09375) { sDuration = "16th"; bDotted = true; } else if (dDuration == 0.0625) sDuration = "16th"; else if (dDuration == 0.046875) { sDuration = "32nd"; bDotted = true; } else if (dDuration == 0.03125) sDuration = "32nd"; else if (dDuration == 0.0234375) { sDuration = "64th"; bDotted = true; } else if (dDuration == 0.015625) sDuration = "64th"; else if (dDuration == 0.01171875) { sDuration = "128th"; bDotted = true; } else if (dDuration == 0.0078125) sDuration = "128th"; else sDuration = "/" + Double.toString(dDuration); Element elType = new Element("type"); elType.appendChild(sDuration); elNote.appendChild(elType); // dotted if (bDotted) { Element elDot = new Element("dot"); elNote.appendChild(elDot); } // notations if (bDoNotation) { Element elNotations = new Element("notations"); if (note.isStartOfTie()) { Element elTied = new Element("tied"); Attribute atStart = new Attribute("type", "start"); elTied.addAttribute(atStart); elNotations.appendChild(elTied); } else if (note.isEndOfTie()) { Element elTied = new Element("tied"); Attribute atStart = new Attribute("type", "stop"); elTied.addAttribute(atStart); elNotations.appendChild(elTied); } elNote.appendChild(elNotations); } if (elCurMeasure == null) doFirstMeasure(true); elCurMeasure.appendChild(elNote); } // doNote public void sequentialNoteEvent(Note note) { } public void parallelNoteEvent(Note note) { doNote(note, true); } /** * converts pulses per minute (PPM) to beats per minute (BPM) assuming 240 pulses per second * In MusicXML, BPM can be fractional, so PPMtoBPM returns a float * @param ppm * @return */ public static float PPMtoBPM(int ppm) { // convert PPM to BPM assuming 240 pulses per second return( new Float((60.f * 240.f) / ppm) ); } /** ** Used for diagnostic purposes. main() makes calls to test the Pattern-to-MusicXML ** renderer. ** @param args not used **/ public static void main(String[] args) { // FrereJacquesRound(); // Entertainer(); metronome(120); } private static void FrereJacquesRound() { File fileXML = new File("C:\\Documents and Settings\\Philip Sobolik\\My Documents\\" + "Visual Studio 2005\\WebSites\\NYSSMA3\\" + "FrereJacquesRound.xml"); try { FileOutputStream fosXML = new FileOutputStream(fileXML, false); // set up the source MusicXML file (parser) MusicXmlRenderer MusicXmlOut = new MusicXmlRenderer(); // MusicXmlParser.setTracing(Parser.TRACING_ON); // set up the target MusicString (renderer) MusicStringParser MusicStringIn = new MusicStringParser(); // attach the render to the parser MusicStringIn.addParserListener(MusicXmlOut); // "Frere Jacques" Pattern pattern1 = new Pattern("C5q D5q E5q C5q |"); // "Dormez-vous?" Pattern pattern2 = new Pattern("E5q F5q G5h |"); // "Sonnez les matines" Pattern pattern3 = new Pattern("G5i A5i G5i F5i E5q C5q |"); // "Ding ding dong" Pattern pattern4 = new Pattern("C5q G4q C5h |"); // Put it all together Pattern song = new Pattern(); song.add(pattern1, 2); // Adds 'pattern1' to 'song' twice song.add(pattern2, 2); // Adds 'pattern2' to 'song' twice song.add(pattern3, 2); // Adds 'pattern3' to 'song' twice song.add(pattern4, 2); // Adds 'pattern4' to 'song' twice // MusicStringIn.parse(new Pattern("T160 I[Cello] "+ // "G3q G3q G3q Eb3q Bb3i G3q Eb3q Bb3i G3h")); // MusicStringIn.parse(song); // "Frere Jacques" as a round Pattern doubleMeasureRest = new Pattern("Rw | Rw |"); // Create the first voice Pattern round1 = new Pattern("V0"); round1.add(song); round1.add(doubleMeasureRest, 2); // Create the second voice Pattern round2 = new Pattern("V1"); round2.add(doubleMeasureRest); round2.add(song); round2.add(doubleMeasureRest); // Create the third voice Pattern round3 = new Pattern("V2"); round3.add(doubleMeasureRest, 2); round3.add(song); // Put the voices together Pattern roundSong = new Pattern(); roundSong.add(round1); roundSong.add(round2); roundSong.add(round3); Player player = new Player(); player.play(roundSong); System.out.println(roundSong.toString()); // start the parser MusicStringIn.parse(roundSong); // write the MusicXML file unformatted /* String p = MusicXmlOut.getMusicXMLString(); fosXML.write(p.getBytes()); // display the MusicXML file as an unformatted string System.out.println(p); System.out.print('\n'); */ // write the MusicXML file formatted Serializer ser = new Serializer(fosXML, "UTF-8"); ser.setIndent(4); ser.write(MusicXmlOut.getMusicXMLDoc()); fosXML.flush(); fosXML.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static void Entertainer() { File fileSrc = new File("F:\\WIN\\JFugue\\org\\jfugue\\extras\\" + "entertainer.jfugue"); File fileXML = new File("C:\\Documents and Settings\\Philip Sobolik\\My Documents\\" + "Visual Studio 2005\\WebSites\\NYSSMA3\\" + "Entertainer.xml"); try { FileOutputStream fosXML = new FileOutputStream(fileXML, false); // set up the source MusicXML file (parser) MusicXmlRenderer MusicXmlOut = new MusicXmlRenderer(); // MusicXmlParser.setTracing(Parser.TRACING_ON); // set up the target MusicString (renderer) MusicStringParser MusicStringIn = new MusicStringParser(); // attach the render to the parser MusicStringIn.addParserListener(MusicXmlOut); // read the song from the file BufferedReader brSrc = new BufferedReader(new FileReader(fileSrc)); LineNumberReader lnrSrc = new LineNumberReader(brSrc); Pattern song = new Pattern(); for (String s = lnrSrc.readLine(); s != null; s = lnrSrc.readLine()) { if (s.length() > 0) if (s.charAt(0) != '#') song.add(s); } lnrSrc.close(); // play the song // Player player = new Player(); // player.play(song); System.out.println(song.toString()); // start the parser MusicStringIn.parse(song); // write the MusicXML file unformatted /* String p = MusicXmlOut.getMusicXMLString(); fosXML.write(p.getBytes()); // display the MusicXML file as an unformatted string System.out.println(p); System.out.print('\n'); */ // write the MusicXML file formatted Serializer ser = new Serializer(fosXML, "UTF-8"); ser.setIndent(4); ser.write(MusicXmlOut.getMusicXMLDoc()); fosXML.flush(); fosXML.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static void metronome(int bpm) { Pattern p = new Pattern("T" + Integer.toString((60 * 240) / bpm)); p.add("A4q", bpm); // should play for 1 minute Player pl = new Player(); pl.play(p); } } jfugue/org/jfugue/DeviceThatWillReceiveMidi.java0000644000175000017500000000744111000430766022451 0ustar giovannigiovanni/* * 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.InvalidMidiDataException; import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.Sequence; import javax.sound.midi.ShortMessage; /** * Represents an attached MIDI device, such as a keyboard - use this class * to send MIDI from your JFugue program to a keyboard or sythesizer. * This class uses javax.sound.MidiDevice, but is not derived from javax.sound.MidiDevice. * * @author David Koelle * @version 3.0 */ public class DeviceThatWillReceiveMidi { private MidiDevice device; private Receiver receiver; /** * Creates a new DeviceThatWillReceiveMidi using JFugue's Intelligent Device Resolver to pick the * most likely device to open. * @throws MidiUnavailableException */ public DeviceThatWillReceiveMidi() throws MidiUnavailableException { this.device = IntelligentDeviceResolver.selectReceiverDevice(); init(); } public DeviceThatWillReceiveMidi(MidiDevice.Info info) throws MidiUnavailableException { this.device = MidiSystem.getMidiDevice(info); init(); } private void init() throws MidiUnavailableException { if (!(device.isOpen())) { device.open(); } this.receiver = device.getReceiver(); } /** * Send the given sequence to the MIDI device - use this to send MIDI files * to your keyboard! * * @param sequence The sequence to send to the MIDI device */ public void sendSequence(Sequence sequence) { TimeFactor.sortAndDeliverMidiMessages(sequence, new MidiMessageRecipient() { public void messageReady(MidiMessage message, long timestamp) { receiver.send(message, -1); } } ); // Send messages to turn all controllers and all notes off for all tracks (channels) ShortMessage allControllersOff = new ShortMessage(); ShortMessage allNotesOff = new ShortMessage(); for (byte track=0; track < 16; track++) { try { allControllersOff.setMessage(ShortMessage.CONTROL_CHANGE, track, (byte)121, (byte)0); receiver.send(allControllersOff, -1); allNotesOff.setMessage(ShortMessage.CONTROL_CHANGE, track, (byte)123, (byte)0); receiver.send(allNotesOff, -1); } catch (InvalidMidiDataException e) { throw new JFugueException(JFugueException.ERROR_PLAYING_MUSIC + e); } } } public void close() { receiver.close(); device.close(); } public Receiver getReceiver() { return this.receiver; } } jfugue/org/jfugue/JFugueDefinitions.java0000644000175000017500000005556111005706214021061 0ustar giovannigiovanni/* * 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 java.util.Map; /** * Loads default definitions into the JFugue dictionary. * This allows users to refer to instruments, percussion sounds, * and controller events by easy-to-remember names. * *

* Dictionary items can be added via the Music String. See * the documentation for more information. *

* *@author David Koelle *@version 2.0 */ public final class JFugueDefinitions { private JFugueDefinitions() { } /** * Loads default definitions into the JFugue dictionary. This includes * all of the string representations for instrument names, percussion sounds, * controller events, and some controller values. * @param dictionaryMap the dictionary instantiated by the parser */ protected static void populateDictionary(Map dictionaryMap) { // // Instrument names // dictionaryMap.put("PIANO" ,"0"); dictionaryMap.put("ACOUSTIC_GRAND" ,"0"); dictionaryMap.put("BRIGHT_ACOUSTIC" ,"1"); dictionaryMap.put("ELECTRIC_GRAND" ,"2"); dictionaryMap.put("HONKEY_TONK" ,"3"); dictionaryMap.put("ELECTRIC_PIANO" ,"4"); dictionaryMap.put("ELECTRIC_PIANO_1" ,"4"); dictionaryMap.put("ELECTRIC_PIANO_2" ,"5"); dictionaryMap.put("HARPISCHORD" ,"6"); dictionaryMap.put("CLAVINET" ,"7"); dictionaryMap.put("CELESTA" ,"8"); dictionaryMap.put("GLOCKENSPIEL" ,"9"); dictionaryMap.put("MUSIC_BOX" ,"10"); dictionaryMap.put("VIBRAPHONE" ,"11"); dictionaryMap.put("MARIMBA" ,"12"); dictionaryMap.put("XYLOPHONE" ,"13"); dictionaryMap.put("TUBULAR_BELLS" ,"14"); dictionaryMap.put("DULCIMER" ,"15"); dictionaryMap.put("DRAWBAR_ORGAN" ,"16"); dictionaryMap.put("PERCUSSIVE_ORGAN" ,"17"); dictionaryMap.put("ROCK_ORGAN" ,"18"); dictionaryMap.put("CHURCH_ORGAN" ,"19"); dictionaryMap.put("REED_ORGAN" ,"20"); dictionaryMap.put("ACCORDIAN" ,"21"); dictionaryMap.put("HARMONICA" ,"22"); dictionaryMap.put("TANGO_ACCORDIAN" ,"23"); dictionaryMap.put("GUITAR" ,"24"); dictionaryMap.put("NYLON_STRING_GUITAR" ,"24"); dictionaryMap.put("STEEL_STRING_GUITAR" ,"25"); dictionaryMap.put("ELECTRIC_JAZZ_GUITAR" ,"26"); dictionaryMap.put("ELECTRIC_CLEAN_GUITAR" ,"27"); dictionaryMap.put("ELECTRIC_MUTED_GUITAR" ,"28"); dictionaryMap.put("OVERDRIVEN_GUITAR" ,"29"); dictionaryMap.put("DISTORTION_GUITAR" ,"30"); dictionaryMap.put("GUITAR_HARMONICS" ,"31"); dictionaryMap.put("ACOUSTIC_BASS" ,"32"); dictionaryMap.put("ELECTRIC_BASS_FINGER" ,"33"); dictionaryMap.put("ELECTRIC_BASS_PICK" ,"34"); dictionaryMap.put("FRETLESS_BASS" ,"35"); dictionaryMap.put("SLAP_BASS_1" ,"36"); dictionaryMap.put("SLAP_BASS_2" ,"37"); dictionaryMap.put("SYNTH_BASS_1" ,"38"); dictionaryMap.put("SYNTH_BASS_2" ,"39"); dictionaryMap.put("VIOLIN" ,"40"); dictionaryMap.put("VIOLA" ,"41"); dictionaryMap.put("CELLO" ,"42"); dictionaryMap.put("CONTRABASS" ,"43"); dictionaryMap.put("TREMOLO_STRINGS" ,"44"); dictionaryMap.put("PIZZICATO_STRINGS" ,"45"); dictionaryMap.put("ORCHESTRAL_STRINGS" ,"46"); dictionaryMap.put("TIMPANI" ,"47"); dictionaryMap.put("STRING_ENSEMBLE_1" ,"48"); dictionaryMap.put("STRING_ENSEMBLE_2" ,"49"); dictionaryMap.put("SYNTH_STRINGS_1" ,"50"); dictionaryMap.put("SYNTH_STRINGS_2" ,"51"); dictionaryMap.put("CHOIR_AAHS" ,"52"); dictionaryMap.put("VOICE_OOHS" ,"53"); dictionaryMap.put("SYNTH_VOICE" ,"54"); dictionaryMap.put("ORCHESTRA_HIT" ,"55"); dictionaryMap.put("TRUMPET" ,"56"); dictionaryMap.put("TROMBONE" ,"57"); dictionaryMap.put("TUBA" ,"58"); dictionaryMap.put("MUTED_TRUMPET" ,"59"); dictionaryMap.put("FRENCH_HORN" ,"60"); dictionaryMap.put("BRASS_SECTION" ,"61"); dictionaryMap.put("SYNTHBRASS_1" ,"62"); dictionaryMap.put("SYNTHBRASS_2" ,"63"); dictionaryMap.put("SOPRANO_SAX" ,"64"); dictionaryMap.put("ALTO_SAX" ,"65"); dictionaryMap.put("TENOR_SAX" ,"66"); dictionaryMap.put("BARITONE_SAX" ,"67"); dictionaryMap.put("OBOE" ,"68"); dictionaryMap.put("ENGLISH_HORN" ,"69"); dictionaryMap.put("BASSOON" ,"70"); dictionaryMap.put("CLARINET" ,"71"); dictionaryMap.put("PICCOLO" ,"72"); dictionaryMap.put("FLUTE" ,"73"); dictionaryMap.put("RECORDER" ,"74"); dictionaryMap.put("PAN_FLUTE" ,"75"); dictionaryMap.put("BLOWN_BOTTLE" ,"76"); dictionaryMap.put("SKAKUHACHI" ,"77"); dictionaryMap.put("WHISTLE" ,"78"); dictionaryMap.put("OCARINA" ,"79"); dictionaryMap.put("LEAD_SQUARE" ,"80"); dictionaryMap.put("SQUARE" ,"80"); dictionaryMap.put("LEAD_SAWTOOTH" ,"81"); dictionaryMap.put("SAWTOOTH" ,"81"); dictionaryMap.put("LEAD_CALLIOPE" ,"82"); dictionaryMap.put("CALLIOPE" ,"82"); dictionaryMap.put("LEAD_CHIFF" ,"83"); dictionaryMap.put("CHIFF" ,"83"); dictionaryMap.put("LEAD_CHARANG" ,"84"); dictionaryMap.put("CHARANG" ,"84"); dictionaryMap.put("LEAD_VOICE" ,"85"); dictionaryMap.put("VOICE" ,"85"); dictionaryMap.put("LEAD_FIFTHS" ,"86"); dictionaryMap.put("FIFTHS" ,"86"); dictionaryMap.put("LEAD_BASSLEAD" ,"87"); dictionaryMap.put("BASSLEAD" ,"87"); dictionaryMap.put("PAD_NEW_AGE" ,"88"); dictionaryMap.put("NEW_AGE" ,"88"); dictionaryMap.put("PAD_WARM" ,"89"); dictionaryMap.put("WARM" ,"89"); dictionaryMap.put("PAD_POLYSYNTH" ,"90"); dictionaryMap.put("POLYSYNTH" ,"90"); dictionaryMap.put("PAD_CHOIR" ,"91"); dictionaryMap.put("CHOIR" ,"91"); dictionaryMap.put("PAD_BOWED" ,"92"); dictionaryMap.put("BOWED" ,"92"); dictionaryMap.put("PAD_METALLIC" ,"93"); dictionaryMap.put("METALLIC" ,"93"); dictionaryMap.put("PAD_HALO" ,"94"); dictionaryMap.put("HALO" ,"94"); dictionaryMap.put("PAD_SWEEP" ,"95"); dictionaryMap.put("SWEEP" ,"95"); dictionaryMap.put("FX_RAIN" ,"96"); dictionaryMap.put("RAIN" ,"96"); dictionaryMap.put("FX_SOUNDTRACK" ,"97"); dictionaryMap.put("SOUNDTRACK" ,"97"); dictionaryMap.put("FX_CRYSTAL" ,"98"); dictionaryMap.put("CRYSTAL" ,"98"); dictionaryMap.put("FX_ATMOSPHERE" ,"99"); dictionaryMap.put("ATMOSPHERE" ,"99"); dictionaryMap.put("FX_BRIGHTNESS" ,"100"); dictionaryMap.put("BRIGHTNESS" ,"100"); dictionaryMap.put("FX_GOBLINS" ,"101"); dictionaryMap.put("GOBLINS" ,"101"); dictionaryMap.put("FX_ECHOES" ,"102"); dictionaryMap.put("ECHOES" ,"102"); dictionaryMap.put("FX_SCI-FI" ,"103"); dictionaryMap.put("SCI-FI" ,"103"); dictionaryMap.put("SITAR" ,"104"); dictionaryMap.put("BANJO" ,"105"); dictionaryMap.put("SHAMISEN" ,"106"); dictionaryMap.put("KOTO" ,"107"); dictionaryMap.put("KALIMBA" ,"108"); dictionaryMap.put("BAGPIPE" ,"109"); dictionaryMap.put("FIDDLE" ,"110"); dictionaryMap.put("SHANAI" ,"111"); dictionaryMap.put("TINKLE_BELL" ,"112"); dictionaryMap.put("AGOGO" ,"113"); dictionaryMap.put("STEEL_DRUMS" ,"114"); dictionaryMap.put("WOODBLOCK" ,"115"); dictionaryMap.put("TAIKO_DRUM" ,"116"); dictionaryMap.put("MELODIC_TOM" ,"117"); dictionaryMap.put("SYNTH_DRUM" ,"118"); dictionaryMap.put("REVERSE_CYMBAL" ,"119"); dictionaryMap.put("GUITAR_FRET_NOISE" ,"120"); dictionaryMap.put("BREATH_NOISE" ,"121"); dictionaryMap.put("SEASHORE" ,"122"); dictionaryMap.put("BIRD_TWEET" ,"123"); dictionaryMap.put("TELEPHONE_RING" ,"124"); dictionaryMap.put("HELICOPTER" ,"125"); dictionaryMap.put("APPLAUSE" ,"126"); dictionaryMap.put("GUNSHOT" ,"127"); // // Percussion names // dictionaryMap.put("ACOUSTIC_BASS_DRUM" ,"35"); dictionaryMap.put("BASS_DRUM" ,"36"); dictionaryMap.put("SIDE_STICK" ,"37"); dictionaryMap.put("ACOUSTIC_SNARE" ,"38"); dictionaryMap.put("HAND_CLAP" ,"39"); dictionaryMap.put("ELECTRIC_SNARE" ,"40"); dictionaryMap.put("LOW_FLOOR_TOM" ,"41"); dictionaryMap.put("CLOSED_HI_HAT" ,"42"); dictionaryMap.put("HIGH_FLOOR_TOM" ,"43"); dictionaryMap.put("PEDAL_HI_HAT" ,"44"); dictionaryMap.put("LOW_TOM" ,"45"); dictionaryMap.put("OPEN_HI_HAT" ,"46"); dictionaryMap.put("LOW_MID_TOM" ,"47"); dictionaryMap.put("HI_MID_TOM" ,"48"); dictionaryMap.put("CRASH_CYMBAL_1" ,"49"); dictionaryMap.put("HIGH_TOM" ,"50"); dictionaryMap.put("RIDE_CYMBAL_1" ,"51"); dictionaryMap.put("CHINESE_CYMBAL" ,"52"); dictionaryMap.put("RIDE_BELL" ,"53"); dictionaryMap.put("TAMBOURINE" ,"54"); dictionaryMap.put("SPLASH_CYMBAL" ,"55"); dictionaryMap.put("COWBELL" ,"56"); dictionaryMap.put("CRASH_CYMBAL_2" ,"57"); dictionaryMap.put("VIBRASLAP" ,"58"); dictionaryMap.put("RIDE_CYMBAL_2" ,"59"); dictionaryMap.put("HI_BONGO" ,"60"); dictionaryMap.put("LOW_BONGO" ,"61"); dictionaryMap.put("MUTE_HI_CONGA" ,"62"); dictionaryMap.put("OPEN_HI_CONGA" ,"63"); dictionaryMap.put("LOW_CONGA" ,"64"); dictionaryMap.put("HIGH_TIMBALE" ,"65"); dictionaryMap.put("LOW_TIMBALE" ,"66"); dictionaryMap.put("HIGH_AGOGO" ,"67"); dictionaryMap.put("LOW_AGOGO" ,"68"); dictionaryMap.put("CABASA" ,"69"); dictionaryMap.put("MARACAS" ,"70"); dictionaryMap.put("SHORT_WHISTLE" ,"71"); dictionaryMap.put("LONG_WHISTLE" ,"72"); dictionaryMap.put("SHORT_GUIRO" ,"73"); dictionaryMap.put("LONG_GUIRO" ,"74"); dictionaryMap.put("CLAVES" ,"75"); dictionaryMap.put("HI_WOOD_BLOCK" ,"76"); dictionaryMap.put("LOW_WOOD_BLOCK" ,"77"); dictionaryMap.put("MUTE_CUICA" ,"78"); dictionaryMap.put("OPEN_CUICA" ,"79"); dictionaryMap.put("MUTE_TRIANGLE" ,"80"); dictionaryMap.put("OPEN_TRIANGLE" ,"81"); // // Controller names // dictionaryMap.put("BANK_SELECT_COARSE" ,"0"); dictionaryMap.put("MOD_WHEEL_COARSE" ,"1"); dictionaryMap.put("BREATH_COARSE" ,"2"); dictionaryMap.put("FOOT_PEDAL_COARSE" ,"4"); dictionaryMap.put("PORTAMENTO_TIME_COARSE" ,"5"); dictionaryMap.put("DATA_ENTRY_COARSE" ,"6"); dictionaryMap.put("VOLUME_COARSE" ,"7"); dictionaryMap.put("BALANCE_COARSE" ,"8"); dictionaryMap.put("PAN_POSITION_COARSE" ,"10"); dictionaryMap.put("EXPRESSION_COARSE" ,"11"); dictionaryMap.put("EFFECT_CONTROL_1_COARSE" ,"12"); dictionaryMap.put("EFFECT_CONTROL_2_COARSE" ,"13"); dictionaryMap.put("SLIDER_1" ,"16"); dictionaryMap.put("SLIDER_2" ,"17"); dictionaryMap.put("SLIDER_3" ,"18"); dictionaryMap.put("SLIDER_4" ,"19"); dictionaryMap.put("BANK_SELECT_FINE" ,"32"); dictionaryMap.put("MOD_WHEEL_FINE" ,"33"); dictionaryMap.put("BREATH_FINE" ,"34"); dictionaryMap.put("FOOT_PEDAL_FINE" ,"36"); dictionaryMap.put("PORTAMENTO_TIME_FINE" ,"37"); dictionaryMap.put("DATA_ENTRY_FINE" ,"38"); dictionaryMap.put("VOLUME_FINE" ,"39"); dictionaryMap.put("BALANCE_FINE" ,"40"); dictionaryMap.put("PAN_POSITION_FINE" ,"42"); dictionaryMap.put("EXPRESSION_FINE" ,"43"); dictionaryMap.put("EFFECT_CONTROL_1_FINE" ,"44"); dictionaryMap.put("EFFECT_CONTROL_2_FINE" ,"45"); dictionaryMap.put("HOLD_PEDAL" ,"64"); dictionaryMap.put("HOLD" ,"64"); dictionaryMap.put("PORTAMENTO" ,"65"); dictionaryMap.put("SUSTENUTO_PEDAL" ,"66"); dictionaryMap.put("SUSTENUTO" ,"66"); dictionaryMap.put("SOFT_PEDAL" ,"67"); dictionaryMap.put("SOFT" ,"67"); dictionaryMap.put("LEGATO_PEDAL" ,"68"); dictionaryMap.put("LEGATO" ,"68"); dictionaryMap.put("HOLD_2_PEDAL" ,"69"); dictionaryMap.put("HOLD_2" ,"69"); dictionaryMap.put("SOUND_VARIATION" ,"70"); dictionaryMap.put("VARIATION" ,"70"); dictionaryMap.put("SOUND_TIMBRE" ,"71"); dictionaryMap.put("TIMBRE" ,"71"); dictionaryMap.put("SOUND_RELEASE_TIME" ,"72"); dictionaryMap.put("RELEASE_TIME" ,"72"); dictionaryMap.put("SOUND_ATTACK_TIME" ,"73"); dictionaryMap.put("ATTACK_TIME" ,"73"); dictionaryMap.put("SOUND_BRIGHTNESS" ,"74"); dictionaryMap.put("BRIGHTNESS" ,"74"); dictionaryMap.put("SOUND_CONTROL_6" ,"75"); dictionaryMap.put("CONTROL_6" ,"75"); dictionaryMap.put("SOUND_CONTROL_7" ,"76"); dictionaryMap.put("CONTROL_7" ,"76"); dictionaryMap.put("SOUND_CONTROL_8" ,"77"); dictionaryMap.put("CONTROL_8" ,"77"); dictionaryMap.put("SOUND_CONTROL_9" ,"78"); dictionaryMap.put("CONTROL_9" ,"78"); dictionaryMap.put("SOUND_CONTROL_10" ,"79"); dictionaryMap.put("CONTROL_10" ,"79"); dictionaryMap.put("GENERAL_PURPOSE_BUTTON_1" ,"80"); dictionaryMap.put("GENERAL_BUTTON_1" ,"80"); dictionaryMap.put("BUTTON_1" ,"80"); dictionaryMap.put("GENERAL_PURPOSE_BUTTON_2" ,"81"); dictionaryMap.put("GENERAL_BUTTON_2" ,"81"); dictionaryMap.put("BUTTON_2" ,"81"); dictionaryMap.put("GENERAL_PURPOSE_BUTTON_3" ,"82"); dictionaryMap.put("GENERAL_BUTTON_3" ,"82"); dictionaryMap.put("BUTTON_3" ,"82"); dictionaryMap.put("GENERAL_PURPOSE_BUTTON_4" ,"83"); dictionaryMap.put("GENERAL_BUTTON_4" ,"83"); dictionaryMap.put("BUTTON_4" ,"83"); dictionaryMap.put("EFFECTS_LEVEL" ,"91"); dictionaryMap.put("EFFECTS" ,"91"); dictionaryMap.put("TREMULO_LEVEL" ,"92"); dictionaryMap.put("TREMULO" ,"92"); dictionaryMap.put("CHORUS_LEVEL" ,"93"); dictionaryMap.put("CHORUS" ,"93"); dictionaryMap.put("CELESTE_LEVEL" ,"94"); dictionaryMap.put("CELESTE" ,"94"); dictionaryMap.put("PHASER_LEVEL" ,"95"); dictionaryMap.put("PHASER" ,"95"); dictionaryMap.put("DATA_BUTTON_INCREMENT" ,"96"); dictionaryMap.put("DATA_BUTTON_INC" ,"96"); dictionaryMap.put("BUTTON_INC" ,"96"); dictionaryMap.put("DATA_BUTTON_DECREMENT" ,"97"); dictionaryMap.put("DATA_BUTTON_DEC" ,"97"); dictionaryMap.put("BUTTON_DEC" ,"97"); dictionaryMap.put("NON_REGISTERED_COARSE" ,"98"); dictionaryMap.put("NON_REGISTERED_FINE" ,"99"); dictionaryMap.put("REGISTERED_COARSE" ,"100"); dictionaryMap.put("REGISTERED_FINE" ,"101"); dictionaryMap.put("ALL_SOUND_OFF" ,"120"); dictionaryMap.put("ALL_CONTROLLERS_OFF" ,"121"); dictionaryMap.put("LOCAL_KEYBOARD" ,"122"); dictionaryMap.put("ALL_NOTES_OFF" ,"123"); dictionaryMap.put("OMNI_MODE_OFF" ,"124"); dictionaryMap.put("OMNI_OFF" ,"124"); dictionaryMap.put("OMNI_MODE_ON" ,"125"); dictionaryMap.put("OMNI_ON" ,"125"); dictionaryMap.put("MONO_OPERATION" ,"126"); dictionaryMap.put("MONO" ,"126"); dictionaryMap.put("POLY_OPERATION" ,"127"); dictionaryMap.put("POLY" ,"127"); // // Combined Controller names // (index = coarse_controller_index * 128 + fine_controller_index) // dictionaryMap.put("BANK_SELECT" ,"16383"); dictionaryMap.put("MOD_WHEEL" ,"161"); dictionaryMap.put("BREATH" ,"290"); dictionaryMap.put("FOOT_PEDAL" ,"548"); dictionaryMap.put("PORTAMENTO_TIME" ,"677"); dictionaryMap.put("DATA_ENTRY" ,"806"); dictionaryMap.put("VOLUME" ,"935"); dictionaryMap.put("BALANCE" ,"1064"); dictionaryMap.put("PAN_POSITION" ,"1322"); dictionaryMap.put("EXPRESSION" ,"1451"); dictionaryMap.put("EFFECT_CONTROL_1" ,"1580"); dictionaryMap.put("EFFECT_CONTROL_2" ,"1709"); dictionaryMap.put("NON_REGISTERED" ,"12770"); dictionaryMap.put("REGISTERED" ,"13028"); // // Values for controllers // dictionaryMap.put("ON" ,"127"); dictionaryMap.put("OFF" ,"0"); dictionaryMap.put("DEFAULT" ,"64"); // // Tempo values // (NEW for JFugue 4.0) dictionaryMap.put("GRAVE" ,"40"); dictionaryMap.put("LARGO" ,"45"); dictionaryMap.put("LARGHETTO" ,"50"); dictionaryMap.put("LENTO" ,"55"); dictionaryMap.put("ADAGIO" ,"60"); dictionaryMap.put("ADAGIETTO" ,"65"); dictionaryMap.put("ANDANTE" ,"70"); dictionaryMap.put("ANDANTINO" ,"80"); dictionaryMap.put("MODERATO" ,"95"); dictionaryMap.put("ALLEGRETTO" ,"110"); dictionaryMap.put("ALLEGRO" ,"120"); dictionaryMap.put("VIVACE" ,"145"); dictionaryMap.put("PRESTO" ,"180"); dictionaryMap.put("PRETISSIMO" ,"220"); } }jfugue/org/jfugue/StreamingMidiRenderer.java0000644000175000017500000001262411001732464021716 0ustar giovannigiovanni/* * JFugue - API for Music Programming * Copyright (C) 2003-2007 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.Sequence; import javax.sound.midi.ShortMessage; /** * Assists the StreamingPlayer in converting Patterns to MIDI. * *@see StreamingPlayer *@author David Koelle *@version 3.2 */ public class StreamingMidiRenderer implements ParserListener { private StreamingMidiEventManager eventManager; private MusicStringParser parser; long initialNoteTime = 0; /** * Instantiates a Renderer */ public StreamingMidiRenderer() { this.parser = new MusicStringParser(); this.parser.addParserListener(this); reset(); } /** * Creates a new MidiEventManager. If this isn't called, * events from multiple calls to render() will be added * to the same eventManager, which means that the second * time render() is called, it will contain music left over * from the first time it was called. (This wasn't a problem * with Java 1.4) * @since 3.0 */ public void reset() { this.eventManager = new StreamingMidiEventManager(); } // ParserListener methods //////////////////////////// public void voiceEvent(Voice voice) { this.eventManager.setCurrentTrack(voice.getVoice()); } public void tempoEvent(Tempo tempo) { // this.parser.setTempo(tempo.getTempo()); } public void instrumentEvent(Instrument instrument) { this.eventManager.addEvent(ShortMessage.PROGRAM_CHANGE, instrument.getInstrument(), 0); } public void layerEvent(Layer layer) { this.eventManager.setCurrentLayer(layer.getLayer()); } public void timeEvent(Time time) { this.eventManager.setTrackTimer(time.getTime()); } public void measureEvent(Measure measure) { // No MIDI is generated when a measure indicator is identified. } public void keySignatureEvent(KeySignature keySig) { this.eventManager.addMetaMessage(0x59, new byte[] { keySig.getKeySig(), keySig.getScale() }); } public void controllerEvent(Controller controller) { this.eventManager.addEvent(ShortMessage.CONTROL_CHANGE, controller.getIndex(), controller.getValue()); } public void channelPressureEvent(ChannelPressure channelPressure) { this.eventManager.addEvent(ShortMessage.CHANNEL_PRESSURE, channelPressure.getPressure()); } public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { this.eventManager.addEvent(ShortMessage.POLY_PRESSURE, polyphonicPressure.getKey(), polyphonicPressure.getPressure()); } public void pitchBendEvent(PitchBend pitchBend) { this.eventManager.addEvent(ShortMessage.PITCH_BEND, pitchBend.getBend()[0], pitchBend.getBend()[1]); } public void noteEvent(Note note) { // Remember the current track time, so we can flip back to it // if there are other notes to play in parallel this.initialNoteTime = this.eventManager.getTrackTimer(); long duration = note.getDuration(); boolean noteOn = !note.isEndOfTie(); boolean noteOff = !note.isStartOfTie(); // Add messages to the track if (note.isRest()) { this.eventManager.advanceTrackTimer(note.getDuration()); } else { initialNoteTime = eventManager.getTrackTimer(); byte attackVelocity = note.getAttackVelocity(); byte decayVelocity = note.getDecayVelocity(); this.eventManager.addNoteEvents(note.getValue(), attackVelocity, decayVelocity, duration, noteOn, noteOff); } } public void sequentialNoteEvent(Note note) { throw new UnsupportedOperationException("Sequential notes (declared using an underscore character) are not supported by JFugue's StreamingMidiRenderer"); } public void parallelNoteEvent(Note note) { long duration = note.getDuration(); this.eventManager.setTrackTimer(this.initialNoteTime); if (note.isRest()) { this.eventManager.advanceTrackTimer(note.getDuration()); } else { byte attackVelocity = note.getAttackVelocity(); byte decayVelocity = note.getDecayVelocity(); this.eventManager.addNoteEvents(note.getValue(), attackVelocity, decayVelocity, duration, !note.isEndOfTie(), !note.isStartOfTie()); } } public void close() { this.eventManager.close(); } } jfugue/org/jfugue/IntelligentDeviceResolver.java0000644000175000017500000000451610776766112022633 0ustar giovannigiovanni/* * 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.MidiDevice; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; public final class IntelligentDeviceResolver { public static MidiDevice selectReceiverDevice() throws MidiUnavailableException { return selectDevice("midi", "usb", "out"); } public static MidiDevice selectTransmitterDevice() throws MidiUnavailableException { return selectDevice("midi", "usb", "in"); } public static MidiDevice selectDevice(String... keywords) throws MidiUnavailableException { int bestMatch = 0; int thisMatch = 0; MidiDevice device = null; MidiDevice.Info[] info = MidiSystem.getMidiDeviceInfo(); for (int i=0; i < info.length; i++) { String infoString = info[i].toString().toLowerCase(); thisMatch = 0; for (int k=0; k < keywords.length; k++) { if (infoString.contains(keywords[k])) { thisMatch++; } } if (thisMatch > bestMatch) { device = MidiSystem.getMidiDevice(info[i]); bestMatch = thisMatch; } } if (device == null) { throw new JFugueException(JFugueException.INTELLIGENT_RESOLVER_FAILED); } return device; } } jfugue/org/jfugue/Player.java0000644000175000017500000003530311066775416016745 0ustar giovannigiovanni/* * 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 java.io.File; import java.io.IOException; import java.net.URL; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MetaEventListener; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiChannel; import javax.sound.midi.MidiFileFormat; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import javax.sound.midi.Synthesizer; /** * Prepares a pattern to be turned into music by the Renderer. This class * also handles saving the sequence derived from a pattern as a MIDI file. * *@see MidiRenderer *@see Pattern *@author David Koelle *@version 2.0 */ public class Player { private Sequencer sequencer; private MusicStringParser parser; private MidiRenderer renderer; private float sequenceTiming = Sequence.PPQ; private int resolution = 120; private boolean paused = false; private boolean started = false; private boolean finished = false; /** * Instantiates a new Player object, which is used for playing music. */ public Player() { this(true); } /** * Instantiates a new Player object, which is used for playing music. * The connected parameter is passed directly to MidiSystem.getSequencer. * Pass false when you do not want to copy a live synthesizer - for example, * if your Player is on a server, and you don't want to create new synthesizers every time * the constructor is called. */ public Player(boolean connected) { try { // Get default sequencer. setSequencer(MidiSystem.getSequencer(connected)); // use non connected sequencer so no copy of live synthesizer will be created. } catch (MidiUnavailableException e) { throw new JFugueException(JFugueException.SEQUENCER_DEVICE_NOT_SUPPORTED_WITH_EXCEPTION + e.getMessage()); } initParser(); } /** * Creates a new Player instance using a Sequencer that you have provided. * @param sequencer The Sequencer to send the MIDI events */ public Player(Sequencer sequencer) { setSequencer(sequencer); initParser(); } /** * Creates a new Player instance using a Sequencer obtained from the Synthesizer that you have provided. * @param synth The Synthesizer you want to use for this Player. */ public Player(Synthesizer synth) throws MidiUnavailableException { this(Player.getSequencerConnectedToSynthesizer(synth)); } private void initParser() { this.parser = new MusicStringParser(); this.renderer = new MidiRenderer(sequenceTiming, resolution); this.parser.addParserListener(this.renderer); } private void initSequencer() { // Close the sequencer and synthesizer getSequencer().addMetaEventListener(new MetaEventListener() { public void meta(MetaMessage event) { if (event.getType() == 47) { close(); } } }); } private void openSequencer() { if (getSequencer() == null) { throw new JFugueException(JFugueException.SEQUENCER_DEVICE_NOT_SUPPORTED); } // Open the sequencer, if it is not already open if (!getSequencer().isOpen()) { try { getSequencer().open(); } catch (MidiUnavailableException e) { throw new JFugueException(JFugueException.SEQUENCER_DEVICE_NOT_SUPPORTED_WITH_EXCEPTION + e.getMessage()); } } } /** * Plays a pattern by setting up a Renderer and feeding the pattern to it. * @param pattern the pattern to play * @see MidiRenderer */ public void play(Pattern pattern) { Sequence sequence = getSequence(pattern); play(sequence); } /** * Plays a pattern by setting up a Renderer and feeding the pattern to it. * @param pattern the pattern to play * @see MidiRenderer */ public void play(Rhythm rhythm) { Pattern pattern = rhythm.getPattern(); Sequence sequence = getSequence(pattern); play(sequence); } /** * Plays a MIDI Sequence * @param sequence the Sequence to play * @throws JFugueException if there is a problem playing the music * @see MidiRenderer */ private void play(Sequence sequence) { // Open the sequencer openSequencer(); // Set the sequence try { getSequencer().setSequence(sequence); } catch (Exception e) { throw new JFugueException(JFugueException.ERROR_PLAYING_MUSIC + e.getMessage()); } setStarted(true); // Start the sequence getSequencer().start(); // Wait for the sequence to finish while (isPlaying() || isPaused()) { try { Thread.sleep(20); // don't hog all of the CPU } catch (InterruptedException e) { throw new JFugueException(JFugueException.ERROR_SLEEP); } } // Close the sequencer getSequencer().close(); setStarted(false); setFinished(true); } /** * Plays a string of music. Be sure to call player.close() after play() has returned. * @param musicString the MusicString (JFugue-formatted string) to play * @version 3.0 */ public void play(String musicString) { if (musicString.indexOf(".mid") > 0) { // If the user tried to call this method with "filename.mid" or "filename.midi", throw the following exception throw new JFugueException(JFugueException.PLAYS_STRING_NOT_FILE_EXC); } Pattern pattern = new Pattern(musicString); play(pattern); } /** * Plays a MIDI file, without doing any conversions to MusicStrings. * Be sure to call player.close() after play() has returned. * @param file the MIDI file to play * @throws IOException * @throws InvalidMidiDataException * @version 3.0 */ public void playMidiDirectly(File file) throws IOException, InvalidMidiDataException { Sequence sequence = MidiSystem.getSequence(file); play(sequence); } /** * Plays a URL that contains a MIDI sequence. Be sure to call player.close() after play() has returned. * @param url the URL to play * @throws IOException * @throws InvalidMidiDataException * @version 3.0 */ public void playMidiDirectly(URL url) throws IOException, InvalidMidiDataException { Sequence sequence = MidiSystem.getSequence(url); play(sequence); } public void play(Anticipator anticipator, Pattern pattern, long offset) { Sequence sequence = getSequence(pattern); Sequence sequence2 = getSequence(pattern); play(anticipator, sequence, sequence2, offset); } public void play(Anticipator anticipator, Sequence sequence, Sequence sequence2, long offset) { anticipator.play(sequence); if (offset > 0) { try { Thread.sleep(offset); } catch (InterruptedException e) { throw new JFugueException(JFugueException.ERROR_SLEEP); } } play(sequence2); } /** * 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()); } } private void setStarted(boolean started) { this.started = started; } private void setFinished(boolean finished) { this.finished = finished; } public boolean isStarted() { return this.started; } public boolean isFinished() { return this.finished; } public boolean isPlaying() { return getSequencer().isRunning(); } public boolean isPaused() { return paused; } public void pause() { paused = true; if (isPlaying()) { getSequencer().stop(); } } public void resume() { paused = false; getSequencer().start(); } public void stop() { paused = false; getSequencer().stop(); getSequencer().setMicrosecondPosition(0); } public void jumpTo(long microseconds) { getSequencer().setMicrosecondPosition(microseconds); } public long getSequenceLength(Sequence sequence) { return sequence.getMicrosecondLength(); } public long getSequencePosition() { return getSequencer().getMicrosecondPosition(); } /** * Saves the MIDI data from a pattern into a file. * @param pattern the pattern to save * @param file the File to save the pattern to. Should include file extension, such as .mid */ public void saveMidi(Pattern pattern, File file) throws IOException { Sequence sequence = getSequence(pattern); int[] writers = MidiSystem.getMidiFileTypes(sequence); if (writers.length == 0) return; MidiSystem.write(sequence, writers[0], file); } /** * Saves the MIDI data from a MusicString into a file. * @param musicString the MusicString to save * @param file the File to save the MusicString to. Should include file extension, such as .mid */ public void saveMidi(String musicString, File file) throws IOException { Pattern pattern = new Pattern(musicString); saveMidi(pattern, file); } /** * Parses a MIDI file and returns a Pattern. This is an excellent example * of JFugue's Parser-Renderer architecture: * *
     *  MidiParser parser = new MidiParser();
     *  MusicStringRenderer renderer = new MusicStringRenderer();
     *  parser.addParserListener(renderer);
     *  parser.parse(sequence);
     * 
* * @param filename The name of the MIDI file * @return a Pattern containing the MusicString representing the MIDI music * @throws IOException If there is a problem opening the MIDI file * @throws InvalidMidiDataException If there is a problem obtaining MIDI resources */ public Pattern loadMidi(File file) throws IOException, InvalidMidiDataException { MidiFileFormat format = MidiSystem.getMidiFileFormat(file); this.sequenceTiming = format.getDivisionType(); this.resolution = format.getResolution(); return Pattern.loadMidi(file); } public static void allNotesOff() { try { allNotesOff(MidiSystem.getSynthesizer()); } catch (MidiUnavailableException e) { throw new JFugueException(JFugueException.GENERAL_ERROR); } } /** * Stops all notes from playing on all MIDI channels. */ public static void allNotesOff(Synthesizer synth) { try { if (!synth.isOpen()) { synth.open(); } MidiChannel[] channels = synth.getChannels(); for (int i=0; i < channels.length; i++) { channels[i].allNotesOff(); } } catch (MidiUnavailableException e) { throw new JFugueException(JFugueException.GENERAL_ERROR); } } /** * 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; } private void setSequencer(Sequencer sequencer) { this.sequencer = sequencer; initSequencer(); } /** * Returns the sequence containing the MIDI data from the given pattern. * @return the Sequence from the given pattern */ public Sequence getSequence(Pattern pattern) { this.renderer.reset(); this.parser.parse(pattern); Sequence sequence = this.renderer.getSequence(); return sequence; } /** * Returns an instance of a Sequencer that uses the provided Synthesizer as its receiver. * This is useful when you have made changes to a specific Synthesizer--for example, you've * loaded in new patches--that you want the Sequencer to use. You can then pass the Sequencer * to the Player constructor. * * @param synth The Synthesizer to use as the receiver for the returned Sequencer * @return a Sequencer with the provided Synthesizer as its receiver * @throws MidiUnavailableException * @version 4.0 */ public static Sequencer getSequencerConnectedToSynthesizer(Synthesizer synth) throws MidiUnavailableException { Sequencer sequencer = MidiSystem.getSequencer(false); // Get Sequencer which is not connected to new Synthesizer. sequencer.open(); if (!synth.isOpen()) { synth.open(); } sequencer.getTransmitter().setReceiver(synth.getReceiver()); // Connect the Synthesizer to our synthesizer instance. return sequencer; } }jfugue/org/jfugue/MusicStringParser.java0000644000175000017500000021503711067767614021143 0ustar giovannigiovanni/* * 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 java.util.HashMap; import java.util.Map; /** * Parses music strings, and fires events for ParserListener interfaces * when tokens are interpreted. The ParserListener does intelligent things * with the resulting events, such as create music, draw sheet music, or * transform the data. * * As of Version 3.0, the Parser supports turning MIDI Sequences into JFugue Patterns with the parse(Sequence) * method. In this case, the ParserListeners established by a ParserBuilder use the parsed * events to construct the Pattern string. * *@author David Koelle *@version 3.0 *@version 4.0 - Note parsing split up into many separate methods; verification added for testing purposes */ public final class MusicStringParser extends Parser { private Map dictionaryMap; private byte keySig = 0; /** * Creates a new Parser object, and populates the dictionary with initial entries. * @see JFugueDefinitions */ public MusicStringParser() { dictionaryMap = new HashMap(); JFugueDefinitions.populateDictionary(dictionaryMap); } /** * Parses a Pattern and fires events to subscribed ParserListener * interfaces. As the Pattern is parsed, events are sent * to ParserLisener interfaces, which are responsible for doing * something interesting with the music data, such as playing the music, * displaying it as sheet music, or transforming the pattern. * *

* The parser breaks a music string into tokens, which are separated by spaces. * It then determines the type of command based on the first character of the * token. If the parser does not recognize the first character of the token, * which is limited to the command letters (K, V, T, I, L, X, #, $, @, &, +, *, |), * the notes (A, B, C, D, E, F, G, R), * and the open-bracket character ( [ ), then the token will be ignored. *

* * @param pattern the Pattern to parse * @throws Exception if there is an error parsing the pattern */ public void parse(Pattern pattern) throws JFugueException { String[] tokens = pattern.getTokens(); // If the user hasn't specified a tempo as the first token, use the default of 120 if (tokens.length > 0) { if (tokens[0].toUpperCase().charAt(0) != 'T') { parseTempoElement("T120"); } } int counter = 0; for (int t=0; t < tokens.length; t++) { parseToken(tokens[t]); counter++; fireProgressReported("Parsing music string...", counter, tokens.length); } } /** * This method takes a single token, and distributes it to a specific * element parser based on the first character in the string. * If the parser does not recognize the first character of the string, * the token will be ignored. * * @param s the single token to parse * @throws JFugueException if there is a problem parsing the string */ private void parseToken(String s) throws JFugueException { // If there are any spaces, get out if (s.indexOf(" ") != -1) { throw new JFugueException(JFugueException.PARSER_SPACES_EXC,s,s); } s = s.toUpperCase(); trace("--------Processing Token: ",s); switch(s.charAt(0)) { case 'V' : parseVoiceElement(s); break; case 'T' : parseTempoElement(s); break; case 'I' : parseInstrumentElement(s); break; case 'L' : parseLayerElement(s); break; // New in 3.0 case 'K' : parseKeySignatureElement(s); break; // New in 3.0 case 'X' : parseControllerElement(s); break; // New in 2.0 case '@' : parseTimeElement(s); break; // New in 3.0 case '*' : parsePolyPressureElement(s); break; // New in 3.0, also known as Key Pressure case '+' : parseChannelPressureElement(s); break; // New in 3.0 case '&' : parsePitchBendElement(s); break; // New in 3.0 case '|' : parseMeasureElement(s); break; // New in 3.0 case '$' : parseDictionaryElement(s); break; // New in 2.0 case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' : case 'G' : case 'R' : case '[' : parseNoteElement(s); break; default : break; // Unknown characters are okay } } /** * Parses a voice element. * @param s the token that contains a voice element * @throws JFugueException if there is a problem parsing the element */ private void parseVoiceElement(String s) throws JFugueException { String voiceNumberString = s.substring(1,s.length()); byte voiceNumber = getByteFromDictionary(voiceNumberString); if (voiceNumber > 15) { // throw new JFugueException(JFugueException.VOICE_EXC,voiceNumberString,s); return; } trace("Voice element: voice = ",voiceNumber); fireVoiceEvent(new Voice(voiceNumber)); } /** * Parses a tempo element. * As of JFugue 4.0, Tempo can be specified in Beats Per Minute, which is much more intuitive than * the original Milliseconds Per Quarter Note. To maintain compatibility with existing JFugue * Music Strings, those wishing to specify Tempo using BPM need to use the full word "Tempo" in * their music string, instead of just the initial "T". * To summarize: * "Tempo120" (or "Tempo[Allegro]") --> Tempo will be is 120 beats per minute * "T120" --> Tempo will be 120 milliseconds per beat. Divide into 60000000 to get BPM. * @param s the token that contains a tempo element * @throws JFugueException if there is a problem parsing the element */ private void parseTempoElement(String s) throws JFugueException { String tempoNumberString = s.substring(1,s.length()); int tempoNumber = getIntFromDictionary(tempoNumberString); trace("Tempo element: tempo = ",tempoNumber); fireTempoEvent(new Tempo(tempoNumber)); } /** * Parses an instrument element. * @param s the token that contains an instrument element * @throws JFugueException if there is a problem parsing the element */ private void parseInstrumentElement(String s) throws JFugueException { String instrumentNumberString = s.substring(1,s.length()); byte instrumentNumber = getByteFromDictionary(instrumentNumberString); trace("Instrument element: instrument = ",instrumentNumber); fireInstrumentEvent(new Instrument(instrumentNumber)); } /** * Parses a layer element. * @param s the token that contains a layer element * @throws JFugueException if there is a problem parsing the element */ private void parseLayerElement(String s) throws JFugueException { String layerNumberString = s.substring(1,s.length()); byte layerNumber = getByteFromDictionary(layerNumberString); trace("Layer element: layer = ",layerNumber); fireLayerEvent(new Layer(layerNumber)); } /** * Parses a time element. * @param s the token that contains a time element * @throws JFugueException if there is a problem parsing the element */ private void parseTimeElement(String s) throws JFugueException { String timeNumberString = s.substring(1,s.length()); long timeNumber = getLongFromDictionary(timeNumberString); trace("Time element: time = ",timeNumber); fireTimeEvent(new Time(timeNumber)); } /** * Parses a key signature element. * @param s the token that contains a key signature * @throws JFugueException if there is a problem parsing the element */ private void parseKeySignatureElement(String s) throws JFugueException { String rootNote = null; String majOrMin = null; if (s.length() == 5) { rootNote = s.substring(1, 5); majOrMin = s.substring(2, 5); } else { rootNote = s.substring(1, 6); majOrMin = s.substring(3, 6); } trace("Key signature element: root=",rootNote," majOrMin=",majOrMin); if (!(majOrMin.equalsIgnoreCase("MAJ") || (majOrMin.equalsIgnoreCase("MIN")))) { throw new JFugueException(JFugueException.KEYSIG_EXC, majOrMin, s); } int scale = (majOrMin.equalsIgnoreCase("MAJ") ? 0 : 1); int keySig = 0; if (rootNote.equalsIgnoreCase("CBMAJ") || rootNote.equalsIgnoreCase("ABMIN")) keySig = -7; else if (rootNote.equalsIgnoreCase("GBMAJ") || rootNote.equalsIgnoreCase("EBMIN")) keySig = -6; else if (rootNote.equalsIgnoreCase("DBMAJ") || rootNote.equalsIgnoreCase("BBMIN")) keySig = -5; else if (rootNote.equalsIgnoreCase("ABMAJ") || rootNote.equalsIgnoreCase("FMIN")) keySig = -4; else if (rootNote.equalsIgnoreCase("EBMAJ") || rootNote.equalsIgnoreCase("CMIN")) keySig = -3; else if (rootNote.equalsIgnoreCase("BBMAJ") || rootNote.equalsIgnoreCase("GMIN")) keySig = -2; else if (rootNote.equalsIgnoreCase("FMAJ") || rootNote.equalsIgnoreCase("DMIN")) keySig = -1; else if (rootNote.equalsIgnoreCase("CMAJ") || rootNote.equalsIgnoreCase("AMIN")) keySig = 0; else if (rootNote.equalsIgnoreCase("GMAJ") || rootNote.equalsIgnoreCase("EMIN")) keySig = +1; else if (rootNote.equalsIgnoreCase("DMAJ") || rootNote.equalsIgnoreCase("BMIN")) keySig = +2; else if (rootNote.equalsIgnoreCase("AMAJ") || rootNote.equalsIgnoreCase("F#MIN")) keySig = +3; else if (rootNote.equalsIgnoreCase("EMAJ") || rootNote.equalsIgnoreCase("C#MIN")) keySig = +4; else if (rootNote.equalsIgnoreCase("BMAJ") || rootNote.equalsIgnoreCase("G#MIN")) keySig = +5; else if (rootNote.equalsIgnoreCase("F#MAJ") || rootNote.equalsIgnoreCase("D#MIN")) keySig = +6; else if (rootNote.equalsIgnoreCase("C#MAJ") || rootNote.equalsIgnoreCase("A#MIN")) keySig = +7; else { throw new JFugueException(JFugueException.KEYSIG_EXC,s); } trace("Key signature: sig=",keySig," scale=",scale); fireKeySignatureEvent(new KeySignature((byte)keySig, (byte)scale)); this.keySig = (byte)keySig; } /** * Parses a measure element. * @param s the token that contains a measure element * @throws JFugueException if there is a problem parsing the element */ private void parseMeasureElement(String s) throws JFugueException { trace("Measure element."); fireMeasureEvent(new Measure()); } /** * Parses a controller element. * @param s the token that contains a controller element * @throws JFugueException if there is a problem parsing the element */ private void parseControllerElement(String s) throws JFugueException { int indexOfEquals = s.indexOf("="); if (-1 == indexOfEquals) { throw new JFugueException(JFugueException.CONTROL_FORMAT_EXC,s,s); } // // Get the Control Index from this token. The Control Index can be one // of two things: // 1. A byte. In this case, simply use the controller event referred to // by that byte. // 2. An int. In this case, the coarse adjuster is the high bits (div), // and the fine adjuster is the low bits (mod). // String controlIndexString = s.substring(1,indexOfEquals); byte controlIndex = 0; int controlIndexInt = -1; try { controlIndex = getByteFromDictionary(controlIndexString); } catch (JFugueException e) { controlIndexInt = getIntFromDictionary(controlIndexString); } String controlValueString = s.substring(indexOfEquals+1,s.length()); // An int was found as the Contoller Index number. Therefore, assume // that the value passed to this Index is also an int, and should be // divided among multiple controllers if (-1 != controlIndexInt) { int controlValue = getIntFromDictionary(controlValueString); byte coarseIndex = (byte)(controlIndexInt / 128); byte fineIndex = (byte)(controlIndexInt % 128); // Special case for BANK_SELECT, which has a high byte of 0 if (16383 == controlValue) { coarseIndex = 0; fineIndex = 32; } byte coarseValue = (byte)(controlValue / 128); byte fineValue = (byte)(controlValue % 128); trace("Combined controller element: coarse-index = ",coarseIndex,", coarse-value = ",coarseValue,"; fine-index = ",fineIndex,", fine-value = ",fineValue); fireControllerEvent(new Controller(coarseIndex, coarseValue)); fireControllerEvent(new Controller(fineIndex, fineValue)); } else { byte controlValue = getByteFromDictionary(controlValueString); trace("Controller element: index = ",controlIndex,", value = ",controlValue); fireControllerEvent(new Controller(controlIndex, controlValue)); } } /** * Parses a channel pressure element. * @param s the token that contains a channel pressure element * @throws JFugueException if there is a problem parsing the element */ private void parseChannelPressureElement(String s) throws JFugueException { // A ChannelPressure token looks like this: // +pressure // // where "pressure" can each be bytes or dictionary items String pressureString = s.substring(1,s.length()); byte pressureNumber = getByteFromDictionary(pressureString); trace("ChannelPressure element: pressure = ",pressureNumber); fireChannelPressureEvent(new ChannelPressure(pressureNumber)); } /** * Parses a polyphonic pressure element. * @param s the token that contains a polyphonic pressure element * @throws JFugueException if there is a problem parsing the element */ private void parsePolyPressureElement(String s) throws JFugueException { // A PolyphonicPressure token looks like this: // *key,pressure // // where "key" and "pressure" can each be bytes or dictionary items String keyString = s.substring(1,s.indexOf(',')); byte keyNumber = getByteFromDictionary(keyString); String pressureString = s.substring(s.indexOf(',')+1, s.length()); byte pressureNumber = getByteFromDictionary(pressureString); trace("PolyphonicPressure element: key = ",keyNumber,", pressure = ",pressureNumber); firePolyphonicPressureEvent(new PolyphonicPressure(keyNumber, pressureNumber)); } /** * Parses a pitch bend element. * @param s the token that contains a pitch bend pressure element * @throws JFugueException if there is a problem parsing the element */ private void parsePitchBendElement(String s) throws JFugueException { // A PitchBend token looks like one of the following: // &lsb,msb // &int // // where "byte1" and "byte2" or "int" can be bytes/ints or dictionary items byte lsb = 0; byte msb = 0; if (s.indexOf(',') > -1) { // We're dealing with two bytes String b1String = s.substring(1,s.indexOf(',')); lsb = getByteFromDictionary(b1String); String b2String = s.substring(s.indexOf(',')+1, s.length()); msb = getByteFromDictionary(b2String); } else { // We're dealing with a single integer, which we will break into bytes String valueString = s.substring(1,s.length()); int value = getIntFromDictionary(valueString); lsb = (byte)(value % 128); msb = (byte)(value / 128); } trace("PitchBend element: byte1 = ",lsb,", byte2 = ",msb); firePitchBendEvent(new PitchBend(lsb, msb)); } /** * Parses a dictionary element. * @param s the token that contains a dictionary element * @throws JFugueException if there is a problem parsing the element */ private void parseDictionaryElement(String s) throws JFugueException { int indexOfEquals = s.indexOf("="); String word = s.substring(1,indexOfEquals); String definition = s.substring(indexOfEquals+1,s.length()); // Replace tilde's with spaces. I don't think this will work, though, since the // MusicString has already been tokenized. definition.replace('~', ' '); word = word.toUpperCase(); trace("Dictionary Definition element: word = ",word,", value = ",definition); dictionaryMap.put(word, definition); } class NoteContext { boolean isRest = false; boolean isNumericNote = false; boolean isChord = false; boolean isFirstNote = true; boolean isSequentialNote = false; boolean isParallelNote = false; boolean isNatural = false; boolean existAnotherNote = true; boolean anotherNoteIsSequential = false; boolean anotherNoteIsParallel = false; boolean isStartOfTie = false; boolean isEndOfTie = false; byte[] halfsteps = new byte[5]; byte numHalfsteps = 0; byte noteNumber = 0; int octaveNumber = 0; double decimalDuration = 0.0; long duration = 0L; byte attackVelocity = Note.DEFAULT_VELOCITY; byte decayVelocity = Note.DEFAULT_VELOCITY; public NoteContext() { for (int i=0; i < 5; i++) { halfsteps[i] = 0; } } } /** * Parses a note element. * @param s the token that contains a note element * @throws JFugueException if there is a problem parsing the element */ private void parseNoteElement(String s) throws JFugueException { NoteContext context = new NoteContext(); while (context.existAnotherNote) { trace("--Parsing note from token "+s); decideSequentialOrParallel(context); int index = 0; int slen = s.length(); // We pass the length of the string because it is an invariant value that is used often index = parseNoteRoot(s, slen, index, context); index = parseNoteOctave(s, slen, index, context); index = parseNoteChord(s, slen, index, context); computeNoteValue(context); index = parseNoteChordInversion(s, slen, index, context); index = parseNoteDuration(s, slen, index, context); index = parseNoteVelocity(s, slen, index, context); s = parseNoteConnector(s, slen, index, context); fireNoteEvents(context); } } private void decideSequentialOrParallel(NoteContext context) { // Test whether this note is already known to be sequential (was connected with _) or parallel (was connected with +) context.isSequentialNote = false; if (context.anotherNoteIsSequential) { context.isSequentialNote = true; context.anotherNoteIsSequential = false; trace("This note is sequential"); } context.isParallelNote = false; if (context.anotherNoteIsParallel) { context.isParallelNote = true; context.anotherNoteIsParallel = false; trace("This note is parallel"); } } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseNoteRoot(String s, int slen, int index, NoteContext context) { switch (s.charAt(index)) { case '[' : return parseNumericNote(s, slen, index, context); case 'R' : return parseRest(s, slen, index, context); default : return parseLetterNote(s, slen, index, context); } } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseNumericNote(String s, int slen, int index, NoteContext context) { int indexOfEndBracket = s.indexOf(']', index); String stringInBrackets = s.substring(1,indexOfEndBracket); context.noteNumber = getByteFromDictionary(stringInBrackets); context.isNumericNote = true; trace("This note is a numeric note with value ", context.noteNumber); return indexOfEndBracket+1; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseRest(String s, int slen, int index, NoteContext context) { context.isRest = true; trace("This note is a Rest"); return index+1; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseLetterNote(String s, int slen, int index, NoteContext context) { switch(s.charAt(index)) { case 'C' : context.noteNumber = 0; break; case 'D' : context.noteNumber = 2; break; case 'E' : context.noteNumber = 4; break; case 'F' : context.noteNumber = 5; break; case 'G' : context.noteNumber = 7; break; case 'A' : context.noteNumber = 9; break; case 'B' : context.noteNumber = 11; break; default : throw new JFugueException(JFugueException.NOTE_EXC, s); } index++; // Check for #, b, or n (sharp, flat, or natural) modifier boolean checkForModifiers = true; while (checkForModifiers) { if (index < slen) { switch(s.charAt(index)) { case '#' : index++; context.noteNumber++; /*if (context.noteNumber == 12) context.noteNumber = 0; */ break; case 'B' : index++; context.noteNumber--; /*if (context.noteNumber == -1) context.noteNumber = 11;*/ break; case 'N' : index++; context.isNatural = true; checkForModifiers = false; break; default : checkForModifiers = false; break; } } else { checkForModifiers = false; } } trace("Note number within an octave (C=0, B=11): ", context.noteNumber); return index; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseNoteOctave(String s, int slen, int index, NoteContext context) { // Don't parse an octave for a rest or a numeric note if (context.isRest || context.isNumericNote) { return index; } // Check for octave. Remember that octaves are optional. char possibleOctave1 = '.'; char possibleOctave2 = '.'; if (index < slen) { possibleOctave1 = s.charAt(index); } if (index+1 < slen) { possibleOctave2 = s.charAt(index+1); } byte definiteOctaveLength = 0; if ((possibleOctave1 >= '0') && (possibleOctave1 <= '9')) { definiteOctaveLength = 1; if ((possibleOctave2 >= '0') && (possibleOctave2 <= '9')) { definiteOctaveLength = 2; } String octaveNumberString = s.substring(index, index+definiteOctaveLength); try { context.octaveNumber = Byte.parseByte(octaveNumberString); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.OCTAVE_EXC, octaveNumberString, s); } if (context.octaveNumber > 10) { throw new JFugueException(JFugueException.OCTAVE_EXC, octaveNumberString, s); } } return index+definiteOctaveLength; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseNoteChord(String s, int slen, int index, NoteContext context) { // Don't parse chord for a rest if (context.isRest) { return index; } String possibleChord3 = null; String possibleChord4 = null; String possibleChord5 = null; String possibleChord6 = null; String possibleChord7 = null; String possibleChord8 = null; try { possibleChord3 = s.substring(index, index+3); possibleChord4 = s.substring(index, index+4); possibleChord5 = s.substring(index, index+5); possibleChord6 = s.substring(index, index+6); possibleChord7 = s.substring(index, index+7); possibleChord8 = s.substring(index, index+8); } catch (IndexOutOfBoundsException e) { // Nothing to do... just needed to catch } int lengthOfChordString = 0; // This represents the length of the string, not the number of halfsteps // Below, 'chordLength' refers to the size of the text for the chord (for example, "min"=3, "dim7"=4), // and 'numHalfsteps' refers to the number of elements in the halfsteps array. // This must be done in order from smaller to larger strings, so the longer string names // take effect. This means 'min' can be overwritten by 'minmaj7', or 'maj' by 'maj7', for example. if (possibleChord3 != null) { if (possibleChord3.equals("MAJ")) { lengthOfChordString = 3; context.numHalfsteps = 2; context.halfsteps[0] = 4; context.halfsteps[1] = 7; } else if (possibleChord3.equals("MIN")) { lengthOfChordString = 3; context.numHalfsteps = 2; context.halfsteps[0] = 3; context.halfsteps[1] = 7; } else if (possibleChord3.equals("AUG")) { lengthOfChordString = 3; context.numHalfsteps = 2; context.halfsteps[0] = 4; context.halfsteps[1] = 8; } else if (possibleChord3.equals("DIM")) { lengthOfChordString = 3; context.numHalfsteps = 2; context.halfsteps[0] = 3; context.halfsteps[1] = 6; } } if (possibleChord4 != null) { if (possibleChord4.equalsIgnoreCase("DOM7")) { lengthOfChordString = 4; context.numHalfsteps = 3; context.halfsteps[0] = 4; context.halfsteps[1] = 7; context.halfsteps[2] = 10; } else if (possibleChord4.equalsIgnoreCase("MAJ7")) { lengthOfChordString = 4; context.numHalfsteps = 3; context.halfsteps[0] = 4; context.halfsteps[1] = 7; context.halfsteps[2] = 11; } else if (possibleChord4.equalsIgnoreCase("MIN7")) { lengthOfChordString = 4; context.numHalfsteps = 3; context.halfsteps[0] = 3; context.halfsteps[1] = 7; context.halfsteps[2] = 10; } else if (possibleChord4.equalsIgnoreCase("SUS4")) { lengthOfChordString = 4; context.numHalfsteps = 2; context.halfsteps[0] = 5; context.halfsteps[1] = 7; } else if (possibleChord4.equalsIgnoreCase("SUS2")) { lengthOfChordString = 4; context.numHalfsteps = 2; context.halfsteps[0] = 2; context.halfsteps[1] = 7; } else if (possibleChord4.equalsIgnoreCase("MAJ6")) { lengthOfChordString = 4; context.numHalfsteps = 3; context.halfsteps[0] = 4; context.halfsteps[1] = 7; context.halfsteps[2] = 9; } else if (possibleChord4.equalsIgnoreCase("MIN6")) { lengthOfChordString = 4; context.numHalfsteps = 3; context.halfsteps[0] = 3; context.halfsteps[1] = 7; context.halfsteps[2] = 9; } else if (possibleChord4.equalsIgnoreCase("DOM9")) { lengthOfChordString = 4; context.numHalfsteps = 4; context.halfsteps[0] = 4; context.halfsteps[1] = 7; context.halfsteps[2] = 10; context.halfsteps[3] = 14; } else if (possibleChord4.equalsIgnoreCase("MAJ9")) { lengthOfChordString = 4; context.numHalfsteps = 4; context.halfsteps[0] = 4; context.halfsteps[1] = 7; context.halfsteps[2] = 11; context.halfsteps[3] = 14; } else if (possibleChord4.equalsIgnoreCase("MIN9")) { lengthOfChordString = 4; context.numHalfsteps = 4; context.halfsteps[0] = 3; context.halfsteps[1] = 7; context.halfsteps[2] = 10; context.halfsteps[3] = 14; } else if (possibleChord4.equalsIgnoreCase("DIM7")) { lengthOfChordString = 4; context.numHalfsteps = 3; context.halfsteps[0] = 3; context.halfsteps[1] = 6; context.halfsteps[2] = 9; } else if (possibleChord4.equalsIgnoreCase("ADD9")) { lengthOfChordString = 4; context.numHalfsteps = 3; context.halfsteps[0] = 4; context.halfsteps[1] = 7; context.halfsteps[2] = 14; } else if (possibleChord4.equalsIgnoreCase("DAVE")) { lengthOfChordString = 4; context.numHalfsteps = 3; context.halfsteps[0] = 7; context.halfsteps[1] = 14; context.halfsteps[2] = 21;} } if (possibleChord5 != null) { if (possibleChord5.equalsIgnoreCase("MIN11")) { lengthOfChordString = 5; context.numHalfsteps = 5; context.halfsteps[0] = 7; context.halfsteps[1] = 10; context.halfsteps[2] = 14; context.halfsteps[3] = 15; context.halfsteps[4] = 17; } else if (possibleChord5.equalsIgnoreCase("DOM11")) { lengthOfChordString = 5; context.numHalfsteps = 4; context.halfsteps[0] = 7; context.halfsteps[1] = 10; context.halfsteps[2] = 14; context.halfsteps[3] = 17; } else if (possibleChord5.equalsIgnoreCase("DOM13")) { lengthOfChordString = 5; context.numHalfsteps = 5; context.halfsteps[0] = 7; context.halfsteps[1] = 10; context.halfsteps[2] = 14; context.halfsteps[3] = 16; context.halfsteps[4] = 21; } else if (possibleChord5.equalsIgnoreCase("MIN13")) { lengthOfChordString = 5; context.numHalfsteps = 5; context.halfsteps[0] = 7; context.halfsteps[1] = 10; context.halfsteps[2] = 14; context.halfsteps[3] = 15; context.halfsteps[4] = 21; } else if (possibleChord5.equalsIgnoreCase("MAJ13")) { lengthOfChordString = 5; context.numHalfsteps = 5; context.halfsteps[0] = 7; context.halfsteps[1] = 11; context.halfsteps[2] = 14; context.halfsteps[3] = 16; context.halfsteps[4] = 21; } } if (possibleChord6 != null) { if (possibleChord6.equalsIgnoreCase("DOM7<5")) { lengthOfChordString = 6; context.numHalfsteps = 3; context.halfsteps[0] = 4; context.halfsteps[1] = 6; context.halfsteps[2] = 10; } else if (possibleChord6.equalsIgnoreCase("DOM7>5")) { lengthOfChordString = 6; context.numHalfsteps = 3; context.halfsteps[0] = 4; context.halfsteps[1] = 8; context.halfsteps[2] = 10; } else if (possibleChord6.equalsIgnoreCase("MAJ7<5")) { lengthOfChordString = 6; context.numHalfsteps = 3; context.halfsteps[0] = 4; context.halfsteps[1] = 6; context.halfsteps[2] = 11; } else if (possibleChord6.equalsIgnoreCase("MAJ7>5")) { lengthOfChordString = 6; context.numHalfsteps = 3; context.halfsteps[0] = 4; context.halfsteps[1] = 8; context.halfsteps[2] = 11; } } if (possibleChord7 != null) { if (possibleChord7.equalsIgnoreCase("minmaj7")) { lengthOfChordString = 7; context.numHalfsteps = 3; context.halfsteps[0] = 3; context.halfsteps[1] = 7; context.halfsteps[2] = 11; } } if (possibleChord8 != null) { if (possibleChord8.equalsIgnoreCase("DOM7<5<9")) { lengthOfChordString = 8; context.numHalfsteps = 4; context.halfsteps[0] = 4; context.halfsteps[1] = 6; context.halfsteps[2] = 10; context.halfsteps[3] = 13; } else if (possibleChord8.equalsIgnoreCase("DOM7<5>9")) { lengthOfChordString = 8; context.numHalfsteps = 4; context.halfsteps[0] = 4; context.halfsteps[1] = 6; context.halfsteps[2] = 10; context.halfsteps[3] = 15; } else if (possibleChord8.equalsIgnoreCase("DOM7>5<9")) { lengthOfChordString = 8; context.numHalfsteps = 4; context.halfsteps[0] = 4; context.halfsteps[1] = 8; context.halfsteps[2] = 10; context.halfsteps[3] = 13; } else if (possibleChord8.equalsIgnoreCase("DOM7>5>9")) { lengthOfChordString = 8; context.numHalfsteps = 4; context.halfsteps[0] = 4; context.halfsteps[1] = 8; context.halfsteps[2] = 10; context.halfsteps[3] = 15; } } if (lengthOfChordString > 0) { context.isChord = true; trace("Chord: chordLength=", lengthOfChordString, ", so chord is one of the following: [ 3=", possibleChord3, " 4=", possibleChord4, " 5=", possibleChord5, " 6=", possibleChord6, " 7=", possibleChord7, " 8=", possibleChord8, " ]"); } return index+lengthOfChordString; } /** This method does a variety of calculations to get the actual value of the note. */ private void computeNoteValue(NoteContext context) { // Don't compute note value for a rest if (context.isRest) { return; } // If we happen not to have an octave yet, set it to a default value. // Default octave: 5 for notes, 3 for chords if ((context.octaveNumber == 0) && (!context.isNumericNote)) { if (context.isChord) { context.octaveNumber = 3; } else { context.octaveNumber = 5; } } trace("Octave: ", context.octaveNumber); // Adjust for Key Signature if ((keySig != 0) && (!context.isNatural)) { if ((keySig <= -1) && (context.noteNumber == 11)) context.noteNumber = 10; if ((keySig <= -2) && (context.noteNumber == 4)) context.noteNumber = 3; if ((keySig <= -3) && (context.noteNumber == 9)) context.noteNumber = 8; if ((keySig <= -4) && (context.noteNumber == 2)) context.noteNumber = 1; if ((keySig <= -5) && (context.noteNumber == 7)) context.noteNumber = 6; if ((keySig <= -6) && (context.noteNumber == 0)) { context.noteNumber = 11; context.octaveNumber--; } if ((keySig <= -7) && (context.noteNumber == 5)) context.noteNumber = 4; if ((keySig >= +1) && (context.noteNumber == 5)) context.noteNumber = 6; if ((keySig >= +2) && (context.noteNumber == 0)) context.noteNumber = 1; if ((keySig >= +3) && (context.noteNumber == 7)) context.noteNumber = 8; if ((keySig >= +4) && (context.noteNumber == 2)) context.noteNumber = 3; if ((keySig >= +5) && (context.noteNumber == 9)) context.noteNumber = 10; if ((keySig >= +6) && (context.noteNumber == 4)) context.noteNumber = 5; if ((keySig >= +7) && (context.noteNumber == 11)) { context.noteNumber = 0; context.octaveNumber++; } trace("After adjusting for Key Signature, noteNumber=", context.noteNumber," octave=", context.octaveNumber); } // Compute the actual note number, based on octave and note if (!context.isNumericNote) { int intNoteNumber = (context.octaveNumber * 12) + context.noteNumber; if ( intNoteNumber > 127) { throw new JFugueException(JFugueException.NOTE_OCTAVE_EXC, Integer.toString(intNoteNumber), ""); } context.noteNumber = (byte)intNoteNumber; trace("Computed note number: ", context.noteNumber); } } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseNoteChordInversion(String s, int slen, int index, NoteContext context) { if (!context.isChord) { return index; } int inversionCount = 0; int inversionRootNote = -1; int inversionOctave = -1; boolean checkForInversion = true; while (checkForInversion) { if (index < slen) { switch(s.charAt(index)) { case '^' : index++; inversionCount++; break; case 'C' : index++; inversionRootNote = 0; break; case 'D' : index++; inversionRootNote = 2; break; case 'E' : index++; inversionRootNote = 4; break; case 'F' : index++; inversionRootNote = 5; break; case 'G' : index++; inversionRootNote = 7; break; case 'A' : index++; inversionRootNote = 9; break; // For 'B', need to differentiate between B note and 'b' flat case 'B' : index++; if (inversionRootNote == -1) { inversionRootNote = 11; } else { inversionRootNote--; } break; case '#' : index++; inversionRootNote++; break; // For '0', need to differentiate between initial 0 and 0 as a second digit (i.e., 10) case '0' : index++; if (inversionOctave == -1) { inversionOctave = 0; } else { inversionOctave = inversionOctave*10; } break; case '1' : index++; inversionOctave = 1; break; case '2' : index++; inversionOctave = 2; break; case '3' : index++; inversionOctave = 3; break; case '4' : index++; inversionOctave = 4; break; case '5' : index++; inversionOctave = 5; break; case '6' : index++; inversionOctave = 6; break; case '7' : index++; inversionOctave = 7; break; case '8' : index++; inversionOctave = 8; break; case '9' : index++; inversionOctave = 9; break; // If [, whoo boy, we're checking for a note number case '[' : int indexEndBracket = s.indexOf(']', index); inversionRootNote = Integer.parseInt(s.substring(index+1, indexEndBracket-1)); index = indexEndBracket+1; break; default : checkForInversion = false; break; } } else { checkForInversion = false; } } // Modify the note values based on the inversion if (inversionCount > 0) { if (inversionRootNote == -1) { // The root is determined by a number of carets. Increase each half-step // before the inversion by 12, the number of notes in an octave. trace("Inversion is base on count: "+inversionCount); trace("Inverting "+context.noteNumber+" to be "+(context.noteNumber+12)); context.noteNumber += 12; for (int i=inversionCount-1; i < context.numHalfsteps; i++) { trace("Inverting "+context.halfsteps[i]+" to be "+(context.halfsteps[i]-12)); context.halfsteps[i] -= 12; } } else { // The root is determined by an inversionRoot. This is much trickier, but we can // still figure it out. if (inversionOctave != -1) { inversionRootNote += inversionOctave * 12; } else if (inversionRootNote < 12) { int currentOctave = context.noteNumber / 12; inversionRootNote += currentOctave * 12; } // Otherwise, inversionRootNote is a numeric note value, like [60] trace("Inversion is base on note: "+inversionRootNote); if ((inversionRootNote > context.noteNumber + context.halfsteps[context.numHalfsteps-1]) || (inversionRootNote < context.noteNumber)) { throw new JFugueException(JFugueException.INVERSION_EXC); } trace("Inverting "+context.noteNumber+" to be "+(context.noteNumber+12)); context.noteNumber += 12; for (int i=0; i < context.numHalfsteps; i++) { if (context.noteNumber + context.halfsteps[i] >= inversionRootNote + 12) { trace("Inverting "+context.halfsteps[i]+" to be "+(context.halfsteps[i]-12)); context.halfsteps[i]-=12; } } } } return index; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseNoteDuration(String s, int slen, int index, NoteContext context) { context.decimalDuration = 0.0; if (index < slen) { switch (s.charAt(index)) { case '/' : index = parseNumericDuration(s, slen, index, context); break; case 'W' : case 'H' : case 'Q' : case 'I' : case 'S' : case 'T' : case 'X' : case 'O' : case '-' : index = parseLetterDuration(s, slen, index, context); break; default : break; } index = parseTuplet(s, slen, index, context); } else { context.decimalDuration = 1.0/4.0; // Default duration is a quarter note } // context.duration = (long) (120.0 * 4.0 * context.decimalDuration); // javax.sound.midi.Sequence resolution is 120 context.duration = (long) (120.0 * context.decimalDuration); // DMK 9/27/08: The *4.0 makes quarter notes 4 times as long as they should be // // Below is incorrect, as identified by M. Ahluwalia // // Tempo is now in Beats Per Minute. Convert this to Pulses Per Quarter (PPQ), then to // // Pulses Per Whole (PPW), then multiply that by durationNumber for WHQITXN notes // double ppq = 60000000.0D / (double)this.getTempo(); // double ppw = ppq * 4.0; // 4 quarter notes in a whole note // context.duration = (long)(ppw * context.decimalDuration) / 4000; trace("Decimal duration is ", context.decimalDuration); trace("Actual duration is ", context.duration); return index; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseLetterDuration(String s, int slen, int index, NoteContext context) { // Check duration boolean durationExists = true; boolean isDotted = false; while (durationExists == true) { int durationNumber = 0; // See if the note has a duration // Duration is optional; default is Q (4) if (index < slen) { char durationChar = s.charAt(index); switch (durationChar) { case '-' : if ((context.decimalDuration == 0) && (!context.isEndOfTie)) { context.isEndOfTie = true; trace("Note is end of tie"); } else { context.isStartOfTie = true; trace("Note is start of tie"); } break; case 'W' : durationNumber = 1; break; case 'H' : durationNumber = 2; break; case 'Q' : durationNumber = 4; break; case 'I' : durationNumber = 8; break; case 'S' : durationNumber = 16; break; case 'T' : durationNumber = 32; break; case 'X' : durationNumber = 64; break; case 'O' : durationNumber = 128; break; default : index--; durationExists = false; break; } index++; if ((index < slen) && (s.charAt(index) == '.')) { isDotted = true; index++; } if (durationNumber > 0) { double d = 1.0/durationNumber; if (isDotted) { context.decimalDuration += d + (d/2.0); } else { context.decimalDuration += d; } } } else { durationExists = false; } } return index; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseNumericDuration(String s, int slen, int index, NoteContext context) { // The duration has come in as a number, like 0.25 for a quarter note. // Advance pointer past the initial slash (/) index++; // Decimal duration is not required to be enclosed by brackets, // but since most of the other numerical input to a MusicString // is required to be in brackets, we should support it. if ('[' == s.charAt(index)) { int indexOfEndingBracket = s.indexOf(']', index); context.decimalDuration += getDoubleFromDictionary(s.substring(index+1, indexOfEndingBracket)); index = indexOfEndingBracket+1; } else { int endingIndex = index; boolean keepAdvancingPointer = true; while (keepAdvancingPointer) { try { char numericDurationChar = s.charAt(endingIndex); if ((numericDurationChar >= '0') && (numericDurationChar <= '9') || (numericDurationChar == '.')) // Decimal dot, not dotted duration { endingIndex++; } else { keepAdvancingPointer = false; } } catch (IndexOutOfBoundsException e) { keepAdvancingPointer = false; } } String durationNumberString = s.substring(index, endingIndex); context.decimalDuration += Double.parseDouble(durationNumberString); index = endingIndex; } trace("Decimal duration is ", context.decimalDuration); return index; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseTuplet(String s, int slen, int index, NoteContext context) { if (index < slen) { if (s.charAt(index) == '*') { trace("Note is a tuplet"); index++; // Figure out tuplet ratio, or figure out when to stop looking for tuplet info boolean stopTupletParsing = false; int indexOfUnitsToMatch = 0; int indexOfNumNotes = 0; int counter = -1; while (!stopTupletParsing) { counter++; if (slen > index+counter) { if (s.charAt(index+counter) == ':') { indexOfNumNotes = index+counter+1; } else if ((s.charAt(index+counter) >= '0') && (s.charAt(index+counter) <= '9')) { if (indexOfUnitsToMatch == 0) { indexOfUnitsToMatch = index+counter; } } else if ((s.charAt(index+counter) == '*')) { // no op... artifact of parsing } else { stopTupletParsing = true; } } else { stopTupletParsing = true; } } index += counter; double numerator = 2.0; double denominator = 3.0; if ((indexOfUnitsToMatch > 0) && (indexOfNumNotes > 0)) { numerator = Double.parseDouble(s.substring(indexOfUnitsToMatch, indexOfNumNotes-1)); denominator = Double.parseDouble(s.substring(indexOfNumNotes, index)); } trace("Tuplet ratio is "+numerator+":"+denominator); double tupletRatio = numerator / denominator; context.decimalDuration = context.decimalDuration * tupletRatio; trace("Decimal duration after tuplet is ", context.decimalDuration); } } return index; } /** Returns the index with which to start parsing the next part of the string, once this method is done with its part */ private int parseNoteVelocity(String s, int slen, int index, NoteContext context) { // Don't compute note velocity for a rest if (context.isRest) { return index; } // Process velocity attributes, if they exist while (index < slen) { int startPoint = index+1; int endPoint = startPoint; char velocityChar = s.charAt(index); int lengthOfByte = 0; if ((velocityChar == '+') || (velocityChar == '_')) break; trace("Identified Velocity character ", velocityChar); boolean byteDone = false; while (!byteDone && (index + lengthOfByte+1 < slen)) { char possibleByteChar = s.charAt(index + lengthOfByte+1); if ((possibleByteChar >= '0') && (possibleByteChar <= '9')) { lengthOfByte++; } else { byteDone = true; } } endPoint = index + lengthOfByte+1; // Or maybe a bracketed string was passed in, instead of a byte if ((index+1 < slen) && (s.charAt(index+1) == '[')) { endPoint = s.indexOf(']',startPoint)+1; } byte velocityNumber = getByteFromDictionary(s.substring(startPoint,endPoint)); switch (velocityChar) { case 'A' : context.attackVelocity = velocityNumber; break; case 'D' : context.decayVelocity = velocityNumber; break; default : throw new JFugueException(JFugueException.NOTE_VELOCITY_EXC, s.substring(startPoint,endPoint), s); } index = endPoint; } trace("Attack velocity = ", context.attackVelocity, "; Decay velocity = ", context.decayVelocity); return index; } /** Returns the String of the next sub-token (the parts after + or _), if one exists; otherwise, returns null */ private String parseNoteConnector(String s, int slen, int index, NoteContext context) { context.existAnotherNote = false; // See if there's another note to process if ((index < slen) && ((s.charAt(index) == '+') || (s.charAt(index) == '_'))) { trace("Another note: string = ", s.substring(index, s.length()-1)); if (s.charAt(index) == '_') { context.anotherNoteIsSequential = true; trace("Next note will be sequential"); } else { context.anotherNoteIsParallel = true; trace("Next note will be parallel"); } index++; context.existAnotherNote = true; return s.substring(index, slen); } return null; } private void fireNoteEvents(NoteContext context) { // Set up the note Note note = new Note(); if (context.isRest) { note.setRest(true); note.setDuration(context.duration); note.setDecimalDuration(context.decimalDuration); note.setAttackVelocity( (byte)0 ); // turn off sound for rest notes note.setDecayVelocity( (byte)0 ); } else { note.setValue(context.noteNumber); note.setDuration(context.duration); note.setStartOfTie(context.isStartOfTie); note.setEndOfTie(context.isEndOfTie); note.setDecimalDuration(context.decimalDuration); note.setAttackVelocity(context.attackVelocity); note.setDecayVelocity(context.decayVelocity); } note.setHasAccompanyingNotes(context.existAnotherNote || context.isChord); // Fire note events if (context.isFirstNote) { note.setType(Note.FIRST); trace("Firing first note event"); fireNoteEvent(note); } else if (context.isSequentialNote) { note.setType(Note.SEQUENTIAL); trace("Firing sequential note event"); fireSequentialNoteEvent(note); } else if (context.isParallelNote) { note.setType(Note.PARALLEL); trace("Firing parallel note event"); fireParallelNoteEvent(note); } if (context.isChord) { for (int i=0; i < context.numHalfsteps; i++) { Note chordNote = new Note((byte)(context.noteNumber+context.halfsteps[i]), context.duration); chordNote.setDecimalDuration(context.decimalDuration); // This won't have any effect on the note, but it's good bookkeeping to have it around. chordNote.setType(Note.PARALLEL); trace("Chord note number: ", (context.noteNumber+context.halfsteps[i])); if (i == context.numHalfsteps-1) { chordNote.setHasAccompanyingNotes(context.existAnotherNote); } else { chordNote.setHasAccompanyingNotes(context.existAnotherNote || context.isChord); } fireParallelNoteEvent(chordNote); } } context.isFirstNote = false; } /** * Looks up a string's value in the dictionary. The dictionary is used to * keep memorable names of obscure numbers - for example, the string FLUTE * is set to a value of 73, so when users want to play music with a flute, * they can say "I[Flute]" instead of "I[73]". * *

* The Dictionary feature also lets users define constants so that if the * value of something were to change, it only needs to be changed in one * place. For example, MY_FAVORITE_INSTRUMENT could be set to 73, then you * can say "I[My_Favorite_Instrument]" when you want to play with that * instrument. If your favorite instrument were ever to change, you only * have to make the change in one place, instead of every place where you * give the Instrument command. *

* * @param bracketedString the string to look up in the dictionary * @returns the definition of the string * @throws JFugueException if there is a problem looking up bracketedString */ private String dictionaryLookup(String bracketedString) throws JFugueException { int indexOfOpeningBracket = bracketedString.indexOf("["); int indexOfClosingBracket = bracketedString.indexOf("]"); String word = null; if ((indexOfOpeningBracket != -1) && (indexOfClosingBracket != -1)) { word = bracketedString.substring(indexOfOpeningBracket+1,indexOfClosingBracket); } else { // It appears that "bracketedString" wasn't bracketed. word = bracketedString; } word = word.toUpperCase(); String definition = (String)dictionaryMap.get(word); while ((definition != null) && (dictionaryMap.containsKey(definition.toUpperCase()))) { definition = (String)dictionaryMap.get(definition.toUpperCase()); } // If there is no definition for this word, see if the word is actually a number. if (null == definition) { char ch = 0; boolean isNumber = true; for (int i=0; i < word.length(); i++) { ch = word.charAt(i); if ((!Character.isDigit(ch) && (ch != '.'))) { isNumber = false; } } if (isNumber) { trace("Dictionary lookup returning the number ",word); return word; } else { throw new JFugueException(JFugueException.WORD_NOT_DEFINED_EXC,word,bracketedString); } } trace("Word ",word," is defined as ",definition); return definition; } /** * Look up a byte from the dictionary * @param bracketedString the string to look up * @returns the byte value of the definition * @throws JFugueException if there is a problem getting a byte from the dictionary look-up */ private byte getByteFromDictionary(String bracketedString) throws JFugueException { String definition = dictionaryLookup(bracketedString); Byte newbyte = null; try { newbyte = new Byte(definition); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_BYTE, definition, bracketedString); } return newbyte.byteValue(); } /** * Look up a long from the dictionary * @param bracketedString the string to look up * @returns the long value of the definition * @throws JFugueException if there is a problem getting a long from the dictionary look-up */ private long getLongFromDictionary(String bracketedString) throws JFugueException { String definition = dictionaryLookup(bracketedString); Long newlong = null; try { newlong = new Long(definition); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_LONG,definition,bracketedString); } return newlong.longValue(); } /** * Look up an int from the dictionary * @param bracketedString the string to look up * @returns the int value of the definition * @throws JFugueException if there is a problem getting a int from the dictionary look-up */ private int getIntFromDictionary(String bracketedString) throws JFugueException { String definition = dictionaryLookup(bracketedString); Integer newint = null; try { newint = new Integer(definition); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_INT,definition,bracketedString); } return newint.intValue(); } /** * Look up a double from the dictionary * @param bracketedString the string to look up * @returns the double value of the definition * @throws JFugueException if there is a problem getting a double from the dictionary look-up */ private double getDoubleFromDictionary(String bracketedString) throws JFugueException { String definition = dictionaryLookup(bracketedString); Double newdouble = null; try { newdouble = new Double(definition); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_DOUBLE,definition,bracketedString); } return newdouble.doubleValue(); } /** * Checks whether a token is valid. This method is provided for testing purposes, * and is not used during normal operation. * @param token the token to test for validity * @return true is the token is valid; false otherwise. */ public boolean isValidToken(String token) { boolean valid = true; try { parseToken(token); } catch (Exception e) { valid = false; } return valid; } public void verifyToken(String token, final String verifyString) { ParserListener listener = new CollatedParserListener() { private StringBuilder results = new StringBuilder(); public void jfugueEvent(JFugueElement e) { results.append(e.getVerifyString()); if (!verifyString.startsWith(results.toString())) { throw new JFugueException(JFugueException.VERIFICATION_EXCEPTION, results.toString(), verifyString); } results.append("; "); } }; addParserListener(listener); parseToken(token); removeParserListener(listener); } /** * Parses a string which presumably contains one token, which is a note. * * @param string The String that contains one token with a note, like "C5" * @return a Note object representing the note parsed from the string */ public static Note getNote(String string) { return getNote(new Pattern(string)); } /** * Parses a pattern which presumably contains one token, which is a note. * * @param pattern The Pattern that contains one token with a note, like "C5" * @return a Note object representing the note parsed from the pattern */ public static Note getNote(Pattern pattern) { final Note rootNote = new Note(); MusicStringParser parser = new MusicStringParser(); ParserListener renderer = new ParserListenerAdapter() { public void noteEvent(Note note) { rootNote.setValue(note.getValue()); rootNote.setDuration(note.getDuration()); rootNote.setDecimalDuration(note.getDecimalDuration()); rootNote.setStartOfTie(note.isStartOfTie()); rootNote.setEndOfTie(note.isEndOfTie()); rootNote.setAttackVelocity(note.getAttackVelocity()); rootNote.setDecayVelocity(note.getDecayVelocity()); rootNote.setRest(note.isRest()); } }; parser.addParserListener(renderer); parser.parse(pattern); return rootNote; } /** * Used for diagnostic purposes. main() makes calls to test the Pattern-to-MIDI * parser. * If you make any changes to this parser, run * this method ("java org.jfugue.MusicStringParser"), and make sure everything * works correctly. * @param args not used */ public static void main(String[] args) { verifyTokenParsing(); } /** * Used for diagnostic purposes. Contains an assortment of tokens that * are known to parse correctly. */ private static void verifyTokenParsing() { MusicStringParser parser = new MusicStringParser(); parser.setTracing(MusicStringParser.TRACING_ON); try { long startTime = System.currentTimeMillis(); // Don't forget -- individual tokens ONLY! No strings with spaces! parser.verifyToken("C", Note.createVerifyString(60, 0.25)); parser.verifyToken("C3", Note.createVerifyString(36, 0.25)); parser.verifyToken("Cb3", Note.createVerifyString(35, 0.25)); parser.verifyToken("B#3", Note.createVerifyString(48, 0.25)); parser.verifyToken("C#3q", Note.createVerifyString(37, 0.25)); parser.verifyToken("C3i", Note.createVerifyString(36, 0.125)); parser.verifyToken("C3qh", Note.createVerifyString(36, 0.75)); parser.verifyToken("C5minw", Note.createCompoundVerifyString(Note.createVerifyString(60, 1.0), Note.createVerifyString(63, 1.0, false, true, false), Note.createVerifyString(67, 1.0, false, true, false))); parser.verifyToken("Cmaj", Note.createCompoundVerifyString(Note.createVerifyString(36, 0.25), Note.createVerifyString(40, 0.25, false, true, false), Note.createVerifyString(43, 0.25, false, true, false))); parser.parseToken("Cdom9"); parser.parseToken("Cmin11"); parser.parseToken("Cdom7<5"); parser.parseToken("Cminmaj7"); parser.parseToken("Cdom7<5<9"); parser.parseToken("Cwhqistxo"); parser.parseToken("C10"); parser.parseToken("V0"); parser.parseToken("V15"); parser.parseToken("I0"); parser.parseToken("I[13]"); parser.parseToken("I[Acoustic_Grand]"); parser.parseToken("IFlute"); parser.parseToken("Cmaj7W"); parser.parseToken("C#5Q"); parser.parseToken("eb3Q."); parser.parseToken("[Cowbell]O"); parser.parseToken("P50"); // An unknown token should just pass through parser.parseToken("A"); parser.parseToken("A+B+C"); parser.parseToken("A_B_C"); parser.parseToken("RW"); parser.parseToken("[105]X"); parser.parseToken("[105]Xa20+[98]X+[78]X"); parser.parseToken("AW+[18]X+[cabasa]Q+Dmin"); parser.parseToken("A/0.25"); parser.parseToken("[70]o"); // 2.0 Dictionary Definition and Controller Events parser.parseToken("$UKELE=72"); parser.parseToken("IUKELE"); parser.parseToken("$Volume=43"); parser.parseToken("X[Volume]=10"); parser.parseToken("X[PORTAMENTO_TIME]=777"); // 2.0 Dictionary Definition in odd situations that should work parser.parseToken("XVolume=ON"); parser.parseToken("[Ukele]q"); // 2.0 Dictionary Definition and non-bytes parser.parseToken("$number1=1"); parser.parseToken("$quarter=0.25"); parser.parseToken("C4/[quarter]"); parser.parseToken("C4q"); parser.parseToken("[Number1]/[Quarter]"); // 2.0 Note velocity parser.parseToken("Cb4qa45"); parser.parseToken("Gb4qd67"); parser.parseToken("F#4qa55d77"); parser.parseToken("B4qa[Volume]d[Number1]"); // 3.0 Layers parser.parseToken("L8"); parser.parseToken("$number1=1"); parser.parseToken("L[Number1]"); // 3.0 Times parser.parseToken("@100002"); // 3.0 Measures parser.parseToken("|"); // 3.0 Tied notes parser.parseToken("Cq-"); parser.parseToken("C5q-"); parser.parseToken("C5q.-"); parser.parseToken("C5qh-"); parser.parseToken("C-q"); parser.parseToken("C5-q"); parser.parseToken("C5-q."); parser.parseToken("C5-qh"); parser.parseToken("C-q-"); parser.parseToken("C--"); // 3.0 Pitch Bend parser.parseToken("&100,50"); parser.parseToken("&512"); parser.parseToken("$number110=110"); parser.parseToken("&[number110],50"); parser.parseToken("&[number110],[number110]"); parser.parseToken("$number1010=1010"); parser.parseToken("&[number1010]"); // 3.0 Channel Pressure parser.parseToken("+100"); parser.parseToken("$number110=110"); parser.parseToken("+[number110]"); parser.parseToken("+[number110]"); // 3.0 Polyphonic Pressure parser.parseToken("*100,20"); parser.parseToken("$number110=110"); parser.parseToken("*[number110],50"); parser.parseToken("*[number110],[number110]"); // 4.0 Chord Inversions parser.parseToken("Cmaj"); parser.parseToken("C7maj"); parser.parseToken("C7maj^"); parser.parseToken("C7maj^^"); parser.parseToken("C7maj^^^"); parser.parseToken("C7maj^E"); parser.parseToken("C7maj^G"); parser.parseToken("C7maj^E7"); parser.parseToken("C7maj^G7"); parser.parseToken("[60]maj^"); parser.parseToken("[60]maj^^"); parser.parseToken("[60]maj^^^"); parser.parseToken("[60]maj^E"); parser.parseToken("[60]maj^[67]"); parser.parseToken("Bb6min13^^^^^^"); // 4.0 Tuplets parser.parseToken("Cq*"); parser.parseToken("Ci*5:4"); parser.parseToken("Cs.*7:8"); parser.parseToken("Chx.*10:11"); // 3.0 Key Signatures parser.parseToken("KC#maj"); parser.parseToken("KAbmin"); parser.parseToken("Cn"); parser.parseToken("Cn6"); // 4.0 New parser parser.parseToken("D3"); parser.parseToken("C##3"); // Should be like D3 long endTime = System.currentTimeMillis(); System.out.println("Time taken: "+(endTime-startTime)+"ms"); } catch (Exception e) { e.printStackTrace(); } } } jfugue/org/jfugue/Note.java0000644000175000017500000006160511005706340016401 0ustar giovannigiovanni/* * 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; /** * Contains all information necessary for a musical note, including * pitch, duration, attack velocity, and decay velocity. * *

* Most of these settings have defaults. The default octave is 5. * The default duration is a quarter note. The default attack and * decay velocities are 64. *

* *@author David Koelle *@version 2.0.1 */ public final class Note implements JFugueElement { private byte value = 0; private long duration = 0; private double decimalDuration = 0.0; private boolean isStartOfTie = false; private boolean isEndOfTie = false; private byte attackVelocity = DEFAULT_VELOCITY; private byte decayVelocity = DEFAULT_VELOCITY; private boolean rest = false; private byte type = 0; private boolean accompanyingNotes = false; /** * Instantiates a new Note object. */ public Note() { this.value = 0; this.duration = 0; this.type = 0; } /** * Instantiates a new Note object with the given note value. * This constructor should only be called in cases where the * duration of the note is not important (for example, * when specifying a root note for a IntervalNotation) * @param value the numeric value of the note. C5 is 60. */ public Note(byte value) { this(value, (long)1); } /** * Instantiates a new Note object with the given note value and duration. * @param value the numeric value of the note. C5 is 60. * @param duration the duration of the note, as milliseconds. */ public Note(byte value, long duration) { this.value = value; this.duration = duration; } /** * Instantiates a new Note object with the given note value and duration. * @param value the numeric value of the note. C5 is 60. * @param duration the duration of the note, as a decimal fraction of a whole note. */ public Note(byte value, double decimalDuration) { this.value = value; this.decimalDuration = decimalDuration; } /** * Instantiates a new Note object with the given note value, duration, and attack and decay velocities. * @param value the numeric value of the note. C5 is 60. * @param duration the duration of the note. */ public Note(byte value, long duration, byte attackVelocity, byte decayVelocity) { this.value = value; this.duration = duration; this.attackVelocity = attackVelocity; this.decayVelocity = decayVelocity; } /** * Instantiates a new Note object with the given note value, duration, and attack and decay velocities. * @param value the numeric value of the note. C5 is 60. * @param duration the duration of the note. */ public Note(byte value, double decimalDuration, byte attackVelocity, byte decayVelocity) { this.value = value; this.decimalDuration = decimalDuration; this.attackVelocity = attackVelocity; this.decayVelocity = decayVelocity; } /** * Indicates whether this Note object actually represents a rest. * @param rest indicates whether this note is rest */ public void setRest(boolean rest) { this.rest = rest; } /** * Returns whether this Note object actually represents a rest. * @return whether this note is a rest */ public boolean isRest() { return this.rest; } /** * Sets the numeric value of this note. C5 is 60. * @param value the value of the note */ public void setValue(byte value) { this.value = value; } /** * Returns the numeric value of this note. C5 is 60. * @return the value of this note */ public byte getValue() { return this.value; } /** * Sets the duration of this note. * @param duration the duration of this note */ public void setDuration(long duration) { this.duration = duration; } /** * Returns the duration of this note. * @return the duration of this note */ public long getDuration() { return this.duration; } /** * Sets the decimal fraction value for the duration. * @param number the decimal fraction for the duration */ public void setDecimalDuration(double duration) { this.decimalDuration = duration; } /** * Returns the decimal fraction value for the duration. * @return the decimal fraction value for the duration */ public double getDecimalDuration() { return this.decimalDuration; } /** * Indicates whether this note has a tie to some future note. * @param tied true if the note is tied, false if not */ public void setStartOfTie(boolean startOfTie) { this.isStartOfTie = startOfTie; } /** * Returns whether this note has a tie to some future note. * @return true is the note is tied, false if not */ public boolean isStartOfTie() { return this.isStartOfTie; } /** * Indicates whether this note is tied to some past note. * @param tied true if the note is tied, false if not */ public void setEndOfTie(boolean endOfTie) { this.isEndOfTie = endOfTie; } /** * Returns whether this note is tied to some past note. * @return true is the note is tied, false if not */ public boolean isEndOfTie() { return this.isEndOfTie; } /** * Sets the attack velocity for this note. * @param velocity the attack velocity */ public void setAttackVelocity(byte velocity) { this.attackVelocity = velocity; } /** * Returns the attack velocity for this note. * @return the attack velocity */ public byte getAttackVelocity() { return this.attackVelocity; } /** * Sets the decay velocity for this note. * @param velocity the decay velocity */ public void setDecayVelocity(byte velocity) { this.decayVelocity = velocity; } /** * Returns the decay velocity for this note. * @return the decay velocity */ public byte getDecayVelocity() { return this.decayVelocity; } /** * Sets whether this Note will have other Notes (sequential or parallel) associated with it. * @param accompanying */ public void setHasAccompanyingNotes(boolean accompanying) { this.accompanyingNotes = accompanying; } /** * Returns whether this Note will have other Notes (sequential or parallel) associated with it. */ public boolean hasAccompanyingNotes() { return this.accompanyingNotes; } /** * Sets the note type - either First, Sequential, or Parallel. * @param type the note type */ public void setType(byte type) { this.type = type; } /** * Returns the note type - either First, Sequential, or Parallel. * @return the note type */ public byte getType() { return this.type; } /** Indicates that this note is the first note in the token. */ public static final byte FIRST = 0; /** Indicates that this note immediately follows a previous note in the same token. */ public static final byte SEQUENTIAL = 1; /** Indicates that this note is played at the same time as a previous note in the same token. */ public static final byte PARALLEL = 2; /** Default value for attack and decay velocity. */ public static final byte DEFAULT_VELOCITY = 64; /** * Returns the Music String representing this element and all of its settings. * For a Note object, the Music String is a note, expressed as either a letter, note, or a bracketed number, [note-value], * and a duration, expressed as either a letter, duration, or a slash followed by a numeric duration, /decimal-duration
* If either the attack or decay velocity is set to a value besides the default, avelocity and/or dvelocity will be added to the string. * If this note is to be played in sequence or in parallel to another note, a + or _ character will be added as appropriate. * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); // If this is a Sequential note or a Parallel note, include that information. if (SEQUENTIAL == this.type) { buffy.append("_"); } else if (PARALLEL == this.type) { buffy.append("+"); } // Add the note value and duration value // buffy.append("["); // buffy.append(this.value); // buffy.append("]/"); // buffy.append(this.decimalDuration); buffy.append(Note.getStringForNote(this.value, this.decimalDuration)); if (this.attackVelocity != DEFAULT_VELOCITY) { buffy.append("a"); buffy.append(this.attackVelocity); } if (this.decayVelocity != DEFAULT_VELOCITY) { buffy.append("d"); buffy.append(this.decayVelocity); } return buffy.toString(); } /** * Returns verification string in this format: * Note: value={#}, duration={#}, startTie={T|F}, endTie={T|F}, attack={#}, decay={#}, isFirst={T|F}, isParallel={T|F}, isSequential={T|F} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("Note: value="); buffy.append(getValue()); buffy.append(", duration="); buffy.append(getDecimalDuration()); buffy.append(", startTie="); buffy.append(isStartOfTie() ? "T" : "F"); buffy.append(", endTie="); buffy.append(isEndOfTie() ? "T" : "F"); buffy.append(", attack="); buffy.append(getAttackVelocity()); buffy.append(", decay="); buffy.append(getDecayVelocity()); buffy.append(", isFirst="); buffy.append(getType() == FIRST ? "T" : "F"); buffy.append(", isParallel="); buffy.append(getType() == PARALLEL ? "T" : "F"); buffy.append(", isSequential="); buffy.append(getType() == SEQUENTIAL ? "T" : "F"); return buffy.toString(); } public static String createVerifyString(int value, double duration) { return createVerifyString(value, duration, false, false, (byte)64, (byte)64, true, false, false); } public static String createVerifyString(int value, double duration, boolean startTie, boolean endTie) { return createVerifyString(value, duration, startTie, endTie, (byte)64, (byte)64, true, false, false); } public static String createVerifyString(int value, double duration, int attack, int decay) { return createVerifyString(value, duration, false, false, attack, decay, true, false, false); } public static String createVerifyString(int value, double duration, boolean isFirst, boolean isParallel, boolean isSequential) { return createVerifyString(value, duration, false, false, (byte)64, (byte)64, isFirst, isParallel, isSequential); } public static String createVerifyString(int value, double duration, boolean startTie, boolean endTie, int attack, int decay, boolean isFirst, boolean isParallel, boolean isSequential) { StringBuffer buffy = new StringBuffer(); buffy.append("Note: value="); buffy.append(value); buffy.append(", duration="); buffy.append(duration); buffy.append(", startTie="); buffy.append(startTie ? "T" : "F"); buffy.append(", endTie="); buffy.append(endTie ? "T" : "F"); buffy.append(", attack="); buffy.append(attack); buffy.append(", decay="); buffy.append(decay); buffy.append(", isFirst="); buffy.append(isFirst ? "T" : "F"); buffy.append(", isParallel="); buffy.append(isParallel ? "T" : "F"); buffy.append(", isSequential="); buffy.append(isSequential ? "T" : "F"); return buffy.toString(); } /** * Easily create compound note verification strings, like: * Note: value=60, duration=1.0, startTie=F, endTie=F, attack=64, decay=64, isFirst=T, isParallel=F, isSequential=F; Note: value=63, duration=0.0, startTie=F, endTie=F, attack=64, decay=64, isFirst=F, isParallel=T, isSequential=F */ public static String createCompoundVerifyString(String... strings) { StringBuffer buffy = new StringBuffer(); for (String string : strings) { buffy.append(string); buffy.append("; "); } return buffy.toString().substring(0, buffy.toString().length()-2); } /** * Returns a MusicString representation of the given MIDI note value and duration -- * which indicates a note and an octave. * @param noteValue this MIDI note value, like 60 * @param decimalDuration the duration of this note, like 0.5 * @return a MusicString value, like C5h */ public static String getStringForNote(byte noteValue, double decimalDuration) { StringBuffer buffy = new StringBuffer(); buffy.append(getStringForNote(noteValue)); buffy.append(getStringForDuration(decimalDuration)); return buffy.toString(); } /** * Returns the frequency, in Hertz, for the given note value. * For example, the frequency for A5 (MIDI note 69) is 440.0 * @param noteValue * @return */ public static double getFrequencyForNote(int noteValue) { double freq = 0.0; double freq0 = 8.1757989156; for (double d=0; d <= noteValue; d++) { freq = freq0 * Math.pow(2.0, d / 12.0); } // Truncate to 4 significant digits double retVal = Math.rint(freq * 10000.0) / 10000.0; return retVal; } /** * Returns a MusicString representation of the given MIDI note value -- * which indicates a note and an octave. * @param noteValue this MIDI note value, like 60 * @return a MusicString value, like C5 */ public static String getStringForNote(byte noteValue) { int note = noteValue % 12; int octave = noteValue / 12; StringBuffer buffy = new StringBuffer(); buffy.append(NOTES[note]); buffy.append(octave); return buffy.toString(); } /** * Returns a MusicString representation of a decimal duration. This code * currently only converts single duration values representing whole, half, * quarter, eighth, etc. durations; and dotted durations associated with those * durations (such as "h.", equal to 0.75). This method does not convert * combined durations (for example, "hi" for 0.625) or anything greater * than a duration of 1.0 (for example, "wwww" for 4.0). For these values, * the original decimal duration is returned in a string, prepended with a "/" * to make the returned value a valid MusicString duration indicator. * * @param decimalDuration The decimal value of the duration to convert * @return a MusicString fragment representing the duration */ public static String getStringForDuration(double decimalDuration) { if (decimalDuration == 1.0) return "w"; else if (decimalDuration == 0.75) return "h."; else if (decimalDuration == 0.5) return "h"; else if (decimalDuration == 0.375) return "q."; else if (decimalDuration == 0.25) return "q"; else if (decimalDuration == 0.1875) return "i."; else if (decimalDuration == 0.125) return "i"; else if (decimalDuration == 0.09375) return "s."; else if (decimalDuration == 0.0625) return "s"; else if (decimalDuration == 0.046875) return "t."; else if (decimalDuration == 0.03125) return "t"; else if (decimalDuration == 0.0234375) return "x."; else if (decimalDuration == 0.015625) return "x"; else if (decimalDuration == 0.01171875) return "o."; else if (decimalDuration == 0.0078125) return "o"; else return "/" + decimalDuration; } /** * Returns the decimal duration that is equal to the given MusicString representation. * This code currently only converts single duration values representing whole, half, * quarter, eighth, etc. durations; and dotted durations associated with those * durations (such as "h.", equal to 0.75). This method does not convert * combined durations (for example, "hi" for 0.625) or anything greater * than a duration of 1.0 (for example, "wwww" for 4.0). For these values, * the original decimal duration is returned in a string, prepended with a "/" * to make the returned value a valid MusicString duration indicator. * * @param stringDuration The MusicString duration character (or dotted character) * @return a decimal value representing the duration, expressed as a fraction of a whole note */ public static double getDecimalForDuration(String stringDuration) { String stringDuration2 = stringDuration.toLowerCase(); if (stringDuration2.equals("w")) return 1.0; else if (stringDuration2.equals("h.")) return 0.75; else if (stringDuration2.equals("h")) return 0.5; else if (stringDuration2.equals("q.")) return 0.375; else if (stringDuration2.equals("q")) return 0.25; else if (stringDuration2.equals("i.")) return 0.1875; else if (stringDuration2.equals("i")) return 0.125; else if (stringDuration2.equals("s.")) return 0.09375; else if (stringDuration2.equals("s")) return 0.0625; else if (stringDuration2.equals("t.")) return 0.046875; else if (stringDuration2.equals("t")) return 0.03125; else if (stringDuration2.equals("x.")) return 0.0234375; else if (stringDuration2.equals("x")) return 0.015625; else if (stringDuration2.equals("o.")) return 0.01171875; else if (stringDuration2.equals("o")) return 0.0078125; else return 0.0; } public static final String[] NOTES = new String[] { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B" }; public static final byte ACOUSTIC_BASS_DRUM = 35; public static final byte BASS_DRUM = 36; public static final byte SIDE_STICK = 37; public static final byte ACOUSTIC_SNARE = 38; public static final byte HAND_CLAP = 39; public static final byte ELECTRIC_SNARE = 40; public static final byte LOW_FLOOR_TOM = 41; public static final byte CLOSED_HI_HAT = 42; public static final byte HIGH_FLOOR_TOM = 43; public static final byte PEDAL_HI_HAT = 44; public static final byte LOW_TOM = 45; public static final byte OPEN_HI_HAT = 46; public static final byte LOW_MID_TOM = 47; public static final byte HI_MID_TOM = 48; public static final byte CRASH_CYMBAL_1 = 49; public static final byte HIGH_TOM = 50; public static final byte RIDE_CYMBAL_1 = 51; public static final byte CHINESE_CYMBAL = 52; public static final byte RIDE_BELL = 53; public static final byte TAMBOURINE = 54; public static final byte SPLASH_CYMBAL = 55; public static final byte COWBELL = 56; public static final byte CRASH_CYMBAL_2 = 57; public static final byte VIBRASLAP = 58; public static final byte RIDE_CYMBAL_2 = 59; public static final byte HI_BONGO = 60; public static final byte LOW_BONGO = 61; public static final byte MUTE_HI_CONGA = 62; public static final byte OPEN_HI_CONGA = 63; public static final byte LOW_CONGA = 64; public static final byte HIGH_TIMBALE = 65; public static final byte LOW_TIMBALE = 66; public static final byte HIGH_AGOGO = 67; public static final byte LOW_AGOGO = 68; public static final byte CABASA = 69; public static final byte MARACAS = 70; public static final byte SHORT_WHISTLE = 71; public static final byte LONG_WHISTLE = 72; public static final byte SHORT_GUIRO = 73; public static final byte LONG_GUIRO = 74; public static final byte CLAVES = 75; public static final byte HI_WOOD_BLOCK = 76; public static final byte LOW_WOOD_BLOCK = 77; public static final byte MUTE_CUICA = 78; public static final byte OPEN_CUICA = 79; public static final byte MUTE_TRIANGLE = 80; public static final byte OPEN_TRIANGLE = 81; } /* Here's a handy chart from http://www.borg.com/~jglatt/tutr/notefreq.htm MIDI MIDI MIDI Note Frequency Note Frequency Note Frequency C 0 8.1757989156 12 16.3515978313 24 32.7031956626 Db 1 8.6619572180 13 17.3239144361 25 34.6478288721 D 2 9.1770239974 14 18.3540479948 26 36.7080959897 Eb 3 9.7227182413 15 19.4454364826 27 38.8908729653 E 4 10.3008611535 16 20.6017223071 28 41.2034446141 F 5 10.9133822323 17 21.8267644646 29 43.6535289291 Gb 6 11.5623257097 18 23.1246514195 30 46.2493028390 G 7 12.2498573744 19 24.4997147489 31 48.9994294977 Ab 8 12.9782717994 20 25.9565435987 32 51.9130871975 A 9 13.7500000000 21 27.5000000000 33 55.0000000000 Bb 10 14.5676175474 22 29.1352350949 34 58.2704701898 B 11 15.4338531643 23 30.8677063285 35 61.7354126570 C 36 65.4063913251 48 130.8127826503 60 261.6255653006 Db 37 69.2956577442 49 138.5913154884 61 277.1826309769 D 38 73.4161919794 50 146.8323839587 62 293.6647679174 Eb 39 77.7817459305 51 155.5634918610 63 311.1269837221 E 40 82.4068892282 52 164.8137784564 64 329.6275569129 F 41 87.3070578583 53 174.6141157165 65 349.2282314330 Gb 42 92.4986056779 54 184.9972113558 66 369.9944227116 G 43 97.9988589954 55 195.9977179909 67 391.9954359817 Ab 44 103.8261743950 56 207.6523487900 68 415.3046975799 A 45 110.0000000000 57 220.0000000000 69 440.0000000000 Bb 46 116.5409403795 58 233.0818807590 70 466.1637615181 B 47 123.4708253140 59 246.9416506281 71 493.8833012561 C 72 523.2511306012 84 1046.5022612024 96 2093.0045224048 Db 73 554.3652619537 85 1108.7305239075 97 2217.4610478150 D 74 587.3295358348 86 1174.6590716696 98 2349.3181433393 Eb 75 622.2539674442 87 1244.5079348883 99 2489.0158697766 E 76 659.2551138257 88 1318.5102276515 100 2637.0204553030 F 77 698.4564628660 89 1396.9129257320 101 2793.8258514640 Gb 78 739.9888454233 90 1479.9776908465 102 2959.9553816931 G 79 783.9908719635 91 1567.9817439270 103 3135.9634878540 Ab 80 830.6093951599 92 1661.2187903198 104 3322.4375806396 A 81 880.0000000000 93 1760.0000000000 105 3520.0000000000 Bb 82 932.3275230362 94 1864.6550460724 106 3729.3100921447 B 83 987.7666025122 95 1975.5332050245 107 3951.0664100490 C 108 4186.0090448096 120 8372.0180896192 Db 109 4434.9220956300 121 8869.8441912599 D 110 4698.6362866785 122 9397.2725733570 Eb 111 4978.0317395533 123 9956.0634791066 E 112 5274.0409106059 124 10548.0818212118 F 113 5587.6517029281 125 11175.3034058561 Gb 114 5919.9107633862 126 11839.8215267723 G 115 6271.9269757080 127 12543.8539514160 Ab 116 6644.8751612791 A 117 7040.0000000000 Bb 118 7458.6201842894 B 119 7902.1328200980 Note: Middle C is note #60. Frequency is in Hertz. */jfugue/org/jfugue/Time.java0000644000175000017500000000460611005706476016402 0ustar giovannigiovanni/* * 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; /** * Represents a timing value, which can be used to indicate when certain events are played. * *@author David Koelle *@version 3.0 */ public final class Time implements JFugueElement { private long time; /** * Creates a new Time object, with the specified time number. * @param time the number of the time to use */ public Time(long time) { setTime(time); } /** * Sets the value of the time for this object. * @param time the number of the time to use */ public void setTime(long time) { this.time = time; } /** * Returns the time used in this object * @return the time used in this object */ public long getTime() { return time; } /** * Returns the Music String representing this element and all of its settings. * For a Time object, the Music String is @time * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("@"); buffy.append(getTime()); return buffy.toString(); } /** * Returns verification string in this format: * Time: time={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("Time: time="); buffy.append(getTime()); return buffy.toString(); } }jfugue/org/jfugue/PatternListenerAdapter.java0000644000175000017500000000267710776766112022145 0ustar giovannigiovanni/* * 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; /** * This Adapter class implements all of the methods of * PatternListener, but the implementations are blank. * If you want something to be a PatternListener, but you don't * want to implement all of the PatternListener methods, extend * this class. * *@author David Koelle *@version 3.0 */ public class PatternListenerAdapter implements PatternListener { /** * Called when a new fragment has been added to a pattern * @param pattern the fragment that has been added */ public void fragmentAdded(Pattern fragment) { } } jfugue/org/jfugue/IntervalNotation.java0000644000175000017500000001017311005706206020767 0ustar giovannigiovanni/* * 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; /** * A IntervalNotation is a MusicString that only contains interval information and durations, not actual notes. * A riff is converted into an actual MusicString by applying a root note. * * @author David Koelle * @version 4.0 */ public class IntervalNotation { private String musicStringWithIntervals; public IntervalNotation(String musicStringWithIntervals) { setMusicStringWithIntervals(musicStringWithIntervals); } public void setMusicStringWithIntervals(String musicStringWithIntervals) { this.musicStringWithIntervals = musicStringWithIntervals; } public String getMusicStringWithIntervals() { return this.musicStringWithIntervals; } public Pattern getPatternForRootNote(String musicString) { return getPatternForRootNote(new Pattern(musicString)); } public Pattern getPatternForRootNote(Pattern pattern) { Note rootNote = MusicStringParser.getNote(pattern); return getPatternForRootNote(rootNote); } public Pattern getPatternForRootNote(Note rootNote) { StringBuilder buddy = new StringBuilder(); String[] tokens = getMusicStringWithIntervals().split(" "); byte rootNoteValue = rootNote.getValue(); // Go through the Pattern, and replace intervals specified within < and > with the root note plus the interval value, minus 1 for (int i=0; i < tokens.length; i++) { int lastAngleBracketPosition = -1; boolean leftAngleBracketExists = (tokens[i].indexOf('<') != -1); if (leftAngleBracketExists) { while (leftAngleBracketExists) { int start = tokens[i].indexOf('<', lastAngleBracketPosition); int end = tokens[i].indexOf('>', start); String intervalString = tokens[i].substring(start+1, end); byte intervalValue = 0; try { intervalValue = Byte.valueOf(intervalString); } catch (NumberFormatException e) { throw new JFugueException(JFugueException.EXPECTED_BYTE, intervalString, tokens[i]); } buddy.append("["); buddy.append(rootNoteValue + intervalValue - 1); buddy.append("]"); lastAngleBracketPosition = end; int nextLeftAngleBracketPosition = tokens[i].indexOf('<', lastAngleBracketPosition-1); if (nextLeftAngleBracketPosition == -1) { buddy.append(tokens[i].substring(end+1, tokens[i].length())); // Add the rest of the token leftAngleBracketExists = false; } else { buddy.append(tokens[i].substring(end+1, nextLeftAngleBracketPosition)); // Add the rest of the token up to the next angle leftAngleBracketExists = true; } } } else { buddy.append(tokens[i]); } buddy.append(" "); } return new Pattern(buddy.toString()); } } jfugue/org/jfugue/ParserProgressListener.java0000644000175000017500000000207710776766112022202 0ustar giovannigiovanni/* * 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 java.util.EventListener; public interface ParserProgressListener extends EventListener { public void progressReported(String description, long partCompleted, long whole); } jfugue/org/jfugue/PatternTransformer.java0000644000175000017500000001323511010303210021311 0ustar giovannigiovanni/* * 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 java.util.HashMap; import java.util.Map; /** * This class is used to transform a pattern. Extend this class to create your own * PatternTransformer, which * listens to parser events and can modify the events that are fired off by the parser. * Some sample * PatternTransformer subclasses are packaged with JFugue; refer to those to see examples * of transformers in action. * * This feature is covered in detail in The Complete Guide to JFugue. * * @see org.jfugue.extras.DiatonicIntervalPatternTransformer * @see org.jfugue.extras.DurationPatternTransformer * @see org.jfugue.extras.IntervalPatternTransformer * @see org.jfugue.extras.ReversePatternTransformer * @author David Koelle * @version 2.0 */ public class PatternTransformer implements ParserListener { /** * Contains the pattern to return at the end of the transformation. * As of version 4.0, this variable is private. Use the protected methods * getReturnPattern() and setReturnPattern() to access the return pattern. */ private Pattern returnPattern; /** * Returns the pattern that the transformer is modifying * @version 4.0 */ protected Pattern getReturnPattern() { return returnPattern; } /** * Sets the pattern that the transformer is modifying * @version 4.0 */ protected void setReturnPattern(Pattern pattern) { this.returnPattern = pattern; } /** Transforms the pattern, according to the event method that you have * presumably extended. */ public Pattern transform(Pattern p) { setReturnPattern(new Pattern()); MusicStringParser parser = new MusicStringParser(); parser.addParserListener(this); try { parser.parse(p); } catch (JFugueException e) { e.printStackTrace(); } return getReturnPattern(); } /** Extend this method to make your transformer modify the voice. */ public void voiceEvent(Voice voice) { returnPattern.addElement(voice); } /** Extend this method to make your transformer modify the tempo. */ public void tempoEvent(Tempo tempo) { returnPattern.addElement(tempo); } /** Extend this method to make your transformer modify the instrument. */ public void instrumentEvent(Instrument instrument) { returnPattern.addElement(instrument); } /** Extend this method to make your transformer modify the layer. */ public void layerEvent(Layer layer) { returnPattern.addElement(layer); } /** Extend this method to make your transformer modify the time. */ public void timeEvent(Time time) { returnPattern.addElement(time); } /** Extend this method to make your transformer modify the time. */ public void keySignatureEvent(KeySignature keySig) { returnPattern.addElement(keySig); } /** Extend this method to make your transformer modify the measure. */ public void measureEvent(Measure measure) { returnPattern.addElement(measure); } /** Extend this method to make your transformer modify the controller messages. */ public void controllerEvent(Controller controller) { returnPattern.addElement(controller); } /** Extend this method to make your transformer modify the channel pressure messages. */ public void channelPressureEvent(ChannelPressure channelPressure) { returnPattern.addElement(channelPressure); } /** Extend this method to make your transformer modify the polyphonic pressure messages. */ public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { returnPattern.addElement(polyphonicPressure); } /** Extend this method to make your transformer modify the pitch bend messages. */ public void pitchBendEvent(PitchBend pitchBend) { returnPattern.addElement(pitchBend); } /** Extend this method to make your transformer modify the note. * Don't forget to also extend sequentialNoteEvent and parallelNoteEvent. */ public void noteEvent(Note note) { returnPattern.addElement(note); } /** Extend this method to make your transformer modify the note. * Don't forget to also extend noteEvent and parallelNoteEvent. */ public void sequentialNoteEvent(Note note) { returnPattern.addElement(note); } /** Extend this method to make your transformer modify the note. * Don't forget to also extend noteEvent and sequentialNoteEvent. */ public void parallelNoteEvent(Note note) { returnPattern.addElement(note); } } jfugue/org/jfugue/Controller.java0000644000175000017500000002443711005706176017630 0ustar giovannigiovanni/* * 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; /** * Contains information for MIDI Controller Events. * *@author David Koelle *@version 2.0 */ public final class Controller implements JFugueElement { byte index; byte value; /** Creates a new Controller object */ public Controller() { this.index = 0; this.value = 0; } /** * Creates a new Controller object, with the specified controller index and value. * * @param index the index of the controller to set * @param value the byte value used to set the controller */ public Controller(byte index, byte value) { this.index = index; this.value = value; } /** * TODO: This method, which is currently not supported, * is intended to take an integer, which contains the MSB and LSB bytes of a controller event, * and parse the integer to the MSB and LSB. Currently, this feature is handled by MusicStringParser, and * has not been rolled into the Controller class (where it should really belong). One difficulty to consider * is that an integer representing an MSB and an LSB actually means that there are two Controller instances, * not just one. Of course, the Controller class itself represents only one controller event. * * @param index * @param value */ // public Controller(int index, byte value) // { // throw new UnsupportedOperationException("Controller(int index, byte value) is not supported. If you're using a byte for the controller index, cast your index appropriately to use Controller(byte index, byte value)."); // } /** * Sets the index of the controller event for this object. * @param index the index of the controller */ public void setIndex(byte index) { this.index = index; } /** * Returns the index of the controller event for this object. * @return the index of the controller */ public byte getIndex() { return this.index; } /** * Sets the value of the controller event for this object. * @param value the byte value used to set the controller */ public void setValue(byte value) { this.value = value; } /** * Returns the value of the controller event for this object. * @return the value of the controller */ public byte getValue() { return this.value; } /** * Returns the Music String representing this element and all of its settings. * For a Controller object, the Music String is Xindex=value * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("X"); buffy.append(getIndex()); buffy.append("="); buffy.append(getValue()); return buffy.toString(); } /** * Returns verification string in this format: * Controller: index={#}, value={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("Controller: index="); buffy.append(getIndex()); buffy.append(", value="); buffy.append(getValue()); return buffy.toString(); } public static final byte BANK_SELECT_COARSE = 0; public static final byte MOD_WHEEL_COARSE = 1; public static final byte BREATH_COARSE = 2; public static final byte FOOT_PEDAL_COARSE = 4; public static final byte PORTAMENTO_TIME_COARSE = 5; public static final byte DATA_ENTRY_COARSE = 6; public static final byte VOLUME_COARSE = 7; public static final byte BALANCE_COARSE = 8; public static final byte PAN_POSITION_COARSE = 10; public static final byte EXPRESSION_COARSE = 11; public static final byte EFFECT_CONTROL_1_COARSE = 12; public static final byte EFFECT_CONTROL_2_COARSE = 13; public static final byte SLIDER_1 = 16; public static final byte SLIDER_2 = 17; public static final byte SLIDER_3 = 18; public static final byte SLIDER_4 = 19; public static final byte BANK_SELECT_FINE = 32; public static final byte MOD_WHEEL_FINE = 33; public static final byte BREATH_FINE = 34; public static final byte FOOT_PEDAL_FINE = 36; public static final byte PORTAMENTO_TIME_FINE = 37; public static final byte DATA_ENTRY_FINE = 38; public static final byte VOLUME_FINE = 39; public static final byte BALANCE_FINE = 40; public static final byte PAN_POSITION_FINE = 42; public static final byte EXPRESSION_FINE = 43; public static final byte EFFECT_CONTROL_1_FINE = 44; public static final byte EFFECT_CONTROL_2_FINE = 45; public static final byte HOLD_PEDAL = 64; public static final byte HOLD = 64; public static final byte PORTAMENTO = 65; public static final byte SUSTENUTO_PEDAL = 66; public static final byte SUSTENUTO = 66; public static final byte SOFT_PEDAL = 67; public static final byte SOFT = 67; public static final byte LEGATO_PEDAL = 68; public static final byte LEGATO = 68; public static final byte HOLD_2_PEDAL = 69; public static final byte HOLD_2 = 69; public static final byte SOUND_VARIATION = 70; public static final byte VARIATION = 70; public static final byte SOUND_TIMBRE = 71; public static final byte TIMBRE = 71; public static final byte SOUND_RELEASE_TIME = 72; public static final byte RELEASE_TIME = 72; public static final byte SOUND_ATTACK_TIME = 73; public static final byte ATTACK_TIME = 73; public static final byte SOUND_BRIGHTNESS = 74; public static final byte BRIGHTNESS = 74; public static final byte SOUND_CONTROL_6 = 75; public static final byte CONTROL_6 = 75; public static final byte SOUND_CONTROL_7 = 76; public static final byte CONTROL_7 = 76; public static final byte SOUND_CONTROL_8 = 77; public static final byte CONTROL_8 = 77; public static final byte SOUND_CONTROL_9 = 78; public static final byte CONTROL_9 = 78; public static final byte SOUND_CONTROL_10 = 79; public static final byte CONTROL_10 = 79; public static final byte GENERAL_PURPOSE_BUTTON_1 = 80; public static final byte GENERAL_BUTTON_1 = 80; public static final byte BUTTON_1 = 80; public static final byte GENERAL_PURPOSE_BUTTON_2 = 81; public static final byte GENERAL_BUTTON_2 = 81; public static final byte BUTTON_2 = 81; public static final byte GENERAL_PURPOSE_BUTTON_3 = 82; public static final byte GENERAL_BUTTON_3 = 82; public static final byte BUTTON_3 = 82; public static final byte GENERAL_PURPOSE_BUTTON_4 = 83; public static final byte GENERAL_BUTTON_4 = 83; public static final byte BUTTON_4 = 83; public static final byte EFFECTS_LEVEL = 91; public static final byte EFFECTS = 91; public static final byte TREMULO_LEVEL = 92; public static final byte TREMULO = 92; public static final byte CHORUS_LEVEL = 93; public static final byte CHORUS = 93; public static final byte CELESTE_LEVEL = 94; public static final byte CELESTE = 94; public static final byte PHASER_LEVEL = 95; public static final byte PHASER = 95; public static final byte DATA_BUTTON_INCREMENT = 96; public static final byte DATA_BUTTON_INC = 96; public static final byte BUTTON_INC = 96; public static final byte DATA_BUTTON_DECREMENT = 97; public static final byte DATA_BUTTON_DEC = 97; public static final byte BUTTON_DEC = 97; public static final byte NON_REGISTERED_COARSE = 98; public static final byte NON_REGISTERED_FINE = 99; public static final byte REGISTERED_COARSE = 100; public static final byte REGISTERED_FINE = 101; public static final byte ALL_SOUND_OFF = 120; public static final byte ALL_CONTROLLERS_OFF = 121; public static final byte LOCAL_KEYBOARD = 122; public static final byte ALL_NOTES_OFF = 123; public static final byte OMNI_MODE_OFF = 124; public static final byte OMNI_OFF = 124; public static final byte OMNI_MODE_ON = 125; public static final byte OMNI_ON = 125; public static final byte MONO_OPERATION = 126; public static final byte MONO = 126; public static final byte POLY_OPERATION = 127; public static final byte POLY = 127; // // Combined Controller names // (index = coarse_controller_index * 128 + fine_controller_index) // public static final int BANK_SELECT = 16383; public static final int MOD_WHEEL = 161; public static final int BREATH = 290; public static final int FOOT_PEDAL = 548; public static final int PORTAMENTO_TIME = 677; public static final int DATA_ENTRY = 806; public static final int VOLUME = 935; public static final int BALANCE = 1064; public static final int PAN_POSITION = 1322; public static final int EXPRESSION = 1451; public static final int EFFECT_CONTROL_1 = 1580; public static final int EFFECT_CONTROL_2 = 1709; public static final int NON_REGISTERED = 12770; public static final int REGISTERED = 13028; // // Values for controllers // public static final byte ON = 127; public static final byte OFF = 0; public static final byte DEFAULT = 64; }jfugue/org/jfugue/MidiParser.java0000644000175000017500000002560311067761326017546 0ustar giovannigiovanni/* * 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.MetaMessage; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiMessage; import javax.sound.midi.Sequence; import javax.sound.midi.ShortMessage; import javax.sound.midi.SysexMessage; import javax.sound.midi.Track; /** * Parses MIDI data, whether from a file, a connected device, or some other stream. * * @version 4.0.3 - A Note event with 0 duration is now sent when a note is first encountered */ public final class MidiParser extends Parser { long[][] tempNoteRegistry = new long[16][255]; byte[][] tempNoteAttackRegistry = new byte[16][255]; int tempo; private static final int DEFAULT_TEMPO = 120; public MidiParser() { this.tempo = DEFAULT_TEMPO; // Create a two dimensional array of bytes [ track, note ] - when a NoteOn event is found, // populate the proper spot in the array with the note's start time. When a NoteOff event // is found, new Time and Note objects are constructed and added to the composition for (int m=0; m < 16; m++) { for (int n=0; n < 255; n++) { tempNoteRegistry[m][n] = 0L; tempNoteAttackRegistry[m][n] = (byte)0; } } } /** * Parses a Sequence and fires events to subscribed ParserListener * interfaces. As the Sequence is parsed, events are sent * to ParserListener interfaces, which are responsible for doing * something interesting with the music data, such as adding notes to a pattern. * * @param sequence the Sequence to parse * @throws Exception if there is an error parsing the pattern */ public void parse(Sequence sequence) { this.tempo = DEFAULT_TEMPO; // Get the MIDI tracks from the sequence. Expect a maximum of 16 tracks. Track[] tracks = sequence.getTracks(); // Compute the size of this adventure for the ParserProgressListener long totalCount = 0; long counter = 0; for (byte i=0; i < tracks.length; i++) { totalCount += tracks[i].size(); } // And now to parse the MIDI! for (int t = 0; t < tracks.length; t++) { int trackSize = tracks[t].size(); if (trackSize > 0) { fireVoiceEvent(new Voice((byte)t)); for (int ev = 0; ev < trackSize; ev++) { counter++; fireProgressReported("Parsing MIDI...", counter, totalCount); MidiEvent event = tracks[t].get(ev); MidiMessage message = event.getMessage(); trace("Message received: ",message); parse(message, event.getTick()); } } } } /** * Delegator method that calls specific parsers depending on the * type of MidiMessage passed in. * @param message the message to parse * @param timestamp the time at which the message was encountered in this track */ public void parse(MidiMessage message, long timestamp) { if (message instanceof ShortMessage) { parseShortMessage((ShortMessage)message, timestamp); } else if (message instanceof SysexMessage) { parseSysexMessage((SysexMessage)message, timestamp); } else if (message instanceof MetaMessage) { parseMetaMessage((MetaMessage)message, timestamp); } } /** * Parses instances of ShortMessage. * @param message The message to parse * @param timestamp the time at which the message was encountered in this track */ private void parseShortMessage(ShortMessage message, long timestamp) { int track = message.getChannel(); switch (message.getCommand()) { case ShortMessage.PROGRAM_CHANGE : // 0xC0, 192 trace("Program change to ",message.getData1()); Instrument instrument = new Instrument((byte)message.getData1()); fireTimeEvent(new Time(timestamp)); fireVoiceEvent(new Voice((byte)track)); fireInstrumentEvent(instrument); break; case ShortMessage.CONTROL_CHANGE : // 0xB0, 176 trace("Controller change to ",message.getData1(),", value = ",message.getData2()); Controller controller = new Controller((byte)message.getData1(), (byte)message.getData2()); fireTimeEvent(new Time(timestamp)); fireVoiceEvent(new Voice((byte)track)); fireControllerEvent(controller); break; case ShortMessage.NOTE_ON : // 0x90, 144 if (message.getData2() == 0) { // A velocity of zero in a note-on event is a note-off event noteOffEvent(timestamp, track, message.getData1(), message.getData2()); } else { noteOnEvent(timestamp, track, message.getData1(), message.getData2()); } break; case ShortMessage.NOTE_OFF : // 0x80, 128 noteOffEvent(timestamp, track, message.getData1(), message.getData2()); break; case ShortMessage.CHANNEL_PRESSURE : // 0xD0, 208 trace("Channel pressure, pressure = ",message.getData1()); ChannelPressure pressure = new ChannelPressure((byte)message.getData1()); fireTimeEvent(new Time(timestamp)); fireVoiceEvent(new Voice((byte)track)); fireChannelPressureEvent(pressure); break; case ShortMessage.POLY_PRESSURE : // 0xA0, 128 trace("Poly pressure on key ",message.getData1(),", pressure = ",message.getData2()); PolyphonicPressure poly = new PolyphonicPressure((byte)message.getData1(), (byte)message.getData2()); fireTimeEvent(new Time(timestamp)); fireVoiceEvent(new Voice((byte)track)); firePolyphonicPressureEvent(poly); break; case ShortMessage.PITCH_BEND : // 0xE0, 224 trace("Pitch Bend, data1= ",message.getData1(),", data2= ",message.getData2()); PitchBend bend = new PitchBend((byte)message.getData1(), (byte)message.getData2()); fireTimeEvent(new Time(timestamp)); fireVoiceEvent(new Voice((byte)track)); firePitchBendEvent(bend); break; default : trace("Unparsed message: ",message.getCommand()); break; } } private void noteOnEvent(long timestamp, int track, int data1, int data2) { trace("Note on ",data1," - attack is ",data2); tempNoteRegistry[track][data1] = timestamp; tempNoteAttackRegistry[track][data1] = (byte)data2; // Added 9/27/2008 - fire a Note with duration 0 to signify a that a Note was pressed Note note = new Note((byte)data1, 0); note.setDecimalDuration(0); note.setAttackVelocity((byte)data2); fireNoteEvent(note); } private void noteOffEvent(long timestamp, int track, int data1, int data2) { long time = tempNoteRegistry[track][data1]; trace("Note off ",data1," - decay is ",data2,". Duration is ",(timestamp - time)); fireTimeEvent(new Time(time)); fireVoiceEvent(new Voice((byte)track)); Note note = new Note((byte)data1, (long)(timestamp - time)); note.setDecimalDuration((double)((timestamp - time) / (this.tempo * 4.0D))); note.setAttackVelocity(tempNoteAttackRegistry[track][data1]); note.setDecayVelocity((byte)data2); fireNoteEvent(note); tempNoteRegistry[track][data1] = 0L; } /** * Parses instances of SysexMessage. * @param message The message to parse * @param timestamp the time at which the message was encountered in this track */ private void parseSysexMessage(SysexMessage message, long timestamp) { // Nothing to do - JFugue doesn't use sysex messages trace("SysexMessage received but not parsed by JFugue (doesn't use them)"); } /** * Parses instances of MetaMessage. * @param message The message to parse * @param timestamp the time at which the message was encountered in this track */ private void parseMetaMessage(MetaMessage message, long timestamp) { switch (message.getType()) { case 0x51 : parseTempo(message, timestamp); break; case 0x59 : break; // Even though we care about Key Signatures, we don't want to read one in from a MIDI file, // because the notes that we'll receive will already be adjusted for the key signature. // MIDI's Key Signature is more about notating sheet music that influencing the played notes. default : break; } // Nothing to do - JFugue doesn't use sysex messages trace("MetaMessage received but not parsed by JFugue (doesn't use them)"); } private void parseTempo(MetaMessage message, long timestamp) { int beatsPerMinute = TimeFactor.parseMicrosecondsPerBeat(message, timestamp); trace("Tempo Event, bpm = ",beatsPerMinute); fireTimeEvent(new Time(timestamp)); fireTempoEvent(new Tempo(beatsPerMinute)); this.tempo = beatsPerMinute; } } jfugue/org/jfugue/Tempo.java0000644000175000017500000000657011005706530016561 0ustar giovannigiovanni/* * 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; /** * Represents tempo changes. Tempo is kept for the whole * song, and is independent of tracks. You may change the * tempo during a song. * * As of JFugue 4.0, Tempo represents the Beats Per Minute (BPM). * In previous versions, Tempo was measured in microseconds per beat, * which is how MIDI maintains this information. * (tempo = 60000 / BPM, and BPM = 60000 / tempo) * *@author David Koelle *@version 2.0 *@version 4.0 */ public final class Tempo implements JFugueElement { private int tempo; /** * Creates a new Tempo object, with the specified tempo value (in BPM). * @param tempo the tempo for this object, in Beats Per Minute */ public Tempo(int tempoInBPM) { setTempo(tempoInBPM); } /** * Sets the value of the tempo for this object. * @param tempo the tempo for this object */ public void setTempo(int tempoInBPM) { this.tempo = tempoInBPM; } /** * Returns the value of the tempo for this object. * @return the value of the tempo for this object */ public int getTempo() { return tempo; } /** * Returns the Music String representing this element and all of its settings. * For a Tempo object, the Music String is Ttempo * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("T"); buffy.append(getTempo()); return buffy.toString(); } /** * Returns verification string in this format: * Tempo: tempo={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("Tempo: tempo="); buffy.append(getTempo()); return buffy.toString(); } public static final int GRAVE = 40; public static final int LARGO = 45; public static final int LARGHETTO = 50; public static final int LENTO = 55; public static final int ADAGIO = 60; public static final int ADAGIETTO = 65; public static final int ANDANTE = 70; public static final int ANDANTINO = 80; public static final int MODERATO = 95; public static final int ALLEGRETTO = 110; public static final int ALLEGRO = 120; public static final int VIVACE = 145; public static final int PRESTO = 180; public static final int PRETISSIMO = 220; }jfugue/org/jfugue/MidiMessageRecipient.java0000644000175000017500000000227511000425036021517 0ustar giovannigiovanni/* * 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.MidiMessage; /** * Used in conjunction with TimeFactor.sortAndDeliverMidiMessages; * handles MIDI messages when they are delivered. * * @author David Koelle * */ public interface MidiMessageRecipient { public void messageReady(MidiMessage message, long timestamp); } jfugue/org/jfugue/DeviceThatWillTransmitMidi.java0000644000175000017500000001316110776766112022704 0ustar giovannigiovanni/* * 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.MidiDevice; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.Sequence; import javax.sound.midi.Sequencer; import javax.sound.midi.Transmitter; /** * Represents an attached MIDI device, such as a keyboard. This class * uses javax.sound.MidiDevice, but is not derived from javax.sound.MidiDevice. * * @author David Koelle * @version 3.0 */ public class DeviceThatWillTransmitMidi { private MidiDevice device; private Transmitter transmitter; private MidiReceiverForTransmitterDevice mrftd; // private Receiver defaultReceiver; /** * Creates a new DeviceThatWillTransmitMidi using JFugue's Intelligent Device Resolver to pick the * most likely device to open. * @throws MidiUnavailableException */ public DeviceThatWillTransmitMidi() throws MidiUnavailableException { this.device = IntelligentDeviceResolver.selectTransmitterDevice(); init(); } public DeviceThatWillTransmitMidi(MidiDevice.Info info) throws MidiUnavailableException { this.device = MidiSystem.getMidiDevice(info); init(); } private void init() throws MidiUnavailableException { try { if (!(device.isOpen())) { device.open(); } this.transmitter = device.getTransmitter(); this.mrftd = new MidiReceiverForTransmitterDevice(); } catch (MidiUnavailableException e) { device.close(); throw e; } } public Transmitter getTransmitter() { return this.transmitter; } public void addParserListener(ParserListener listener) { this.mrftd.getParser().addParserListener(listener); } public void removeParserListener(ParserListener listener) { this.mrftd.getParser().removeParserListener(listener); } /** * Reads a pattern from the external device - use this to record the * keys you're pressing on the keyboard! * * This method will return a JFugue Pattern, which you can then * manipulate to your heart's content. * * @return The Pattern representing the music played on the device */ public void startListening() { this.transmitter.setReceiver(this.mrftd); } public void stopListening() { // this.transmitter.setReceiver(this.defaultReceiver); device.close(); // this.transmitter.close(); // this.mrftd.close(); } public void listenForMillis(long millis) throws InterruptedException { startListening(); Thread.sleep(millis); stopListening(); } public Pattern getPatternFromListening() { return this.mrftd.getPattern(); } public Sequence getSequenceFromListening() { return this.mrftd.getSequence(); } public void close() { transmitter.close(); device.close(); // this.mrftd.close(); // this.transmitter.close(); } class MidiReceiverForTransmitterDevice implements Receiver { private MidiParser parser; private Sequencer sequencer; private Receiver sequencerReceiver; private MusicStringRenderer renderer; public MidiReceiverForTransmitterDevice() { System.out.println("Built mrftd"); parser = new MidiParser(); renderer = new MusicStringRenderer(); parser.addParserListener(renderer); try { sequencer = MidiSystem.getSequencer(); sequencerReceiver = sequencer.getReceiver(); } catch (MidiUnavailableException e) { e.printStackTrace(); } } public Parser getParser() { return this.parser; } public void send(MidiMessage message, long timestamp) { System.out.println("Parsing "+message+" ts: "+timestamp); parser.parse(message, timestamp / (1000 * 4)); sequencerReceiver.send(message, timestamp); } public void close() { sequencerReceiver.close(); } public Pattern getPattern() { return renderer.getPattern(); } public Sequence getSequence() { return sequencer.getSequence(); } } }jfugue/org/jfugue/Voice.java0000644000175000017500000000457511005706502016544 0ustar giovannigiovanni/* * 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; /** * Represents voice changes, also known as track changes. * *@author David Koelle *@version 1.0 */ public final class Voice implements JFugueElement { private byte voice; /** * Creates a new Voice object, with the specified voice value. * @param voice the voice for this object */ public Voice(byte voice) { setVoice(voice); } /** * Sets the value of the voice for this object. * @param tempo the voice for this object */ public void setVoice(byte voice) { this.voice = voice; } /** * Returns the voice used in this object * @return the voice used in this object */ public byte getVoice() { return voice; } /** * Returns the Music String representing this element and all of its settings. * For a Voice object, the Music String is Vvoice * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("V"); buffy.append(getVoice()); return buffy.toString(); } /** * Returns verification string in this format: * Voice: voice={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("Voice: voice="); buffy.append(getVoice()); return buffy.toString(); } }jfugue/org/jfugue/Pattern.java0000644000175000017500000005003111067512212017101 0ustar giovannigiovanni/* * 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 java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.Serializable; import java.util.ArrayList; import java.util.EventListener; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MidiSystem; import javax.swing.event.EventListenerList; /** * This class represents a segment of music. By representing segments of music * as patterns, JFugue gives users the opportunity to play around with pieces * of music in new and interesting ways. Patterns may be added together, transformed, * or otherwise manipulated to expand the possibilities of creative music. * * @author David Koelle * @version 2.0 * @version 4.0 - Added Pattern Properties * @version 4.0.3 - Now implements Serializable */ public class Pattern implements Serializable { private StringBuilder musicString; private Map properties; /** * Instantiates a new pattern */ public Pattern() { this(""); } /** * Instantiates a new pattern using the given music string * @param s the music string */ public Pattern(String musicString) { setMusicString(musicString); properties = new HashMap(); } /** Copy constructor */ public Pattern(Pattern pattern) { this(new String(pattern.getMusicString())); Iterator iter = pattern.getProperties().keySet().iterator(); while (iter.hasNext()) { String key = iter.next(); String value = pattern.getProperty(key); setProperty(key, value); } } /** * This constructor creates a new Pattern that contains each of the given patterns * @version 4.0 * */ public Pattern(Pattern... patterns) { this(); for (Pattern p : patterns) { this.add(p); } } /** * Creates a Pattern given a MIDI file - do not use. * Note the Package scope, limiting this method to be called * only by JFugue. If you want to load MIDI, use Player.loadMidi, * which sets the sequence timing correctly. * @param file * @throws IOException * @throws InvalidMidiDataException */ static Pattern loadMidi(File file) throws IOException, InvalidMidiDataException { MidiParser parser = new MidiParser(); MusicStringRenderer renderer = new MusicStringRenderer(); parser.addParserListener(renderer); parser.parse(MidiSystem.getSequence(file)); Pattern pattern = new Pattern(renderer.getPattern().getMusicString()); return pattern; } /** * Sets the music string kept by this pattern. * @param s the music string */ public void setMusicString(String musicString) { this.musicString = new StringBuilder(); this.musicString.append(musicString); } /** * Adds to the music string kept by this pattern. * @param s the music string to add */ private void appendMusicString(String appendString) { this.musicString.append(appendString); } /** * Returns the music string kept in this pattern * @return the music string */ public String getMusicString() { return this.musicString.toString(); } /** * Inserts a MusicString before this music string. * NOTE - this does not call fragmentAdded! * @param musicString the string to insert */ public void insert(String musicString) { this.musicString.insert(0, " "); this.musicString.insert(0, musicString); } /** * Adds an additional pattern to the end of this pattern. * @param pattern the pattern to add */ public void add(Pattern pattern) { fireFragmentAdded(pattern); appendMusicString(" "); appendMusicString(pattern.getMusicString()); } /** * Adds a music string to the end of this pattern. * @param musicString the music string to add */ public void add(String musicString) { add(new Pattern(musicString)); } /** * Adds an additional pattern to the end of this pattern. * @param pattern the pattern to add */ public void add(Pattern pattern, int numTimes) { for (int i=0; i < numTimes; i++) { fireFragmentAdded(pattern); appendMusicString(" "); appendMusicString(pattern.getMusicString()); } } /** * Adds a music string to the end of this pattern. * @param musicString the music string to add */ public void add(String musicString, int numTimes) { add(new Pattern(musicString), numTimes); } /** * Adds a number of patterns sequentially * @param musicString the music string to add * @version 4.0 */ public void add(Pattern... patterns) { for (Pattern pattern : patterns) { add(pattern); } } /** * Adds a number of patterns sequentially * @param musicString the music string to add * @version 4.0 */ public void add(String... musicStrings) { for (String string : musicStrings) { add(string); } } /** * Adds an individual element to the pattern. This takes into * account the possibility that the element may be a sequential or * parallel note, in which case no space is placed before it. * @param element the element to add */ public void addElement(JFugueElement element) { String elementMusicString = element.getMusicString(); // Don't automatically add a space if this is a continuing note event if ((elementMusicString.charAt(0) == '+') || (elementMusicString.charAt(0) == '_')) { appendMusicString(elementMusicString); } else { appendMusicString(" "); appendMusicString(elementMusicString); fireFragmentAdded(new Pattern(elementMusicString)); } } /** * Sets the title for this Pattern. * As of JFugue 4.0, the title is set as a property with the key Pattern.TITLE * @param title the title for this Pattern */ public void setTitle(String title) { setProperty(TITLE, title); } /** * Returns the title of this Pattern * As of JFugue 4.0, the title is set as a property with the key Pattern.TITLE * @return the title of this Pattern */ public String getTitle() { return getProperty(TITLE); } /** * Get a property on this pattern, such as "author" or "date". * @version 4.0 */ public String getProperty(String key) { return properties.get(key); } /** * Set a property on this pattern, such as "author" or "date". * @version 4.0 */ public void setProperty(String key, String value) { properties.put(key, value); } /** * Get all properties set on this pattern, such as "author" or "date". * @version 4.0 */ public Map getProperties() { return properties; } /** * Repeats the music string in this pattern * by the given number of times. * Example: If the pattern is "A B", calling repeat(4) will * make the pattern "A B A B A B A B". * @version 3.0 */ public void repeat(int times) { repeat(null, getMusicString(), times, null); } /** * Only repeats the portion of this music string * that starts at the string index * provided. This allows some initial header information to only be specified * once in a repeated pattern. * Example: If the pattern is "T0 A B", calling repeat(4, 3) will * make the pattern "T0 A B A B A B A B". * @version 3.0 */ public void repeat(int times, int beginIndex) { String string = getMusicString(); repeat(string.substring(0, beginIndex), string.substring(beginIndex), times, null); } /** * Only repeats the portion of this music string * that starts and ends at the * string indices provided. This allows some initial header information and * trailing information to only be specified once in a repeated pattern. * Example: If the pattern is "T0 A B C", calling repeat(4, 3, 5) * will make the pattern "T0 A B A B A B A B C". * @version 3.0 */ public void repeat(int times, int beginIndex, int endIndex) { String string = getMusicString(); repeat(string.substring(0, beginIndex), string.substring(beginIndex, endIndex), times, string.substring(endIndex)); } private void repeat(String header, String repeater, int times, String trailer) { StringBuffer buffy = new StringBuffer(); // Add the header, if it exists if (header != null) { buffy.append(header); } // Repeat and add the repeater for (int i=0; i < times; i++) { buffy.append(repeater); if (i < times-1) { buffy.append(" "); } } // Add the trailer, if it exists if (trailer != null) { buffy.append(trailer); } // Create the new Pattern and return it this.setMusicString(buffy.toString()); } /** * Returns a new Pattern that is a subpattern of this pattern. * @return subpattern of this pattern * @version 3.0 */ public Pattern subPattern(int beginIndex) { return new Pattern(substring(beginIndex)); } /** * Returns a new Pattern that is a subpattern of this pattern. * @return subpattern of this pattern * @version 3.0 */ public Pattern subPattern(int beginIndex, int endIndex) { return new Pattern(substring(beginIndex, endIndex)); } protected String substring(int beginIndex) { return getMusicString().substring(beginIndex); } protected String substring(int beginIndex, int endIndex) { return getMusicString().substring(beginIndex, endIndex); } public static Pattern loadPattern(File file) throws IOException { StringBuffer buffy = new StringBuffer(); Pattern pattern = new Pattern(); BufferedReader bread = new BufferedReader(new InputStreamReader(new FileInputStream(file))); while (bread.ready()) { String s = bread.readLine(); if ((s != null) && (s.length() > 1)) { if (s.charAt(0) != '#') { buffy.append(" "); buffy.append(s); } else { String key = s.substring(1, s.indexOf(':')).trim(); String value = s.substring(s.indexOf(':')+1, s.length()).trim(); if (key.equalsIgnoreCase(TITLE)) { pattern.setTitle(value); } else { pattern.setProperty(key, value); } } } } bread.close(); pattern.setMusicString(buffy.toString()); return pattern; } /** * Saves the pattern as a text file * @param filename the filename to save under */ public void savePattern(File file) throws IOException { BufferedWriter out = new BufferedWriter(new FileWriter(file)); if ((getProperties().size() > 0) || (getTitle() != null)) { out.write("#\n"); if (getTitle() != null) { out.write("# "); out.write("Title: "); out.write(getTitle()); out.write("\n"); } Iterator iter = getProperties().keySet().iterator(); while (iter.hasNext()) { String key = iter.next(); if (!key.equals(TITLE)) { String value = getProperty(key); out.write("# "); out.write(key); out.write(": "); out.write(value); out.write("\n"); } } out.write("#\n"); out.write("\n"); } String musicString = getMusicString(); while (musicString.length() > 0) { if ((musicString.length() > 80) && (musicString.indexOf(' ', 80) > -1)) { int indexOf80ColumnSpace = musicString.indexOf(' ', 80); out.write(musicString.substring(0, indexOf80ColumnSpace)); out.newLine(); musicString = musicString.substring(indexOf80ColumnSpace, musicString.length()); } else { out.write(musicString); musicString = ""; } } out.close(); } /** * Returns a String containing key-value pairs stored in this object's properties, * separated by semicolons and spaces. * Values are returned in the following form: * key1: value1; key2: value2; key3: value3 * * @return a String containing key-value pairs stored in this object's properties, separated by semicolons and spaces */ public String getPropertiesAsSentence() { StringBuilder buddy = new StringBuilder(); Iterator iter = getProperties().keySet().iterator(); while (iter.hasNext()) { String key = iter.next(); String value = getProperty(key); buddy.append(key); buddy.append(": "); buddy.append(value); buddy.append("; "); } String result = buddy.toString(); return result.substring(0, result.length()-2); // Take off the last semicolon-space } /** * Returns a String containing key-value pairs stored in this object's properties, * separated by newline characters. * * Values are returned in the following form: * key1: value1\n * key2: value2\n * key3: value3\n * * @return a String containing key-value pairs stored in this object's properties, separated by newline characters */ public String getPropertiesAsParagraph() { StringBuilder buddy = new StringBuilder(); Iterator iter = getProperties().keySet().iterator(); while (iter.hasNext()) { String key = iter.next(); String value = getProperty(key); buddy.append(key); buddy.append(": "); buddy.append(value); buddy.append("\n"); } String result = buddy.toString(); return result.substring(0, result.length()); } /** * Changes all timestamp values by the offsetTime passed in. * NOTE: This method is only useful for patterns that have been converted from a MIDI file. * @param offsetTime */ public void offset(long offsetTime) { StringBuffer buffy = new StringBuffer(); String[] tokens = getMusicString().split(" "); for (int i=0; i < tokens.length; i++) { if ((tokens[i].length() > 0) && (tokens[i].charAt(0) == '@')) { String timeNumberString = tokens[i].substring(1,tokens[i].length()); if (timeNumberString.indexOf("[") == -1) { long timeNumber = new Long(timeNumberString).longValue(); long newTime = timeNumber + offsetTime; if (newTime < 0) newTime = 0; buffy.append("@" + newTime); } else { buffy.append(tokens[i]); } } else { buffy.append(tokens[i]); } buffy.append(" "); } setMusicString(buffy.toString()); } /** * Returns an array of strings representing each token in the Pattern. * @return */ public String[] getTokens() { StringTokenizer strtok = new StringTokenizer(musicString.toString()," \n\t"); List list = new ArrayList(); while (strtok.hasMoreTokens()) { String token = strtok.nextToken(); if (token != null) { list.add(token); } } String[] retVal = new String[list.size()]; list.toArray(retVal); return retVal; } /** * Indicates whether the provided musicString is composed of valid elements * that can be parsed by the Parser. * @param musicString the musicString to test * @return whether the musicString is valid * @version 3.0 */ // public static boolean isValidMusicString(String musicString) // { // try { // Parser parser = new Parser(); // parser.parse(musicString); // } catch (JFugueException e) // { // return false; // } // return true; // } // // Listeners // /** List of ParserListeners */ protected EventListenerList listenerList = new EventListenerList (); /** * Adds a PatternListener. The listener will receive events when new * parts are added to the pattern. * * @param listener the listener that is to be notified when new parts are added to the pattern */ public void addPatternListener (PatternListener l) { listenerList.add (PatternListener.class, l); } /** * Removes a PatternListener. * * @param listener the listener to remove */ public void removePatternListener (PatternListener l) { listenerList.remove (PatternListener.class, l); } protected void clearPatternListeners () { EventListener[] l = listenerList.getListeners (PatternListener.class); int numListeners = l.length; for (int i = 0; i < numListeners; i++) { listenerList.remove (PatternListener.class, (PatternListener)l[i]); } } /** Tells all PatternListener interfaces that a fragment has been added. */ private void fireFragmentAdded(Pattern fragment) { Object[] listeners = listenerList.getListenerList (); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == PatternListener.class) { ((PatternListener)listeners[i + 1]).fragmentAdded(fragment); } } } /** * @version 3.0 */ public String toString() { return getMusicString(); } public static final String TITLE = "Title"; } jfugue/org/jfugue/MidiEventManager.java0000644000175000017500000002017311003717576020661 0ustar giovannigiovanni/* * 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.InvalidMidiDataException; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiEvent; import javax.sound.midi.Sequence; import javax.sound.midi.ShortMessage; import javax.sound.midi.Track; /** * Places musical data into the MIDI sequence. * *

* This was named EventManager in previous versions of JFugue. *

* *@author David Koelle *@version 2.0 *@version 3.0 - renamed to MidiEventManager */ public final class MidiEventManager { private final int CHANNELS = 16; private final int LAYERS = 16; private byte currentTrack = 0; private byte[] currentLayer = new byte[CHANNELS]; private long time[][] = new long[CHANNELS][LAYERS]; private Sequence sequence; private Track track[] = new Track[CHANNELS]; public MidiEventManager(float sequenceTiming, int resolution) { try { this.sequence = new Sequence(sequenceTiming, resolution); } catch (Exception e) { e.printStackTrace(); } for (int i=0; i < CHANNELS; i++) { for (int u=0; u < LAYERS; u++) { time[i][u] = 0; } currentLayer[i] = 0; track[i] = sequence.createTrack(); } currentTrack = 0; } /** * Sets the current track, or channel, to which new events will be added. * @param track the track to select */ public void setCurrentTrack(byte track) { currentTrack = track; } /** * Sets the current layer within the track to which new events will be added. * @param track the track to select */ public void setCurrentLayer(byte layer) { currentLayer[currentTrack] = layer; } /** * Advances the timer for the current track by the specified duration, * which is specified in Pulses Per Quarter (PPQ) * @param duration the duration to increase the track timer */ public void advanceTrackTimer(long duration) { time[currentTrack][currentLayer[currentTrack]] += duration; } /** * Sets the timer for the current track by the given time, * which is specified in Pulses Per Quarter (PPQ) * @param newTime the time at which to set the track timer */ public void setTrackTimer(long newTime) { time[currentTrack][currentLayer[currentTrack]] = newTime; } /** * Returns the timer for the current track. * @return the timer value for the current track, specified in Pulses Per Quarter (PPQ) */ public long getTrackTimer() { return time[currentTrack][currentLayer[currentTrack]]; } /** * Adds a MetaMessage to the current track. * * @param command the MIDI command represented by this message * @param data1 the first data byte * @param data2 the second data byte */ public void addMetaMessage(int type, byte[] bytes) { try { MetaMessage message = new MetaMessage(); message.setMessage(type, bytes, bytes.length); MidiEvent event = new MidiEvent(message, getTrackTimer()); track[currentTrack].add(event); } catch (InvalidMidiDataException e) { // We've kept a good eye on the data. This exception won't happen. e.printStackTrace(); } } /** * Adds a MIDI event to the current track. * * @param command the MIDI command represented by this message * @param data1 the first data byte */ public void addEvent(int command, int data1) { try { ShortMessage message = new ShortMessage(); message.setMessage(command, currentTrack, data1); MidiEvent event = new MidiEvent(message, getTrackTimer()); track[currentTrack].add(event); } catch (InvalidMidiDataException e) { // We've kept a good eye on the data. This exception won't happen. e.printStackTrace(); } } /** * Adds a MIDI event to the current track. * * @param command the MIDI command represented by this message * @param data1 the first data byte * @param data2 the second data byte */ public void addEvent(int command, int data1, int data2) { try { ShortMessage message = new ShortMessage(); message.setMessage(command, currentTrack, data1, data2); MidiEvent event = new MidiEvent(message, getTrackTimer()); track[currentTrack].add(event); } catch (InvalidMidiDataException e) { // We've kept a good eye on the data. This exception won't happen. e.printStackTrace(); } } /** * Adds a ShortMessage.NOTE_ON event to the current track, using attack and * decay velocity values. Also adds a ShortMessage.NOTE_OFF command for * the note, using the duration parameter to space the NOTE_OFF command properly. * * Both the NOTE_ON and NOTE_OFF events can be suppressed. This is useful * when notes are tied to other notes. * * @param data1 the first data byte, which contains the note value * @param data2 the second data byte for the NOTE_ON event, which contains the attack velocity * @param data3 the second data byte for the NOTE_OFF event, which contains the decay velocity * @param duration the duration of the note * @param addNoteOn whether a ShortMessage.NOTE_ON event should be created for for this event. For the end of a tied note, this should be false; otherwise it should be true. * @param addNoteOff whether a ShortMessage.NOTE_OFF event should be created for for this event. For the start of a tied note, this should be false; otherwise it should be true. */ public void addNoteEvent(int data1, int data2, int data3, long duration, boolean addNoteOn, boolean addNoteOff) { try { if (addNoteOn) { ShortMessage message = new ShortMessage(); message.setMessage(ShortMessage.NOTE_ON, currentTrack, data1, data2); MidiEvent event = new MidiEvent(message, getTrackTimer()); track[currentTrack].add(event); } advanceTrackTimer(duration); if (addNoteOff) { ShortMessage message2 = new ShortMessage(); message2.setMessage(ShortMessage.NOTE_OFF, currentTrack, data1, data3); MidiEvent event2 = new MidiEvent(message2, getTrackTimer()); track[currentTrack].add(event2); } } catch (InvalidMidiDataException e) { // We've kept a good eye on the data. This exception won't happen. e.printStackTrace(); } } /** * Returns the current sequence, which is a collection of tracks. * If your goal is to add events to the sequence, you don't want to use this method to * get the sequence; instead, use the addEvent methods to add your events. * @return the current sequence */ public Sequence getSequence() { return sequence; } }jfugue/org/jfugue/Anticipator.java0000644000175000017500000000507311000430246017740 0ustar giovannigiovanni/* * 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.MidiMessage; import javax.sound.midi.Sequence; /** * This class can be used in conjunction with a call to Player.play() to * inform your application about musical events before they happen. This * is useful if you're creating an application that requires advance notice * of a musical event - for example, an animation program that must wind up * or swing an arm back before striking a note. * * This feature is covered in detail in "The Complete Guide to JFugue" * * @author David Koelle * @version 3.0 */ public class Anticipator { protected MidiParser parser; public Anticipator() { this.parser = new MidiParser(); } /** * Adds a ParserListener. * * @param listener the listener to remove */ public void addParserListener(ParserListener l) { this.parser.addParserListener(l); } /** * Removes a ParserListener. * * @param listener the listener to remove */ public void removeParserListener(ParserListener l) { this.parser.removeParserListener(l); } protected void play(final Sequence sequence) { final Thread anticipatingThread = new Thread() { public void run() { TimeFactor.sortAndDeliverMidiMessages(sequence, new MidiMessageRecipient() { public void messageReady(MidiMessage message, long timestamp) { parser.parse(message, timestamp); } } ); } }; anticipatingThread.start(); } } jfugue/org/jfugue/PitchBend.java0000644000175000017500000000533411005706440017332 0ustar giovannigiovanni/* * 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; /** * Represents pitch bend changes. * *@author David Koelle *@version 3.0 */ public final class PitchBend implements JFugueElement { private byte lsb; private byte msb; /** * Creates a new Pitch Bend object, with the specified tempo value. * Integer value = msb * 0x80 + lsb (0x80 hex == 128 dec) * @param lsb the least significant byte for the pitch bend for this object * @param msb the most significant byte for the pitch bend for this object */ public PitchBend(byte lsb, byte msb) { setPitchBend(lsb, msb); } /** * Sets the value of the pitch bend for this object. * @param tempo the pitch bend for this object */ public void setPitchBend(byte lsb, byte msb) { this.lsb = lsb; this.msb = msb; } /** * Returns the value of the pitch bend for this object. * @return the value of the pitch bend for this object */ public byte[] getBend() { return new byte[] { lsb, msb }; } /** * Returns the Music String representing this element and all of its settings. * For a PitchBend object, the Music String is &int or &lsb,msb * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("&"); buffy.append(getBend()[1] * 0x80 + getBend()[0]); return buffy.toString(); } /** * Returns verification string in this format: * PitchBend: bend={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("PitchBend: bend="); buffy.append(getBend()); return buffy.toString(); } }jfugue/org/jfugue/Instrument.java0000644000175000017500000003200611010307710017627 0ustar giovannigiovanni/* * 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; /** * Represents instrument changes, also known as patch changes. * *@author David Koelle *@version 2.0 */ public final class Instrument implements JFugueElement { private byte instrument; /** * Creates a new Instrument object, with the specified instrument number. * @param instrument the number of the instrument to use */ public Instrument(byte instrument) { setInstrument(instrument); } /** * Sets the value of the instrument for this object. * @param instrument the number of the instrument to use */ public void setInstrument(byte instrument) { this.instrument = instrument; } /** * Returns the instrument used in this object * @return the instrument used in this object */ public byte getInstrument() { return instrument; } /** * Returns the name of the instrument used in this object * @return the name of the instrument used in this object */ public String getInstrumentName() { return INSTRUMENT_NAME[getInstrument()]; } /** * Returns the Music String representing this element and all of its settings. * For an Instrument object, the Music String is Iinstrument-number * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("I["); buffy.append(INSTRUMENT_NAME[getInstrument()]); buffy.append("]"); return buffy.toString(); } /** * Returns verification string in this format: * Instrument: instrument={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("Instrument: instrument="); buffy.append(getInstrument()); return buffy.toString(); } public static final String[] INSTRUMENT_NAME = new String[] { "Piano", "Bright_Acoustic", "Electric_Grand", "Honkey_Tonk", "Electric_Piano", "Electric_Piano_2", "Harpischord", "Clavinet", "Celesta", "Glockenspiel", "Music_Box", "Vibraphone", "Marimba", "Xylophone", "Tubular_Bells", "Dulcimer", "Drawbar_Organ", "Percussive_Organ", "Rock_Organ", "Church_Organ", "Reed_Organ", "Accordian", "Harmonica", "Tango_Accordian", "Guitar", "Steel_String_Guitar", "Electric_Jazz_Guitar", "Electric_Clean_Guitar", "Electric_muted_Guitar", "Overdriven_Guitar", "Distortion_Guitar", "Guitar_Harmonics", "Acoustic_Bass", "Electric_Bass_Finger", "Electric_Bass_Pick", "Fretless_Bass", "Slap_Bass_1", "Slap_Bass_2", "Synth_Bass_1", "Synth_Bass_2", "Violin", "Viola", "Cello", "Contrabass", "Tremolo_Strings", "Pizzicato_Strings", "Orchestral_Strings", "Timpani", "String_Ensemble_1", "String_Ensemble_2", "Synth_strings_1", "Synth_strings_2", "Choir_Aahs", "Voice_Oohs", "Synth_Voice", "Orchestra_Hit", "Trumpet", "Trombone", "Tuba", "Muted_Trumpet", "French_Horn", "Brass_Section", "Synth_brass_1", "Synth_brass_2", "Soprano_Sax", "Alto_Sax", "Tenor_Sax", "Baritone_Sax", "Oboe", "English_Horn", "Bassoon", "Clarinet", "Piccolo", "Flute", "Recorder", "Pan_Flute", "Blown_Bottle", "Skakuhachi", "Whistle", "Ocarina", "Square", "Sawtooth", "Calliope", "Chiff", "Charang", "Voice", "Fifths", "Basslead", "New_Age", "Warm", "Polysynth", "Choir", "Bowed", "Metallic", "Halo", "Sweep", "Rain", "Soundtrack", "Crystal", "Atmosphere", "Brightness", "Goblins", "Echoes", "Sci-fi", "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bagpipe", "Fiddle", "Shanai", "Tinkle_Bell", "Agogo", "Steel_Drums", "Woodblock", "Taiko_Drum", "Melodic_Tom", "Synth_Drum", "Reverse_Cymbal", "Guitar_Fret_Noise", "Breath_Noise", "Seashore", "Bird_Tweet", "Telephone_Ring", "Helicopter", "Applause", "Gunshot" }; public static final byte PIANO = 0; public static final byte ACOUSTIC_GRAND = 0; public static final byte BRIGHT_ACOUSTIC = 1; public static final byte ELECTRIC_GRAND = 2; public static final byte HONKEY_TONK = 3; public static final byte ELECTRIC_PIANO = 4; public static final byte ELECTRIC_PIANO_1 = 4; public static final byte ELECTRIC_PIANO_2 = 5; public static final byte HARPISCHORD = 6; public static final byte CLAVINET = 7; public static final byte CELESTA = 8; public static final byte GLOCKENSPIEL = 9; public static final byte MUSIC_BOX = 10; public static final byte VIBRAPHONE = 11; public static final byte MARIMBA = 12; public static final byte XYLOPHONE = 13; public static final byte TUBULAR_BELLS = 14; public static final byte DULCIMER = 15; public static final byte DRAWBAR_ORGAN = 16; public static final byte PERCUSSIVE_ORGAN = 17; public static final byte ROCK_ORGAN = 18; public static final byte CHURCH_ORGAN = 19; public static final byte REED_ORGAN = 20; public static final byte ACCORDIAN = 21; public static final byte HARMONICA = 22; public static final byte TANGO_ACCORDIAN = 23; public static final byte GUITAR = 24; public static final byte NYLON_STRING_GUITAR = 24; public static final byte STEEL_STRING_GUITAR = 25; public static final byte ELECTRIC_JAZZ_GUITAR = 26; public static final byte ELECTRIC_CLEAN_GUITAR = 27; public static final byte ELECTRIC_MUTED_GUITAR = 28; public static final byte OVERDRIVEN_GUITAR = 29; public static final byte DISTORTION_GUITAR = 30; public static final byte GUITAR_HARMONICS = 31; public static final byte ACOUSTIC_BASS = 32; public static final byte ELECTRIC_BASS_FINGER = 33; public static final byte ELECTRIC_BASS_PICK = 34; public static final byte FRETLESS_BASS = 35; public static final byte SLAP_BASS_1 = 36; public static final byte SLAP_BASS_2 = 37; public static final byte SYNTH_BASS_1 = 38; public static final byte SYNTH_BASS_2 = 39; public static final byte VIOLIN = 40; public static final byte VIOLA = 41; public static final byte CELLO = 42; public static final byte CONTRABASS = 43; public static final byte TREMOLO_STRINGS = 44; public static final byte PIZZICATO_STRINGS = 45; public static final byte ORCHESTRAL_STRINGS = 46; public static final byte TIMPANI = 47; public static final byte STRING_ENSEMBLE_1 = 48; public static final byte STRING_ENSEMBLE_2 = 49; public static final byte SYNTH_STRINGS_1 = 50; public static final byte SYNTH_STRINGS_2 = 51; public static final byte CHOIR_AAHS = 52; public static final byte VOICE_OOHS = 53; public static final byte SYNTH_VOICE = 54; public static final byte ORCHESTRA_HIT = 55; public static final byte TRUMPET = 56; public static final byte TROMBONE = 57; public static final byte TUBA = 58; public static final byte MUTED_TRUMPET = 59; public static final byte FRENCH_HORN = 60; public static final byte BRASS_SECTION = 61; public static final byte SYNTHBRASS_1 = 62; public static final byte SYNTHBRASS_2 = 63; public static final byte SOPRANO_SAX = 64; public static final byte ALTO_SAX = 65; public static final byte TENOR_SAX = 66; public static final byte BARITONE_SAX = 67; public static final byte OBOE = 68; public static final byte ENGLISH_HORN = 69; public static final byte BASSOON = 70; public static final byte CLARINET = 71; public static final byte PICCOLO = 72; public static final byte FLUTE = 73; public static final byte RECORDER = 74; public static final byte PAN_FLUTE = 75; public static final byte BLOWN_BOTTLE = 76; public static final byte SKAKUHACHI = 77; public static final byte WHISTLE = 78; public static final byte OCARINA = 79; public static final byte LEAD_SQUARE = 80; public static final byte SQUARE = 80; public static final byte LEAD_SAWTOOTH = 81; public static final byte SAWTOOTH = 81; public static final byte LEAD_CALLIOPE = 82; public static final byte CALLIOPE = 82; public static final byte LEAD_CHIFF = 83; public static final byte CHIFF = 83; public static final byte LEAD_CHARANG = 84; public static final byte CHARANG = 84; public static final byte LEAD_VOICE = 85; public static final byte VOICE = 85; public static final byte LEAD_FIFTHS = 86; public static final byte FIFTHS = 86; public static final byte LEAD_BASSLEAD = 87; public static final byte BASSLEAD = 87; public static final byte PAD_NEW_AGE = 88; public static final byte NEW_AGE = 88; public static final byte PAD_WARM = 89; public static final byte WARM = 89; public static final byte PAD_POLYSYNTH = 90; public static final byte POLYSYNTH = 90; public static final byte PAD_CHOIR = 91; public static final byte CHOIR = 91; public static final byte PAD_BOWED = 92; public static final byte BOWED = 92; public static final byte PAD_METALLIC = 93; public static final byte METALLIC = 93; public static final byte PAD_HALO = 94; public static final byte HALO = 94; public static final byte PAD_SWEEP = 95; public static final byte SWEEP = 95; public static final byte FX_RAIN = 96; public static final byte RAIN = 96; public static final byte FX_SOUNDTRACK = 97; public static final byte SOUNDTRACK = 97; public static final byte FX_CRYSTAL = 98; public static final byte CRYSTAL = 98; public static final byte FX_ATMOSPHERE = 99; public static final byte ATMOSPHERE = 99; public static final byte FX_BRIGHTNESS = 100; public static final byte BRIGHTNESS = 100; public static final byte FX_GOBLINS = 101; public static final byte GOBLINS = 101; public static final byte FX_ECHOES = 102; public static final byte ECHOES = 102; public static final byte FX_SCI = 103; public static final byte SCI = 103; public static final byte SITAR = 104; public static final byte BANJO = 105; public static final byte SHAMISEN = 106; public static final byte KOTO = 107; public static final byte KALIMBA = 108; public static final byte BAGPIPE = 109; public static final byte FIDDLE = 110; public static final byte SHANAI = 111; public static final byte TINKLE_BELL = 112; public static final byte AGOGO = 113; public static final byte STEEL_DRUMS = 114; public static final byte WOODBLOCK = 115; public static final byte TAIKO_DRUM = 116; public static final byte MELODIC_TOM = 117; public static final byte SYNTH_DRUM = 118; public static final byte REVERSE_CYMBAL = 119; public static final byte GUITAR_FRET_NOISE = 120; public static final byte BREATH_NOISE = 121; public static final byte SEASHORE = 122; public static final byte BIRD_TWEET = 123; public static final byte TELEPHONE_RING = 124; public static final byte HELICOPTER = 125; public static final byte APPLAUSE = 126; public static final byte GUNSHOT = 127; }jfugue/org/jfugue/KeySignature.java0000644000175000017500000001016211005706234020100 0ustar giovannigiovanni/* * 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; /** * Represents key signature changes. Key signatures are kept for the * whole song, independent of tracks. You may change the key signature * during a song. The Player will automatically adjust note values based * on the current key signature - for example, in an F Major key signature, * B will be converted to B-flat automatically, unless the B is noted as * a natural B (i.e., "Bn") * *@author David Koelle *@version 3.0 */ public final class KeySignature implements JFugueElement { private byte keySig; private byte scale; /** * Creates a new key signature object, with the specified tempo value. * @param keySig the key signature for this object, -7 to +7 - see MIDI specification for more details * @param scale whether this is a major (0) or minor (1) key signature */ public KeySignature(byte keySig, byte scale) { setKeySig(keySig); setScale(scale); } /** * Sets the key signature, from -7 to +7, for this object. * See the MIDI specification for more details * @param keySig the key signature for this object */ public void setKeySig(byte keySig) { this.keySig = keySig; } /** * Sets the scale - 0 for major, 1 for minor. * See the MIDI specification for more details * @param scale the scale for this object */ public void setScale(byte scale) { this.scale = scale; } /** * Returns the key signature for this object. * @return the key signature for this object */ public byte getKeySig() { return this.keySig; } /** * Returns the scale for this object. * @return the scale for this object */ public byte getScale() { return this.scale; } /** * Returns the Music String representing this element and all of its settings. * For a key signature object, the Music String is Kkeysig, * where 'keysig' is a root note followed by 'maj' or 'min' (i.e., Cbmaj for C-flat major) * @return the Music String for this element */ public String getMusicString() { StringBuilder keySigSB = new StringBuilder(); keySigSB.append("K"); if (scale == 0) { keySigSB.append(majorSigs[getKeySig()+7]); keySigSB.append("maj"); } else { keySigSB.append(minorSigs[getKeySig()+7]); keySigSB.append("min"); } return keySigSB.toString(); } /** * Returns verification string in this format: * KeySig: keySig={#}, scale={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("KeySig: keySig="); buffy.append(getKeySig()); buffy.append(", scale="); buffy.append(getScale()); return buffy.toString(); } private static final String[] majorSigs = new String[] { "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#" }; private static final String[] minorSigs = new String[] { "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "D#", "A#" }; }jfugue/org/jfugue/JFugueException.java0000644000175000017500000001734010775771604020557 0ustar giovannigiovanni/* * 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; /** * Handles JFugue parsing exceptions. * *@author David Koelle *@version 2.0 */ public class JFugueException extends RuntimeException { /** * Create a new JFugueException. * * @param exc The string representing the exception. */ public JFugueException(String exc) { super(exc); } /** * Create a new JFugueException. * * @param exc The string representing the exception. This should contain the * character, so 'param' can be placed into the string. * @param token The token or dictionary entry in which the exception has been discovered */ public JFugueException(String exc, String token) { super(exc + " Found while parsing the following token, word, or definition: "+token); } /** * Create a new JFugueException. * * @param exc The string representing the exception. This should contain the * character, so 'param' can be placed into the string. * @param param The direct object of the exception, the thing that has had some problem with it * @param token The token or dictionary entry in which the exception has been discovered */ public JFugueException(String exc, String param, String token) { super(exc.substring(0, exc.indexOf('*')) + param + exc.substring(exc.indexOf('*')+1, exc.length()) + " Found while parsing the following token, word, or definition: "+token); } /** The Voice command, Vvoice, is out of range. */ public static final String VOICE_EXC = "Voice * is not a number, or is not in the range 0 - 127."; /** The Tempo command, Ttempo, is out of range. */ public static final String TEMPO_EXC = "Tempo * is not a number"; /** The KeySignature command, Kkeysig, is poorly formed. */ public static final String KEYSIG_EXC = " * is not a proper key signature; should be like KC#maj or KAbmin."; /** The Layer command, Llayer, is out of range. */ public static final String LAYER_EXC = "Layer * is not a number, or is not in the range 0 - 127."; /** The Instrument command, Iinstrument, is not a valid instrument. */ public static final String INSTRUMENT_EXC = "Instrument * is not a valid instrument name, or is not in the range 0 - 127."; /** The index of the Controller command, Xindex=value, is not a valid controller. */ public static final String CONTROL_EXC = "Control * is not a valid controller name, or is not in the range 0 - 127."; /** The Note command does not specify a valid percussion sound. */ public static final String NOTE_EXC = "Note * is not a valid drum sound name, or is not in the range 0 - 127."; /** The Octave specifier within the Note command is out of range. */ public static final String OCTAVE_EXC = "Octave * is not a number, or is not in the range 0 - 10."; /** The Octave value calculated by the parser is out of range. */ public static final String NOTE_OCTAVE_EXC = "The note value *, calculated by computing (octave*12)+noteValue, is not in the range 0 - 127."; /** The Duration part of the MusicString has an error. */ public static final String NOTE_DURATION_EXC = "The Duration part of the MusicString has an error."; /** The Velocity character is not known. */ public static final String NOTE_VELOCITY_EXC = "The velocity character in * is unknown."; /** The root note for a chord inversion has an error. */ public static final String INVERSION_EXC = "The root given for a chord inversion is less than the initial chord root, or greater than the range of the chord."; /** The parser encountered spaces in a single token. */ public static final String PARSER_SPACES_EXC = "The token * sent to Parser.parse() contains spaces. A token is one unit of musical data, and should not contain a space."; /** The parser cannot find a definition for the given word. */ public static final String WORD_NOT_DEFINED_EXC = "The word * has no definition. Check the spelling, or define the word before using it. See the JFugue Instruction Manual for information on defining words."; /** The Controller command, Xindex=value, is malformed. */ public static final String CONTROL_FORMAT_EXC = "The controller token * is missing an equals sign. See the JFugue Instruction Manual for information on using the Controller token."; /** The parser expected a byte. */ public static final String EXPECTED_BYTE = "The JFugue Parser expected a byte, but encountered the value * which is not a byte."; /** The parser expected a long. */ public static final String EXPECTED_LONG = "The JFugue Parser expected a long, but encountered the value * which is not a long."; /** The parser expected an int. */ public static final String EXPECTED_INT = "The JFugue Parser expected an int, but encountered the value * which is not an int."; /** The parser expected a double. */ public static final String EXPECTED_DOUBLE = "The JFugue Parser expected a double, but encountered the value * which is not a double."; /** The MIDI System cannot instantiate a sequencer. */ public static final String SEQUENCER_DEVICE_NOT_SUPPORTED_WITH_EXCEPTION = "The MIDI System cannot instantiate a sequencer. Although this error is reported by JFugue, the problem is not with JFugue itself. Find resources for using MIDI on your specific system. The exception message from MidiSystem.getSequencer() is: "; /** The MIDI System cannot instantiate a sequencer. */ public static final String SEQUENCER_DEVICE_NOT_SUPPORTED = "The MIDI System cannot instantiate a sequencer. Although this error is reported by JFugue, the problem is not with JFugue itself. Find resources for using MIDI on your specific system."; /** Player.play(String) plays a music string, not a filename */ public static final String PLAYS_STRING_NOT_FILE_EXC = "play(String) plays a music string, not a filename. Try using play(File)."; /** Error playing music */ public static final String ERROR_PLAYING_MUSIC = "Error playing music: "; /** Error while sleep */ public static final String ERROR_SLEEP = "Error while sleeping"; /** Error resolving MidiDevice with Intelligent Resolver */ public static final String INTELLIGENT_RESOLVER_FAILED = "IntelligentDeviceResolver not intelligent enough. Be explicit about the MidiDevice."; /** Verification exception */ public static final String VERIFICATION_EXCEPTION = "The result of parsing, '*', was not expected."; /** General error */ public static final String GENERAL_ERROR = "General error: "; /** Error for MusicXMLParser (TODO: Should this reside in MusicXMLParser?) */ public static final String BEAT_UNIT_MUST_BE_QUARTER = "MusicXML tag \"beat-unit\" must be set to \"quarter\""; }jfugue/org/jfugue/ParserListener.java0000644000175000017500000001017610776766112020454 0ustar giovannigiovanni/* * 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 java.util.EventListener; /** * Classes that implement ParserListener and add themselves as listeners * to a Parser object will receive events when * the Parser inteprets tokens from a Music String. * @see MusicStringParser * *@author David Koelle *@version 3.0 */ public interface ParserListener extends EventListener { /** * Called when the parser encounters a voice event. * @param voice the event that has been parsed * @see Voice */ public void voiceEvent(Voice voice); /** * Called when the parser encounters a tempo event. * @param tempo the event that has been parsed * @see Tempo */ public void tempoEvent(Tempo tempo); /** * Called when the parser encounters an instrument event. * @param instrument the event that has been parsed * @see Instrument */ public void instrumentEvent(Instrument instrument); /** * Called when the parser encounters a layer event. * @param layer the event that has been parsed * @see Layer */ public void layerEvent(Layer layer); /** * Called when the parser encounters a measure event. * @param measure the event that has been parsed * @see Measure */ public void measureEvent(Measure measure); /** * Called when the parser encounters a time event. * @param time the event that has been parsed * @see Time */ public void timeEvent(Time time); /** * Called when the parser encounters a key signature event. * @param time the event that has been parsed * @see KeySignature */ public void keySignatureEvent(KeySignature keySig); /** * Called when the parser encounters a controller event. * @param controller the event that has been parsed */ public void controllerEvent(Controller controller); /** * Called when the parser encounters a channel pressure event. * @param channelPressure the event that has been parsed * @see ChannelPressure */ public void channelPressureEvent(ChannelPressure channelPressure); /** * Called when the parser encounters a polyphonic pressure event. * @param polyphonicPressure the event that has been parsed * @see PolyphonicPressure */ public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure); /** * Called when the parser encounters a pitch bend event. * @param pitchBend the event that has been parsed * @see PitchBend */ public void pitchBendEvent(PitchBend pitchBend); /** * Called when the parser encounters an initial note event. * @param note the event that has been parsed * @see Note */ public void noteEvent(Note note); /** * Called when the parser encounters a sequential note event. * @param note the event that has been parsed * @see Note */ public void sequentialNoteEvent(Note note); /** * Called when the parser encounters a parallel note event. * @param note the event that has been parsed * @see Note */ public void parallelNoteEvent(Note note); } jfugue/org/jfugue/EasyAnticipatorListener.java0000644000175000017500000000502110776766112022310 0ustar giovannigiovanni/* * 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; public abstract class EasyAnticipatorListener extends ParserListenerAdapter { private Voice activeVoice; private Instrument activeInstrument; public EasyAnticipatorListener() { activeVoice = new Voice((byte)0); activeInstrument = new Instrument((byte)0); } private int tempo; public void tempoEvent(Tempo tempo) { this.tempo = tempo.getTempo(); System.out.println("tempo = "+tempo.getTempo()); } public void voiceEvent(Voice voice) { this.activeVoice = voice; } public void instrumentEvent(Instrument instrument) { this.activeInstrument = instrument; } public void noteEvent(Note note) { extendedNoteEvent(activeVoice, activeInstrument, note); } public void parallelNoteEvent(Note note) { extendedNoteEvent(activeVoice, activeInstrument, note); } public void sequentialNoteEvent(Note note) { extendedNoteEvent(activeVoice, activeInstrument, note); // sleep(note.getDuration()); } /** Duration is in PPQ, need to translate that into msec */ // TODO: Is duration ALWAYS in PPQ, or does it depend on sequenceTiming? private void sleep(long durationInPPQ) { try { long msec = ((durationInPPQ / 4 ) ); Thread.sleep(msec); } catch (InterruptedException e) { throw new JFugueException(JFugueException.ERROR_SLEEP); } } public abstract void extendedNoteEvent(Voice voice, Instrument instrument, Note note); } jfugue/org/jfugue/JFugueElement.java0000644000175000017500000000357511067512022020175 0ustar giovannigiovanni/* * 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 java.io.Serializable; /** * This is the base class for the JFugue elements, including * Voice, Instrument, Note, Controller, and Tempo. It requires that * elements be able to return a Music String representation of * their settings. * *@author David Koelle *@version 2.0 *@version 4.0 - Added getVerifyString() *@version 4.0.3 - Now extends Serializable */ public interface JFugueElement extends Serializable { /** * Returns the Music String representing this element and all of its settings. * @return the Music String for this element */ public String getMusicString(); /** * Returns a verification string, which should contain a String representation * of all of the aspects of the given element. This should be in the * following form: * Thing: key=value, key=value, key=value,... * For example: * Note: value=60, duration=0.25 * * @version 4.0 */ public String getVerifyString(); } jfugue/org/jfugue/DurationPatternTool.java0000644000175000017500000000407611012130530021442 0ustar giovannigiovanni/* * 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 org.jfugue.*; /** * Calculates the length of the given pattern, in milliseconds * *@author David Koelle *@version 2.0 *@version 4.0.2 - Changed from returning PPQ to milliseconds * */ public class DurationPatternTool extends ParserListenerAdapter { private byte activeVoice = 0; private long voiceDuration[]; public DurationPatternTool() { reset(); } public void voiceEvent(Voice voice) { this.activeVoice = voice.getVoice(); } // Only look at the first Note events, not parallel or sequential ones. public void noteEvent(Note note) { long duration = note.getDuration(); this.voiceDuration[this.activeVoice] += duration; } public void reset() { voiceDuration = new long[16]; for (int i=0; i < 16; i++) { voiceDuration[i] = 0L; } } public long getDuration() { long returnDuration = 0L; for (int i=0; i < 16; i++) { if (voiceDuration[i] > returnDuration) { returnDuration = voiceDuration[i]; } } return returnDuration; } } jfugue/org/jfugue/Rhythm.java0000644000175000017500000001507111067512212016744 0ustar giovannigiovanni/* * 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 java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Provides the ability to build a rhythm using a simple strings in which * individual characters represent MusicString elements. * * For example, you can develop a drum beat that looks like this: * * oo'' o' oo'' o' oo'' o' oo'' o'... * * * As of JFugue 4.0, the Rhythm allows the addition of voices outside of the * percussion track using the addVoice() method. * * This feature is covered in detail in "The Complete Guide to JFugue" * *@see Player *@author David Koelle *@version 3.0 *@version 4.0.3 - Now implements Serializable */ public class Rhythm implements Serializable { private Map charToNote; private String[] layers; private String[] voices; private String[] voiceDetails; private int MAX_LAYERS = 127; private int MAX_VOICES = 16; private int PERCUSSION_TRACK = 9; public Rhythm() { charToNote = new HashMap(); layers = new String[MAX_LAYERS]; voices = new String[MAX_VOICES]; voiceDetails = new String[MAX_VOICES]; } public void addSubstitution(char stringChar, String musicString) { charToNote.put(stringChar, musicString); } public String getSubstitution(char stringChar) { return charToNote.get(stringChar); } public void removeSubstitution(char stringChar) { charToNote.remove(stringChar); } public void setLayer(int layer, String rhythmString) { if ((layer < 0) || (layer > MAX_LAYERS)) { throw new JFugueException(JFugueException.LAYER_EXC, Integer.toString(layer), rhythmString); } layers[layer] = rhythmString; } public String getLayer(int layer) { return this.layers[layer]; } public void clearLayer(int layer) { this.layers[layer] = null; } public void setVoice(int voice, String rhythmString) { if ((voice < 0) || (voice > MAX_LAYERS) || (voice == PERCUSSION_TRACK)) { throw new JFugueException(JFugueException.VOICE_EXC, Integer.toString(voice), rhythmString); } voices[voice] = rhythmString; } public String getVoice(int voice) { return this.voices[voice]; } public void clearVoice(int voice) { this.voices[voice] = null; } public void setVoiceDetails(int voice, String musicString) { if ((voice < 0) || (voice > MAX_LAYERS) || (voice == PERCUSSION_TRACK)) { throw new JFugueException(JFugueException.VOICE_EXC, Integer.toString(voice), musicString); } voiceDetails[voice] = musicString; } public String getVoiceDetails(int voice) { return this.voiceDetails[voice]; } public void clearVoiceDetails(int voice) { this.voiceDetails[voice] = null; } public String getMusicString() { StringBuffer buffy = new StringBuffer(); // Start by adding the percussion track buffy.append("V9 "); for (int i=0; i < MAX_LAYERS; i++) { String rhythmString = getLayer(i); if (rhythmString != null) { buffy.append("L"); buffy.append(i); buffy.append(" "); for (int r=0; r < rhythmString.length(); r++) { char ch = rhythmString.charAt(r); String substitution = getSubstitution(ch); if (substitution != null) { buffy.append(substitution); buffy.append(" "); } } } } // Add the voices for (int i=0; i < MAX_VOICES; i++) { boolean voiceCommandAdded = false; String detailsString = getVoiceDetails(i); if (detailsString != null) { buffy.append("V"); buffy.append(i); voiceCommandAdded = true; buffy.append(" "); buffy.append(detailsString); buffy.append(" "); } String rhythmString = getVoice(i); if (rhythmString != null) { if (!voiceCommandAdded) { buffy.append("V"); buffy.append(i); buffy.append(" "); } for (int r=0; r < rhythmString.length(); r++) { char ch = rhythmString.charAt(r); String substitution = getSubstitution(ch); if (substitution != null) { buffy.append(substitution); buffy.append(" "); } } } } return buffy.toString(); } public Pattern getPattern() { return new Pattern(getMusicString()); } public Pattern getPatternWithInterval(Pattern pattern) { IntervalNotation interval = new IntervalNotation(getMusicString()); return interval.getPatternForRootNote(pattern); } public Pattern getPatternWithInterval(String string) { IntervalNotation interval = new IntervalNotation(getMusicString()); return interval.getPatternForRootNote(string); } public Pattern getPatternWithInterval(Note rootNote) { IntervalNotation interval = new IntervalNotation(getMusicString()); return interval.getPatternForRootNote(rootNote); } } jfugue/org/jfugue/ChannelPressure.java0000644000175000017500000000502311005706164020571 0ustar giovannigiovanni/* * 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; /** * Represents channel pressure changes. * *@author David Koelle *@version 3.0 */ public final class ChannelPressure implements JFugueElement { private byte pressure; /** * Creates a new channel pressure object, with the specified key and pressure values. * @param key the key to apply pressure to * @param pressure the pressure to apply */ public ChannelPressure(byte pressure) { setPressure(pressure); } /** * Sets the pressure value of this object. * @param pressure the pressure for this object */ public void setPressure(byte pressure) { this.pressure = pressure; } /** * Returns the pressure for this object. * @return the pressure for this object */ public byte getPressure() { return this.pressure; } /** * Returns the Music String representing this element and all of its settings. * For a channel pressure object, the Music String is +key,pressure * @return the Music String for this element */ public String getMusicString() { StringBuffer buffy = new StringBuffer(); buffy.append("+"); buffy.append(getPressure()); return buffy.toString(); } /** * Returns verification string in this format: * ChannelPressure: pressure={#} * @version 4.0 */ public String getVerifyString() { StringBuffer buffy = new StringBuffer(); buffy.append("ChannelPressure: pressure="); buffy.append(getPressure()); return buffy.toString(); } }jfugue/org/jfugue/TimeFactor.java0000644000175000017500000001547611067516266017554 0ustar giovannigiovanni/* * 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 java.util.HashMap; import java.util.List; import java.util.Map; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiEvent; import javax.sound.midi.MidiMessage; import javax.sound.midi.Sequence; public final class TimeFactor { public static double DEFAULT_BPM = 120.0d; public static int QUARTER_DURATIONS_IN_WHOLE = 4; public static final double getTimeFactor(Sequence sequence, double bpm) { double divisionType = sequence.getDivisionType(); double resolution = sequence.getResolution(); // If division type is PPQ, resolution is ticks per beat. // Since a beat is the length of time given to a one quarter note, this essentially // means that ticks per beat == pulses per quarter note (PPQ or PPQN) if (divisionType == Sequence.PPQ) { // System.out.println("DivisionType is PPQ"); // System.out.println("Resolution is "+resolution); } else { // System.out.println("DivisionType is SMPTE"); } // Useful resources: http://www.borg.com/~jglatt/tech/midifile/tempo.htm and http://www.borg.com/~jglatt/tech/midifile/ppqn.htm // If bit 15 of division is a zero, the bits 14 thru 0 represent the number of delta-time ticks which make up a // quarter-note. For instance, if division is 96, then a time interval of an eighth-note between two events // in the file would be 48. // // If bit 15 of division is a one, delta-times in a file correspond to subdivisions of a second, in a way consistent // with SMPTE and MIDI time code. Bits 14 thru 8 contain one of the four values -24, -25, -29, or -30, corresponding // to the four standard SMPTE and MIDI time code formats (-29 corresponds to 30 drop frame), and represents the number // of frames per second. These negative numbers are stored in two's complement form. The second byte (stored positive) // is the resolution within a frame: typical values may be 4 (MIDI time code resolution), 8, 10, 80 (bit resolution), // or 100. This system allows exact specification of time- code-based tracks, but also allows millisecond-based tracks // by specifying 25 frames/sec and a resolution of 40 units per frame. If the events in a file are stored with bit // resolution of thirty-frame time code, the division word would be E250 hex. if (bpm == 0.0) { bpm = DEFAULT_BPM; } return 60000.0 / (resolution * bpm); } public static final byte[] convertToThreeTempoBytes(int tempo) { double tempoInMsPerBeat = TimeFactor.convertBPMToMicrosecondsPerBeat(tempo); double d1 = Math.floor(tempoInMsPerBeat / 16384.0); double d2 = Math.floor((tempoInMsPerBeat % 16384.0) / 128.0); double d3 = Math.floor((tempoInMsPerBeat % 16384.0) % 128.0); return new byte[] { (byte)d1, (byte)d2, (byte)d3 }; } public static final int parseMicrosecondsPerBeat(MetaMessage message, long timestamp) { int tempo = message.getData()[0]*16384 + message.getData()[1]*128 + message.getData()[2]; int beatsPerMinute = (int)convertMicrosecondsPerBeatToBPM(tempo); return beatsPerMinute; } /** Converts microseconds per beat to BPM -- and vice versa */ public static final double convertMicrosecondsPerBeatToBPM(double value) { double microsecondsPerMinute = 60000000.0D; if (value == 0.0d) { return 0.0d; } return microsecondsPerMinute / value; } /** Converts microseconds per beat to BPM -- and vice versa */ public static final double convertBPMToMicrosecondsPerBeat(int bpm) { double microsecondsPerMinute = 60000000.0D; if (bpm == 0) { return 0; } return microsecondsPerMinute / bpm; } /** * Takes all of the MIDI events in the given Sequence, sorts them according to * when they are to be played, and sends the events to the MidiMessageRecipient * when the each event is ready to be played. * * @param sequence The Sequence with messages to sort and deliver * @param recipient the handler of the delivered message */ public static final void sortAndDeliverMidiMessages(Sequence sequence, MidiMessageRecipient recipient) { double timeFactor = 1.0; Map> timeMap = new HashMap>(); long longestTime = TimeEventManager.sortSequenceByTimestamp(sequence, timeMap); long lastTime = 0; for (long time=0; time < longestTime; time++) { List midiEventList = (List)timeMap.get(time); if (midiEventList != null) { for (MidiEvent event : midiEventList) { MidiMessage message = event.getMessage(); if ((message.getMessage().length >= 2) && (message.getMessage()[1] == 0x51) && (message instanceof MetaMessage)) { int bpm = parseMicrosecondsPerBeat((MetaMessage)message, time); timeFactor = TimeFactor.getTimeFactor(sequence, bpm); System.out.println("TimeFactor is "+timeFactor); } recipient.messageReady(message, time); } try { long sleepTime = (int)(((time - lastTime) * (TimeFactor.QUARTER_DURATIONS_IN_WHOLE+0.20))); Thread.sleep(sleepTime); // (int) (1 * timeFactor)); lastTime = time; } catch (Exception ex) { throw new JFugueException(JFugueException.ERROR_SLEEP); } } } } } jfugue/org/jfugue/MicrotoneNotation.java0000644000175000017500000001046011005706254021144 0ustar giovannigiovanni/* * 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 java.util.HashMap; import java.util.Map; /** * Facilitates playing microtonal music - Indian, Turkish, Indonesian, etc. styles. * Also useful for performing slides and other effects that rely on "the notes in between the notes". * * This feature is covered in detail in "The Complete Guide to JFugue" * * @author David Koelle * @version 3.0 * @version 4.0 - renamed from MicrotoneHelper; mictoronal notes now encased in angle brackets instead of square brackets, for consistency */ public class MicrotoneNotation { private Map keyToFreqDict; private Map keyToMusicStringDict; public MicrotoneNotation() { keyToFreqDict = new HashMap(); keyToMusicStringDict = new HashMap(); } public void put(String key, double freq) { keyToFreqDict.put(key, freq); keyToMusicStringDict.put(key, convertFrequencyToMusicString(freq)); } public double get(String key) { return keyToFreqDict.get(key); } public String getMusicString(String key) { return keyToMusicStringDict.get(key); } /** * Converts the given frequency to a music string that involves * the Pitch Wheel and notes to create the frequency * @param freq the frequency * @return a MusicString that represents the frequency */ public static String convertFrequencyToMusicString(double freq) { double totalCents = 1200 * Math.log(freq / 16.3515978312876) / Math.log(2); double octave = Math.round(totalCents / 1200.0); double semitoneCents = totalCents - (octave * 1200.0); double semitone = Math.round(semitoneCents / 100.0); double cents = 8192 + Math.round(semitoneCents - (semitone * 100)); double note = ((octave+1)*12)+semitone; // This gives a MIDI value, 0 - 128 if (note > 127) note = 127; StringBuilder buffy = new StringBuilder(); buffy.append("&"); buffy.append((int)cents); buffy.append(" ["); buffy.append((int)note); buffy.append("]"); return buffy.toString(); } public static String getResetPitchWheelString() { return (" &8192"); // Reset the pitch wheel. 8192 = original pitch wheel position } public Pattern getPattern(String notation) { StringBuilder buddy = new StringBuilder(); String[] tokens = notation.split(" "); // Go through the Pattern, and replace known microtone keys with microtone music strings for (int i=0; i < tokens.length; i++) { if ((tokens[i].length() > 0) && (tokens[i].charAt(0) == '<')) { int end = tokens[i].indexOf('>'); String possibleKey = tokens[i].substring(1, end); if (keyToMusicStringDict.containsKey(possibleKey)) { buddy.append(keyToMusicStringDict.get(possibleKey)); buddy.append(tokens[i].substring(end+1, tokens[i].length())); // Add the rest of the token buddy.append(getResetPitchWheelString()); } else { buddy.append(tokens[i]); } } else { buddy.append(tokens[i]); } buddy.append(" "); } return new Pattern(buddy.toString()); } } jfugue/org/jfugue/Measure.java0000644000175000017500000000344211005706246017075 0ustar giovannigiovanni/* * 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; /** * Represents a measure marker. This has no bearing on the audio produced, * but is useful for making music strings more readable, and for listening * to progress as a song is played. * *@author David Koelle *@version 3.0 */ public final class Measure implements JFugueElement { /** * Creates a new Measure object, which is simply an indicator * that a measure line has been parsed in a MusicString */ public Measure() { } /** * Returns the Music String representing this element. * For a Measure object, the Music String is | * @return the Music String for this element */ public String getMusicString() { return "|"; } /** * Returns verification string in this format: * Measure * @version 4.0 */ public String getVerifyString() { return "Measure"; } }jfugue/org/jfugue/MusicalEffects.java0000644000175000017500000001063111005706274020370 0ustar giovannigiovanni/* * 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; /** * EXPERIMENTAL - * Contains a variety of static methods that create Patterns * that play various musical ornaments * * @author David Koelle * @version 4.0 */ public class MusicalEffects { /** * Returns a Pattern that plays two notes in rapid succession * (for a total of each note being played numHammers times) * over the given duration. * * The resulting Pattern will have note1 and note2 both represented numHammers times. * * Example: * hammerOn(new Note("C5"), new Note("E5"), 0.5, 4); * will produce this Pattern: [60]/0.125 [64]/0.125 [60]/0.125 [64]/0.125 [60]/0.125 [64]/0.125 [60]/0.125 [64]/0.125 * * @param note1 First note to play * @param note2 Second note to play * @param duration Value representing total duration for the resulting pattern. 1.0=whole note * @param numHammers Number of times to repeat each note */ public static Pattern hammerOn(Note note1, Note note2, double duration, int numHammers) { StringBuilder buddy = new StringBuilder(); double durationPerHammer = duration / numHammers; buddy.append("["); buddy.append(note1.getValue()); buddy.append("]/"); buddy.append(durationPerHammer / 2.0); buddy.append(" ["); buddy.append(note2.getValue()); buddy.append("]/"); buddy.append(durationPerHammer / 2.0); Pattern pattern = new Pattern(buddy.toString()); pattern.repeat(numHammers); return pattern; } /** * Returns a Pattern that plays a slide between two notes * over the given duration. * * TODO: This is currently a naive implementation, which sounds 'numSteps' notes, each * with a duration of 'duration/numSteps'. This means that if you're sliding from a F to a G, * for example, you could get music that looks like F F F F F F F F G G G G G G, with each note * having a very short duration. The problem with this is that the sound of each note stopping and * starting again is noticeable. A more intelligent implementation would sound each note for as * long as necessary, then sound a different note only when the microtonal math requires it. Otherwise, * the pitch wheel messages should cause the note to change while it is playing. * This implementation may require one or more new methods in MicrotoneNotation. */ public static Pattern slide(Note note1, Note note2, double duration, int numSteps) { StringBuilder buddy = new StringBuilder(); double durationPerStep = duration / numSteps; double freq1 = Note.getFrequencyForNote(note1.getValue()); double freq2 = Note.getFrequencyForNote(note2.getValue()); double differencePerStep = (freq2-freq1) / numSteps; for (int i=0; i < numSteps; i++) { buddy.append(MicrotoneNotation.convertFrequencyToMusicString(freq1)); buddy.append("/"); buddy.append(durationPerStep); buddy.append(MicrotoneNotation.getResetPitchWheelString()); buddy.append(" "); freq1 += differencePerStep; } Pattern pattern = new Pattern(buddy.toString()); return pattern; } /** * Right now, this is a pass-through to hammerOn() * @see hammerOn */ public static Pattern trill(Note note1, Note note2, double duration, int numSteps) { return hammerOn(note1, note2, duration, numSteps); } } jfugue/org/jfugue/StreamingPlayer.java0000644000175000017500000001431211013106016020564 0ustar giovannigiovanni/* * 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); } private void openSequencer() { if (getSequencer() == null) { throw new JFugueException(JFugueException.SEQUENCER_DEVICE_NOT_SUPPORTED); } // Open the sequencer, if it is not already open if (!getSequencer().isOpen()) { try { getSequencer().open(); } catch (MidiUnavailableException e) { throw new JFugueException(JFugueException.SEQUENCER_DEVICE_NOT_SUPPORTED_WITH_EXCEPTION + e.getMessage()); } } } /** * 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)); } } jfugue/org/jfugue/extras/0000755000175000017500000000000010777605044016145 5ustar giovannigiovannijfugue/org/jfugue/extras/DurationPatternTransformer.java0000644000175000017500000000545311003730654024352 0ustar giovannigiovanni/* * 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.extras; import org.jfugue.*; /** * The DurationPatternTransformer multiplies the duration of all notes in the given * Pattern by a factor passed as a parameter. * *

* This transformer can be used to increase or decrease the duration of notes. To increase * the duration, use a variable greater than 1.0. To decrease the duration, use a value * less than 1.0. The default value for this transformer is 1.0, which will result in * no change to your Pattern. *

* *

* For general information on how Pattern Transformers work, refer to the JFugue * documentation. *

* *@author David Koelle *@version 2.0 */ public class DurationPatternTransformer extends PatternTransformer { private double factor; /** * Instantiates a new DurationPatternTransformer object. The default value by which * to multiply the duration is 1.0, which will result in no change to the given Music * String. */ public DurationPatternTransformer(double factor) { this.factor = factor; } /** Transforms the given note */ public void noteEvent(Note note) { double durationValue = note.getDecimalDuration(); durationValue *= this.factor; note.setDecimalDuration(durationValue); getReturnPattern().addElement(note); } /** Transforms the given note */ public void sequentialNoteEvent(Note note) { double durationValue = note.getDecimalDuration(); durationValue *= this.factor; note.setDecimalDuration(durationValue); getReturnPattern().addElement(note); } /** Transforms the given note */ public void parallelNoteEvent(Note note) { double durationValue = note.getDecimalDuration(); durationValue *= this.factor; note.setDecimalDuration(durationValue); getReturnPattern().addElement(note); } } jfugue/org/jfugue/extras/FilePlayer.java0000644000175000017500000000641511000431046021025 0ustar giovannigiovanni/* * 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.extras; import java.io.File; import java.io.IOException; import org.jfugue.Pattern; import org.jfugue.Player; /** * Plays music strings from a text file. * *

* Here's a sample file: *

 * #
 * # "Inventio 13" - Beethoven   (First Measure)
 * #
 *
 * V1 T160
 * V1 Rs  E4s A4s C5s B4s E4s B4s D5s C5i      E5i     G#4i    E5i
 * V2 A2i     A3q             G#3i    A3s G#3s A3s C4s B3s E3s B3s D4s
 *
*

* *

* Note the use of # as a comment character when used as the first character of a line. *

* *

* To use FilePlayer, enter "java org.jfugue.FilePlayer input-filename [output-filename]" from the * command prompt, where input-filename is the name of your text file that specifies * the music, and output-filename is the name of the MIDI file to create. (If you * don't give output-filename, it will default to player.mid) *

* *@author David Koelle *@version 2.0 * */ public class FilePlayer { /** Given a filename, returns a string of the contents of that file. If the file * contains properly-formed music strings, then the contents of the file can * be placed directly into a Pattern. *

* This method will regard any line that begins with a # character as a comment, * and will not return the commented line. Note - # characters at locations * other than the first character of a line will not be seen as comment characters. */ public static void main(String[] args) { String inFilename = null; String outFilename = null; if ((args.length > 1) && (args[1] != null)) { inFilename = args[1]; } else { System.out.println("Need to specify an input file!"); System.exit(0); } if ((args.length > 2) && (args[2] != null)) { outFilename = args[2]; } // Create a player, and play the music! try { Pattern pattern = Pattern.loadPattern(new File(inFilename)); Player player = new Player(); player.saveMidi(pattern, new File(outFilename)); player.play(pattern); player.close(); } catch (IOException e) { e.printStackTrace(); } System.exit(0); } } jfugue/org/jfugue/extras/IntervalTransformer.java0000644000175000017500000000325610777506350023025 0ustar giovannigiovanni/* * 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.extras; import org.jfugue.Note; import org.jfugue.PatternTransformer; public class IntervalTransformer extends PatternTransformer { private int delta; /** Delta can be a positive or negative number indicating * how many half-steps the note should be changed to. * An octave is 12 steps. * @param delta */ public IntervalTransformer(int delta) { this.delta = delta; } public void noteEvent(Note note) { byte currentValue = note.getValue(); int newValue = currentValue + delta; if ((newValue > 0) && (newValue < 128)) { note.setValue((byte)(currentValue + delta)); } else { note.setValue((byte)0); } getReturnPattern().addElement(note); } } jfugue/org/jfugue/extras/DiatonicIntervalPatternTransformer.java0000644000175000017500000001403111003730552026011 0ustar giovannigiovanni/* * 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.extras; import org.jfugue.*; /** * The DiatonicIntervalPatternTransformer transposes all notes in the given * Pattern by a diatonic interval (1 -- unison, 2 -- second, ... 8 -- octave, etc.). * It only handles upward motion and assumes the key of C. However, it could be * used in conjunction with IntervalPatternTransformer to change to another key and/or * perform downward motion. * *

* For general information on how Pattern Transformers work, refer to the JFugue * documentation. *

* *@author Bill Manaris (based on code by David Koelle) *@version 2.0m */ public class DiatonicIntervalPatternTransformer extends PatternTransformer { private int interval = 1; /** * @param interval Number of diatonic intervals by which to change each note, can be only positive */ public DiatonicIntervalPatternTransformer(int interval) { this.interval = interval; } /* Helper method, used to transpose a note by a certain diatonic interval */ private Note adjustNote(Note note, int interval) { int noteValue; // holds note's MIDI value (0 .. 127) int scaleDegree; // holds the scale degree of a given note (assume key of C) boolean isPassingNote; // true if note is not in the scale of C, false otherwise. int octave = 0; // holds octaves spanned by diatonic interval (zero means interval is 0..7) noteValue = (int)note.getValue(); // check if interval is outside 1..7 range if (interval > 7) { octave = interval / 8; // get octaves spanned by interval (1 or more) interval = (interval + 1) % 8; // map interval into 1..7 range } else if (interval < 1) System.err.println("Error! DiatonicIntervalPatternTransformer can handle only positive intervals (" + interval + ")"); // retrieve the scale degree of this note scaleDegree = NoteToDegree[noteValue % 12]; // check if the note is in the scale of C (see NoteToDegree[] array) isPassingNote = (scaleDegree < 0); // if it's NOT in the scale, sharpen it temporarily, so as to be in the scale // for transposition purposes; but we need to remember to flatten it afterwards // (see NoteToDegree[] array) if (isPassingNote) { scaleDegree = -scaleDegree; noteValue += 1; } // transpose note to the given interval noteValue += DegreeToNote[scaleDegree + interval-1] - DegreeToNote[scaleDegree]; // if originally not in the scale, let's flatten it (see NoteToDegree[] array) if (isPassingNote) noteValue -= 1; // adjust note by octaves spanned by interval noteValue += (octave * 12); // handle overflow caused by a large/small interval and/or note value if ((noteValue > 128) || (noteValue < 0)) { System.err.println("Warning! Note value overflow (" + noteValue + ") in DiatonicIntervalPatternTransformer"); noteValue %= 128; // wrap around (correct note, but many octaves off -- catches the ear's attention) } note.setValue((byte)noteValue); // update MIDI value return note; } /** Transforms the given note */ public void noteEvent(Note note) { note = adjustNote(note, interval); getReturnPattern().addElement(note); } /** Transforms the given note */ public void sequentialNoteEvent(Note note) { note = adjustNote(note, interval); getReturnPattern().addElement(note); } /** Transforms the given note */ public void parallelNoteEvent(Note note) { note = adjustNote(note, interval); getReturnPattern().addElement(note); } /* Array to map a note value (0 .. 11) to the corresponding scale degree in C NoteToDegree array (A negative value indicates a flat note that is not in the scale. We use the negated value of the note on our right, to facilitate transposition of passing notes. This can be easily performed by taking the absolute value (equivalent to sharpening the note, so that is becomes the scale note to the right, transposing this scale note, and then subtracting one from the resultant note value (returning it to its flattened state).) index (MIDI note % 12): 0 1 2 3 4 5 6 7 8 9 10 11 value (scale degree) : 1 -2 2 -3 3 4 -4 5 -6 6 -7 7 */ private static byte NoteToDegree[] = {1, -2, 2, -3, 3, 4, -5, 5, -6, 6, -7, 7}; /* Array to map a scale degree (1 .. 13) to the corresponding note value in C DegreeToNote array (index 0 is not being used, so we give it a "dummy" -128 value) index (scale degree): 1 2 3 4 5 6 7 8 9 10 11 12 13 value (MIDI note) : 0 2 4 5 7 9 11 12 14 16 17 19 21 */ private static byte DegreeToNote[] = {-128, 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21}; } jfugue/org/jfugue/extras/IntervalPatternTransformer.java0000644000175000017500000000467111003730734024351 0ustar giovannigiovanni/* * 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.extras; import org.jfugue.*; /** * The IntervalPatternTransformer alters music by changing the interval, or step, for each * note in the given Pattern. For example, a C5 (note 60) raised 3 steps would turn into a * D#5 (note 63). The interval is passed in as a parameter. * *

* For general information on how Pattern Transformers work, refer to the JFugue * documentation. *

* *@author David Koelle *@version 2.0 */ public class IntervalPatternTransformer extends PatternTransformer { private int interval; /** * Instantiates a new IntervalPatternTransformer object. The default value by which * to increase the duration is 1. */ public IntervalPatternTransformer(int interval) { this.interval = interval; } /** Transforms the given note */ public void noteEvent(Note note) { byte noteValue = note.getValue(); noteValue += this.interval; note.setValue(noteValue); getReturnPattern().addElement(note); } /** Transforms the given note */ public void sequentialNoteEvent(Note note) { byte noteValue = note.getValue(); noteValue += this.interval; note.setValue(noteValue); getReturnPattern().addElement(note); } /** Transforms the given note */ public void parallelNoteEvent(Note note) { byte noteValue = note.getValue(); noteValue += this.interval; note.setValue(noteValue); getReturnPattern().addElement(note); } } jfugue/org/jfugue/extras/Midi2JFugue.java0000644000175000017500000000371410777506346021074 0ustar giovannigiovanni/* * 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.extras; import java.io.File; import java.io.IOException; import javax.sound.midi.InvalidMidiDataException; import org.jfugue.Pattern; import org.jfugue.Player; public class Midi2JFugue { public static void main(String[] args) { if (args.length < 3) { printUsage(); System.exit(0); } Player player = new Player(); Pattern pattern = null; try { pattern = player.loadMidi(new File(args[1])); pattern.savePattern(new File(args[2])); } catch (IOException e) { e.printStackTrace(); } catch (InvalidMidiDataException e) { e.printStackTrace(); } System.exit(0); } public static void printUsage() { System.out.println("Midi2JFugue - convert MIDI files to a JFugue MusicString pattern"); System.out.println("Usage: Midi2JFugue "); System.out.println("Example: Midi2JFugue MySong.mid MyPattern.jfugue"); } } jfugue/org/jfugue/extras/GetPatternForVoiceTool.java0000644000175000017500000001332511000430252023335 0ustar giovannigiovanni/* * 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.extras; import org.jfugue.ChannelPressure; import org.jfugue.Controller; import org.jfugue.Instrument; import org.jfugue.JFugueElement; import org.jfugue.KeySignature; import org.jfugue.Layer; import org.jfugue.Measure; import org.jfugue.Note; import org.jfugue.ParserListenerAdapter; import org.jfugue.Pattern; import org.jfugue.PitchBend; import org.jfugue.PolyphonicPressure; import org.jfugue.Tempo; import org.jfugue.Time; import org.jfugue.Voice; /** * Returns all of the MusicString events that are played in the requested Voice (i.e., Channel) * *@author David Koelle *@version 3.0 * */ public class GetPatternForVoiceTool extends ParserListenerAdapter { private byte voice = 0; private byte activeVoice = 0; private Pattern pattern; public GetPatternForVoiceTool(int voice) { this.voice = (byte)voice; reset(); } public void reset() { pattern = new Pattern(); activeVoice = 0; } public void voiceEvent(Voice voice) { if (activeVoice != voice.getVoice()) { this.activeVoice = voice.getVoice(); addElementIfActiveVoice(voice); } } /** * Tempo changes affect the voice regardless of what voice they appear to be in */ public void tempoEvent(Tempo tempo) { pattern.add(tempo.getMusicString()); } /** * Key Signature changes affect the voice regardless of what voice they appear to be in */ public void keySignatureEvent(KeySignature keySig) { pattern.add(keySig.getMusicString()); } /** * Called when the parser encounters an instrument event. * @param instrument the event that has been parsed * @see Instrument */ public void instrumentEvent(Instrument instrument) { addElementIfActiveVoice(instrument); } /** * Called when the parser encounters a layer event. * @param layer the event that has been parsed * @see Layer */ public void layerEvent(Layer layer) { addElementIfActiveVoice(layer); } /** * Called when the parser encounters a measure event. * @param measure the event that has been parsed * @see Measure */ public void measureEvent(Measure measure) { addElementIfActiveVoice(measure); } /** * Called when the parser encounters a time event. * @param time the event that has been parsed * @see Time */ public void timeEvent(Time time) { addElementIfActiveVoice(time); } /** * Called when the parser encounters a controller event. * @param controller the event that has been parsed */ public void controllerEvent(Controller controller) { addElementIfActiveVoice(controller); } /** * Called when the parser encounters a channel pressure event. * @param channelPressure the event that has been parsed * @see ChannelPressure */ public void channelPressureEvent(ChannelPressure channelPressure) { addElementIfActiveVoice(channelPressure); } /** * Called when the parser encounters a polyphonic pressure event. * @param polyphonicPressure the event that has been parsed * @see PolyphonicPressure */ public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { addElementIfActiveVoice(polyphonicPressure); } /** * Called when the parser encounters a pitch bend event. * @param pitchBend the event that has been parsed * @see PitchBend */ public void pitchBendEvent(PitchBend pitchBend) { addElementIfActiveVoice(pitchBend); } /** * Called when the parser encounters an initial note event. * @param note the event that has been parsed * @see Note */ public void noteEvent(Note note) { addElementIfActiveVoice(note); } /** * Called when the parser encounters a sequential note event. * @param note the event that has been parsed * @see Note */ public void sequentialNoteEvent(Note note) { addElementIfActiveVoice(note); } /** * Called when the parser encounters a parallel note event. * @param note the event that has been parsed * @see Note */ public void parallelNoteEvent(Note note) { addElementIfActiveVoice(note); } private void addElementIfActiveVoice(JFugueElement element) { if (activeVoice == voice) { pattern.add(element.getMusicString()); } } public Pattern getPattern() { return pattern; } } jfugue/org/jfugue/extras/InvertPatternTransformer.java0000644000175000017500000000507711010304314024022 0ustar giovannigiovanni/* * 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.extras; import org.jfugue.*; /** * Inverts the notes of the given pattern, around the provided "fulcrum" note. * Suppose you are inverting about the note "D5". When this * transformer comes across a "C5" note, for example, it computes the difference * between the nodes, then replaces C5 with a new note that is the same number * of intervals on the other side of the fulcrum. C5 would become E5. * *@author David Koelle *@version 4.0 * */ public class InvertPatternTransformer extends PatternTransformer { private byte fulcrumNoteValue; public InvertPatternTransformer(Note note) { this.fulcrumNoteValue = note.getValue(); } /** Transforms the given note */ @Override public void noteEvent(Note note) { doNoteEvent(note); } /** Transforms the given note */ @Override public void sequentialNoteEvent(Note note) { doNoteEvent(note); } /** Transforms the given note */ @Override public void parallelNoteEvent(Note note) { doNoteEvent(note); } private void doNoteEvent(Note note) { byte noteValue = note.getValue(); if (noteValue > fulcrumNoteValue) { note.setValue((byte)(fulcrumNoteValue - (noteValue - fulcrumNoteValue))); getReturnPattern().addElement(note); } else if (noteValue < fulcrumNoteValue) { note.setValue((byte)(fulcrumNoteValue - (fulcrumNoteValue - noteValue))); getReturnPattern().addElement(note); } else { // No change in note value getReturnPattern().addElement(note); } } } jfugue/org/jfugue/extras/DurationPatternTool.java0000644000175000017500000000405710777506350022777 0ustar giovannigiovanni/* * 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.extras; import org.jfugue.*; /** * Calculates the length of the given pattern, in pulses per quarter (PPQ) * *@author David Koelle *@version 2.0 * */ public class DurationPatternTool extends ParserListenerAdapter { private byte activeVoice = 0; private double voiceDuration[]; public DurationPatternTool() { reset(); } public void voiceEvent(Voice voice) { this.activeVoice = voice.getVoice(); } // Only look at the first Note events, not parallel or sequential ones. public void noteEvent(Note note) { double duration = note.getDecimalDuration(); this.voiceDuration[this.activeVoice] += duration; } public void reset() { voiceDuration = new double[16]; for (int i=0; i < 16; i++) { voiceDuration[i] = 0.0; } } public double getDuration() { double returnDuration = 0; for (int i=0; i < 16; i++) { if (voiceDuration[i] > returnDuration) { returnDuration = voiceDuration[i]; } } return new Double(returnDuration); } } jfugue/org/jfugue/extras/GetInstrumentsUsedTool.java0000644000175000017500000000357311005706506023460 0ustar giovannigiovanni/* * 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.extras; import java.util.ArrayList; import java.util.List; import org.jfugue.Instrument; import org.jfugue.MusicStringParser; import org.jfugue.ParserListenerAdapter; import org.jfugue.Pattern; /** * Returns a List containing all of the instruments used in the given pattern. * *@author David Koelle *@version 4.0 * */ public class GetInstrumentsUsedTool extends ParserListenerAdapter { private List instruments; public GetInstrumentsUsedTool() { instruments = new ArrayList(); } @Override public void instrumentEvent(Instrument instrument) { byte b = instrument.getInstrument(); if (!instruments.contains(b)) { instruments.add(b); } } public List getInstrumentsUsedInPattern(Pattern pattern) { MusicStringParser parser = new MusicStringParser(); parser.addParserListener(this); parser.parse(pattern); return instruments; } } jfugue/org/jfugue/extras/ReversePatternTransformer.java0000644000175000017500000000631510777506346024216 0ustar giovannigiovanni/* * 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.extras; import org.jfugue.*; /** * Reverses a given pattern. * *@author David Koelle *@version 2.0 */ public class ReversePatternTransformer extends PatternTransformer { public ReversePatternTransformer() { super(); } /** * ReversePatternTransformer does not require that the user specify any variables. */ public String getParameters() { return ""; } public String getDescription() { return "Reverses the given pattern"; } public void voiceEvent(Voice voice) { insert(voice.getMusicString(), " "); } public void timeEvent(Time time) { // nothing to do? } public void tempoEvent(Tempo tempo) { insert(tempo.getMusicString(), " "); } public void keySignatureEvent(KeySignature keySig) { insert(keySig.getMusicString(), " "); } public void instrumentEvent(Instrument instrument) { insert(instrument.getMusicString(), " "); } public void controllerEvent(Controller controller) { insert(controller.getMusicString(), " "); } public void channelPressureEvent(ChannelPressure channelPressure) { insert(channelPressure.getMusicString(), " "); } public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { insert(polyphonicPressure.getMusicString(), " "); } public void pitchBendEvent(PitchBend pitchBend) { insert(pitchBend.getMusicString(), " "); } public void noteEvent(Note note) { insert(note.getMusicString(), " "); } public void sequentialNoteEvent(Note note) { insert(note.getMusicString().substring(1, note.getMusicString().length()), "_"); } public void parallelNoteEvent(Note note) { insert(note.getMusicString().substring(1, note.getMusicString().length()), "+"); } private void insert(String string, String connector) { StringBuilder buddy = new StringBuilder(); buddy.append(string); buddy.append(connector); buddy.append(getReturnPattern().getMusicString()); getReturnPattern().setMusicString(buddy.toString()); } public static final String INTERVAL = "interval"; }jfugue/org/jfugue/CollatedParserListener.java0000644000175000017500000000560311005706172022105 0ustar giovannigiovanni/* * 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; /** * This implementation of a ParserListener listens for all events, and funnels * them all to the abstract jfugueEvent() method. Therefore, you can extend * this class if you want to create a ParserListener that will handle all * JFugue elements in the same way. For example, this is used by the * MusicStringParser to verify the results of a parse. In this case, all * JFugue events are handled in the same way: they're all asked for their * verification string. * * @author David Koelle * @version 4.0 */ public abstract class CollatedParserListener implements ParserListener { public void channelPressureEvent(ChannelPressure channelPressure) { jfugueEvent(channelPressure); } public void controllerEvent(Controller controller) { jfugueEvent(controller); } public void instrumentEvent(Instrument instrument) { jfugueEvent(instrument); } public void keySignatureEvent(KeySignature keySig) { jfugueEvent(keySig); } public void layerEvent(Layer layer) { jfugueEvent(layer); } public void measureEvent(Measure measure) { jfugueEvent(measure); } public void noteEvent(Note note) { jfugueEvent(note); } public void parallelNoteEvent(Note note) { jfugueEvent(note); } public void pitchBendEvent(PitchBend pitchBend) { jfugueEvent(pitchBend); } public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure) { jfugueEvent(polyphonicPressure); } public void sequentialNoteEvent(Note note) { jfugueEvent(note); } public void tempoEvent(Tempo tempo) { jfugueEvent(tempo); } public void timeEvent(Time time) { jfugueEvent(time); } public void voiceEvent(Voice voice) { jfugueEvent(voice); } public abstract void jfugueEvent(JFugueElement element); } jfugue/org/jfugue/TimeEventManager.java0000644000175000017500000001006111000430232020642 0ustar giovannigiovanni/* * 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 java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.sound.midi.MidiEvent; import javax.sound.midi.Sequence; import javax.sound.midi.Track; /** * Takes the events in a MIDI sequence and places them into a time-based * map. This is done so the events can be played back in order of when * the events occur, regardless of the tracks they happen to be in. This is * useful when sending events to an external device, or any occasion * when iterating through the tracks is not useful because the tracks would be * played sequentially rather than in parallel. * * @author David Koelle * @version 3.0 */ public final class TimeEventManager { public static final long sortSequenceByTimestamp(Sequence sequence, Map> timeMap) { // Keep track of how long the sequence is long longestTime = 0; // Iterate through the tracks, and store the events into our time map Track[] tracks = sequence.getTracks(); for (int i=0; i < tracks.length; i++) { for (int e=0; e < tracks[i].size(); e++) { // Get MIDI message and time data from event MidiEvent event = tracks[i].get(e); long timestamp = event.getTick(); // Put the MIDI message into the time map List list = null; if ((list = (ArrayList)timeMap.get(timestamp)) == null) { // Add a new list to the map if one doesn't already exist // for the timestamp in question list = new ArrayList(); timeMap.put(timestamp, list); } list.add(event); // Update the longest time known, if required if (timestamp > longestTime) { longestTime = timestamp; } } } return longestTime; } /** * Returns the events from this sequence in temporal order. This is * done in a two step process: * 1. mapSequence() populates timeMap. Each timestamp key in timeMap is mapped to * a List of events that take place at that time * 2. A list of all events from all timestamps is created and returned * @return The events from the sequence, in temporal order */ public static final List getAllEventsSortedByTimestamp(Sequence sequence) { Map> timeMap = new HashMap>(); long longestTime = sortSequenceByTimestamp(sequence, timeMap); List totalList = new ArrayList(); for (long l=0; l < longestTime; l++) { Long key = new Long(l); if (timeMap.containsKey(key)) { List list = (List)timeMap.get(key); totalList.addAll(list); } } return totalList; } }