//----------------------------------------------------------------------------// // // // C l e f // // // //----------------------------------------------------------------------------// // <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.entity; import omr.glyph.Glyphs; import omr.glyph.Shape; import omr.glyph.facets.Glyph; import omr.score.visitor.ScoreVisitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Point; /** * Class {@code Clef} encapsulates a clef. * * <p>The following image, directly pulled from wikipedia, explains the most * popular clefs today (Treble, Alto, Tenor and Bass) and for each presents * where the "Middle C" note (C4) would take place. These informations are used * by methods {@link #octaveOf(omr.score.entity.Clef, int)} and * {@link #noteStepOf(omr.score.entity.Clef, int)}.</p> * <p> * <img * src="http://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Middle_C_in_four_clefs.svg/600px-Middle_C_in_four_clefs.svg.png" * /> * * @author Hervé Bitteur */ public class Clef extends MeasureNode { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger(Clef.class); /** A dummy default clef to be used when no current clef is defined */ private static Clef defaultClef = new Clef( null, null, Shape.G_CLEF, null, +2, null); //~ Instance fields -------------------------------------------------------- /** Precise clef shape, from Clefs range in Shape class */ private Shape shape; /** * Step line of the clef : -4 for top line (Baritone), -2 for Bass and * Tenor, * 0 for Alto, +2 for Treble and Mezzo-Soprano, +4 for bottom line * (Soprano). */ private int pitchPosition; //~ Constructors ----------------------------------------------------------- //------// // Clef // //------// /** * Create a Clef instance * * @param measure the containing measure * @param staff the assigned staff * @param shape precise clef shape * @param center center wrt system (in units) * @param pitchPosition pitch position * @param glyph underlying glyph, if any */ public Clef (Measure measure, Staff staff, Shape shape, Point center, int pitchPosition, Glyph glyph) { super(measure); if (glyph != null) { addGlyph(glyph); } setStaff(staff); this.shape = (shape == Shape.G_CLEF_SMALL) ? Shape.G_CLEF : shape; setCenter(center); this.pitchPosition = pitchPosition; getBox(); // Not really needed } //------// // Clef // //------// /** * Create a Clef instance, by cloning another clef * * @param measure the containing measure * @param staff the assigned staff * @param other the existing clef to clone */ public Clef (Measure measure, Staff staff, Clef other) { this( measure, staff, other.getShape(), other.getCenter(), other.getPitchPosition(), null); } //~ Methods ---------------------------------------------------------------- //------------// // noteStepOf // //------------// /** * Report the note step that corresponds to a note in the provided pitch * position, using the current clef if any, otherwise using the default clef * (G_CLEF) * * @param clef the provided current clef * @param pitchPosition the pitch position of the provided note * @return the corresponding note step */ public static Note.Step noteStepOf (Clef clef, int pitchPosition) { if (clef == null) { return defaultClef.noteStepOf(pitchPosition); } else { return clef.noteStepOf(pitchPosition); } } //--------// // accept // //--------// @Override public boolean accept (ScoreVisitor visitor) { return visitor.visit(this); } //------------------// // getPitchPosition // //------------------// /** * Report the vertical position of this clef within the staff * * @return the pitch position */ public int getPitchPosition () { return pitchPosition; } //----------// // getShape // //----------// /** * Report the precise shape of this clef * * @return the clef shape */ public Shape getShape () { return shape; } //----------// // octaveOf // //----------// /** * Report the octave corresponding to a note at the provided pitch position, * assuming we are governed by the provided clef, otherwise (if clef is * null) * we use the default clef (G_CLEF) * * @param clef the current clef if any * @param pitchPosition the pitch position of the note * @return the corresponding octave */ public static int octaveOf (Clef clef, int pitchPosition) { if (clef == null) { return defaultClef.octaveOf(pitchPosition); } else { return clef.octaveOf(pitchPosition); } } //----------// // populate // //----------// /** * Create the relevant Clef entity that translates the provided glyph * * @param glyph the provided glyph * @param measure the containing measure * @param staff the containing staff * @param center the precise location in the system * @return true if Clef was successfully created */ public static boolean populate (Glyph glyph, Measure measure, Staff staff, Point center) { Shape shape = glyph.getShape(); switch (shape) { case G_CLEF: case G_CLEF_SMALL: case G_CLEF_8VA: case G_CLEF_8VB: glyph.setTranslation( new Clef(measure, staff, shape, center, 2, glyph)); return true; case C_CLEF: // Depending on precise clef position, we can have // an Alto C-clef (pp=0) or a Tenor C-clef (pp=+2) [or other stuff] int pp = (int) Math.rint(staff.pitchPositionOf(center)); glyph.setTranslation( new Clef(measure, staff, shape, center, pp, glyph)); return true; case F_CLEF: case F_CLEF_SMALL: case F_CLEF_8VA: case F_CLEF_8VB: glyph.setTranslation( new Clef(measure, staff, shape, center, -2, glyph)); return true; case PERCUSSION_CLEF: glyph.setTranslation( new Clef(measure, staff, shape, center, 0, glyph)); return true; default: measure.addError(glyph, "No implementation yet for " + shape); return false; } } //----------// // toString // //----------// @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append("{Clef"); sb.append(" ") .append(shape); sb.append(" pp=") .append(pitchPosition); sb.append(" ") .append(Glyphs.toString(getGlyphs())); sb.append("}"); return sb.toString(); } //------------// // noteStepOf // //------------// /** * Report the note step corresponding to a note at the provided pitch * position, assuming we are governed by this clef * * @param pitchPosition the pitch position of the note * @return the corresponding note step */ private Note.Step noteStepOf (int pitchPosition) { switch (shape) { case G_CLEF: case G_CLEF_SMALL: case G_CLEF_8VA: case G_CLEF_8VB: return Note.Step.values()[(71 - pitchPosition) % 7]; case C_CLEF: // Depending on precise clef position, we can have // an Alto C-clef (pp=0) or a Tenor C-clef (pp=+2) [or other stuff] return Note.Step.values()[(72 - this.pitchPosition - pitchPosition) % 7]; case F_CLEF: case F_CLEF_SMALL: case F_CLEF_8VA: case F_CLEF_8VB: return Note.Step.values()[(73 - pitchPosition) % 7]; case PERCUSSION_CLEF: return null; default: logger.error("No note step defined for {}", this); return null; // To keep compiler happy } } //----------// // octaveOf // //----------// /** * Report the octave corresponding to a note at the provided pitch position, * assuming we are governed by this clef * * @param pitchPosition the pitch position of the note * @return the corresponding octave */ private int octaveOf (int pitchPosition) { switch (shape) { case G_CLEF: case G_CLEF_SMALL: return (34 - pitchPosition) / 7; case G_CLEF_8VA: return ((34 - pitchPosition) / 7) + 1; case G_CLEF_8VB: return ((34 - pitchPosition) / 7) - 1; case C_CLEF: // Depending on precise clef position, we can have // an Alto C-clef (pp=0) or a Tenor C-clef (pp=+2) [or other stuff] return (28 - this.pitchPosition - pitchPosition) / 7; case F_CLEF: case F_CLEF_SMALL: return (22 - pitchPosition) / 7; case F_CLEF_8VA: return ((22 - pitchPosition) / 7) + 1; case F_CLEF_8VB: return ((22 - pitchPosition) / 7) - 1; case PERCUSSION_CLEF: return 0; default: logger.error("No note octave defined for {}", this); return 0; // To keep compiler happy } } }