/*
* JFugue, an Application Programming Interface (API) for Music Programming
* http://www.jfugue.org
*
* Copyright (C) 2003-2014 David Koelle
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.staccato.tools;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jfugue.midi.TrackTimeManager;
import org.jfugue.parser.ParserListener;
import org.jfugue.pattern.PatternProducer;
import org.jfugue.theory.Chord;
import org.jfugue.theory.Note;
import org.staccato.StaccatoParser;
import org.staccato.StaccatoUtil;
public class StaccatoElementSorter extends TrackTimeManager implements ParserListener {
private Map<Double, List<ElementWithTrack>> timeMap;
private double initialNoteBeatTime;
public StaccatoElementSorter() {
super();
timeMap = new HashMap<Double, List<ElementWithTrack>>();
}
/** Returns the map of sorted elements */
public Map<Double, List<ElementWithTrack>> getSortedElements() {
return timeMap;
}
/** Static method to sort elements in a given Staccato pattern */
public static Map<Double, List<ElementWithTrack>> sortElements(PatternProducer patternProducer) {
return sortElements(patternProducer.getPattern().toString());
}
/** Static method to sort elements in a given Staccato string */
public static Map<Double, List<ElementWithTrack>> sortElements(String string) {
StaccatoParser parser = new StaccatoParser();
StaccatoElementSorter sorter = new StaccatoElementSorter();
parser.addParserListener(sorter);
parser.parse(string);
return sorter.getSortedElements();
}
@Override
public void beforeParsingStarts() { }
@Override
public void afterParsingFinished() { }
@Override
public void onTrackChanged(byte track) {
super.setCurrentTrack(track);
// Purposefully, the track change element is not added to the map
}
@Override
public void onLayerChanged(byte layer) {
super.setCurrentLayer(layer);
// Purposefully, the layer change element is not added to the map
}
@Override
public void onInstrumentParsed(byte instrument) {
addToTimeMap(StaccatoUtil.createInstrumentElement(instrument));
}
@Override
public void onTempoChanged(int tempoBPM) {
addToTimeMap(StaccatoUtil.createTempoElement(tempoBPM));
}
@Override
public void onKeySignatureParsed(byte key, byte scale) {
addToTimeMap(StaccatoUtil.createKeySignatureElement(key, scale));
}
@Override
public void onTimeSignatureParsed(byte numerator, byte powerOfTwo) {
addToTimeMap(StaccatoUtil.createTimeSignatureElement(numerator, powerOfTwo));
}
@Override
public void onBarLineParsed(long id) {
addToTimeMap(StaccatoUtil.createBarLineElement(id));
}
@Override
public void onTrackBeatTimeBookmarked(String timeBookmarkId) {
super.addTrackTickTimeBookmark(timeBookmarkId);
// Purposefully, the bookmark element is not added to the map
}
@Override
public void onTrackBeatTimeBookmarkRequested(String timeBookmarkId) {
double time = super.getTrackBeatTimeBookmark(timeBookmarkId);
super.setTrackBeatTime(time);
// Purposefully, the bookmark element is not added to the map
}
@Override
public void onTrackBeatTimeRequested(double time) {
super.setTrackBeatTime(time);
// Purposefully, the time element is not added to the map
}
@Override
public void onPitchWheelParsed(byte lsb, byte msb) {
addToTimeMap(StaccatoUtil.createPitchWheelElement(lsb, msb));
}
@Override
public void onChannelPressureParsed(byte pressure) {
addToTimeMap(StaccatoUtil.createChannelPressureElement(pressure));
}
@Override
public void onPolyphonicPressureParsed(byte key, byte pressure) {
addToTimeMap(StaccatoUtil.createPolyphonicPressureElement(key, pressure));
}
@Override
public void onSystemExclusiveParsed(byte... bytes) {
addToTimeMap(StaccatoUtil.createSystemExclusiveElement(bytes));
}
@Override
public void onControllerEventParsed(byte controller, byte value) {
addToTimeMap(StaccatoUtil.createControllerEventElement(controller, value));
}
@Override
public void onLyricParsed(String lyric) {
addToTimeMap(StaccatoUtil.createLyricElement(lyric));
}
@Override
public void onMarkerParsed(String marker) {
addToTimeMap(StaccatoUtil.createMarkerElement(marker));
}
@Override
public void onFunctionParsed(String id, Object message) {
addToTimeMap(StaccatoUtil.createFunctionElement(id, message));
}
@Override
public void onNoteParsed(Note note) {
addNote(note);
}
@Override
public void onChordParsed(Chord chord) {
for (Note note : chord.getNotes()) {
this.addNote(note);
}
}
private void addNote(Note note) {
if (note.getDuration() == 0.0) {
note.useDefaultDuration();
}
// If this is the first note in a sequence of harmonic or melodic notes, remember what time it is.
if (note.isFirstNote()) {
this.initialNoteBeatTime = getTrackBeatTime();
}
// If we're going to the next sequence in a parallel note situation, roll back the time to the beginning of the first note.
// A note will never be a parallel note if a first note has not happened first.
if (note.isHarmonicNote()) {
setTrackBeatTime(this.initialNoteBeatTime);
}
// If the note is a rest, simply advance the track time and get outta here
if (note.isRest()) {
advanceTrackBeatTime(note.getDuration());
return;
}
addToTimeMap(StaccatoUtil.createNoteElement(note));
advanceTrackBeatTime(note.getDuration());
}
private void addToTimeMap(String string) {
ElementWithTrack elementWithTrack = new ElementWithTrack(getCurrentTrack(), getCurrentLayer(), string);
List<ElementWithTrack> elementList = timeMap.get(getTrackBeatTime());
if (elementList == null) {
elementList = new ArrayList<ElementWithTrack>();
timeMap.put(getTrackBeatTime(), elementList);
}
elementList.add(elementWithTrack);
}
}