//----------------------------------------------------------------------------//
// //
// L y r i c s I t e m //
// //
//----------------------------------------------------------------------------//
// <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.constant.ConstantSet;
import omr.glyph.facets.Glyph;
import omr.glyph.facets.GlyphContent;
import omr.score.visitor.ScoreVisitor;
import omr.sheet.Scale;
import omr.text.TextLine;
import omr.text.TextWord;
import omr.util.TreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Class {@code LyricsItem} is specific subclass of Text, meant for one
* item of a lyrics line (Syllable, Hyphen, Extension, Elision)
*
* @author Hervé Bitteur
*/
public class LyricsItem
extends Text
implements Comparable<LyricsItem>
{
//~ Static fields/initializers ---------------------------------------------
/** Specific application parameters */
private static final Constants constants = new Constants();
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
LyricsItem.class);
/** Comparator based on line number */
public static final Comparator<LyricsItem> numberComparator = new Comparator<LyricsItem>()
{
@Override
public int compare (LyricsItem o1,
LyricsItem o2)
{
return o1.lyricsLine.getId() - o2.lyricsLine.getId();
}
};
//~ Enumerations -----------------------------------------------------------
/**
* Describes the kind of this lyrics item.
*/
public static enum ItemKind
{
//~ Enumeration constant initializers ----------------------------------
/** Just an elision */
Elision,
/** Just an extension */
Extension,
/** A hyphen between syllables */
Hyphen,
/** A real syllable */
Syllable;
}
/**
* Describes more precisely a syllable inside a word.
*/
public static enum SyllabicType
{
//~ Enumeration constant initializers ----------------------------------
/** Single-syllable word */
SINGLE,
/** Syllable that begins a word */
BEGIN,
/** Syllable at the middle of a
* word */
MIDDLE,
/** Syllable that ends a word */
END;
}
//~ Instance fields --------------------------------------------------------
/** Lyrics kind. */
private ItemKind itemKind;
/** Characteristics of the lyrics syllable, if any. */
private SyllabicType syllabicType;
/** The containing lyrics line. */
private LyricsLine lyricsLine;
/**
* The carried text for this item.
* (only a part of the containing sentence, as opposed to other texts)
*/
private String content;
/** The exact corresponding word. */
private TextWord word;
/** Width of the item. */
private final int width;
/** The glyph which contributed to this lyrics item. */
private final Glyph seed;
/** Mapped note/chord, if any. */
private Chord mappedChord;
//~ Constructors -----------------------------------------------------------
//------------//
// LyricsItem //
//------------//
/**
* Creates a new LyricsItem object.
*
* @param sentence The containing sentence
* @param location The starting point (left side, base line) wrt system
* @param seed The glyph that initiated the creation of this lyrics item
* @param width The width of the related item
* @param content The underlying text for this lyrics item
*/
public LyricsItem (TextLine sentence,
Point location,
Glyph seed,
int width,
String content)
{
super(sentence, location);
this.seed = seed;
this.width = width;
this.content = content;
word = seed.getTextWord();
if (content.equals(GlyphContent.ELISION_STRING)) {
itemKind = ItemKind.Elision;
} else if (content.equals(GlyphContent.EXTENSION_STRING)) {
itemKind = ItemKind.Extension;
} else if (content.equals(GlyphContent.HYPHEN_STRING)) {
itemKind = ItemKind.Hyphen;
} else {
itemKind = ItemKind.Syllable;
}
}
//~ Methods ----------------------------------------------------------------
//--------//
// accept //
//--------//
@Override
public boolean accept (ScoreVisitor visitor)
{
return visitor.visit(this);
}
//-----------//
// compareTo //
//-----------//
@Override
public int compareTo (LyricsItem other)
{
if (this == other) {
return 0;
}
// Comparison is based on abscissa only
return Integer.signum(
getReferencePoint().x - other.getReferencePoint().x);
}
//--------------------//
// defineSyllabicType //
//--------------------//
public void defineSyllabicType (LyricsItem prevItem,
LyricsItem nextItem)
{
if ((prevItem != null) && (prevItem.itemKind == ItemKind.Hyphen)) {
if ((nextItem != null) && (nextItem.itemKind == ItemKind.Hyphen)) {
syllabicType = SyllabicType.MIDDLE;
} else {
syllabicType = SyllabicType.END;
}
} else {
if ((nextItem != null) && (nextItem.itemKind == ItemKind.Hyphen)) {
syllabicType = SyllabicType.BEGIN;
} else {
syllabicType = SyllabicType.SINGLE;
}
}
}
//--------//
// getBox //
//--------//
@Override
public Rectangle getBox ()
{
return seed.getBounds();
}
//------------//
// getContent //
//------------//
/**
* Report the current string value of this text (just the lyrics item)
*
* @return the string value of this text
*/
@Override
public String getContent ()
{
return content;
}
//-------------//
// getItemKind //
//-------------//
public ItemKind getItemKind ()
{
return itemKind;
}
//---------------//
// getLyricsLine //
//---------------//
public LyricsLine getLyricsLine ()
{
return lyricsLine;
}
//-----------------//
// getSyllabicType //
//-----------------//
public SyllabicType getSyllabicType ()
{
return syllabicType;
}
//---------------------//
// getTranslationLinks //
//---------------------//
@Override
public List<Line2D> getTranslationLinks (Glyph glyph)
{
if (mappedChord != null) {
return mappedChord.getTranslationLinks(glyph);
} else {
return Collections.emptyList();
}
}
//----------//
// getWidth //
//----------//
@Override
public int getWidth ()
{
return width;
}
//---------//
// getWord //
//---------//
public TextWord getWord ()
{
return word;
}
//-----------//
// mapToNote //
//----------//
public void mapToNote ()
{
// We connect only syllables
if (itemKind != ItemKind.Syllable) {
return;
}
// Left is too far on left, middle is too far on right, we use width/4
int centerX = getReferencePoint().x + (width / 4);
SystemPart part = lyricsLine.getPart();
int maxDx = part.getScale()
.toPixels(constants.maxItemDx);
for (TreeNode mNode : part.getMeasures()) {
Measure measure = (Measure) mNode;
// Select only possible measures
if ((measure.getLeftX() - maxDx) > centerX) {
break;
}
if ((measure.getBarline()
.getRightX() + maxDx) < centerX) {
continue;
}
// Look for best aligned chord in proper staff
int bestDx = Integer.MAX_VALUE;
Chord bestChord = null;
for (Chord chord : measure.getChordsAbove(getReferencePoint())) {
if (chord.getStaff() == lyricsLine.getStaff()) {
int dx = Math.abs(chord.getHeadLocation().x - centerX);
if (bestDx > dx) {
bestDx = dx;
bestChord = chord;
}
}
}
if (bestDx <= maxDx) {
for (TreeNode nNode : bestChord.getNotes()) {
Note note = (Note) nNode;
if (!note.isRest()) {
note.addSyllable(this);
mappedChord = bestChord;
if ((word != null) && (word.getGlyph() != null)) {
word.getGlyph()
.setTranslation(this);
}
}
return;
}
}
}
addError(seed, "Could not find note for " + this);
}
//-------------//
// setItemKind //
//-------------//
public void setItemKind (ItemKind itemKind)
{
this.itemKind = itemKind;
}
//-----------------//
// setSyllabicType //
//-----------------//
public void setSyllabicType (SyllabicType syllabicType)
{
this.syllabicType = syllabicType;
}
//-----------------------//
// computeReferencePoint //
//-----------------------//
@Override
protected void computeReferencePoint ()
{
Rectangle itemBox = seed.getBounds();
setReferencePoint(new Point(itemBox.x, getSentence()
.getLocation().y));
}
//-----------------//
// internalsString //
//-----------------//
@Override
protected String internalsString ()
{
StringBuilder sb = new StringBuilder();
if (itemKind != null) {
sb.append(" ")
.append(itemKind);
}
if (getSyllabicType() != null) {
sb.append(" ")
.append(getSyllabicType());
}
if (mappedChord != null) {
sb.append(" mappedTo:Ch#")
.append(mappedChord.getId());
}
return sb.toString();
}
//--------------//
// setLyricLine //
//--------------//
void setLyricLine (LyricsLine line)
{
this.lyricsLine = line;
}
//~ Inner Classes ----------------------------------------------------------
//-----------//
// Constants //
//-----------//
private static final class Constants
extends ConstantSet
{
//~ Instance fields ----------------------------------------------------
Scale.Fraction maxItemDx = new Scale.Fraction(
4,
"Maximum horizontal distance between a note and its lyrics item");
}
}