//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include "durationtype.h"
#include "mscore.h"
#include "note.h"
#include "sig.h"
#include "measure.h"
namespace Ms {
static const int POW_MAX_DOTS = qPow(2, MAX_DOTS);
//---------------------------------------------------------
// dots
//---------------------------------------------------------
static int getDots(int base, int rest, char* dots)
{
if (base < 16)
return rest;
*dots = 0;
if (rest >= base / 2) {
*dots = *dots + 1;
rest -= base / 2;
}
if (rest >= base / 4) {
*dots = *dots + 1;
rest -= base / 4;
}
if (rest >= base / 8) {
*dots = *dots + 1;
rest -= base / 8;
}
if (rest >= base / 16) {
*dots = *dots + 1;
rest -= base / 16;
}
if (*dots > MAX_DOTS)
*dots = MAX_DOTS;
return rest;
}
//---------------------------------------------------------
// setDots
//---------------------------------------------------------
void TDuration::setDots(int v)
{
if (v > MAX_DOTS)
v = MAX_DOTS;
_dots = v;
}
//---------------------------------------------------------
// setVal
//---------------------------------------------------------
void TDuration::setVal(int ticks)
{
if (ticks == 0)
_val = DurationType::V_MEASURE;
else {
TDuration dt;
for (int i = 0; i < int(TDuration::DurationType::V_ZERO); ++i) {
dt.setType(TDuration::DurationType(i));
int t = dt.ticks();
if (ticks / t) {
int remain = ticks % t;
if ((t - remain) < (t/4)) {
_val = DurationType(i - 1);
return;
}
_val = DurationType(i);
getDots(t, remain, &_dots);
return;
}
}
qDebug("2: no duration type for ticks %d", ticks);
_val = DurationType::V_QUARTER; // fallback default value
}
}
//---------------------------------------------------------
// ticks
//---------------------------------------------------------
int TDuration::ticks() const
{
int t;
switch(_val) {
case DurationType::V_QUARTER: t = MScore::division; break;
case DurationType::V_1024TH: t = MScore::division / 256; break;
case DurationType::V_512TH: t = MScore::division / 128; break;
case DurationType::V_256TH: t = MScore::division / 64; break;
case DurationType::V_128TH: t = MScore::division / 32; break;
case DurationType::V_64TH: t = MScore::division / 16; break;
case DurationType::V_32ND: t = MScore::division / 8; break;
case DurationType::V_16TH: t = MScore::division / 4; break;
case DurationType::V_EIGHTH: t = MScore::division / 2; break;
case DurationType::V_HALF: t = MScore::division * 2; break;
case DurationType::V_WHOLE: t = MScore::division * 4; break;
case DurationType::V_BREVE: t = MScore::division * 8; break;
case DurationType::V_LONG: t = MScore::division * 16; break;
case DurationType::V_ZERO:
case DurationType::V_MEASURE:
return 0;
default:
case DurationType::V_INVALID:
return -1;
}
int tmp = t;
for (int i = 0; i < _dots; ++i)
tmp += (t >> (i+1));
return tmp;
}
//---------------------------------------------------------
// name
//---------------------------------------------------------
QString TDuration::name() const
{
switch(_val) {
case DurationType::V_QUARTER: return "quarter";
case DurationType::V_EIGHTH: return "eighth";
case DurationType::V_1024TH: return "1024th";
case DurationType::V_512TH: return "512th";
case DurationType::V_256TH: return "256th";
case DurationType::V_128TH: return "128th";
case DurationType::V_64TH: return "64th";
case DurationType::V_32ND: return "32nd";
case DurationType::V_16TH: return "16th";
case DurationType::V_HALF: return "half";
case DurationType::V_WHOLE: return "whole";
case DurationType::V_MEASURE: return "measure";
case DurationType::V_BREVE: return "breve";
case DurationType::V_LONG: return "long";
default:
qDebug("TDuration::name(): invalid duration type %d", static_cast<int>(_val));
// fall through
case DurationType::V_ZERO:
case DurationType::V_INVALID: return "";
}
}
//---------------------------------------------------------
// headType
//---------------------------------------------------------
NoteHead::Type TDuration::headType() const
{
NoteHead::Type headType = NoteHead::Type::HEAD_WHOLE;
switch(_val) {
case DurationType::V_1024TH:
case DurationType::V_512TH:
case DurationType::V_256TH:
case DurationType::V_128TH:
case DurationType::V_64TH:
case DurationType::V_32ND:
case DurationType::V_16TH:
case DurationType::V_EIGHTH:
case DurationType::V_QUARTER:
headType = NoteHead::Type::HEAD_QUARTER;
break;
case DurationType::V_HALF:
headType = NoteHead::Type::HEAD_HALF;
break;
case DurationType::V_MEASURE:
case DurationType::V_WHOLE:
headType = NoteHead::Type::HEAD_WHOLE;
break;
case DurationType::V_BREVE:
headType = NoteHead::Type::HEAD_BREVIS;
break;
case DurationType::V_LONG:
headType = NoteHead::Type::HEAD_BREVIS;
break;
default:
case DurationType::V_INVALID:
case DurationType::V_ZERO:
headType = NoteHead::Type::HEAD_QUARTER;
break;
}
return headType;
}
//---------------------------------------------------------
// hooks
//---------------------------------------------------------
int TDuration::hooks() const
{
static const int table[] = {
// V_LONG, V_BREVE, V_WHOLE, V_HALF, V_QUARTER, V_EIGHTH, V_16TH,
0, 0, 0, 0, 0, 1, 2,
// V_32ND, V_64TH, V_128TH, V_256TH, V_512TH, V_1024TH,
3, 4, 5, 6, 7, 8,
// V_ZERO, V_MEASURE, V_INVALID
0, 0, 0
};
return table[int(_val)];
}
//---------------------------------------------------------
// hasStem
//---------------------------------------------------------
bool TDuration::hasStem() const
{
switch(_val) {
case DurationType::V_1024TH:
case DurationType::V_512TH:
case DurationType::V_256TH:
case DurationType::V_128TH:
case DurationType::V_64TH:
case DurationType::V_32ND:
case DurationType::V_16TH:
case DurationType::V_EIGHTH:
case DurationType::V_QUARTER:
case DurationType::V_HALF:
case DurationType::V_LONG:
return true;
default:
return false;
}
}
//---------------------------------------------------------
// setVal
//---------------------------------------------------------
TDuration::TDuration(const QString& s)
{
setType(s);
_dots = 0;
}
//---------------------------------------------------------
// setType
//---------------------------------------------------------
void TDuration::setType(const QString& s)
{
if (s == "quarter")
_val = DurationType::V_QUARTER;
else if (s == "eighth")
_val = DurationType::V_EIGHTH;
else if (s == "1024th")
_val = DurationType::V_1024TH;
else if (s == "512th")
_val = DurationType::V_512TH;
else if (s == "256th")
_val = DurationType::V_256TH;
else if (s == "128th")
_val = DurationType::V_128TH;
else if (s == "64th")
_val = DurationType::V_64TH;
else if (s == "32nd")
_val = DurationType::V_32ND;
else if (s == "16th")
_val = DurationType::V_16TH;
else if (s == "half")
_val = DurationType::V_HALF;
else if (s == "whole")
_val = DurationType::V_WHOLE;
else if (s == "breve")
_val = DurationType::V_BREVE;
else if (s == "long")
_val = DurationType::V_LONG;
else if (s == "measure")
_val = DurationType::V_MEASURE;
else {
// _val = V_INVALID;
_val = DurationType::V_QUARTER;
qDebug("TDuration::setType(%s): unknown, assume \"quarter\"", qPrintable(s));
}
}
//---------------------------------------------------------
// shiftType
// if stepDotted = false, duration type will inc/dec by nSteps with _dots remaining same
// if stepDotted = true, duration will round toward zero to next single-dotted or undotted duration and then will included dotted durations when stepping
//---------------------------------------------------------
void TDuration::shiftType(int nSteps, bool stepDotted)
{
if (_val == DurationType::V_MEASURE || _val == DurationType::V_INVALID || _val == DurationType::V_ZERO)
setType(DurationType::V_INVALID);
else {
int newValue;
int newDots;
if (stepDotted) {
// figure out the new duration in terms of the number of single dotted or undotted steps from DurationType::V_LONG
int roundDownSingleDots = (_dots > 0) ? -1 : 0;
int newValAsNumSingleDotSteps = int(_val) * 2 + roundDownSingleDots + nSteps;
// convert that new duration back into terms of DurationType integer value and number of dots
newDots = newValAsNumSingleDotSteps % 2; // odd means there is a dot
newValue = newValAsNumSingleDotSteps / 2 + newDots; // if new duration has a dot, then that
}
else {
newDots = _dots;
newValue = int(_val) + nSteps;
}
if ((newValue < int(DurationType::V_LONG)) || (newValue > int(DurationType::V_1024TH)) ||
((newValue >= int(DurationType::V_1024TH)) && (newDots >= 1)) ||
((newValue >= int(DurationType::V_512TH)) && (newDots >= 2)) ||
((newValue >= int(DurationType::V_256TH)) && (newDots >= 3)) ||
((newValue >= int(DurationType::V_128TH)) && (newDots >= 4)))
setType(DurationType::V_INVALID);
else {
setType(DurationType(newValue));
setDots(newDots);
}
}
}
//---------------------------------------------------------
// operator<
//---------------------------------------------------------
bool TDuration::operator<(const TDuration& t) const
{
if (t._val < _val)
return true;
if (t._val == _val) {
if (_dots < t._dots)
return true;
}
return false;
}
//---------------------------------------------------------
// operator>=
//---------------------------------------------------------
bool TDuration::operator>=(const TDuration& t) const
{
if (t._val > _val)
return true;
if (t._val == _val) {
if (_dots >= t._dots)
return true;
}
return false;
}
//---------------------------------------------------------
// operator<=
//---------------------------------------------------------
bool TDuration::operator<=(const TDuration& t) const
{
if (t._val < _val)
return true;
if (t._val == _val) {
if (_dots <= t._dots)
return true;
}
return false;
}
//---------------------------------------------------------
// operator>
//---------------------------------------------------------
bool TDuration::operator>(const TDuration& t) const
{
if (t._val > _val)
return true;
if (t._val == _val) {
if (_dots > t._dots)
return true;
}
return false;
}
//---------------------------------------------------------
// fraction
//---------------------------------------------------------
Fraction TDuration::fraction() const
{
int z = 1;
unsigned n;
switch(_val) {
case DurationType::V_1024TH: n = 1024; break;
case DurationType::V_512TH: n = 512; break;
case DurationType::V_256TH: n = 256; break;
case DurationType::V_128TH: n = 128; break;
case DurationType::V_64TH: n = 64; break;
case DurationType::V_32ND: n = 32; break;
case DurationType::V_16TH: n = 16; break;
case DurationType::V_EIGHTH: n = 8; break;
case DurationType::V_QUARTER: n = 4; break;
case DurationType::V_HALF: n = 2; break;
case DurationType::V_WHOLE: n = 1; break;
case DurationType::V_BREVE: z = 2; n = 1; break;
case DurationType::V_LONG: z = 4; n = 1; break;
case DurationType::V_ZERO: z = 0; n = 1; break;
default: z = 0; n = 0; break; // zero+invalid fraction
}
Fraction a(z, n);
if (a.isValid()) {
for (int i = 0; i < _dots; ++i) {
n *= 2;
a += Fraction(z, n);
}
}
return a;
}
// Longest TDuration that fits into Fraction. Must fit exactly if truncate = false.
TDuration::TDuration(const Fraction& l, bool truncate, int maxDots, DurationType maxType)
{
#ifdef NDEBUG
Q_UNUSED(truncate);
#endif
setType(maxType); // use maxType to avoid testing all types if you know that l is smaller than a certain DurationType
setDots(maxDots);
truncateToFraction(l, maxDots);
Q_ASSERT(truncate || (fraction() - l).numerator() == 0); // check for exact fit
}
//---------------------------------------------------------
// truncateToFraction
//---------------------------------------------------------
void TDuration::truncateToFraction(const Fraction& l, int maxDots)
{
// try to fit in l by reducing number of duration dots
if (setDotsToFitFraction(l, _dots))
return;
// that wasn't enough so now change type too
for (shiftType(1); isValid(); shiftType(1)) {
if (setDotsToFitFraction(l, maxDots))
return; // duration fits fits in l
}
}
//---------------------------------------------------------
// setDotsToFitFraction
//---------------------------------------------------------
bool TDuration::setDotsToFitFraction(const Fraction& l, int maxDots)
{
for ( ; maxDots >= 0; maxDots--) {
_dots = maxDots; // ensures _dots >= 0 if function returns false.
if ((fraction() - l).numerator() <= 0)
return true; // duration fits in l
}
return false; // doesn't fit by changing dots alone (type needs to be changed too)
}
//---------------------------------------------------------
// operator -=
//---------------------------------------------------------
TDuration& TDuration::operator-=(const TDuration& t)
{
Fraction f1 = fraction() - t.fraction();
TDuration d(f1);
_val = d._val;
_dots = d._dots;
return *this;
}
//---------------------------------------------------------
// operator +=
//---------------------------------------------------------
TDuration& TDuration::operator+=(const TDuration& t)
{
Fraction f1 = fraction() + t.fraction();
TDuration d(f1);
_val = d._val;
_dots = d._dots;
return *this;
}
//---------------------------------------------------------
// toDurationList
//---------------------------------------------------------
std::vector<TDuration> toDurationList(Fraction l, bool useDots, int maxDots, bool printRestRemains)
{
std::vector<TDuration> dList;
dList.reserve(8);
if (!useDots)
maxDots = 0;
for (TDuration dd(l, true, maxDots); dd.isValid() && l.numerator() > 0; dd = TDuration(l, true, maxDots, dd.type())) {
dList.push_back(dd);
l -= dd.fraction();
}
if (printRestRemains && l.numerator() != 0)
qDebug("toDurationList:: rest remains %d/%d", l.numerator(), l.denominator());
return dList;
}
//---------------------------------------------------------
// toRhythmicDurationList
//---------------------------------------------------------
std::vector<TDuration> toRhythmicDurationList(const Fraction& l, bool isRest, int rtickStart, const TimeSigFrac& nominal, Measure* msr, int maxDots)
{
std::vector<TDuration> dList;
dList.reserve(8);
if (msr->isAnacrusis())
rtickStart = nominal.ticksPerMeasure() - rtickStart;
else if (isRest && l == msr->len()) {
TDuration d = TDuration(TDuration::DurationType::V_MEASURE);
dList.push_back(d);
return dList;
}
if (nominal.isCompound())
splitCompoundBeatsForList(&dList, l, isRest, rtickStart, nominal, maxDots);
else
populateRhythmicList(&dList, l, isRest, rtickStart, nominal, maxDots);
return dList;
}
//---------------------------------------------------------
// populateRhythmicList
//---------------------------------------------------------
void populateRhythmicList(std::vector<TDuration>* dList, const Fraction& l, bool isRest, int rtickStart, const TimeSigFrac& nominal, int maxDots)
{
int rtickEnd = rtickStart + l.ticks();
bool needToSplit = false; // do we need to split?
int rtickSplit = 0; // tick to split on if we need to
// CHECK AT SUBBEAT LEVEL
int startLevel = nominal.rtick2subbeatLevel(rtickStart);
int endLevel = nominal.rtick2subbeatLevel(rtickEnd);
int strongestLevelCrossed = nominal.strongestSubbeatLevelInRange(rtickStart, rtickEnd, &rtickSplit); // sets rtickSplit
if ((startLevel < 0) || (endLevel < 0) || (strongestLevelCrossed < 0)) {
// Beyond maximum subbeat level so just split into largest possible durations.
std::vector<TDuration> dList2 = toDurationList(l, maxDots > 0, maxDots, false);
dList->insert(dList->end(), dList2.begin(), dList2.end());
return;
}
// split if we cross something stronger than where we start and end
if ((strongestLevelCrossed < startLevel) && (strongestLevelCrossed < endLevel))
needToSplit = true;
// but don't split for level 1 syncopation (allow eight-note, quarter, quarter... to cross unstressed beats)
if (startLevel == endLevel && strongestLevelCrossed == startLevel - 1)
needToSplit = false;
// nor for the next simplest case of level 2 syncopation (allow sixteenth-note, eighth, eighth... to cross unstressed beats)
if (startLevel == endLevel && strongestLevelCrossed == startLevel - 2) {
// but disallow sixteenth-note, quarter, quarter...
int ticksToNext = nominal.ticksToNextSubbeat(rtickStart, startLevel - 1);
int ticksPastPrev = nominal.ticksPastSubbeat(rtickStart, startLevel - 1);
needToSplit = ticksToNext != ticksPastPrev;
}
if (!needToSplit && strongestLevelCrossed == 0) {
// NOW CHECK AT DENOMINATOR UNIT LEVEL AND BEAT LEVEL
BeatType startBeat = nominal.rtick2beatType(rtickStart);
BeatType endBeat = nominal.rtick2beatType(rtickEnd);
int dUnitsCrossed = 0; // number of timeSig denominator units the note/rest crosses
// if there is a choice of which beat to split on, should we use the first or last?
bool useLast = startBeat <= BeatType::SIMPLE_UNSTRESSED; // split on the later beat if starting on a beat
BeatType strongestBeatCrossed = nominal.strongestBeatInRange(rtickStart, rtickEnd, &dUnitsCrossed, &rtickSplit, useLast);
needToSplit = forceRhythmicSplit(isRest, startBeat, endBeat, dUnitsCrossed, strongestBeatCrossed, nominal);
}
if (!needToSplit) {
// CHECK THERE IS A DURATION THAT FITS
// crossed beats/subbeats were not important so try to avoid splitting
TDuration d = TDuration(l, true, maxDots);
if (d.fraction() == l) {
// we can use a single duration - no need to split!
dList->push_back(l);
return;
}
// no single TDuration fits so must split anyway
}
// Split on the strongest beat or subbeat crossed
Fraction leftSplit = Fraction::fromTicks(rtickSplit - rtickStart);
Fraction rightSplit = l - leftSplit;
// Recurse to see if we need to split further before adding to list
populateRhythmicList(dList, leftSplit, isRest, rtickStart, nominal, maxDots);
populateRhythmicList(dList, rightSplit, isRest, rtickSplit , nominal, maxDots);
}
//---------------------------------------------------------
// splitCompoundBeatsForList
// Split compound notes/rests where they enter a compound beat.
//---------------------------------------------------------
void splitCompoundBeatsForList(std::vector<TDuration>* dList, const Fraction& l, bool isRest, int rtickStart, const TimeSigFrac& nominal, int maxDots)
{
int rtickEnd = rtickStart + l.ticks();
BeatType startBeat = nominal.rtick2beatType(rtickStart);
BeatType endBeat = nominal.rtick2beatType(rtickEnd);
if (startBeat > BeatType::COMPOUND_UNSTRESSED) {
// Not starting on a compound beat so mustn't extend into next compound beat
int splitTicks = nominal.ticksToNextBeat(rtickStart);
if (rtickEnd - rtickStart > splitTicks) {
// Duration extends into next beat so must split
Fraction leftSplit = Fraction::fromTicks(splitTicks);
Fraction rightSplit = l - leftSplit;
populateRhythmicList(dList, leftSplit, isRest, rtickStart, nominal, maxDots); // this side is ok to proceed
splitCompoundBeatsForList(dList, rightSplit, isRest, rtickStart + splitTicks, nominal, maxDots); // not checked yet
return;
}
}
if (endBeat > BeatType::COMPOUND_UNSTRESSED) {
// Not ending on a compound beat so mustn't extend into previous compound beat
int splitTicks = nominal.ticksPastBeat(rtickEnd);
if (rtickEnd - rtickStart > splitTicks) {
// Duration extends into previous beat so must split
Fraction rightSplit = Fraction::fromTicks(splitTicks);
Fraction leftSplit = l - rightSplit;
populateRhythmicList(dList, leftSplit, isRest, rtickStart, nominal, maxDots); // must add leftSplit to list first
populateRhythmicList(dList, rightSplit, isRest, rtickEnd - splitTicks, nominal, maxDots);
return;
}
}
// Duration either starts and ends on compound beats, or it remains within a single compound beat
populateRhythmicList(dList, l, isRest, rtickStart, nominal, maxDots);
}
//---------------------------------------------------------
// forceRhythmicSplit
// Where to split notes and force a tie to indicate rhythm.
//
// The function assumes the following:
// * Note/rest has already been split at measure boundaries.
// * Full measure rest was used if possible
// * Note/rest already split where it enters a compound beat.
//
// Usage: Set crossedBeat to the strongest BeatType crossed by
// the note. Split note if function returns true. Repeat with
// the two new notes, and so on, until function returns false.
//
// Note: If no single TDuration can fill the gap then the note
// *has* to be split, regardless of what this function returns.
// Non-rhythmic splits should still occur on the strongest beat.
//
// Implementation: When comparing BeatTypes use <, <=, >, >=
// instead of == and != as appropriate (see sig.h). E.g. to match
// all full beats: "<= SIMPLE_UNSTRESSED". This will match for
// all compound and simple full beats, and not any subbeats.
//---------------------------------------------------------
bool forceRhythmicSplit(bool isRest, BeatType startBeat, BeatType endBeat, int dUnitsCrossed, BeatType strongestBeatCrossed, const TimeSigFrac& nominal) {
// Assumption: Notes were split at measure boundary before this function was
// called. (Necessary because timeSig might be different in next measure.)
Q_ASSERT(strongestBeatCrossed != BeatType::DOWNBEAT);
// Assumption: compound notes have already been split where they enter a compound beat.
// (Necessary because the split beat is not always the strongest beat in this case.)
Q_ASSERT(!nominal.isCompound() || strongestBeatCrossed >= BeatType::COMPOUND_SUBBEAT
|| (startBeat <= BeatType::COMPOUND_UNSTRESSED && endBeat <= BeatType::COMPOUND_UNSTRESSED));
// SPECIAL CASES
// nothing can cross a stressed beat in an irregular time signature
if (strongestBeatCrossed <= BeatType::SIMPLE_STRESSED && !nominal.isTriple() && !nominal.isDuple())
return true;
if (isRest) {
// rests must not cross the middle of a bar with numerator == 2 (e.g. 2/4 bar) even though the beat is unstressed
if (strongestBeatCrossed <= BeatType::SIMPLE_UNSTRESSED && nominal.numerator() == 2)
return true;
// rests must not cross a beat in a triple meter - simple (e.g. 3/4) or compound (e.g. 9/8)
if (strongestBeatCrossed <= BeatType::SIMPLE_UNSTRESSED && nominal.isTriple())
return true;
}
// GENERAL RULES
if (nominal.isCompound())
return forceRhythmicSplitCompound(isRest, startBeat, endBeat, dUnitsCrossed, strongestBeatCrossed);
else
return forceRhythmicSplitSimple(isRest, startBeat, endBeat, dUnitsCrossed, strongestBeatCrossed);
}
//---------------------------------------------------------
// forceRhythmicSplitCompound
//---------------------------------------------------------
bool forceRhythmicSplitCompound(bool isRest, BeatType startBeat, BeatType endBeat, int dUnitsCrossed, BeatType strongestBeatCrossed)
{
switch (strongestBeatCrossed) {
case BeatType::COMPOUND_STRESSED:
// Assumption: compound notes have already been split where they enter a compound beat.
Q_ASSERT(startBeat <= BeatType::COMPOUND_UNSTRESSED && endBeat <= BeatType::COMPOUND_UNSTRESSED);
// Notes are guaranteed to and start on a compound beat so we can pretend we have a simple measure.
return forceRhythmicSplitSimple(isRest, startBeat, endBeat, dUnitsCrossed / 3, BeatType::SIMPLE_STRESSED);
case BeatType::COMPOUND_UNSTRESSED:
// Same assumption as before
Q_ASSERT(startBeat <= BeatType::COMPOUND_UNSTRESSED && endBeat <= BeatType::COMPOUND_UNSTRESSED);
// No further conditions since note are guaranteed to start and end on a compound beat.
return false;
case BeatType::COMPOUND_SUBBEAT:
// don't split anything that takes up a full compound beat
if (startBeat <= BeatType::COMPOUND_UNSTRESSED && endBeat <= BeatType::COMPOUND_UNSTRESSED)
return false;
// split rests that don't start on a compound beat
if (isRest && startBeat > BeatType::COMPOUND_UNSTRESSED)
return true;
// Remaining groupings within compound triplets are the same as for simple triple (3/4, 3/8, etc.)
return forceRhythmicSplitSimple(isRest, startBeat, endBeat, dUnitsCrossed, BeatType::SIMPLE_UNSTRESSED);
default: // BeatType::SUBBEAT
return forceRhythmicSplitSimple(isRest, startBeat, endBeat, dUnitsCrossed, strongestBeatCrossed);
}
}
//---------------------------------------------------------
// forceRhythmicSplitSimple
// Implementation: This function is also called for compound
// measures so be careful when comparing BeatTypes. Use <, <=,
// >, >= instead of == and != when appropriate. (See sig.h)
//---------------------------------------------------------
bool forceRhythmicSplitSimple(bool isRest, BeatType startBeat, BeatType endBeat, int beatsCrossed, BeatType strongestBeatCrossed)
{
switch (strongestBeatCrossed) {
case BeatType::SIMPLE_STRESSED:
// Must split rests on a stressed beat.
if (isRest)
return true;
// Don't split notes that start or end on a stressed beat. Enables double-dotting in 4/4.
// (Don't remove this to disable double-dotting, instead set maxDots = 1 elsewhere.)
if (startBeat <= BeatType::SIMPLE_STRESSED || endBeat <= BeatType::SIMPLE_STRESSED)
return false;
// Don't split notes that both start and end on unstressed beats or stronger.
if (startBeat <= BeatType::SIMPLE_UNSTRESSED && endBeat <= BeatType::SIMPLE_UNSTRESSED)
return false;
// anything else must split on stressed beat.
return true;
case BeatType::SIMPLE_UNSTRESSED:
// Don't split notes or rests if starting and ending on stressed beat.
if (startBeat <= BeatType::SIMPLE_STRESSED && endBeat <= BeatType::SIMPLE_STRESSED)
return false;
// Split rests that don't start or end on a beat. Notes may cross 1 unstressed beat.
if (startBeat == BeatType::SUBBEAT || endBeat == BeatType::SUBBEAT)
return isRest || (beatsCrossed > 1);
return false;
default: // BeatType::SUBBEAT
return false;
}
}
//---------------------------------------------------------
// print
//---------------------------------------------------------
QString TDuration::durationTypeUserName() const
{
QString s = QObject::tr("Custom");
switch(_val) {
case DurationType::V_LONG: s = QObject::tr("Longa" ); break;
case DurationType::V_BREVE: s = QObject::tr("Breve" ); break;
case DurationType::V_WHOLE: s = QObject::tr("Whole" ); break;
case DurationType::V_HALF: s = QObject::tr("Half" ); break;
case DurationType::V_QUARTER: s = QObject::tr("Quarter"); break;
case DurationType::V_EIGHTH: s = QObject::tr("Eighth" ); break;
case DurationType::V_16TH: s = QObject::tr("16th" ); break;
case DurationType::V_32ND: s = QObject::tr("32nd" ); break;
case DurationType::V_64TH: s = QObject::tr("64th" ); break;
case DurationType::V_128TH: s = QObject::tr("128th" ); break;
case DurationType::V_256TH: s = QObject::tr("256th" ); break;
case DurationType::V_512TH: s = QObject::tr("512th" ); break;
case DurationType::V_1024TH: s = QObject::tr("1024th" ); break;
case DurationType::V_ZERO: s = QObject::tr("Zero" ); break;
case DurationType::V_MEASURE: s = QObject::tr("Measure"); break;
case DurationType::V_INVALID: s = QObject::tr("Invalid"); break;
};
return s;
}
//---------------------------------------------------------
// setType
//---------------------------------------------------------
void TDuration::setType(DurationType t)
{
_val = t;
if (_val == DurationType::V_MEASURE)
_dots = 0;
}
//---------------------------------------------------------
// isValid
//---------------------------------------------------------
bool TDuration::isValid(Fraction f)
{
TDuration t;
t.setType(DurationType::V_LONG);
t.setDots(4);
t.truncateToFraction(f, 4);
return ((t.fraction() - f).numerator() == 0);
}
}
↑ V793 It is odd that the result of the 'ticks / t' statement is a part of the condition. Perhaps, this statement should have been compared with something else.