package org.herac.tuxguitar.io.ptb; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.herac.tuxguitar.io.ptb.base.PTBar; import org.herac.tuxguitar.io.ptb.base.PTComponent; import org.herac.tuxguitar.io.ptb.base.PTDirection; import org.herac.tuxguitar.io.ptb.base.PTPosition; import org.herac.tuxguitar.io.ptb.base.PTSection; import org.herac.tuxguitar.io.ptb.base.PTSong; import org.herac.tuxguitar.io.ptb.base.PTSongInfo; import org.herac.tuxguitar.io.ptb.base.PTSymbol; import org.herac.tuxguitar.io.ptb.base.PTTrack; import org.herac.tuxguitar.io.ptb.base.PTTrackInfo; public class PTSongSynchronizerUtil { private static class PTIndex { /** Index Of Component **/ protected int c; /** Index Of Position **/ protected int p; /** Index Of Section **/ protected int s; protected PTIndex(int s, int p, int c) { this.s = s; this.p = p; this.c = c; } } private static class PTSongSynchronizerData { /** Active Symbol To Search Directions ( D.C, D.S, D.SS ) **/ protected int findActiveSymbol; /** Define if there is an Alternative Ending Running **/ protected boolean repeatAlternative; /** Define If Repeat Is In Progress **/ protected boolean repeatInProgress; /** Current Repetition Number **/ protected int repeatNumber; /** Current Repeat Start Index **/ protected PTIndex repeatStart; /** Define If Next Components Should Be Skipped **/ protected boolean skip; protected PTSongSynchronizerData() { this.repeatStart = null; this.repeatNumber = 0; this.repeatAlternative = false; this.skip = false; } } private static void applyInfo(PTSongInfo src, PTSongInfo dst) { src.copy(dst); } private static void applyInfos(PTTrack src, PTTrack dst) { for (final PTTrackInfo srcInfo : src.getInfos()) { dst.getInfos().add(srcInfo.getClone()); } } private static void applyRepeats(PTTrack src, PTTrack dst) { applyRepeats(src, dst, new PTIndex(0, 0, 0), new PTSongSynchronizerData(), new ArrayList<PTDirection>()); } private static void applyRepeats(PTTrack src, PTTrack dst, PTIndex index, PTSongSynchronizerData rd, List<PTDirection> useds) { for (int s = index.s; s < src.getSections().size(); s++) { PTSection srcSection = (PTSection) src.getSections().get(s); srcSection.sort(); PTSection dstSection = new PTSection(srcSection.getNumber()); dstSection.setStaffs(srcSection.getStaffs()); dst.getSections().add(dstSection); for (int p = (s == index.s ? index.p : 0); p < srcSection.getPositions() .size(); p++) { PTPosition srcPosition = (PTPosition) srcSection.getPositions().get(p); srcPosition.sort(); PTPosition dstPosition = new PTPosition(srcPosition.getPosition()); dstSection.getPositions().add(dstPosition); for (int c = (s == index.s && p == index.p ? index.c : 0); c < srcPosition .getComponents().size(); c++) { PTComponent component = (PTComponent) srcPosition.getComponents() .get(c); if (!rd.skip) { dstPosition.addComponent(component.getClone()); } // ------------------------------ PTBar // ------------------------------// if (component instanceof PTBar) { PTBar bar = (PTBar) component; if (bar.getRepeatClose() > 0 && rd.repeatStart != null) { rd.repeatNumber++; rd.repeatInProgress = true; rd.repeatAlternative = false; rd.skip = false; if (rd.repeatNumber < bar.getRepeatClose()) { applyRepeats(src, dst, rd.repeatStart, rd, useds); return; } rd.repeatStart = null; rd.repeatNumber = 0; } if (bar.isRepeatStart()) { rd.repeatStart = new PTIndex(s, p, c); if (!rd.repeatInProgress) { rd.repeatNumber = 0; } rd.repeatInProgress = false; } } // ------------------------------ PTSymbol // ------------------------------// else if (component instanceof PTSymbol) { PTSymbol symbol = (PTSymbol) component; rd.skip = false; if (!rd.repeatAlternative && ((symbol.getEndNumber() & 1) != 0)) { boolean validEnding = ((symbol.getEndNumber() & (1 << (rd.repeatNumber))) != 0); if (rd.repeatNumber > 0 && !validEnding) { rd.skip = true; } rd.repeatAlternative = true; } } // ------------------------------ PTDirection // ------------------------------// else if (component instanceof PTDirection) { PTDirection direction = (PTDirection) component; boolean validRepeat = (direction.getRepeat() == 0 || (rd.repeatStart != null && (rd.repeatNumber + 1) == direction .getRepeat())); boolean validDirection = (direction.getActiveSymbol() == rd.findActiveSymbol); if (validDirection && validRepeat) { rd.findActiveSymbol = 0; if (direction.getDirection() == PTDirection.DIRECTION_FINE) { // Used to mark when to stop playing (usually the end of the // score) if (canUseDirection(direction, useds)) { return; } } else if (direction.getDirection() == PTDirection.DIRECTION_DA_CAPO) { // Go back to the beginning of the score and play from there if (canUseDirection(direction, useds)) { applyRepeats(src, dst, new PTIndex(0, 0, 0), rd, useds); return; } } else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO) { // Go back to the Segno and play from there if (canUseDirection(direction, useds)) { PTIndex segno = findUnusedDirection(src, useds, PTDirection.DIRECTION_SEGNO, s, p); if (segno != null) { applyRepeats(src, dst, segno, rd, useds); return; } } } else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_SEGNO) { // Go back to the Segno Segno and play from there if (canUseDirection(direction, useds)) { PTIndex segno = findUnusedDirection(src, useds, PTDirection.DIRECTION_SEGNO_SEGNO, s, p); if (segno != null) { applyRepeats(src, dst, segno, rd, useds); return; } } } else if (direction.getDirection() == PTDirection.DIRECTION_TO_CODA) { // Go to the Coda sign and play from there. Used in conjunction // with D.C./D.S. al Coda signs. if (canUseDirection(direction, useds)) { PTIndex coda = findUnusedDirection(src, useds, PTDirection.DIRECTION_CODA, -1, -1); if (coda != null) { applyRepeats(src, dst, coda, rd, useds); return; } } } else if (direction.getDirection() == PTDirection.DIRECTION_TO_DOUBLE_CODA) { // Go to the Double Coda and play from there. Used in // conjunction with D.C./D.S. al Double Coda signs. if (canUseDirection(direction, useds)) { PTIndex coda = findUnusedDirection(src, useds, PTDirection.DIRECTION_DOUBLE_CODA, -1, -1); if (coda != null) { applyRepeats(src, dst, coda, rd, useds); return; } } } else if (direction.getDirection() == PTDirection.DIRECTION_DA_CAPO_AL_CODA) { // Go back to the beginning of the score and play from there // until the To Coda sign is reached, // then jump to the Coda sign. if (canUseDirection(direction, useds)) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DC; applyRepeats(src, dst, new PTIndex(0, 0, 0), rd, useds); return; } } else if (direction.getDirection() == PTDirection.DIRECTION_DA_CAPO_AL_DOUBLE_CODA) { // Go back to the beginning of the score and play from there // until the To Double Coda sign is reached, // then jump to the Double Coda sign. if (canUseDirection(direction, useds)) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DC; applyRepeats(src, dst, new PTIndex(0, 0, 0), rd, useds); return; } } else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_AL_CODA) { // Go back to the Segno sign and play from there until the To // Coda sign is reached, // then jump to the Coda sign. if (canUseDirection(direction, useds)) { PTIndex segno = findUnusedDirection(src, useds, PTDirection.DIRECTION_SEGNO, s, p); if (segno != null) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DS; applyRepeats(src, dst, segno, rd, useds); return; } } } else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_AL_DOUBLE_CODA) { // Go back to the Segno sign and play from there until the To // Double Coda sign is reached, // then jump to the Double Coda sign. if (canUseDirection(direction, useds)) { PTIndex segno = findUnusedDirection(src, useds, PTDirection.DIRECTION_SEGNO, s, p); if (segno != null) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DS; applyRepeats(src, dst, segno, rd, useds); return; } } } else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_SEGNO_AL_CODA) { // Go back to the Segno Segno sign and play from there until the // To Coda sign is reached, // then jump to the Coda sign. if (canUseDirection(direction, useds)) { PTIndex segno = findUnusedDirection(src, useds, PTDirection.DIRECTION_SEGNO_SEGNO, s, p); if (segno != null) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DSS; applyRepeats(src, dst, segno, rd, useds); return; } } } else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_SEGNO_AL_DOUBLE_CODA) { // Go back to the Segno Segno sign and play from there until the // To Double Coda sign is reached, // then jump to the Double Coda sign. if (canUseDirection(direction, useds)) { PTIndex segno = findUnusedDirection(src, useds, PTDirection.DIRECTION_SEGNO_SEGNO, s, p); if (segno != null) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DSS; applyRepeats(src, dst, segno, rd, useds); return; } } } else if (direction.getDirection() == PTDirection.DIRECTION_DA_CAPO_AL_FINE) { // Go back to the beginning and play until the Fine sign is // reached. if (canUseDirection(direction, useds)) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DC; applyRepeats(src, dst, new PTIndex(0, 0, 0), rd, useds); return; } } else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_AL_FINE) { // Go back to the Segno sign and play until the Fine sign is // reached. PTIndex segno = findUnusedDirection(src, useds, PTDirection.DIRECTION_SEGNO, s, p); if (segno != null) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DS; applyRepeats(src, dst, segno, rd, useds); return; } } else if (direction.getDirection() == PTDirection.DIRECTION_DAL_SEGNO_SEGNO_AL_FINE) { // Go back to the Segno Segno sign and play until the Fine sign // is reached. PTIndex segno = findUnusedDirection(src, useds, PTDirection.DIRECTION_SEGNO_SEGNO, s, p); if (segno != null) { rd.findActiveSymbol = PTDirection.ACTIVE_SYMBOL_DSS; applyRepeats(src, dst, segno, rd, useds); return; } } } } } } } } private static boolean canUseDirection(PTDirection direction, List useds) { boolean inUse = false; for (int i = 0; i < useds.size() && !inUse; i++) { PTDirection used = (PTDirection) useds.get(i); if (used.equals(direction)) { return false; } } useds.add(direction); return true; } private static PTIndex findUnusedDirection(PTTrack src, List useds, int value, int sEndIndex, int pEndIndex) { return findUnusedDirection(src, useds, value, sEndIndex, pEndIndex, 0); } private static PTIndex findUnusedDirection(PTTrack src, List useds, int value, int sEndIndex, int pEndIndex, int activeSymbol) { for (int s = 0; s < (sEndIndex >= 0 ? sEndIndex + 1 : src.getSections() .size()); s++) { PTSection section = (PTSection) src.getSections().get(s); for (int p = 0; p < (s == sEndIndex ? pEndIndex + 1 : section .getPositions().size()); p++) { PTPosition position = (PTPosition) section.getPositions().get(p); for (int c = 0; c < position.getComponents().size(); c++) { PTComponent component = (PTComponent) position.getComponents().get(c); if (component instanceof PTDirection) { PTDirection direction = (PTDirection) component; if (direction.getDirection() == value && (activeSymbol == 0 || direction.getActiveSymbol() == activeSymbol)) { if (canUseDirection(direction, useds)) { return new PTIndex(s, p, 0); } } } } } } return null; } private static void synchronizeTrack(PTTrack src, PTTrack dst) { applyRepeats(src, dst); applyInfos(src, dst); } public static void synchronizeTracks(PTSong src, PTSong dst) { applyInfo(src.getInfo(), dst.getInfo()); synchronizeTrack(src.getTrack1(), dst.getTrack1()); synchronizeTrack(src.getTrack2(), dst.getTrack2()); } }