diff --git a/midica.jar b/midica.jar index 6db11b9..f6bf3ba 100644 Binary files a/midica.jar and b/midica.jar differ diff --git a/src/org/midica/Midica.java b/src/org/midica/Midica.java index fddabda..8c7c97e 100644 --- a/src/org/midica/Midica.java +++ b/src/org/midica/Midica.java @@ -30,10 +30,10 @@ public class Midica { * After switching to a new major version, this has to be set to "-1" manually, so that * precommit.pl starts with "0" again. */ - public static final int VERSION_MINOR = 0; + public static final int VERSION_MINOR = 1; /** UNIX timestamp of the last commit */ - public static final int COMMIT_TIME = 1589554992; + public static final int COMMIT_TIME = 1589716979; /** Branch name. Automatically changed by precommit.pl */ public static final String BRANCH = "master"; diff --git a/src/org/midica/config/Config.java b/src/org/midica/config/Config.java index 9ed02dc..1c5623b 100644 --- a/src/org/midica/config/Config.java +++ b/src/org/midica/config/Config.java @@ -185,11 +185,11 @@ public class Config { public static final String DC_MUST_ADD_CONFIG = "dc_must_add_config"; public static final String DC_MUST_ADD_QUALITY_SCORE = "dc_must_add_quality_score"; public static final String DC_MUST_ADD_STATISTICS = "dc_must_add_statistics"; + public static final String DC_MUST_ADD_STRATEGY_STAT = "dc_must_add_strategy_stat"; public static final String DC_LENGTH_STRATEGY = "dc_length_strategy"; public static final String DC_MAX_TARGET_TICKS_ON = "dc_max_target_ticks_on"; - public static final String DC_NEXT_NOTE_ON_TOLERANCE = "dc_next_note_on_tolerance"; public static final String DC_MIN_DURATION_TO_KEEP = "dc_min_duration_to_keep"; - public static final String DC_DURATION_TICK_TOLERANCE = "dc_duration_tick_tolerance"; + public static final String DC_LENGTH_TICK_TOLERANCE = "dc_length_tick_tolerance"; public static final String DC_DURATION_RATIO_TOLERANCE = "dc_duration_ratio_tolerance"; public static final String DC_USE_PRE_DEFINED_CHORDS = "dc_use_pre_defined_chords"; public static final String DC_CHORD_NOTE_ON_TOLERANCE = "dc_chord_note_on_tolerance"; @@ -318,11 +318,11 @@ public static HashMap getDefaultDecompileConfig() { dcDefaults.put( DC_MUST_ADD_CONFIG, "" + MidicaPLExporter.DEFAULT_MUST_ADD_CONFIG ); dcDefaults.put( DC_MUST_ADD_QUALITY_SCORE, "" + MidicaPLExporter.DEFAULT_MUST_ADD_QUALITY_SCORE ); dcDefaults.put( DC_MUST_ADD_STATISTICS, "" + MidicaPLExporter.DEFAULT_MUST_ADD_STATISTICS ); + dcDefaults.put( DC_MUST_ADD_STRATEGY_STAT, "" + MidicaPLExporter.DEFAULT_MUST_ADD_STRATEGY_STAT ); dcDefaults.put( DC_LENGTH_STRATEGY, "" + MidicaPLExporter.DEFAULT_LENGTH_STRATEGY ); dcDefaults.put( DC_MAX_TARGET_TICKS_ON, "" + MidicaPLExporter.DEFAULT_MAX_TARGET_TICKS_ON ); - dcDefaults.put( DC_NEXT_NOTE_ON_TOLERANCE, "" + MidicaPLExporter.DEFAULT_NEXT_NOTE_ON_TOLERANCE ); dcDefaults.put( DC_MIN_DURATION_TO_KEEP, "" + MidicaPLExporter.DEFAULT_MIN_DURATION_TO_KEEP ); - dcDefaults.put( DC_DURATION_TICK_TOLERANCE, "" + MidicaPLExporter.DEFAULT_DURATION_TICK_TOLERANCE ); + dcDefaults.put( DC_LENGTH_TICK_TOLERANCE, "" + MidicaPLExporter.DEFAULT_LENGTH_TICK_TOLERANCE ); dcDefaults.put( DC_DURATION_RATIO_TOLERANCE, "" + MidicaPLExporter.DEFAULT_DURATION_RATIO_TOLERANCE ); dcDefaults.put( DC_USE_PRE_DEFINED_CHORDS, "" + MidicaPLExporter.DEFAULT_USE_PRE_DEFINED_CHORDS ); dcDefaults.put( DC_CHORD_NOTE_ON_TOLERANCE, "" + MidicaPLExporter.DEFAULT_CHORD_NOTE_ON_TOLERANCE ); @@ -723,11 +723,11 @@ private static void restoreDefaultKeyBindings() { addDefaultKeyBinding( Dict.KEY_DC_ADD_CONFIG, KeyEvent.VK_C, 0 ); addDefaultKeyBinding( Dict.KEY_DC_ADD_SCORE, KeyEvent.VK_Q, 0 ); addDefaultKeyBinding( Dict.KEY_DC_ADD_STATISTICS, KeyEvent.VK_S, 0 ); + addDefaultKeyBinding( Dict.KEY_DC_ADD_STRATEGY_STAT, KeyEvent.VK_R, 0 ); addDefaultKeyBinding( Dict.KEY_DC_NOTE_LENGTH_STRATEGY, KeyEvent.VK_P, InputEvent.CTRL_DOWN_MASK ); addDefaultKeyBinding( Dict.KEY_DC_MAX_TARGET_TICKS_ON, KeyEvent.VK_L, InputEvent.CTRL_DOWN_MASK ); - addDefaultKeyBinding( Dict.KEY_DC_TOL_NEXT_ON, KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK ); addDefaultKeyBinding( Dict.KEY_DC_MIN_DUR_TO_KEEP, KeyEvent.VK_K, InputEvent.CTRL_DOWN_MASK ); - addDefaultKeyBinding( Dict.KEY_DC_TOL_DUR_TICK, KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK ); + addDefaultKeyBinding( Dict.KEY_DC_TOL_TICK_LEN, KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK ); addDefaultKeyBinding( Dict.KEY_DC_TOL_DUR_RATIO, KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK ); addDefaultKeyBinding( Dict.KEY_DC_CRD_PREDEFINED, KeyEvent.VK_P, InputEvent.CTRL_DOWN_MASK ); addDefaultKeyBinding( Dict.KEY_DC_CRD_NOTE_ON, KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK ); diff --git a/src/org/midica/config/Dict.java b/src/org/midica/config/Dict.java index b2fa740..4e97e22 100644 --- a/src/org/midica/config/Dict.java +++ b/src/org/midica/config/Dict.java @@ -701,11 +701,11 @@ public class Dict { public static final String KEY_DC_ADD_CONFIG = "key_dc_config"; public static final String KEY_DC_ADD_SCORE = "key_dc_add_score"; public static final String KEY_DC_ADD_STATISTICS = "key_dc_add_statistics"; + public static final String KEY_DC_ADD_STRATEGY_STAT = "key_dc_add_strategy_stat"; public static final String KEY_DC_NOTE_LENGTH_STRATEGY = "key_dc_note_length_strategy"; public static final String KEY_DC_MAX_TARGET_TICKS_ON = "key_dc_max_target_ticks_on"; - public static final String KEY_DC_TOL_NEXT_ON = "key_dc_tol_next_on"; public static final String KEY_DC_MIN_DUR_TO_KEEP = "key_dc_min_dur_to_keep"; - public static final String KEY_DC_TOL_DUR_TICK = "key_dc_tol_dur_tick"; + public static final String KEY_DC_TOL_TICK_LEN = "key_dc_tol_tick_len"; public static final String KEY_DC_TOL_DUR_RATIO = "key_dc_tol_dur_ratio"; public static final String KEY_DC_CRD_PREDEFINED = "key_dc_crd_predefined"; public static final String KEY_DC_CRD_NOTE_ON = "key_dc_crd_note_on"; @@ -827,14 +827,13 @@ public class Dict { public static final String DC_ADD_CONFIG = "dc_add_config"; public static final String DC_ADD_SCORE = "dc_add_score"; public static final String DC_ADD_STATISTICS = "dc_add_statistics"; + public static final String DC_ADD_STRATEGY_STAT = "dc_add_strategy_stat"; public static final String NOTE_LENGTH_STRATEGY = "note_length_strategy"; public static final String MAX_TARGET_TICKS_NEXT_ON = "max_target_ticks_next_on"; - public static final String NEXT_NOTE_ON_TOLERANCE = "next_note_on_tolerance"; - public static final String NEXT_NOTE_ON_TOLERANCE_D = "next_note_on_tolerance_d"; public static final String MIN_DURATION_TO_KEEP = "min_duration_to_keep"; public static final String MIN_DURATION_TO_KEEP_D = "min_duration_to_keep_d"; - public static final String DURATION_TICK_TOLERANCE = "duration_tick_tolerance"; - public static final String DURATION_TICK_TOLERANCE_D = "duration_tick_tolerance_d"; + public static final String LENGTH_TICK_TOLERANCE = "length_tick_tolerance"; + public static final String LENGTH_TICK_TOLERANCE_D = "length_tick_tolerance_d"; public static final String DURATION_RATIO_TOLERANCE = "duration_ratio_tolerance"; public static final String DURATION_RATIO_TOLERANCE_D = "duration_ratio_tolerance_d"; public static final String USE_PRE_DEFINED_CHORDS = "use_pre_defined_chords"; @@ -2066,12 +2065,12 @@ private static void initLanguageEnglish() { set( KEY_DC_ADD_TICK_COMMENTS, "Toggle Checkbox: Add Tick Comments" ); set( KEY_DC_ADD_CONFIG, "Toggle Checkbox: Add Configuration" ); set( KEY_DC_ADD_SCORE, "Toggle Checkbox: Add Quality Score" ); - set( KEY_DC_ADD_STATISTICS, "Toggle Checkbox: Add Statistics" ); + set( KEY_DC_ADD_STATISTICS, "Toggle Checkbox: Add Quality Statistics" ); + set( KEY_DC_ADD_STRATEGY_STAT, "Toggle Checkbox: Add Stragegy Statistics" ); set( KEY_DC_NOTE_LENGTH_STRATEGY, "Open Selection: Note Length Strategy" ); set( KEY_DC_MAX_TARGET_TICKS_ON, "Focus the text field for the Max target ticks" ); - set( KEY_DC_TOL_NEXT_ON, "Focus the text field for the Next Note-ON Tolerance" ); set( KEY_DC_MIN_DUR_TO_KEEP, "Focus the text field for the Min Duration to keep" ); - set( KEY_DC_TOL_DUR_TICK, "Focus the text field for the Duration Tick Tolerance" ); + set( KEY_DC_TOL_TICK_LEN, "Focus the text field for the Note Length Tick Tolerance" ); set( KEY_DC_TOL_DUR_RATIO, "Focus the text field for the Duration Ratio Tolerance" ); set( KEY_DC_CRD_PREDEFINED, "Toggle Checkbox: Use Predefined Chords" ); set( KEY_DC_CRD_NOTE_ON, "Focus the text field for the chord Note-ON tick tolerance" ); @@ -2189,7 +2188,7 @@ private static void initLanguageEnglish() { + "
— Duration*: Tries to avoid a duration change, if possible. The note length is adjusted as close as possible to the previous note's duration." + "
— Press length: Uses the press length (Note-ON to Note-OFF) and chooses the lowest possible length with at least this number of ticks." + "
The priority combobox configures which strategy to prefer if more than one is possible." - + "
The other fields can be used to fine-tune the Next ON and Duration strategy." + + "
The other fields can be used to fine-tune the strategies." + "
* In ALDA the duration is called 'quantization'" ); set( DC_TABINFO_CHORDS, "Settings to control chords." + "
Pre-defined chords are only used for MidicaPL, otherwise ignored." @@ -2211,20 +2210,18 @@ private static void initLanguageEnglish() { set( DC_ADD_CONFIG, "Add Configuration" ); set( DC_ADD_SCORE, "Add Quality Score" ); set( DC_ADD_STATISTICS, "Add Quality Statistics" ); + set( DC_ADD_STRATEGY_STAT, "Add Strategy Statistics" ); set( NOTE_LENGTH_STRATEGY, "Priority of strategies" ); set( MAX_TARGET_TICKS_NEXT_ON, "Maximum note length for
the 'Next ON' strategy" ); - set( NEXT_NOTE_ON_TOLERANCE, "Next Note-ON Tolerance" ); - set( NEXT_NOTE_ON_TOLERANCE_D, "If the length, calculated with the Next ON strategy, is so many ticks longer" - + "
than a note length L, then L is still used instead of the next longer note length." ); set( MIN_DURATION_TO_KEEP, "Minimum Duration to keep" ); set( MIN_DURATION_TO_KEEP_D, "Minimum Duration for using the duration strategy." + "
1.0 = 100%;   0.1 = 10%;   0.01 = 1%" ); - set( DURATION_TICK_TOLERANCE, "Duration Tick Tolerance" ); - set( DURATION_TICK_TOLERANCE_D, "If the length, calculated with the Duration strategy, is so many ticks longer" - + "
than a note length L, then L is still used instead of the next longer note length." ); + set( LENGTH_TICK_TOLERANCE, "Note Length Tick Tolerance" ); + set( LENGTH_TICK_TOLERANCE_D, "If the calculated note length is so many ticks longer than a note length L,
" + + "then L is still used instead of the next longer note length." ); set( DURATION_RATIO_TOLERANCE, "Duration Ratio Tolerance" ); set( DURATION_RATIO_TOLERANCE_D, "The duration is not changed, if the difference between the old duration and the" - + "
duration calculated with the Duration strategy is smaller than this value." + + "
new (calculated) duration is smaller than this value." + "
0.1 = 10%;   0.01 = 1%;   0.001 = 0.1%" ); set( USE_PRE_DEFINED_CHORDS, "Pre-defined Chords" ); set( USE_PRE_DEFINED_CHORDS_D, "Use Pre-defined Chords (instead of inline chords)" ); diff --git a/src/org/midica/file/write/AldaExporter.java b/src/org/midica/file/write/AldaExporter.java index 7a986cf..de2519d 100644 --- a/src/org/midica/file/write/AldaExporter.java +++ b/src/org/midica/file/write/AldaExporter.java @@ -80,16 +80,16 @@ public String createOutput() { currentSliceNumber++; } + output.append(NEW_LINE + NEW_LINE); // config - if (MUST_ADD_CONFIG) { - output.append(NEW_LINE + NEW_LINE + createConfig()); - } + output.append(createConfig()); - // statistics - if (MUST_ADD_STATISTICS) { - output.append(createStatistics()); - } + // quality statistics + output.append(createQualityStats()); + + // strategy statistics + output.append(createStrategyStats()); return output.toString(); } diff --git a/src/org/midica/file/write/Decompiler.java b/src/org/midica/file/write/Decompiler.java index 69af17c..f325bfc 100644 --- a/src/org/midica/file/write/Decompiler.java +++ b/src/org/midica/file/write/Decompiler.java @@ -85,6 +85,11 @@ public abstract class Decompiler extends Exporter { protected static final byte STAT_NOTE_SUMMANDS = 35; protected static final byte STAT_NOTE_MULTIPLE = 36; + // constants for the used note length strategy + private static final byte STAT_STRATEGY_NEXT_ON = 41; + private static final byte STAT_STRATEGY_DURATION = 42; + private static final byte STAT_STRATEGY_PRESS = 43; + protected static final String NEW_LINE = System.getProperty("line.separator"); // decompile constants @@ -101,11 +106,11 @@ public abstract class Decompiler extends Exporter { public static final boolean DEFAULT_MUST_ADD_CONFIG = true; public static final boolean DEFAULT_MUST_ADD_QUALITY_SCORE = true; public static final boolean DEFAULT_MUST_ADD_STATISTICS = true; + public static final boolean DEFAULT_MUST_ADD_STRATEGY_STAT = true; public static final byte DEFAULT_LENGTH_STRATEGY = STRATEGY_NEXT_DURATION_PRESS; public static final long DEFAULT_MAX_TARGET_TICKS_ON = 3840; // 2 full notes - public static final long DEFAULT_NEXT_NOTE_ON_TOLERANCE = 3; public static final float DEFAULT_MIN_DURATION_TO_KEEP = 0.05f; - public static final long DEFAULT_DURATION_TICK_TOLERANCE = 2; + public static final long DEFAULT_LENGTH_TICK_TOLERANCE = 2; public static final float DEFAULT_DURATION_RATIO_TOLERANCE = 0.014f; public static final boolean DEFAULT_USE_PRE_DEFINED_CHORDS = true; public static final long DEFAULT_CHORD_NOTE_ON_TOLERANCE = 0; @@ -130,12 +135,12 @@ public abstract class Decompiler extends Exporter { protected static boolean MUST_ADD_CONFIG = DEFAULT_MUST_ADD_CONFIG; protected static boolean MUST_ADD_QUALITY_SCORE = DEFAULT_MUST_ADD_QUALITY_SCORE; protected static boolean MUST_ADD_STATISTICS = DEFAULT_MUST_ADD_STATISTICS; + protected static boolean MUST_ADD_STRATEGY_STAT = DEFAULT_MUST_ADD_STRATEGY_STAT; protected static byte LENGTH_STRATEGY = DEFAULT_LENGTH_STRATEGY; protected static long MAX_TARGET_TICKS_ON = DEFAULT_MAX_TARGET_TICKS_ON; protected static long MAX_SOURCE_TICKS_ON = 0L; - protected static long NEXT_NOTE_ON_TOLERANCE = DEFAULT_NEXT_NOTE_ON_TOLERANCE; protected static float MIN_DURATION_TO_KEEP = DEFAULT_MIN_DURATION_TO_KEEP; - protected static long DURATION_TICK_TOLERANCE = DEFAULT_DURATION_TICK_TOLERANCE; + protected static long LENGTH_TICK_TOLERANCE = DEFAULT_LENGTH_TICK_TOLERANCE; protected static float DURATION_RATIO_TOLERANCE = DEFAULT_DURATION_RATIO_TOLERANCE; protected static boolean USE_PRE_DEFINED_CHORDS = DEFAULT_USE_PRE_DEFINED_CHORDS; protected static long CHORD_NOTE_ON_TOLERANCE = DEFAULT_CHORD_NOTE_ON_TOLERANCE; @@ -184,8 +189,15 @@ public abstract class Decompiler extends Exporter { protected TreeMap>> noteOnOff = null; protected TreeMap lyricsSyllables = null; - /** stores statistics to estimate the decompilation quality */ - protected TreeMap> statistics = null; + /** + * Stores statistics to estimate the decompilation quality and to count the used strategies. + * Parts of this structure: + * + * - channel number (or a higher number for the total statistic) + * - type number (value of the STAT_... variable) + * - appearance count + */ + private TreeMap> statistics = null; /** * Stores each **slice** of the sequence. @@ -336,11 +348,11 @@ private void refreshConfig() { MUST_ADD_CONFIG = Boolean.parseBoolean( sessionConfig.get(Config.DC_MUST_ADD_CONFIG) ); MUST_ADD_QUALITY_SCORE = Boolean.parseBoolean( sessionConfig.get(Config.DC_MUST_ADD_QUALITY_SCORE) ); MUST_ADD_STATISTICS = Boolean.parseBoolean( sessionConfig.get(Config.DC_MUST_ADD_STATISTICS) ); + MUST_ADD_STRATEGY_STAT = Boolean.parseBoolean( sessionConfig.get(Config.DC_MUST_ADD_STRATEGY_STAT) ); LENGTH_STRATEGY = Byte.parseByte( sessionConfig.get(Config.DC_LENGTH_STRATEGY) ); MAX_TARGET_TICKS_ON = Long.parseLong( sessionConfig.get(Config.DC_MAX_TARGET_TICKS_ON) ); - NEXT_NOTE_ON_TOLERANCE = Long.parseLong( sessionConfig.get(Config.DC_NEXT_NOTE_ON_TOLERANCE) ); MIN_DURATION_TO_KEEP = Float.parseFloat( sessionConfig.get(Config.DC_MIN_DURATION_TO_KEEP) ); - DURATION_TICK_TOLERANCE = Long.parseLong( sessionConfig.get(Config.DC_DURATION_TICK_TOLERANCE) ); + LENGTH_TICK_TOLERANCE = Long.parseLong( sessionConfig.get(Config.DC_LENGTH_TICK_TOLERANCE) ); DURATION_RATIO_TOLERANCE = Float.parseFloat( sessionConfig.get(Config.DC_DURATION_RATIO_TOLERANCE) ); USE_PRE_DEFINED_CHORDS = Boolean.parseBoolean( sessionConfig.get(Config.DC_USE_PRE_DEFINED_CHORDS) ); CHORD_NOTE_ON_TOLERANCE = Long.parseLong( sessionConfig.get(Config.DC_CHORD_NOTE_ON_TOLERANCE) ); @@ -359,7 +371,11 @@ private void refreshConfig() { } /** - * Initializes data structures for the statistics to estimate the decompilation quality. + * Initializes data structures for statistics. + * This is used for the following statistics: + * + * - quality statistics (to estimate the decompilation quality) + * - strategy statistics (counts which strategy is used how often) */ private void initStatistics() { statistics = new TreeMap<>(); @@ -373,6 +389,8 @@ private void initStatistics() { // init sub statistics for all channels and total for (Byte channelOrTotal : statistics.keySet()) { TreeMap channelStats = statistics.get(channelOrTotal); + + // quality statistics channelStats.put( STAT_RESTS, 0 ); channelStats.put( STAT_REST_SKIPPED, 0 ); channelStats.put( STAT_REST_TRIPLETS, 0 ); @@ -383,6 +401,11 @@ private void initStatistics() { channelStats.put( STAT_NOTE_TRIPLETS, 0 ); channelStats.put( STAT_NOTE_SUMMANDS, 0 ); channelStats.put( STAT_NOTE_MULTIPLE, 0 ); + + // strategy statistics + channelStats.put( STAT_STRATEGY_NEXT_ON, 0 ); + channelStats.put( STAT_STRATEGY_DURATION, 0 ); + channelStats.put( STAT_STRATEGY_PRESS, 0 ); } } @@ -1115,13 +1138,13 @@ protected ArrayList getLengthsForSum(long ticks, boolean isRest) { * - the **duration percentage** is the duration option of a MidicaPL channel command or * the quantization attribute in ALDA * - * For the calculation, one one of the following values is used: + * For the calculation, one of the following strategies is used: * * - The length between note-ON and note-ON of the next note, if this is reasonable. * - The length according to the channel's current duration ratio. * - The lowest possible of the predefined values, that is higher than the note press length. * - * The priority of the actually chosen value to be used is controlled by the configuration. + * The priority of the actually chosen strategy to be used is controlled by the configuration. * * @param onTick Note-ON tick of the note. * @param offTick Note-OFF tick of the note. @@ -1139,9 +1162,9 @@ protected long[] getNoteLengthProperties(long onTick, long offTick, byte channel // TODO: Handle the case of different durations in the same tick, // TODO: because than instruments.get(channel).getDurationRatio() will already be outdated. - // 1st strategy: calculate note length according to the current duration ratio + // DURATION strategy: calculate note length according to the current duration ratio float oldDuration = instrumentsByChannel.get(channel).getDurationRatio(); - long noteTicksByDur = (long) ((pressTicks / (double) oldDuration)) - DURATION_TICK_TOLERANCE; + long noteTicksByDur = (long) ((pressTicks / (double) oldDuration)) - LENGTH_TICK_TOLERANCE; noteTicksByDur = getNoteLengthByPressTicks(noteTicksByDur); float durationByDur = calculateDuration(noteTicksByDur, pressTicks); float durationDiff = oldDuration > durationByDur ? oldDuration - durationByDur : durationByDur - oldDuration; @@ -1151,14 +1174,14 @@ protected long[] getNoteLengthProperties(long onTick, long offTick, byte channel canUseByDur = false; } - // 2nd strategy: calculate note length according to the next note-ON + // next-ON strategy: calculate note length according to the next note-ON tick long noteTicksByOn = -1; float durationByOn = -1; Long nextNoteOnTick = noteHistory.get(channel).ceilingKey(onTick + 1); if (nextNoteOnTick != null) { - noteTicksByOn = nextNoteOnTick - onTick - NEXT_NOTE_ON_TOLERANCE; - noteTicksByOn = getNoteLengthByPressTicks(noteTicksByOn); - durationByOn = calculateDuration(noteTicksByOn, pressTicks); + noteTicksByOn = nextNoteOnTick - onTick - LENGTH_TICK_TOLERANCE; + noteTicksByOn = getNoteLengthByPressTicks(noteTicksByOn); + durationByOn = calculateDuration(noteTicksByOn, pressTicks); } boolean canUseNextOn = noteTicksByOn > 0 && durationByOn > 0; if (noteTicksByOn > MAX_SOURCE_TICKS_ON) { @@ -1185,15 +1208,26 @@ else if (STRATEGY_DURATION_NEXT_PRESS == LENGTH_STRATEGY) { if (canUseNextOn) { noteTicks = noteTicksByOn; durationRatio = durationByOn; + incrementStats(STAT_STRATEGY_NEXT_ON, channel); } else if (canUseByDur) { noteTicks = noteTicksByDur; durationRatio = durationByDur; + incrementStats(STAT_STRATEGY_DURATION, channel); } else { // fallback strategy: calculate note length only by press length + pressTicks -= LENGTH_TICK_TOLERANCE; + pressTicks = pressTicks < 1 ? 1 : pressTicks; noteTicks = getNoteLengthByPressTicks(pressTicks); durationRatio = calculateDuration(noteTicks, pressTicks); + incrementStats(STAT_STRATEGY_PRESS, channel); + } + + // ignore small duration changes + durationDiff = oldDuration > durationRatio ? oldDuration - durationRatio : durationRatio - oldDuration; + if (durationDiff < DURATION_RATIO_TOLERANCE) { + durationRatio = oldDuration; } // calculate duration ratio @@ -1326,16 +1360,18 @@ protected void incrementStats(Byte type, Byte channel) { } /** - * Creates the statistics to be printed at the end of the produced file. + * Creates the quality statistics to be printed at the end of the produced file. * - * @return statistics block + * @return quality statistics block */ - protected String createStatistics() { + protected String createQualityStats() { + if (! MUST_ADD_STATISTICS && ! MUST_ADD_QUALITY_SCORE) + return ""; + StringBuilder statLines = new StringBuilder(""); String comment = getCommentSymbol(); - - if (MUST_ADD_STATISTICS) - statLines.append(comment + " " + "STATISTICS:" + NEW_LINE); + statLines.append(comment + " " + "QUALITY STATISTICS:" + NEW_LINE); + statLines.append(comment + NEW_LINE); // channels for (byte channel = 0; channel < 16; channel++) { @@ -1345,30 +1381,32 @@ protected String createStatistics() { if (0 == channelStats.get(STAT_NOTES) && 0 == channelStats.get(STAT_RESTS)) continue; - if (MUST_ADD_STATISTICS) - statLines.append(comment + " Channel " + channel + ":" + NEW_LINE); - statLines.append( createStatisticPart(channelStats, false) ); + statLines.append(comment + " Channel " + channel + ":" + NEW_LINE); + statLines.append( createQualityStatsPart(channelStats) ); } // total - if (MUST_ADD_STATISTICS) - statLines.append(comment + " TOTAL:" + NEW_LINE); - statLines.append( createStatisticPart(statistics.get(STAT_TOTAL), true) ); + statLines.append(comment + " TOTAL:" + NEW_LINE); + statLines.append(createQualityStatsPart(statistics.get(STAT_TOTAL))); + statLines.append(NEW_LINE); return statLines.toString(); } /** - * Creates the statistics for one part (either a channel or total). + * Creates the quality statistics for one part (either a channel or total). * - * @param subStat statistic structure for the part (channel or total) - * @param isTotal **true**, if this is for the total statistics, **false** for channel statistics + * @param subStat statistic structure for the part (channel or total) * @return the created statistics. */ - private String createStatisticPart(TreeMap subStat, boolean isTotal) { + private String createQualityStatsPart(TreeMap subStat) { StringBuilder stats = new StringBuilder(""); String comment = getCommentSymbol(); + // format strings + final String fmtName = "%-20s"; + final String fmtVal = "%1$10s"; + // markers for the quality score int markerCount = 0; double markerSum = 0; @@ -1386,7 +1424,7 @@ private String createStatisticPart(TreeMap subStat, boolean isTota restsPercent *= 100; String restsPercentStr = String.format("%.2f", restsPercent); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Rests/Notes: " + rests + "/" + notes + " (" + restsPercentStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Rests/Notes:") + String.format(fmtVal, rests + "/" + notes) + " (" + restsPercentStr + "%)" + NEW_LINE); markerCount++; markerSum += (100.0D - restsPercent); } @@ -1398,7 +1436,7 @@ private String createStatisticPart(TreeMap subStat, boolean isTota restsSkipped *= 100; String restsSkippedStr = String.format("%.2f", restsSkipped); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Skipped: " + subStat.get(STAT_REST_SKIPPED) + " (" + restsSkippedStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Skipped:") + String.format(fmtVal, subStat.get(STAT_REST_SKIPPED)) + " (" + restsSkippedStr + "%)" + NEW_LINE); markerCount++; markerSum += (100.0D - restsSkipped); @@ -1408,7 +1446,7 @@ private String createStatisticPart(TreeMap subStat, boolean isTota summandsPercent *= 100; String summandsPercentStr = String.format("%.2f", summandsPercent); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Summands: " + summands + " (" + summandsPercentStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Summands:") + String.format(fmtVal, summands) + " (" + summandsPercentStr + "%)" + NEW_LINE); markerCount++; markerSum += 100.0D - (summandsPercent - 100.0D); @@ -1419,7 +1457,7 @@ private String createStatisticPart(TreeMap subStat, boolean isTota tripletsPercent *= 100; String tripletsStr = String.format("%.2f", tripletsPercent); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Triplets: " + triplets + " (" + tripletsStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Triplets:") + String.format(fmtVal, triplets) + " (" + tripletsStr + "%)" + NEW_LINE); markerCount++; markerSum += (100.0D - tripletsPercent); } @@ -1439,7 +1477,7 @@ private String createStatisticPart(TreeMap subStat, boolean isTota summandsPercent *= 100; String summandsPercentStr = String.format("%.2f", summandsPercent); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Summands: " + summands + " (" + summandsPercentStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Summands:") + String.format(fmtVal, summands) + " (" + summandsPercentStr + "%)" + NEW_LINE); markerCount++; markerSum += 100.0D - (summandsPercent - 100.0D); @@ -1450,7 +1488,7 @@ private String createStatisticPart(TreeMap subStat, boolean isTota tripletsPercent *= 100; String tripletsStr = String.format("%.2f", tripletsPercent); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Triplets: " + triplets + " (" + tripletsStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Triplets:") + String.format(fmtVal, triplets) + " (" + tripletsStr + "%)" + NEW_LINE); markerCount++; markerSum += (100.0D - tripletsPercent); } @@ -1461,7 +1499,7 @@ private String createStatisticPart(TreeMap subStat, boolean isTota velocitiesPercent *= 100; String velocitiesPercentStr = String.format("%.2f", velocitiesPercent); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Velocity changes: " + velocities + " (" + velocitiesPercentStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Velocity changes:") + String.format(fmtVal, velocities) + " (" + velocitiesPercentStr + "%)" + NEW_LINE); markerCount++; markerSum += (100.0D - velocitiesPercent); @@ -1471,7 +1509,7 @@ private String createStatisticPart(TreeMap subStat, boolean isTota durationPercent *= 100; String durationPercentStr = String.format("%.2f", durationPercent); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Duration changes: " + durations + " (" + durationPercentStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Duration changes:") + String.format(fmtVal, durations) + " (" + durationPercentStr + "%)" + NEW_LINE); markerCount++; markerSum += (100.0D - durationPercent); @@ -1481,30 +1519,98 @@ private String createStatisticPart(TreeMap subStat, boolean isTota multiplePercent *= 100; String multiplePercentStr = String.format("%.2f", multiplePercent); if (MUST_ADD_STATISTICS) - stats.append(comment + "\t\t" + "Multiple option: " + multiple + " (" + multiplePercentStr + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + String.format(fmtName, "Multiple option:") + String.format(fmtVal, multiple) + " (" + multiplePercentStr + "%)" + NEW_LINE); markerCount++; markerSum += (100.0D - multiplePercent); } } // quality score - if (isTotal) { - if (MUST_ADD_STATISTICS && MUST_ADD_QUALITY_SCORE) - stats.append(comment + NEW_LINE); - double totalScore = ((double) markerSum) / markerCount; + if (MUST_ADD_QUALITY_SCORE) { + double totalScore = ((double) markerSum) / markerCount; String totalScoreStr = String.format("%.2f", totalScore); - if (MUST_ADD_QUALITY_SCORE) - stats.append(comment + " QUALITY SCORE: " + totalScoreStr + NEW_LINE); + stats.append(comment + "\tQuality Score: " + totalScoreStr + NEW_LINE); } // empty line - if (MUST_ADD_STATISTICS || MUST_ADD_QUALITY_SCORE) { - if (isTotal) - stats.append(NEW_LINE); - else if (MUST_ADD_STATISTICS) - stats.append(comment + NEW_LINE); + if (MUST_ADD_STATISTICS) { + stats.append(comment + NEW_LINE); + } + + return stats.toString(); + } + + /** + * Creates the strategy statistics to be printed at the end of the produced file. + * + * @return strategy statistics block + */ + protected String createStrategyStats() { + if (! MUST_ADD_STRATEGY_STAT) + return ""; + + StringBuilder statLines = new StringBuilder(""); + String comment = getCommentSymbol(); + + statLines.append(comment + " " + "STRATEGY STATISTICS:" + NEW_LINE); + statLines.append(comment + NEW_LINE); + + // channels + for (byte channel = 0; channel < 16; channel++) { + TreeMap channelStats = statistics.get(channel); + + int sum = channelStats.get(STAT_STRATEGY_NEXT_ON) + + channelStats.get(STAT_STRATEGY_DURATION) + + channelStats.get(STAT_STRATEGY_PRESS); + + // nothing to do? + if (0 == sum) + continue; + + statLines.append(comment + " Channel " + channel + ":" + NEW_LINE); + statLines.append(createStrategyStatsPart(channelStats)); } + statLines.append(comment + " TOTAL:" + NEW_LINE); + statLines.append(createStrategyStatsPart(statistics.get(STAT_TOTAL))); + statLines.append(NEW_LINE); + + return statLines.toString(); + } + + /** + * Creates the strategy statistics for one part (either a channel or total). + * + * @param subStat statistic structure for the part (channel or total) + * @return the created statistics. + */ + private String createStrategyStatsPart(TreeMap subStat) { + StringBuilder stats = new StringBuilder(""); + String comment = getCommentSymbol(); + + // get counts + int countNextOn = subStat.get(STAT_STRATEGY_NEXT_ON); + int countDuration = subStat.get(STAT_STRATEGY_DURATION); + int countPress = subStat.get(STAT_STRATEGY_PRESS); + int countAll = countNextOn + countDuration + countPress; + + // format counts + String strCountNextOn = String.format("%6d", countNextOn); + String strCountDuration = String.format("%6d", countDuration); + String strCountPress = String.format("%6d", countPress); + String strCountAll = String.format("%6d", countAll); + + // calculate percentages + String percentNextOn = String.format("%.2f", ((double) 100) * ((double) countNextOn) / ((double) (countAll))); + String percentDuration = String.format("%.2f", ((double) 100) * ((double) countDuration) / ((double) (countAll))); + String percentPress = String.format("%.2f", ((double) 100) * ((double) countPress) / ((double) (countAll))); + + // add the lines + stats.append(comment + "\t\t" + "Sum: " + strCountAll + NEW_LINE); + stats.append(comment + "\t\t" + "Next ON: " + strCountNextOn + " (" + percentNextOn + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + "Duration: " + strCountDuration + " (" + percentDuration + "%)" + NEW_LINE); + stats.append(comment + "\t\t" + "Press: " + strCountPress + " (" + percentPress + "%)" + NEW_LINE); + return stats.toString(); } @@ -1514,12 +1620,12 @@ else if (MUST_ADD_STATISTICS) * @return configuration block */ protected String createConfig() { + if (! MUST_ADD_CONFIG) + return ""; + StringBuilder statLines = new StringBuilder(""); String comment = getCommentSymbol(); - if ( ! MUST_ADD_CONFIG ) - return statLines.toString(); - // headline statLines.append(comment + " " + "CONFIGURATION:" + NEW_LINE); statLines.append(comment + NEW_LINE); @@ -1532,7 +1638,7 @@ protected String createConfig() { String value = sessionConfig.get(key); statLines.append(comment + " " + key + "\t" + value + NEW_LINE); } - statLines.append(NEW_LINE + NEW_LINE); + statLines.append(NEW_LINE); return statLines.toString(); } diff --git a/src/org/midica/file/write/MidicaPLExporter.java b/src/org/midica/file/write/MidicaPLExporter.java index f0d890c..4c3c462 100644 --- a/src/org/midica/file/write/MidicaPLExporter.java +++ b/src/org/midica/file/write/MidicaPLExporter.java @@ -79,10 +79,13 @@ public String createOutput() { } // config - output.append( createConfig() ); + output.append(createConfig()); - // statistics - output.append( createStatistics() ); + // quality statistics + output.append(createQualityStats()); + + // strategy statistics + output.append(createStrategyStats()); return output.toString(); } diff --git a/src/org/midica/ui/file/DecompileConfigController.java b/src/org/midica/ui/file/DecompileConfigController.java index 76cf90c..2d49dc9 100644 --- a/src/org/midica/ui/file/DecompileConfigController.java +++ b/src/org/midica/ui/file/DecompileConfigController.java @@ -117,11 +117,11 @@ private void initSessionConfig(boolean fromConfig) { initWidgetConfig( Config.DC_MUST_ADD_CONFIG, view.cbxAddConfig, Boolean.class, fromConfig ); initWidgetConfig( Config.DC_MUST_ADD_QUALITY_SCORE, view.cbxAddScore, Boolean.class, fromConfig ); initWidgetConfig( Config.DC_MUST_ADD_STATISTICS, view.cbxAddStatistics, Boolean.class, fromConfig ); + initWidgetConfig( Config.DC_MUST_ADD_STRATEGY_STAT, view.cbxAddStrategyStat, Boolean.class, fromConfig ); initWidgetConfig( Config.DC_LENGTH_STRATEGY, view.cbxLengthStrategy, Integer.class, fromConfig ); initWidgetConfig( Config.DC_MAX_TARGET_TICKS_ON, view.cbxMaxTargetTicksOn, Integer.class, fromConfig ); - initWidgetConfig( Config.DC_NEXT_NOTE_ON_TOLERANCE, view.fldNextNoteOnTolerance, Integer.class, fromConfig ); initWidgetConfig( Config.DC_MIN_DURATION_TO_KEEP, view.fldMinDurToKeep, Float.class, fromConfig ); - initWidgetConfig( Config.DC_DURATION_TICK_TOLERANCE, view.fldDurationTickTolerance, Integer.class, fromConfig ); + initWidgetConfig( Config.DC_LENGTH_TICK_TOLERANCE, view.fldLengthTickTolerance, Integer.class, fromConfig ); initWidgetConfig( Config.DC_DURATION_RATIO_TOLERANCE, view.fldDurationRatioTolerance, Float.class, fromConfig ); initWidgetConfig( Config.DC_USE_PRE_DEFINED_CHORDS, view.cbxPredefinedChords, Boolean.class, fromConfig ); initWidgetConfig( Config.DC_CHORD_NOTE_ON_TOLERANCE, view.fldChordNoteOnTolerance, Integer.class, fromConfig ); diff --git a/src/org/midica/ui/file/DecompileConfigView.java b/src/org/midica/ui/file/DecompileConfigView.java index cb7eb65..5f01377 100644 --- a/src/org/midica/ui/file/DecompileConfigView.java +++ b/src/org/midica/ui/file/DecompileConfigView.java @@ -62,11 +62,11 @@ public class DecompileConfigView extends JDialog { JCheckBox cbxAddConfig; JCheckBox cbxAddScore; JCheckBox cbxAddStatistics; + JCheckBox cbxAddStrategyStat; JComboBox cbxLengthStrategy; JComboBox cbxMaxTargetTicksOn; - JTextField fldNextNoteOnTolerance; JTextField fldMinDurToKeep; - JTextField fldDurationTickTolerance; + JTextField fldLengthTickTolerance; JTextField fldDurationRatioTolerance; JCheckBox cbxPredefinedChords; JTextField fldChordNoteOnTolerance; @@ -108,11 +108,11 @@ public DecompileConfigView(DecompileConfigIcon dcIcon, JDialog owner) { cbxAddConfig = new JCheckBox(Dict.get(Dict.DC_ADD_CONFIG)); cbxAddScore = new JCheckBox(Dict.get(Dict.DC_ADD_SCORE)); cbxAddStatistics = new JCheckBox(Dict.get(Dict.DC_ADD_STATISTICS)); + cbxAddStrategyStat = new JCheckBox(Dict.get(Dict.DC_ADD_STRATEGY_STAT)); cbxLengthStrategy = new JComboBox<>(); cbxMaxTargetTicksOn = new JComboBox<>(); - fldNextNoteOnTolerance = new JTextField(); fldMinDurToKeep = new JTextField(); - fldDurationTickTolerance = new JTextField(); + fldLengthTickTolerance = new JTextField(); fldDurationRatioTolerance = new JTextField(); cbxPredefinedChords = new JCheckBox(); fldChordNoteOnTolerance = new JTextField(); @@ -262,11 +262,16 @@ private Container createDebugArea(String tabKey) { cbxAddScore.addActionListener(controller); area.add(cbxAddScore, constrLeft); - // statistics + // quality statistics constrLeft.gridy++; cbxAddStatistics.addActionListener(controller); area.add(cbxAddStatistics, constrLeft); + // note length stragegy statistics + constrLeft.gridy++; + cbxAddStrategyStat.addActionListener(controller); + area.add(cbxAddStrategyStat, constrLeft); + return wrapTabContent(area); } @@ -324,24 +329,6 @@ private Container createNoteLengthArea(String tabKey) { area.add(cbxMaxTargetTicksOn, constrCenter); constrCenter.gridwidth = 1; - // next note-on tolerance - // label - constrLeft.gridy++; - JLabel lblNextOnTol = new JLabel( Dict.get(Dict.NEXT_NOTE_ON_TOLERANCE) ); - Laf.makeBold(lblNextOnTol); - area.add(lblNextOnTol, constrLeft); - - // field - constrCenter.gridy++; - fldNextNoteOnTolerance.getDocument().addDocumentListener(controller); - fldNextNoteOnTolerance.setPreferredSize(new Dimension(TEXT_FIELD_WIDTH, TEXT_FIELD_HEIGHT)); - area.add(fldNextNoteOnTolerance, constrCenter); - - // description - constrRight.gridy++; - JLabel descNextOnTol = new JLabel( Dict.get(Dict.NEXT_NOTE_ON_TOLERANCE_D) ); - area.add(descNextOnTol, constrRight); - // separator constrLeft.gridy++; constrCenter.gridy++; @@ -367,23 +354,30 @@ private Container createNoteLengthArea(String tabKey) { JLabel descMinDuration = new JLabel( Dict.get(Dict.MIN_DURATION_TO_KEEP_D) ); area.add(descMinDuration, constrRight); - // duration tick tolerance + // separator + constrLeft.gridy++; + constrCenter.gridy++; + constrRight.gridy++; + constrFull.gridy = constrRight.gridy; + area.add(Laf.createSeparator(), constrFull); + + // note length tick tolerance // label constrLeft.gridy++; - JLabel lblTickTol = new JLabel( Dict.get(Dict.DURATION_TICK_TOLERANCE) ); - Laf.makeBold(lblTickTol); - area.add(lblTickTol, constrLeft); + JLabel lblLenTickTol = new JLabel( Dict.get(Dict.LENGTH_TICK_TOLERANCE) ); + Laf.makeBold(lblLenTickTol); + area.add(lblLenTickTol, constrLeft); // field constrCenter.gridy++; - fldDurationTickTolerance.getDocument().addDocumentListener(controller); - fldDurationTickTolerance.setPreferredSize(new Dimension(TEXT_FIELD_WIDTH, TEXT_FIELD_HEIGHT)); - area.add(fldDurationTickTolerance, constrCenter); + fldLengthTickTolerance.getDocument().addDocumentListener(controller); + fldLengthTickTolerance.setPreferredSize(new Dimension(TEXT_FIELD_WIDTH, TEXT_FIELD_HEIGHT)); + area.add(fldLengthTickTolerance, constrCenter); // description constrRight.gridy++; - JLabel descTickTol = new JLabel( Dict.get(Dict.DURATION_TICK_TOLERANCE_D) ); - area.add(descTickTol, constrRight); + JLabel descLenTickTol = new JLabel( Dict.get(Dict.LENGTH_TICK_TOLERANCE_D) ); + area.add(descLenTickTol, constrRight); // duration ratio tolerance // label @@ -869,13 +863,13 @@ private void addKeyBindings() { keyBindingManager.addBindingsForTabLevel3( cbxAddConfig, Dict.KEY_DC_ADD_CONFIG ); keyBindingManager.addBindingsForTabLevel3( cbxAddScore, Dict.KEY_DC_ADD_SCORE ); keyBindingManager.addBindingsForTabLevel3( cbxAddStatistics, Dict.KEY_DC_ADD_STATISTICS ); + keyBindingManager.addBindingsForTabLevel3( cbxAddStrategyStat, Dict.KEY_DC_ADD_STRATEGY_STAT ); // note length tab keyBindingManager.addBindingsForTabLevel3( cbxLengthStrategy, Dict.KEY_DC_NOTE_LENGTH_STRATEGY ); keyBindingManager.addBindingsForTabLevel3( cbxMaxTargetTicksOn, Dict.KEY_DC_MAX_TARGET_TICKS_ON ); - keyBindingManager.addBindingsForTabLevel3( fldNextNoteOnTolerance, Dict.KEY_DC_TOL_NEXT_ON ); keyBindingManager.addBindingsForTabLevel3( fldMinDurToKeep, Dict.KEY_DC_MIN_DUR_TO_KEEP ); - keyBindingManager.addBindingsForTabLevel3( fldDurationTickTolerance, Dict.KEY_DC_TOL_DUR_TICK ); + keyBindingManager.addBindingsForTabLevel3( fldLengthTickTolerance, Dict.KEY_DC_TOL_TICK_LEN ); keyBindingManager.addBindingsForTabLevel3( fldDurationRatioTolerance, Dict.KEY_DC_TOL_DUR_RATIO ); // chords tab