//----------------------------------------------------------------------------// // // // K e y S i g n a t u r e V e r i f i e r // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.score; import omr.constant.ConstantSet; import omr.glyph.Evaluation; import omr.glyph.GlyphNetwork; import omr.glyph.Glyphs; import omr.glyph.Grades; import omr.glyph.Shape; import omr.glyph.facets.Glyph; import omr.grid.StaffInfo; import omr.math.GeoUtil; import omr.score.entity.Barline; import omr.score.entity.KeySignature; import omr.score.entity.Measure; import omr.score.entity.ScoreSystem; import omr.score.entity.Staff; import omr.score.entity.SystemPart; import omr.sheet.Scale; import omr.sheet.SystemInfo; import omr.util.Predicate; import omr.util.TreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Rectangle; import java.util.Collection; /** * Class {@code KeySignatureVerifier} verifies, at system level, that all * vertical measures exhibit the same key signature, and correct them if * necessary. * * @author Herv� Bitteur */ public class KeySignatureVerifier { //~ Static fields/initializers --------------------------------------------- /** Specific application parameters */ private static final Constants constants = new Constants(); /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger( KeySignatureVerifier.class); //~ Instance fields -------------------------------------------------------- // The system concerned private final ScoreSystem system; // Total number of staves in the system private final int staffNb; //~ Constructors ----------------------------------------------------------- //----------------------// // KeySignatureVerifier // //----------------------// /** * Creates a new KeySignatureVerifier object. * * @param system the system at hand */ public KeySignatureVerifier (ScoreSystem system) { this.system = system; staffNb = system.getInfo() .getStaves() .size(); } //~ Methods ---------------------------------------------------------------- //------------// // verifyKeys // //------------// /** * Perform verifications (and corrections when possible) when all keysigs * have been generated for the system: The key signature must be the same * (in terms of fifths) for the same measure index in all parts and staves. */ public void verifyKeys () { logger.debug( "\n------------------------------------------------------"); logger.debug("verifySystemKeys for {}", system); // Number of measures in the system final int measureNb = system.getFirstPart() .getMeasures() .size(); // Verify each measure index on turn for (int im = 0; im < measureNb; im++) { logger.debug("measure index ={}", im); verifyVerticalMeasure(im); } logger.debug( "\n======================================================"); } //-------------// // checkKeySig // //-------------// private Glyph checkKeySig (Collection<Glyph> glyphs, final KeySignature bestKey) { logger.debug( "Merging {} for shape {}", Glyphs.toString(glyphs), bestKey.getShape()); SystemInfo systemInfo = system.getInfo(); Glyphs.purgeManuals(glyphs); if (glyphs.isEmpty()) { return null; } Glyph compound = systemInfo.buildTransientCompound(glyphs); // Check if a proper key sig appears in the top evaluations Evaluation vote = GlyphNetwork.getInstance() .rawVote( compound, Grades.keySigMinGrade, new Predicate<Shape>() { @Override public boolean check (Shape shape) { return shape == bestKey.getShape(); } }); if (vote != null) { // We now have a key sig! logger.debug( "{} built from {}", vote.shape, Glyphs.toString(glyphs)); compound = systemInfo.addGlyph(compound); compound.setShape(vote.shape, Evaluation.ALGORITHM); return compound; } else { logger.debug( "{}Could not find {} in {}", systemInfo.getLogPrefix(), bestKey.getShape(), Glyphs.toString(glyphs)); return null; } } //------------------// // getContextString // //------------------// private String getContextString (int measureIndex, int systemStaffIndex) { return system.getContextString() + "M" + (measureIndex + 1) + "F" + staffOf(systemStaffIndex) .getId(); } //--------------// // getMeasureOf // //--------------// private Measure getMeasureOf (int staffIndex, int measureIndex) { int staffOffset = 0; for (TreeNode node : system.getParts()) { SystemPart part = (SystemPart) node; staffOffset += part.getStaves() .size(); if (staffIndex < staffOffset) { return (Measure) part.getMeasures() .get(measureIndex); } } logger.error("Illegal systemStaffIndex: {}", staffIndex); return null; } //-------------// // harmonizeTo // //-------------// private void harmonizeTo (KeySignature bestKey, KeySignature[] keyVector, int iMeasure) { Rectangle bestBox = bestKey.getBox(); for (int iStaff = 0; iStaff < staffNb; iStaff++) { KeySignature ks = keyVector[iStaff]; // Is this staff OK? if ((ks != null) && ks.getKey() .equals(bestKey.getKey())) { continue; } Staff staff = staffOf(iStaff); StaffInfo staffInfo = staff.getInfo(); logger.debug( "{} Forcing key signature to {}", getContextString(iMeasure, iStaff), bestKey.getKey()); try { // Define the box to intersect keysig glyph(s) int xCenter = bestBox.x + (bestBox.width / 2); Rectangle inner = new Rectangle( xCenter, staffInfo.getFirstLine().yAt(xCenter) + (staffInfo.getHeight() / 2), 0, 0); inner.grow((bestBox.width / 2), (staffInfo.getHeight() / 2)); // Draw the box, for visual debug SystemPart part = system.getPartAt(GeoUtil.centerOf(inner)); Barline barline = part.getStartingBarline(); if (barline != null) { Glyph line = Glyphs.firstOf( barline.getGlyphs(), Barline.linePredicate); if (line != null) { line.addAttachment("k" + staff.getId(), inner); } } // We now must find a key sig out of these glyphs Collection<Glyph> glyphs = system.getInfo() .lookupIntersectedGlyphs( inner); Glyph compound = checkKeySig(glyphs, bestKey); if (compound != null) { if (ks != null) { ks.getParent() .getChildren() .remove(ks); } Measure measure = getMeasureOf(iStaff, iMeasure); ks = new KeySignature(measure, staff); ks.addGlyph(compound); } } catch (Exception ex) { logger.warn("Cannot copy key", ex); ks.addError("Cannot copy key"); } // TODO deassign glyphs that do not contribute to the key ? } } //---------// // staffOf // //---------// private Staff staffOf (int systemStaffIndex) { int staffOffset = 0; for (TreeNode node : system.getParts()) { SystemPart part = (SystemPart) node; int partStaffNb = part.getStaves() .size(); staffOffset += partStaffNb; if (systemStaffIndex < staffOffset) { return (Staff) part.getStaves() .get( (partStaffNb + systemStaffIndex) - staffOffset); } } logger.error("Illegal systemStaffIndex: {}", systemStaffIndex); return null; } //-----------------------// // verifyVerticalMeasure // //-----------------------// private void verifyVerticalMeasure (int im) { // Retrieve a key, if any, for this measure in each staff KeySignature[] keyVector = new KeySignature[staffNb]; int staffOffset = 0; boolean keyFound = false; for (TreeNode node : system.getParts()) { SystemPart part = (SystemPart) node; Measure measure = (Measure) part.getMeasures() .get(im); for (TreeNode ksnode : measure.getKeySignatures()) { KeySignature ks = (KeySignature) ksnode; keyFound = true; keyVector[ks.getStaff() .getId() - 1 + staffOffset] = ks; } staffOffset += part.getStaves() .size(); } // Some keys found in this vertical measure? if (keyFound) { logger.debug( "{} key(s) found in M{}", system.getContextString(), im); // Browse all staves for sharp/flat compatibility // If compatible, adjust all keysigs to the longest boolean compatible = true; boolean adjustment = false; KeySignature bestKey = null; for (int iStaff = 0; iStaff < staffNb; iStaff++) { KeySignature ks = keyVector[iStaff]; if (ks == null) { logger.debug("Key signatures will need to be created"); adjustment = true; } else if (bestKey == null) { bestKey = ks; } else if (!bestKey.getKey() .equals(ks.getKey())) { logger.debug("Key signatures will need adjustment"); adjustment = true; if ((ks.getKey() * bestKey.getKey()) < 0) { logger.debug("Non compatible key signatures"); compatible = false; break; } else if (Math.abs(bestKey.getKey()) < Math.abs( ks.getKey())) { // Keep longest key bestKey = ks; } } } // Force key signatures to this value, if compatible if (compatible && adjustment) { harmonizeTo(bestKey, keyVector, im); } } } //~ Inner Classes ---------------------------------------------------------- //-----------// // Constants // //-----------// private static final class Constants extends ConstantSet { //~ Instance fields ---------------------------------------------------- Scale.Fraction yOffset = new Scale.Fraction( 0.5d, "Key signature vertical offset since staff line"); } }