//----------------------------------------------------------------------------// // // // S y s t e m T r a n s l a t o 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.glyph.Shape; import static omr.glyph.ShapeSet.*; import omr.glyph.facets.Glyph; import omr.score.entity.Arpeggiate; import omr.score.entity.Articulation; import omr.score.entity.Barline; import omr.score.entity.BeamGroup; import omr.score.entity.BeamItem; import omr.score.entity.Chord; import omr.score.entity.Clef; import omr.score.entity.Coda; import omr.score.entity.DotTranslation; import omr.score.entity.Fermata; import omr.score.entity.KeySignature; import omr.score.entity.Measure; import omr.score.entity.MeasureNode; import omr.score.entity.Note; import omr.score.entity.Ornament; import omr.score.entity.Page; import omr.score.entity.Pedal; import omr.score.entity.ScoreSystem; import omr.score.entity.Segno; import omr.score.entity.Slur; import omr.score.entity.Staff; import omr.score.entity.SystemPart; import omr.score.entity.Text; import omr.score.entity.TimeSignature; import omr.score.entity.Tuplet; import omr.score.entity.Wedge; import omr.sheet.Sheet; import omr.sheet.SystemInfo; import omr.text.TextLine; import omr.util.HorizontalSide; import omr.util.TreeNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Point; import java.awt.Rectangle; import java.util.Collections; import java.util.List; /** * Class {@code SystemTranslator} performs all translation tasks for * a given system, translating glyphs to score entities. */ public class SystemTranslator { //~ Static fields/initializers --------------------------------------------- /** Usual logger utility */ private static final Logger logger = LoggerFactory.getLogger( SystemTranslator.class); //~ Instance fields -------------------------------------------------------- /** The physical system. */ private final SystemInfo systemInfo; /** The logical system. */ private ScoreSystem system; /** The current systempart. */ private SystemPart currentPart; /** The current staff. */ private Staff currentStaff; /** The current point in current system. */ private Point currentCenter; /** The current measure. */ private Measure currentMeasure; //~ Constructors ----------------------------------------------------------- // //------------------// // SystemTranslator // //------------------// /** * Creates a new SystemTranslator object. * * @param systemInfo the dedicated system */ public SystemTranslator (SystemInfo systemInfo) { this.systemInfo = systemInfo; } //~ Methods ---------------------------------------------------------------- // //----------------// // translateFinal // //----------------// /** * Final actions to be done, starting on this first impacted system, * until the very last system in the page. */ public void translateFinal () { system = systemInfo.getScoreSystem(); logger.debug("buildFinal starting from {}", system); final Page page = system.getPage(); final Sheet sheet = page.getSheet(); // Connect parts across systems new PageReduction(page).reduce(); // Get the (sub) list of all systems for final processing List<SystemInfo> systems = sheet.getSystems() .subList( system.getId() - 1, sheet.getSystems().size()); // All actions for completed systems for (SystemInfo info : systems) { ScoreSystem syst = info.getScoreSystem(); syst.fillMissingParts(); syst.connectSystemInitialSlurs(); syst.connectTiedVoices(); syst.refineLyricSyllables(); } } //-----------------// // translateSystem // //-----------------// /** * This is where glyph information is translated to score entity * information. */ public void translateSystem () { // Translations in proper order // Whole score impact //------------------- // Brace translate(new BraceTranslator()); // Clef translate(new ClefTranslator()); // Time signature translate(new TimeTranslator()); // Key translate(new KeyTranslator()); // Measure impact //--------------- // Chord, Note translate(new ChordTranslator()); // Beam (-> chord), BeamItem, BeamGroup translate(new BeamTranslator()); // Flag (-> chord) translate(new FlagTranslator()); // Dots (-> chord) as staccato / augmentation / repeat translate(new DotTranslator()); // Tuplets (-> chords) translate(new TupletTranslator()); // Accidental (-> note) translate(new AccidentalTranslator()); // Slur (-> chord or note) translate(new SlurTranslator()); // Finalize measure slots, ties, voices & durations, barlines translate(new MeasureTranslator()); // Local impact //------------- // Fermata translate(new FermataTranslator()); // Arpeggiate translate(new ArpeggiateTranslator()); // Articulation translate(new ArticulationTranslator()); // Crescendo / decrescendo translate(new WedgeTranslator()); // Pedal on / off translate(new PedalTranslator()); // Segno translate(new SegnoTranslator()); // Coda translate(new CodaTranslator()); // Ornaments translate(new OrnamentTranslator()); // Dynamics translate(new DynamicsTranslator()); // Text (-> Lyrics, Directions, etc...) translate(new TextTranslator()); } //-----------// // translate // //-----------// /** * Drive the translation at system level of certain glyphs as * handled by the provided translator. * * @param translator the specific translator for this task */ private void translate (Translator translator) { system = systemInfo.getScoreSystem(); // Browse the system collection of glyphs translator.translateGlyphs(); // Processing at end of system if any translator.completeSystem(); } //~ Inner Classes ---------------------------------------------------------- //------------// // Translator // //------------// /** * Class {@code Translator} is an abstract class that defines the * pattern for every translation engine. */ private abstract class Translator { //~ Instance fields ---------------------------------------------------- /** Name of this translator (for debugging) */ protected final String name; //~ Constructors ------------------------------------------------------- public Translator (String name) { super(); this.name = name; logger.debug("Creating {}", this); } //~ Methods ------------------------------------------------------------ /** * Check if provided glyph is relevant. * * @param glyph the glyph at hand * @return true if the glyph at hand is relevant for the translator */ public abstract boolean isRelevant (Glyph glyph); /** * Specific browsing of a given measure. * * @param measure the given measure */ public void browse (Measure measure) { } /** * Hook for final processing at end of the system. */ public void completeSystem () { for (TreeNode node : system.getParts()) { SystemPart part = (SystemPart) node; for (TreeNode mn : part.getMeasures()) { Measure measure = (Measure) mn; try { browse(measure); } catch (Exception ex) { logger.warn( measure.getContextString() + " Exception in measure browsing", ex); } } } } /** * Compute the location system environment of the provided glyph. * Results are written in global variables currentXXX. * * @param glyph the glyph to locate */ public void computeLocation (Glyph glyph) { currentCenter = glyph.getLocation(); currentStaff = system.getStaffAt(currentCenter); currentPart = currentStaff.getPart(); currentMeasure = currentPart.getMeasureAt(currentCenter); } @Override public String toString () { return "{Translator " + name + "}"; } /** * Perform the desired translation * * @param glyph the glyph at hand */ public abstract void translate (Glyph glyph); /** * Browsing on every glyph within the system. */ protected void translateGlyphs () { for (Glyph glyph : system.getInfo() .getGlyphs()) { Shape shape = glyph.getShape(); if (glyph.isWellKnown() && (shape != Shape.CLUTTER) && !glyph.isTranslated()) { // Check for glyph relevance if (isRelevant(glyph)) { // Determine part/staff/measure containment computeLocation(glyph); try { // Perform the translation on this glyph translate(glyph); } catch (Exception ex) { logger.warn( "Error translating glyph #" + glyph.getId() + " by " + this, ex); } } } } } } //----------------------// // AccidentalTranslator // //----------------------// private class AccidentalTranslator extends Translator { //~ Constructors ------------------------------------------------------- public AccidentalTranslator () { super("Accidental"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { return Accidentals.contains(glyph.getShape()); } @Override public void translate (Glyph glyph) { Note.populateAccidental(glyph, currentMeasure, currentCenter); } } //----------------------// // ArpeggiateTranslator // //----------------------// private class ArpeggiateTranslator extends Translator { //~ Constructors ------------------------------------------------------- public ArpeggiateTranslator () { super("Arpeggiate"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { return glyph.getShape() == Shape.ARPEGGIATO; } @Override public void translate (Glyph glyph) { Arpeggiate.populate(glyph, currentMeasure, currentCenter); } } //------------------------// // ArticulationTranslator // //------------------------// private class ArticulationTranslator extends Translator { //~ Constructors ------------------------------------------------------- public ArticulationTranslator () { super("Articulation"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); // ARPEGGIATO is processed by ArpeggiateTranslator. // DOT_set-shape staccato is processed by DotTranslation, // while STACCATO-shape staccato is processed here return Articulations.contains(shape) && (shape != Shape.ARPEGGIATO); } @Override public void translate (Glyph glyph) { Articulation.populate(glyph, currentMeasure, currentCenter); } } //----------------// // BeamTranslator // //----------------// private class BeamTranslator extends Translator { //~ Constructors ------------------------------------------------------- public BeamTranslator () { super("Beam"); } //~ Methods ------------------------------------------------------------ @Override public void browse (Measure measure) { // Link beams to chords, and populate beam groups BeamGroup.populate(measure); } @Override public void computeLocation (Glyph glyph) { // Staff, measure and staff point need specific processing // We use the attached stem(s) to determine proper containment Glyph stem = glyph.getFirstStem(); if (stem != null) { super.computeLocation(stem); } else { currentMeasure.addError( glyph, "Beam glyph with no attached stem"); super.computeLocation(glyph); } } @Override public boolean isRelevant (Glyph glyph) { return Beams.contains(glyph.getShape()); } @Override public void translate (Glyph glyph) { BeamItem.populate(glyph, currentMeasure); } } //-----------------// // BraceTranslator // //-----------------// private class BraceTranslator extends Translator { //~ Constructors ------------------------------------------------------- public BraceTranslator () { super("Brace"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); return (shape == Shape.BRACE) || (shape == Shape.BRACKET); } @Override public void translate (Glyph glyph) { currentPart.setBrace(glyph); } } //-----------------// // ChordTranslator // //-----------------// private class ChordTranslator extends Translator { //~ Constructors ------------------------------------------------------- public ChordTranslator () { super("Chord"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); return Rests.contains(shape) || NoteHeads.contains(shape) || Notes.contains(shape); } @Override public void translate (Glyph glyph) { Chord.populate(glyph, currentMeasure); } } //----------------// // ClefTranslator // //----------------// private class ClefTranslator extends Translator { //~ Constructors ------------------------------------------------------- public ClefTranslator () { super("Clef"); } //~ Methods ------------------------------------------------------------ @Override public void browse (Measure measure) { // Sort the clefs according to containing staff Collections.sort(measure.getClefs(), MeasureNode.staffComparator); } @Override public boolean isRelevant (Glyph glyph) { return Clefs.contains(glyph.getShape()); } @Override public void translate (Glyph glyph) { Clef.populate(glyph, currentMeasure, currentStaff, currentCenter); } } //----------------// // CodaTranslator // //----------------// private class CodaTranslator extends Translator { //~ Constructors ------------------------------------------------------- public CodaTranslator () { super("Coda"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); return shape == Shape.CODA; } @Override public void translate (Glyph glyph) { Coda.populate(glyph, currentMeasure, currentCenter); } } //---------------// // DotTranslator // //---------------// private class DotTranslator extends Translator { //~ Constructors ------------------------------------------------------- public DotTranslator () { super("Dot"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { return Dots.contains(glyph.getShape()); } @Override public void translate (Glyph glyph) { DotTranslation.populateDot(glyph, currentMeasure, currentCenter); } } //--------------------// // DynamicsTranslator // //--------------------// private class DynamicsTranslator extends Translator { //~ Constructors ------------------------------------------------------- public DynamicsTranslator () { super("Dynamics"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); return Dynamics.contains(shape) && (shape != Shape.CRESCENDO) && (shape != Shape.DECRESCENDO); } @Override public void translate (Glyph glyph) { omr.score.entity.Dynamics.populate( glyph, currentMeasure, currentCenter); } } //-------------------// // FermataTranslator // //-------------------// private class FermataTranslator extends Translator { //~ Constructors ------------------------------------------------------- public FermataTranslator () { super("Fermata"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { return (glyph.getShape() == Shape.FERMATA) || (glyph.getShape() == Shape.FERMATA_BELOW); } @Override public void translate (Glyph glyph) { Fermata.populate(glyph, currentMeasure, currentCenter); } } //----------------// // FlagTranslator // //----------------// private class FlagTranslator extends Translator { //~ Constructors ------------------------------------------------------- public FlagTranslator () { super("Flag"); } //~ Methods ------------------------------------------------------------ @Override public void browse (Measure measure) { if (logger.isDebugEnabled()) { // Print flag/beam value of each chord logger.debug("Flag/Beams for {}", measure.getContextString()); for (TreeNode node : measure.getChords()) { Chord chord = (Chord) node; logger.debug(chord.toString()); if (!chord.getBeams() .isEmpty()) { logger.debug(" Beams:{}", chord.getBeams().size()); } if (chord.getFlagsNumber() > 0) { logger.debug(" Flags:{}", chord.getFlagsNumber()); } // Just to be sure if ((chord.getBeams() .size() * chord.getFlagsNumber()) != 0) { chord.addError("Inconsistent Flag/Beam configuration"); } } } } @Override public void computeLocation (Glyph glyph) { // We use the attached stem(s) to determine proper containment Glyph stem = glyph.getStem(HorizontalSide.LEFT); if (stem != null) { super.computeLocation(stem); } else { system.addError( glyph, "Flag glyph " + glyph.getId() + " with no attached stem"); super.computeLocation(glyph); } } @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); return Flags.contains(shape); } @Override public void translate (Glyph glyph) { Chord.populateFlag(glyph, currentMeasure); } } //---------------// // KeyTranslator // //---------------// private class KeyTranslator extends Translator { //~ Constructors ------------------------------------------------------- public KeyTranslator () { super("Key"); } //~ Methods ------------------------------------------------------------ @Override public void completeSystem () { try { new KeySignatureVerifier(system).verifyKeys(); } catch (Exception ex) { logger.warn("Error verifying keys for " + system, ex); } } @Override public boolean isRelevant (Glyph glyph) { return (glyph.getShape().isSharpBased()) || (glyph.getShape().isFlatBased()); } @Override public void translate (Glyph glyph) { // Key signature or just accidental ? KeySignature.populate( glyph, currentMeasure, currentStaff, currentCenter); } } //-------------------// // MeasureTranslator // //-------------------// private class MeasureTranslator extends Translator { //~ Instance fields ---------------------------------------------------- SlotBuilder slotBuilder = new SlotBuilder(system); //~ Constructors ------------------------------------------------------- public MeasureTranslator () { super("Measure"); } //~ Methods ------------------------------------------------------------ @Override public void browse (Measure measure) { // Check that a chord is not tied to different chords measure.checkTiedChords(); // Build slots and voices slotBuilder.buildSlots(measure); // Make sure all barline glyphs point to it Barline barline = measure.getBarline(); if (barline != null) { barline.translateGlyphs(); } } @Override public void completeSystem () { super.completeSystem(); // Make sure all starting barline glyphs point to it for (TreeNode pn : system.getParts()) { SystemPart part = (SystemPart) pn; Barline barline = part.getStartingBarline(); if (barline != null) { barline.translateGlyphs(); } } } @Override public boolean isRelevant (Glyph glyph) { return false; } @Override public void translate (Glyph glyph) { } @Override protected void translateGlyphs () { } } //--------------------// // OrnamentTranslator // //--------------------// private class OrnamentTranslator extends Translator { //~ Constructors ------------------------------------------------------- public OrnamentTranslator () { super("Ornament"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { final Shape shape = glyph.getShape(); return (shape == Shape.TR) || (shape == Shape.TURN) || (shape == Shape.MORDENT) || (shape == Shape.INVERTED_MORDENT); } @Override public void translate (Glyph glyph) { Ornament.populate(glyph, currentMeasure, currentCenter); } } //-----------------// // PedalTranslator // //-----------------// private class PedalTranslator extends Translator { //~ Constructors ------------------------------------------------------- public PedalTranslator () { super("Pedal"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); return (shape == Shape.PEDAL_MARK) || (shape == Shape.PEDAL_UP_MARK); } @Override public void translate (Glyph glyph) { Pedal.populate(glyph, currentMeasure, currentCenter); } } //-----------------// // SegnoTranslator // //-----------------// private class SegnoTranslator extends Translator { //~ Constructors ------------------------------------------------------- public SegnoTranslator () { super("Segno"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); return shape == Shape.SEGNO; } @Override public void translate (Glyph glyph) { Segno.populate(glyph, currentMeasure, currentCenter); } } //----------------// // SlurTranslator // //----------------// private class SlurTranslator extends Translator { //~ Constructors ------------------------------------------------------- public SlurTranslator () { super("Slur"); } //~ Methods ------------------------------------------------------------ @Override public void computeLocation (Glyph glyph) { } @Override public boolean isRelevant (Glyph glyph) { return glyph.getShape() == Shape.SLUR; } @Override public void translate (Glyph glyph) { Slur.populate(glyph, system); } } //----------------// // TextTranslator // //----------------// private class TextTranslator extends Translator { //~ Instance fields ---------------------------------------------------- Rectangle systemBox; //~ Constructors ------------------------------------------------------- public TextTranslator () { super("Text"); } //~ Methods ------------------------------------------------------------ @Override public void completeSystem () { for (TextLine sentence : system.getInfo() .getSentences()) { Text.populate(sentence, sentence.getLocation()); } for (TreeNode node : system.getParts()) { SystemPart part = (SystemPart) node; part.populateLyricsLines(); part.mapSyllables(); } } @Override public boolean isRelevant (Glyph glyph) { return false; // Text is not handled at glyph but system level } @Override public void translate (Glyph glyph) { // Void } } //----------------// // TimeTranslator // //----------------// private class TimeTranslator extends Translator { //~ Constructors ------------------------------------------------------- public TimeTranslator () { super("Time"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { return Times.contains(glyph.getShape()); } @Override public void translate (Glyph glyph) { TimeSignature.populate( glyph, currentMeasure, currentStaff, currentCenter); } } //-------------------// // TupletTranslator // //-------------------// private class TupletTranslator extends Translator { //~ Constructors ------------------------------------------------------- public TupletTranslator () { super("Tuplet"); } //~ Methods ------------------------------------------------------------ @Override public boolean isRelevant (Glyph glyph) { return Tuplets.contains(glyph.getShape()); } @Override public void translate (Glyph glyph) { Tuplet.populate(glyph, currentMeasure, currentCenter); } } //-----------------// // WedgeTranslator // //-----------------// private class WedgeTranslator extends Translator { //~ Constructors ------------------------------------------------------- public WedgeTranslator () { super("Wedge"); } //~ Methods ------------------------------------------------------------ @Override public void computeLocation (Glyph glyph) { // Take the left edge for glyph center Rectangle box = glyph.getBounds(); currentCenter = new Point(box.x, box.y + (box.height / 2)); currentStaff = system.getStaffAt(currentCenter); currentPart = currentStaff.getPart(); currentMeasure = currentPart.getMeasureAt(currentCenter); } @Override public boolean isRelevant (Glyph glyph) { Shape shape = glyph.getShape(); return (shape == Shape.CRESCENDO) || (shape == Shape.DECRESCENDO); } @Override public void translate (Glyph glyph) { Wedge.populate(glyph, currentMeasure, currentCenter); } } }