//----------------------------------------------------------------------------//
// //
// M e a s u r e //
// //
//----------------------------------------------------------------------------//
// <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.facets.Glyph;
import omr.math.Rational;
import omr.score.entity.TimeSignature.InvalidTimeSignature;
import omr.score.visitor.ScoreVisitor;
import omr.util.TreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* Class {@code Measure} handles a measure of a system part, that is
* all entities within the same measure time frame, for all staves
* that compose the system part.
*
* <p>As a ScoreNode, the children of a Measure are : ending Barline, list of
* TimeSignature(s), list of Clef(s), list of KeySignature(s), list of Chord(s)
* and list of Beam(s).</p>
*
* <p>Measure Ids are stored with respect to their containing page only, they
* are page-based ids. Displayed to the user are score-based ids.</p>
*
* @author Hervé Bitteur
*/
public class Measure
extends MeasureNode
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(Measure.class);
//~ Instance fields --------------------------------------------------------
/** To flag dummy barline instances */
private boolean dummy;
/** To flag a temporary measure (for playback) */
private boolean temporary;
/** Flag for implicit (pickup or repeat last half) measure */
private boolean implicit;
/** Flag for firstHalf measure */
private boolean firstHalf;
/** Child: Left-inside bar line, if any */
private Barline insideBarline;
/** Child: Ending bar line, if any */
private Barline barline;
/** Child: Potential one time signature per staff */
private Container timesigs;
/** Children: possibly several clefs per staff */
private Container clefs;
/** Children: possibly several KeySignature's per staff */
private Container keysigs;
/** Children: possibly several Chord's per staff */
private Container chords;
/** Children: possibly several Beam's per staff */
private Container beams;
/** Measure Id */
private MeasureId.PageBased id;
/** Identified time slots within the measure */
private List<Slot> slots;
/**
* Chords of just whole rest (thus handled outside slots)
* These chords are also contained in 'chords' container, like plain ones
*/
private List<Chord> wholeChords;
/** Groups of beams in this measure */
private List<BeamGroup> beamGroups;
/** Theoretical measure duration, based on current time signature */
private Rational expectedDuration;
/** Actual measure duration, based on durations of contained chords */
private Rational actualDuration;
/** Flag to indicate a excess duration */
private Rational excess;
/** Voices within this measure, sorted by increasing voice id */
private List<Voice> voices;
//~ Constructors -----------------------------------------------------------
//---------//
// Measure //
//---------//
/**
* Create a measure with the specified parameters.
*
* @param part the containing system part
*/
public Measure (SystemPart part)
{
super(part);
cleanupNode();
}
//~ Methods ----------------------------------------------------------------
//--------//
// accept //
//--------//
@Override
public boolean accept (ScoreVisitor visitor)
{
return visitor.visit(this);
}
//----------//
// addChild //
//----------//
/**
* Override normal behavior, so that a given child is stored in its
* proper type collection (clef to clef list, etc...).
*
* @param node the child to insert into the measure
*/
@Override
public void addChild (TreeNode node)
{
// Special children lists
if (node instanceof Clef) {
clefs.addChild(node);
} else if (node instanceof TimeSignature) {
timesigs.addChild(node);
} else if (node instanceof KeySignature) {
keysigs.addChild(node);
} else if (node instanceof Beam) {
beams.addChild(node);
} else if (node instanceof Chord) {
chords.addChild(node);
} else {
super.addChild(node);
}
// Side effect for barline
if (node instanceof Barline) {
barline = (Barline) node; // Ending barline
}
}
//----------//
// addGroup //
//----------//
/**
* Add a beam goup to this measure.
*
* @param group a beam group to add
*/
public void addGroup (BeamGroup group)
{
beamGroups.add(group);
}
//----------//
// addVoice //
//----------//
public void addVoice (Voice voice)
{
voices.add(voice);
}
// //-------------//
// // buildVoices //
// //-------------//
// /**
// * Browse the slots and chords, in order to compute the various
// * voices and start times.
// */
// public void buildVoices ()
// {
// // Debug
// if (logger.isDebugEnabled()) {
// printChords("Initial chords for ");
// }
//
// // The 'activeChords' collection gathers the chords that are "active"
// // (not terminated) at the time slot being considered. Initially, it
// // contains just the whole chords.
// List<Chord> activeChords = new ArrayList<>(getWholeChords());
// Collections.sort(activeChords, Chord.byAbscissa);
//
// // Create voices for whole chords
// for (Chord chord : activeChords) {
// chord.setStartTime(Rational.ZERO);
// Voice.createWholeVoice(chord);
// }
//
// // Process slot after slot, if any
// try {
// for (Slot slot : getSlots()) {
// slot.buildVoices(activeChords);
// }
// } catch (Exception ex) {
// logger.warn(
// "Error building voices in measure " + getPageId(),
// ex);
// }
//
// // Debug
// if (logger.isDebugEnabled()) {
// printVoices("Final voices for ");
// }
// }
//---------------//
// checkDuration //
//---------------//
/**
* Check the duration as computed in this measure from its
* contained voices, compared to its theoretical duration.
*/
public void checkDuration ()
{
// Check duration of each voice
for (Voice voice : voices) {
voice.checkDuration();
}
}
//-----------------//
// checkTiedChords //
//-----------------//
/**
* Check ties for all chords of this measure.
*/
public void checkTiedChords ()
{
// Use a copy of chords collection, to avoid concurrent modifications
for (TreeNode cn : chords.getChildrenCopy()) {
Chord chord = (Chord) cn;
chord.checkTies();
}
}
//-------------//
// cleanupNode //
//-------------//
/**
* Get rid of all nodes of this measure, except the barlines.
*/
public void cleanupNode ()
{
// Remove all direct children except barlines
for (Iterator<TreeNode> it = children.iterator(); it.hasNext();) {
VisitableNode node = (VisitableNode) it.next();
if (!(node instanceof Barline)) {
it.remove();
}
}
// Invalidate data
expectedDuration = null;
excess = null;
implicit = false;
setFirstHalf(false);
// (Re)Allocate specific children lists
clefs = new Container(this, "Clefs");
keysigs = new Container(this, "KeySigs");
timesigs = new Container(this, "TimeSigs");
chords = new Container(this, "Chords");
beams = new Container(this, "Beams");
// dynamics = new DynamicList(this);
// lyriclines = new LyricList(this);
// texts = new TextList(this);
// Should this be a MeasureNode ??? TODO
slots = new ArrayList<>();
beamGroups = new ArrayList<>();
wholeChords = new ArrayList<>();
voices = new ArrayList<>();
}
//-----------------------//
// createTemporaryBefore //
//-----------------------//
/**
* Create a temporary initial measure to be exported right before
* this measure, just to set up global parameters (clef, time, key).
*
* @return the created dummy measure
*/
public Measure createTemporaryBefore ()
{
Measure dummyMeasure = new Measure(null);
dummyMeasure.setTemporary(true);
dummyMeasure.setDummy(true);
// Populate the dummy measure, staff per staff
SystemPart part = this.getPart();
for (TreeNode sn : part.getStaves()) {
Staff staff = (Staff) sn;
int right = getLeftX(); // Right of dummy = Left of current
int midY = (staff.getTopLeft().y + (staff.getHeight() / 2))
- getSystem().getTopLeft().y;
Point staffPoint = new Point(right, midY);
// Clef?
Clef clef = getClefBefore(staffPoint, staff);
if (clef != null) {
new Clef(
dummyMeasure,
staff,
clef.getShape(),
new Point(right - 40, midY),
clef.getPitchPosition(),
null); // No glyph
}
// Key?
KeySignature key = getKeyBefore(staffPoint, staff);
if (key != null) {
key.createDummyCopy(
dummyMeasure,
new Point(right - 30, midY));
}
// Time?
TimeSignature time = getCurrentTimeSignature();
if (time != null) {
time.createDummyCopy(
dummyMeasure,
new Point(right - 20, midY));
}
}
return dummyMeasure;
}
//-------------------//
// getActualDuration //
//-------------------//
/**
* Report the duration of this measure, as computed from its
* contained voices.
*
* @return the (actual) measure duration, or 0 if no rest / note exist in
* this measure
*/
public Rational getActualDuration ()
{
if (actualDuration != null) {
return actualDuration;
} else {
///logger.warn(getContextString() + " no actual duration");
return Rational.ZERO;
}
}
//------------//
// getBarline //
//------------//
/**
* Report the ending bar line.
*
* @return the ending bar line
*/
public Barline getBarline ()
{
return barline;
}
//---------------//
// getBeamGroups //
//---------------//
/**
* Report the collection of beam groups.
*
* @return the list of beam groups
*/
public List<BeamGroup> getBeamGroups ()
{
return beamGroups;
}
//----------//
// getBeams //
//----------//
/**
* Report the collection of beams.
*
* @return the list of beams
*/
public List<TreeNode> getBeams ()
{
return beams.getChildren();
}
//-----------//
// getChords //
//-----------//
/**
* Report the collection of chords.
*
* @return the list of chords
*/
public List<TreeNode> getChords ()
{
return chords.getChildren();
}
//----------------//
// getChordsAbove //
//----------------//
/**
* Report the collection of chords whose head is located in the
* staff above the provided point.
*
* @param point the provided point
* @return the (perhaps empty) collection of chords
*/
public Collection<Chord> getChordsAbove (Point point)
{
Staff desiredStaff = getSystem().getStaffAbove(point);
Collection<Chord> found = new ArrayList<>();
for (TreeNode node : getChords()) {
Chord chord = (Chord) node;
if (chord.getStaff() == desiredStaff) {
Point head = chord.getHeadLocation();
if (head != null && head.y < point.y) {
found.add(chord);
}
}
}
return found;
}
//----------------//
// getChordsBelow //
//----------------//
/**
* Report the collection of chords whose head is located in the
* staff below the provided point.
*
* @param point the provided point
* @return the (perhaps empty) collection of chords
*/
public Collection<Chord> getChordsBelow (Point point)
{
Staff desiredStaff = getSystem().getStaffBelow(point);
Collection<Chord> found = new ArrayList<>();
for (TreeNode node : getChords()) {
Chord chord = (Chord) node;
if (chord.getStaff() == desiredStaff) {
Point head = chord.getHeadLocation();
if (head != null && head.y > point.y) {
found.add(chord);
}
}
}
return found;
}
//--------------//
// getClefAfter //
//--------------//
/**
* Report the first clef, if any, defined after this measure point
* (looking in end of the measure, then in next measures, then in
* next systems) while staying in the same logical staff.
*
* @param point the point after which to look
* @return the first clef defined, or null
*/
public Clef getClefAfter (Point point)
{
// Which staff we are in
Clef clef;
int staffId = getPart().getStaffAt(point).getId();
// Look in this measure, with same staff, going forward
for (TreeNode cn : getClefs()) {
clef = (Clef) cn;
if ((clef.getStaff().getId() == staffId)
&& (clef.getCenter().x >= point.x)) {
return clef;
}
}
// Look in all following measures, with the same staff id
Measure measure = this;
while ((measure = measure.getFollowing()) != null) {
clef = measure.getFirstMeasureClef(staffId);
if (clef != null) {
return clef;
}
}
return null; // No clef later defined
}
//---------------//
// getClefBefore //
//---------------//
/**
* Same functionally than the other method, but with a staff
* provided.
*
* @param point the point before which to look
* @param staff the containing staff (if null, it is derived from center.y)
* @return the latest clef defined, or null
*/
public Clef getClefBefore (Point point,
Staff staff)
{
// First, look in this measure, with same staff, going backwards
Clef clef = getMeasureClefBefore(point, staff);
if (clef != null) {
return clef;
}
// Which staff we are in
int staffId = getStaffId(point, staff);
// Look in all preceding measures, with the same staff id
Measure measure = this;
while ((measure = measure.getPrecedingInPage()) != null) {
clef = measure.getLastMeasureClef(staffId);
if (clef != null) {
return clef;
}
}
return null; // No clef previously defined
}
//-------------//
// getClefList //
//-------------//
/**
* Report the node that collect the clefs.
*
* @return the clef list node
*/
public Container getClefList ()
{
return clefs;
}
//----------//
// getClefs //
//----------//
/**
* Report the collection of clefs.
*
* @return the list of clefs
*/
public List<TreeNode> getClefs ()
{
return clefs.getChildren();
}
//-----------------//
// getClosestChord //
//-----------------//
/**
* From a provided Chord collection, report the chord which has the
* closest abscissa to a provided point.
*
* @param chords the collection of chords to browse
* @param point the reference point
* @return the abscissa-wise closest chord
*/
public Chord getClosestChord (Collection<Chord> chords,
Point point)
{
Chord bestChord = null;
int bestDx = Integer.MAX_VALUE;
for (Chord chord : chords) {
int dx = Math.abs(chord.getHeadLocation().x - point.x);
if (dx < bestDx) {
bestDx = dx;
bestChord = chord;
}
}
return bestChord;
}
//-----------------//
// getClosestChord //
//-----------------//
/**
* Report the chord of this measure which has the closest
* abscissa to a provided point.
*
* @param point the reference point
* @return the abscissa-wise closest chord, perhaps null
*/
public Chord getClosestChord (Point point)
{
Chord bestChord = null;
int bestDx = Integer.MAX_VALUE;
for (TreeNode node : getChords()) {
Chord chord = (Chord) node;
int dx = Math.abs(chord.getHeadLocation().x - point.x);
if (dx < bestDx) {
bestDx = dx;
bestChord = chord;
}
}
return bestChord;
}
//----------------------//
// getClosestChordAbove //
//----------------------//
/**
* Report the chord above the provided point which has the closest
* abscissa to the provided point.
*
* @param point the reference point
* @return the abscissa-wise closest chord among the chords above, if any.
*/
public Chord getClosestChordAbove (Point point)
{
return getClosestChord(getChordsAbove(point), point);
}
//----------------------//
// getClosestChordBelow //
//----------------------//
/**
* Report the chord below the provided point which has the closest
* abscissa to the provided point.
*
* @param point the reference point
* @return the abscissa-wise closest chord among the chords below, if any.
*/
public Chord getClosestChordBelow (Point point)
{
return getClosestChord(getChordsBelow(point), point);
}
//----------------//
// getClosestSlot //
//----------------//
/**
* Report the time slot which has the closest abscissa to a
* provided point.
*
* @param point the reference point
* @return the abscissa-wise closest slot
*/
public Slot getClosestSlot (Point point)
{
Slot bestSlot = null;
int bestDx = Integer.MAX_VALUE;
for (Slot slot : getSlots()) {
int dx = Math.abs(slot.getX() - point.x);
if (dx < bestDx) {
bestDx = dx;
bestSlot = slot;
}
}
return bestSlot;
}
//---------------------------//
// getClosestWholeChordAbove //
//---------------------------//
/**
* Report the whole chord above the provided point which has the
* closest abscissa to the provided point.
*
* @param point the reference point
* @return the abscissa-wise closest chord among the chords below, if any.
*/
public Chord getClosestWholeChordAbove (Point point)
{
return getClosestChord(getWholeChordsAbove(point), point);
}
//---------------------------//
// getClosestWholeChordBelow //
//---------------------------//
/**
* Report the whole chord below the provided point which has the
* closest abscissa to the provided point.
*
* @param point the reference point
* @return the abscissa-wise closest chord among the chords below, if any.
*/
public Chord getClosestWholeChordBelow (Point point)
{
return getClosestChord(getWholeChordsBelow(point), point);
}
//-------------------------//
// getCurrentTimeSignature //
//-------------------------//
/**
* Report the time signature which applies in this measure,
* whether a time signature actually starts this measure in
* whatever staff, or whether a time signature was found in a
* previous measure, even in preceding pages.
* <p><b>NOTA</b>This method looks up for time sig in preceding pages
* as well</p>
*
* @return the current time signature, or null if not found at all
*/
public TimeSignature getCurrentTimeSignature ()
{
// Backward from this measure to the beginning of the score
Measure measure = this;
Page page = getPage();
while (measure != null) {
// Check in the measure
TimeSignature ts = measure.getTimeSignature();
if (ts != null) {
return ts;
}
// Move to preceding measure (same part)
measure = measure.getPrecedingInPage();
if (measure == null) {
page = page.getPrecedingInScore();
if (page == null) {
return null;
} else {
measure = page.getLastSystem().getLastPart().
getLastMeasure();
}
}
}
return null; // Not found !!!
}
//-------------------//
// getDirectionChord //
//-------------------//
/**
* Retrieve the most suitable chord for a direction.
*
* @param point the system-based location
* @return the most suitable chord, or null
*/
public Chord getDirectionChord (Point point)
{
// Priority on slot
Chord chord = getEventChord(point);
// Then on staff
if (chord == null) {
chord = getClosestChordAbove(point);
}
if (chord == null) {
chord = getClosestChordBelow(point);
}
return chord;
}
//---------------//
// getEventChord //
//---------------//
/**
* Retrieve the most suitable chord to connect the event point to.
*
* @param point the system-based location
* @return the most suitable chord, or null
*/
public Chord getEventChord (Point point)
{
// Choose the x-closest slot
Slot slot = getClosestSlot(point);
if (slot != null) {
// First, try staff just above
Chord chordAbove = slot.getChordJustAbove(point);
if (chordAbove != null) {
return chordAbove;
}
// Second, try staff just below
Chord chordBelow = slot.getChordJustBelow(point);
if (chordBelow != null) {
return chordBelow;
}
}
return null;
}
//-----------//
// getExcess //
//-----------//
/**
* Report the excess duration of this measure, if any.
*
* @return the duration in excess, or null
*/
public Rational getExcess ()
{
return excess;
}
//---------------------//
// getExpectedDuration //
//---------------------//
/**
* Report the theoretical duration of this measure, based on
* current time signature.
*
* @return the expected measure duration
* @throws InvalidTimeSignature
*/
public Rational getExpectedDuration ()
throws InvalidTimeSignature
{
try {
if (expectedDuration == null) {
int numerator;
int denominator;
TimeSignature ts = getCurrentTimeSignature();
if (ts != null) {
numerator = ts.getNumerator();
denominator = ts.getDenominator();
} else {
numerator = 4;
denominator = 4;
}
expectedDuration = new Rational(numerator, denominator);
}
return expectedDuration;
} catch (NullPointerException npe) {
throw new InvalidTimeSignature();
}
}
//---------------------//
// getFirstMeasureClef //
//---------------------//
/**
* Report the first clef (if any) in this measure, tagged with
* the provided staff.
*
* @param staffId the imposed related staff id
* @return the first clef, or null
*/
public Clef getFirstMeasureClef (int staffId)
{
// Going forward
for (TreeNode cn : getClefs()) {
Clef clef = (Clef) cn;
if (clef.getStaff().getId() == staffId) {
return clef;
}
}
return null;
}
//--------------------//
// getFirstMeasureKey //
//--------------------//
/**
* Report the first key signature (if any) in this measure,
* tagged with the provided staff.
*
* @param staffId the imposed related staff id
* @return the first key signature, or null
*/
public KeySignature getFirstMeasureKey (int staffId)
{
for (TreeNode kn : getKeySignatures()) {
KeySignature key = (KeySignature) kn;
if (key.getStaff().getId() == staffId) {
return key;
}
}
return null;
}
//--------------//
// getFollowing //
//--------------//
/**
* Report the following measure of this one, either in this
* system / part, or in the following system /part.
*
* @return the following measure, or null if none
*/
public Measure getFollowing ()
{
Measure nextMeasure = (Measure) getNextSibling();
if (nextMeasure != null) {
return nextMeasure;
}
SystemPart followingPart = getPart().getFollowing();
if (followingPart != null) {
return followingPart.getFirstMeasure();
} else {
return null;
}
}
//------------//
// getIdValue //
//------------//
/**
* Report the numeric value of the measure id.
*
* @return the numeric value of measure id
*/
public int getIdValue ()
{
return (id == null) ? 0 : id.value;
}
//------------------//
// getInsideBarline //
//------------------//
/**
* @return the insideBarline, if any.
*/
public Barline getInsideBarline ()
{
return insideBarline;
}
//--------------//
// getKeyBefore //
//--------------//
/**
* Report the key signature which applies in this measure, whether
* a key signature actually starts this measure in the same staff,
* or whether a key signature was found in a previous measure,
* for the same staff.
*
* @param point the point before which to look
* @param staff the containing staff (cannot be null)
* @return the current key signature, or null if not found
*/
public KeySignature getKeyBefore (Point point,
Staff staff)
{
if (point == null) {
throw new NullPointerException();
}
KeySignature ks;
int staffId = staff.getId();
// Look in this measure, with same staff, going backwards
for (int ik = getKeySignatures().size() - 1; ik >= 0; ik--) {
ks = (KeySignature) getKeySignatures().get(ik);
if ((ks.getStaff().getId() == staffId)
&& (ks.getCenter().x < point.x)) {
return ks;
}
}
// Look in previous measures in the system part and the preceding ones
Measure measure = this;
while ((measure = measure.getPrecedingInPage()) != null) {
ks = measure.getLastMeasureKey(staffId);
if (ks != null) {
return ks;
}
}
return null; // Not found (in this page)
}
//---------------//
// getKeySigList //
//---------------//
/**
* Report the list that collects the KeySignature instances.
*
* @return the single instance of KeySigList
*/
public Container getKeySigList ()
{
return keysigs;
}
//------------------//
// getKeySignatures //
//------------------//
/**
* Report the collection of KeySignature's.
*
* @return the list of KeySignature's
*/
public List<TreeNode> getKeySignatures ()
{
return keysigs.getChildren();
}
//--------------------//
// getLastMeasureClef //
//--------------------//
/**
* Report the last clef (if any) in this measure, tagged with the
* provided staff.
*
* @param staffId the imposed related staff id
* @return the last clef, or null
*/
public Clef getLastMeasureClef (int staffId)
{
// Going backwards
for (int ic = getClefs().size() - 1; ic >= 0; ic--) {
Clef clef = (Clef) getClefs().get(ic);
if (clef.getStaff().getId() == staffId) {
return clef;
}
}
return null;
}
//-------------------//
// getLastMeasureKey //
//-------------------//
/**
* Report the last key signature (if any) in this measure,
* tagged with the provided staff.
*
* @param staffId the imposed related staff id
* @return the last key signature, or null
*/
public KeySignature getLastMeasureKey (int staffId)
{
// Going backwards
for (int ik = getKeySignatures().size() - 1; ik >= 0; ik--) {
KeySignature key = (KeySignature) getKeySignatures().get(ik);
if (key.getStaff().getId() == staffId) {
return key;
}
}
return null;
}
//------------------//
// getLastSoundTime //
//------------------//
/**
* Report the time, counted from beginning of this measure, when
* sound stops which means that ending rests are not counted.
*
* @return the relative time of last Midi "note off" in this measure
*/
public Rational getLastSoundTime ()
{
Rational lastTime = Rational.ZERO;
for (TreeNode chordNode : getChords()) {
Chord chord = (Chord) chordNode;
if (!chord.isAllRests()) {
Rational time = chord.getStartTime().plus(chord.getDuration());
if (time.compareTo(lastTime) > 0) {
lastTime = time;
}
}
}
return lastTime;
}
//----------//
// getLeftX //
//----------//
/**
* Report the abscissa of the start of the measure.
*
* @return abscissa of left side of the measure
*/
public Integer getLeftX ()
{
return getBox().x;
}
//----------------------//
// getMeasureClefBefore //
//----------------------//
/**
* Report the current clef, if any, defined within this measure
* and staff, and located before this measure point.
*
* @param point the point before which to look
* @param staff the containing staff (if null, it is derived from point.y)
* @return the measure clef defined, or null
*/
public Clef getMeasureClefBefore (Point point,
Staff staff)
{
Clef clef = null;
// Which staff we are in
int staffId = getStaffId(point, staff);
// Look in this measure, with same staff, going backwards
for (int ic = getClefs().size() - 1; ic >= 0; ic--) {
clef = (Clef) getClefs().get(ic);
if ((clef.getStaff().getId() == staffId)
&& (clef.getCenter().x <= point.x)) {
return clef;
}
}
return null; // No clef previously defined
}
//-----------//
// getPageId //
//-----------//
/**
* Report the page-based measure id.
*
* @return the page-based measure id
*/
public MeasureId.PageBased getPageId ()
{
return id;
}
//--------------------//
// getPrecedingInPage //
//--------------------//
/**
* Report the preceding measure of this one, either in this
* system / part, or in the preceding system /part, but still
* in the same page.
*
* @return the preceding measure, or null if not found in the page
*/
public Measure getPrecedingInPage ()
{
Measure prevMeasure = (Measure) getPreviousSibling();
if (prevMeasure != null) {
return prevMeasure;
}
SystemPart precedingPart = getPart().getPrecedingInPage();
if (precedingPart != null) {
return precedingPart.getLastMeasure();
} else {
return null;
}
}
//-----------//
// getRightX //
//-----------//
/**
* Report the abscissa of the end of the measure.
*
* @return part-based abscissa of right side of the measure
*/
public Integer getRightX ()
{
return getBox().x + getBox().width;
}
//------------//
// getScoreId //
//------------//
/**
* Report the image of the score-based measure id.
*
* @return the score-based measure id string
*/
public String getScoreId ()
{
MeasureId.PageBased mid = id;
if (mid != null) {
return mid.toScoreString();
} else {
return null;
}
}
//----------//
// getSlots //
//----------//
/**
* Report the ordered collection of slots.
*
* @return the collection of slots
*/
public List<Slot> getSlots ()
{
return slots;
}
//------------//
// getStaffId //
//------------//
/**
* Report the id of the staff containing the provided point.
*
* @param point the provided point
* @param staff the staff if known, otherwise null
* @return the staff id
*/
public int getStaffId (Point point,
Staff staff)
{
return (staff != null) ? staff.getId()
: getPart().getStaffAt(point).getId();
}
//----------------//
// getTimeSigList //
//----------------//
/**
* Report the node that collects the TimeSignature instances.
*
* @return the node of TimeSignature instances
*/
public Container getTimeSigList ()
{
return timesigs;
}
//------------------//
// getTimeSignature //
//------------------//
/**
* Report the potential time signature in this measure for the
* related staff.
*
* @param staff the related staff
* @return the time signature, or null if not found
*/
public TimeSignature getTimeSignature (Staff staff)
{
for (TreeNode node : timesigs.getChildren()) {
TimeSignature ts = (TimeSignature) node;
if (ts.getStaff() == staff) {
return ts;
}
}
return null; // Not found
}
//------------------//
// getTimeSignature //
//------------------//
/**
* Report the potential time signature in this measure
* (whatever the staff).
*
* @return the time signature, or null if not found
*/
public TimeSignature getTimeSignature ()
{
for (TreeNode node : timesigs.getChildren()) {
return (TimeSignature) node;
}
return null; // Not found
}
//-----------//
// getVoices //
//-----------//
public List<Voice> getVoices ()
{
return voices;
}
//-----------------//
// getVoicesNumber //
//-----------------//
/**
* Report the number of voices in this measure.
*
* @return the number fo voices computed
*/
public int getVoicesNumber ()
{
return voices.size();
}
//----------------//
// getWholeChords //
//----------------//
/**
* Report the collection of whole chords.
*
* @return the whole chords of this measure
*/
public Collection<Chord> getWholeChords ()
{
return wholeChords;
}
//---------------------//
// getWholeChordsAbove //
//---------------------//
/**
* Report the collection of whole chords located in the
* staff above the provided point.
*
* @param point the provided point
* @return the (perhaps empty) collection of chords
*/
public Collection<Chord> getWholeChordsAbove (Point point)
{
Staff desiredStaff = getSystem().getStaffAbove(point);
Collection<Chord> found = new ArrayList<>();
for (Chord chord : getWholeChords()) {
if ((chord.getStaff() == desiredStaff)
&& (chord.getHeadLocation().y < point.y)) {
found.add(chord);
}
}
return found;
}
//---------------------//
// getWholeChordsBelow //
//---------------------//
/**
* Report the collection of whole chords located in the
* staff below the provided point.
*
* @param point the provided point
* @return the (perhaps empty) collection of whole chords
*/
public Collection<Chord> getWholeChordsBelow (Point point)
{
Staff desiredStaff = getSystem().getStaffBelow(point);
Collection<Chord> found = new ArrayList<>();
for (Chord chord : getWholeChords()) {
if ((chord.getStaff() == desiredStaff)
&& (chord.getHeadLocation().y > point.y)) {
found.add(chord);
}
}
return found;
}
//----------//
// getWidth //
//----------//
/**
* Report the width, in units, of the measure.
*
* @return the measure width, or null in case of dummy measure
*/
public Integer getWidth ()
{
if (isDummy()) {
return null;
} else {
return getRightX() - getLeftX();
}
}
//---------//
// isDummy //
//---------//
public boolean isDummy ()
{
return dummy;
}
//-------------//
// isFirstHalf //
//-------------//
/**
* @return the firstHalf
*/
public boolean isFirstHalf ()
{
return firstHalf;
}
//------------//
// isImplicit //
//------------//
/**
* Report whether this measure is implicit.
*
* @return true if measure is implicit
*/
public boolean isImplicit ()
{
return implicit;
}
//-------------//
// isTemporary //
//-------------//
public boolean isTemporary ()
{
return temporary;
}
//----------------//
// mergeWithRight //
//----------------//
/**
* Merge this measure with the content of the following measure on
* the right.
*
* @param right the following measure
*/
public void mergeWithRight (Measure right)
{
clefs.getChildren().addAll(right.clefs.getChildren());
keysigs.getChildren().addAll(right.keysigs.getChildren());
timesigs.getChildren().addAll(right.timesigs.getChildren());
chords.getChildren().addAll(right.chords.getChildren());
beams.getChildren().addAll(right.beams.getChildren());
slots.addAll(right.slots);
beamGroups.addAll(right.beamGroups);
wholeChords.addAll(right.wholeChords);
voices.addAll(right.voices);
setBox(getBox().union(right.getBox()));
insideBarline = barline;
addChild(right.barline);
}
//-------------//
// printChords //
//-------------//
/**
* Print the chords of this measure on standard output.
*
* @param title a specific title, or null
*/
public void printChords (String title)
{
StringBuilder sb = new StringBuilder();
if (title != null) {
sb.append(title);
}
sb.append(this);
for (TreeNode cn : getChords()) {
Chord chord = (Chord) cn;
sb.append("\n").append(chord);
}
logger.info(sb.toString());
}
//------------//
// printSlots //
//------------//
/**
* Print the slots of this measure on standard output.
*
* @param title a specific title, or null
*/
public void printSlots (String title)
{
StringBuilder sb = new StringBuilder();
if (title != null) {
sb.append(title);
}
sb.append(this);
for (Slot slot : this.getSlots()) {
sb.append("\n").append(slot.toChordString());
}
logger.info(sb.toString());
}
//------------//
// printVoices//
//------------//
/**
* Print the voices of this measure on standard output.
*
* @param title a potential title for this printout, or null
*/
public void printVoices (String title)
{
StringBuilder sb = new StringBuilder();
// Title
if (title != null) {
sb.append(title);
}
// Measure
sb.append(this);
// Slot headers
if (!slots.isEmpty()) {
sb.append("\n ");
for (Slot slot : slots) {
if (slot.getStartTime() != null) {
sb.append("|").append(String.format("%-5s", slot.
getStartTime()));
}
}
sb.append("|").append(getCurrentDuration());
}
for (Voice voice : voices) {
sb.append("\n").append(voice.toStrip());
}
logger.info(sb.toString());
}
//----------------//
// resetAbscissae //
//----------------//
/**
* Reset the coordinates of the measure, they will be lazily
* recomputed when needed.
*/
public void resetAbscissae ()
{
reset();
if (barline != null) {
barline.reset();
}
}
//-------------------//
// setActualDuration //
//-------------------//
/**
* Register in this measure its actual duration.
*
* @param actualDuration the duration value
*/
public void setActualDuration (Rational actualDuration)
{
this.actualDuration = actualDuration;
}
//----------//
// setDummy //
//----------//
public void setDummy (boolean dummy)
{
this.dummy = dummy;
}
//-----------//
// setExcess //
//-----------//
/**
* Assign an excess duration for this measure.
*
* @param excess the duration in excess
*/
public void setExcess (Rational excess)
{
this.excess = excess;
}
//---------------------//
// setExpectedDuration //
//---------------------//
public void setExpectedDuration (Rational expectedDuration)
{
this.expectedDuration = expectedDuration;
}
//--------------//
// setFirstHalf //
//--------------//
/**
* @param firstHalf the firstHalf to set
*/
public void setFirstHalf (boolean firstHalf)
{
this.firstHalf = firstHalf;
}
//-------------//
// setImplicit //
//-------------//
/**
* Flag this measure as implicit.
*/
public void setImplicit ()
{
implicit = true;
}
//----------//
// setLeftX //
//----------//
public void setLeftX (int val)
{
Rectangle newBox = getBox();
newBox.width = val;
setBox(newBox);
}
//-----------//
// setPageId //
//-----------//
/**
* Assign the proper page-based id to this measure.
*
* @param id the proper page-based measure id value
* @param secondHalf true if the measure is the second half of a repeat
*/
public void setPageId (int id,
boolean secondHalf)
{
this.id = new MeasureId.PageBased(this, id, secondHalf);
}
//-----------//
// setPageId //
//-----------//
/**
* Assign the proper page-based id to this measure.
*
* @param pageId the page-based id
*/
public void setPageId (MeasureId.PageBased pageId)
{
this.id = new MeasureId.PageBased(this, pageId);
}
//--------------//
// setTemporary //
//--------------//
public void setTemporary (boolean temporary)
{
this.temporary = temporary;
}
//---------//
// shorten //
//---------//
/**
* Flag this measure as partial (shorter than expected duration).
*
* @param shortening how much the measure duration is to be reduced
*/
public void shorten (Rational shortening)
{
// Remove any final forward mark consistent with the shortening
for (Voice voice : voices) {
Rational duration = voice.getTermination();
if (duration != null) {
if (duration.equals(shortening)) {
if (!voice.isWhole()) {
// Remove the related mark
Chord chord = voice.getLastChord();
if (chord != null) {
int nbMarks = chord.getMarks().size();
if (nbMarks > 0) {
Mark mark = chord.getMarks().get(nbMarks - 1);
logger.debug("{} Removing final forward: {}",
getContextString(),
(Rational) mark.getData());
chord.getMarks().remove(mark);
} else {
chord.
addError(
"No final mark to remove in a partial measure");
return;
}
} else {
addError("No final chord in " + voice);
return;
}
}
} else {
addError(
"Non consistent partial measure shortening:"
+ shortening.opposite() + " " + voice + ": "
+ duration.opposite());
return;
}
}
}
}
//----------//
// toString //
//----------//
@Override
public String toString ()
{
return "{Measure#" + id + "}";
}
//------------//
// computeBox //
//------------//
@Override
protected void computeBox ()
{
// Start of the measure
int leftX;
Measure prevMeasure = (Measure) getPreviousSibling();
if (prevMeasure == null) { // Very first measure in the staff
leftX = getSystem().getTopLeft().x;
} else {
leftX = prevMeasure.getBarline().getCenter().x;
}
// End of the measure
int rightX;
if (barline != null) {
rightX = barline.getCenter().x;
} else {
// Last measure of a part/system with no ending barline
ScoreSystem system = getSystem();
rightX = system.getTopLeft().x + system.getDimension().width;
}
Rectangle partBox = getPart().getBox();
setBox(
new Rectangle(
leftX,
partBox.y,
rightX - leftX,
partBox.height));
}
//---------------//
// computeCenter //
//---------------//
/**
* The 'center' here is the middle of the measure.
*/
@Override
protected void computeCenter ()
{
Point bl = barline.getCenter();
setCenter(new Point((bl.x + getLeftX()) / 2, bl.y));
}
//---------------//
// addWholeChord //
//---------------//
/**
* Insert a note as a whole (or multi) rest.
*
* @param glyph the underlying glyph
*/
void addWholeChord (Glyph glyph)
{
Chord chord = new Chord(this, null);
// No slot for this chord, but a whole rest
new Note(chord, glyph); // Records note in chord
wholeChords.add(chord);
}
//---------------//
// addWholeChord //
//---------------//
public void addWholeChord (Chord chord)
{
wholeChords.add(chord);
}
//--------------//
// addWholeRest //
//--------------//
/**
* Insert a whole rest at provided center.
*
* @param center the location for the rest note
*/
void addWholeRest (Staff staff,
Point center)
{
Chord chord = new Chord(this, null);
Note.createWholeRest(staff, chord, center);
wholeChords.add(chord);
chord.setStartTime(Rational.ZERO);
Voice.createWholeVoice(chord);
}
//--------------------//
// getCurrentDuration //
//--------------------//
private String getCurrentDuration ()
{
Rational measureDur = Rational.ZERO;
for (Slot slot : getSlots()) {
if (slot.getStartTime() != null) {
for (Chord chord : slot.getChords()) {
Rational chordEnd = slot.getStartTime().plus(chord.
getDuration());
if (chordEnd.compareTo(measureDur) > 0) {
measureDur = chordEnd;
}
}
}
}
if (measureDur.equals(Rational.ZERO) && !getWholeChords().isEmpty()) {
return "W";
}
return String.format("%-5s", measureDur.toString());
}
//-------------//
// swapVoiceId //
//-------------//
/**
* Change the id of the provided voice to the provided id
* (and change the other voice, if any, which owned the provided id).
*
* @param voice the voice whose id must be changed
* @param id the new id
*/
public void swapVoiceId (Voice voice,
int id)
{
// Existing voice?
Voice oldOwner = null;
for (Voice v : getVoices()) {
if (v.getId() == id) {
oldOwner = v;
break;
}
}
// Change voice id
int oldId = voice.getId();
voice.setId(id);
// Assign the oldId to the oldOwner, if any
if (oldOwner != null) {
oldOwner.setId(oldId);
}
}
}