package org.herac.tuxguitar.midiinput; import java.util.Iterator; import java.util.SortedSet; import java.util.TreeSet; import org.apache.log4j.Logger; import org.herac.tuxguitar.gui.TuxGuitar; import org.herac.tuxguitar.gui.tools.scale.ScaleManager; import org.herac.tuxguitar.util.TGSynchronizer; class MiScaleFinder { static class TestScale { int f_Key; int f_ScaleSize; int[] f_Sequence; } /** The Logger for this class. */ public static final transient Logger LOG = Logger .getLogger(MiScaleFinder.class); static private TestScale[] buildReferenceSequences(int inLoPitch, int inHiPitch, int inScaleIndex) { ScaleManager scaleMgr = TuxGuitar.instance().getScaleManager(); int[] model = scaleDefToModel(scaleMgr.getScaleKeys(inScaleIndex)); int[] intervals = scaleModelToIntervals(model); TestScale[] sequences = new TestScale[12]; // LOG.debug(); // LOG.debug("Scale: " + scaleMgr.getScaleName(inScaleIndex)); // LOG.debug("Lowest Pitch: " + inLoPitch); // LOG.debug("Highest Pitch: " + inHiPitch); // LOG.debug("Model: " + Arrays.toString(model)); // LOG.debug("Intervals: " + Arrays.toString(intervals)); // build sequences, backwards, one per key for (int key = 0; key < 12; key++) { // compute sequence length int sequenceLength = 0; for (int pitch = inLoPitch - key, intervalsIndex = 0; pitch <= inHiPitch;) { sequenceLength++; pitch += intervals[intervalsIndex]; intervalsIndex = (intervalsIndex + 1 >= intervals.length ? 0 : intervalsIndex + 1); } // initialize sequence sequences[key] = new TestScale(); sequences[key].f_Key = (inLoPitch - key) % 12; sequences[key].f_ScaleSize = model.length; sequences[key].f_Sequence = new int[sequenceLength]; // fill sequence for (int pitch = inLoPitch - key, intervalsIndex = 0, i = 0; pitch <= inHiPitch;) { sequences[key].f_Sequence[i++] = pitch; pitch += intervals[intervalsIndex]; intervalsIndex = (intervalsIndex + 1 >= intervals.length ? 0 : intervalsIndex + 1); } // LOG.debug("key: " + key + ", sequence: " + // Arrays.toString(sequences[key].f_Sequence)); } return (sequences); } static private int countMatches(SortedSet<Byte> inScale, int[] inRefSequence) { int count = 0; for (final Byte pitch : inScale) { boolean found = false; for (int i = 0; i < inRefSequence.length && !found; i++) if (pitch == inRefSequence[i]) found = true; if (!found) return 0; else count++; } return count; } static public int findMatchingScale(SortedSet<Byte> inScale) { ScaleManager scaleMgr = TuxGuitar.instance().getScaleManager(); int scalesCount = scaleMgr.countScales(), minScaleSize = 12, maxMatches = 0, scaleIndex = ScaleManager.NONE_SELECTION, scaleKey = 0; if (!inScale.isEmpty()) { int loPitch = ((Byte) inScale.first()).intValue(), hiPitch = ((Byte) inScale .last()).intValue(); // LOG.debug("Input: " + inScale); // LOG.debug("loPitch: " + loPitch); // LOG.debug("hiPitch: " + hiPitch); for (int s = 0; s < scalesCount; s++) { TestScale[] refSequences = buildReferenceSequences(loPitch, hiPitch, s); for (int key = 0; key < 12; key++) { int matches = countMatches(inScale, refSequences[key].f_Sequence); if (matches > maxMatches) { maxMatches = matches; scaleIndex = s; scaleKey = refSequences[key].f_Key; minScaleSize = refSequences[key].f_ScaleSize; // LOG.debug(); // LOG.debug("more matches: " + scaleMgr.getScaleName(scaleIndex)); // LOG.debug("maxMatches: " + maxMatches + " minScaleSize: " + // minScaleSize); } else if (maxMatches > 0 && matches == maxMatches && refSequences[key].f_ScaleSize < minScaleSize) { maxMatches = matches; scaleIndex = s; scaleKey = refSequences[key].f_Key; minScaleSize = refSequences[key].f_ScaleSize; // LOG.debug(""); // LOG.debug("smaller scale: " + scaleMgr.getScaleName(scaleIndex)); // LOG.debug("maxMatches: " + maxMatches + " minScaleSize: " + // minScaleSize); } } } } selectScale(scaleIndex, scaleKey); return (scaleIndex); } static private int[] scaleDefToModel(String inScaleDefinition) { String[] keys = inScaleDefinition.split(","); int[] model = new int[keys.length]; for (int i = 0; i < keys.length; i++) model[i] = (Integer.parseInt(keys[i]) - 1); return (model); } static private int[] scaleModelToIntervals(int[] inModel) { int[] intervals = new int[inModel.length]; for (int i = 1; i < inModel.length; i++) intervals[i - 1] = inModel[i] - inModel[i - 1]; intervals[inModel.length - 1] = 12 - inModel[inModel.length - 1]; return (intervals); } static public void selectScale(final int inIndex, final int inKey) { try { TGSynchronizer.instance().addRunnable(new TGSynchronizer.TGRunnable() { public void run() throws Throwable { TuxGuitar.instance().getScaleManager().selectScale(inIndex, inKey); } }); } catch (Throwable e) { LOG.error(e); } } }