summaryrefslogtreecommitdiffstats
path: root/src/base/NotationQuantizer.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/base/NotationQuantizer.cpp
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/base/NotationQuantizer.cpp')
-rw-r--r--src/base/NotationQuantizer.cpp1205
1 files changed, 1205 insertions, 0 deletions
diff --git a/src/base/NotationQuantizer.cpp b/src/base/NotationQuantizer.cpp
new file mode 100644
index 0000000..9e76a94
--- /dev/null
+++ b/src/base/NotationQuantizer.cpp
@@ -0,0 +1,1205 @@
+// -*- c-basic-offset: 4 -*-
+
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "NotationQuantizer.h"
+#include "BaseProperties.h"
+#include "NotationTypes.h"
+#include "Selection.h"
+#include "Composition.h"
+#include "Sets.h"
+#include "Profiler.h"
+
+#include <iostream>
+#include <cmath>
+#include <cstdio> // for sprintf
+#include <ctime>
+
+using std::cout;
+using std::cerr;
+using std::endl;
+
+//#define DEBUG_NOTATION_QUANTIZER 1
+
+namespace Rosegarden {
+
+using namespace BaseProperties;
+
+class NotationQuantizer::Impl
+{
+public:
+ Impl(NotationQuantizer *const q) :
+ m_unit(Note(Note::Demisemiquaver).getDuration()),
+ m_simplicityFactor(13),
+ m_maxTuplet(3),
+ m_articulate(true),
+ m_q(q),
+ m_provisionalBase("notationquantizer-provisionalBase"),
+ m_provisionalAbsTime("notationquantizer-provisionalAbsTime"),
+ m_provisionalDuration("notationquantizer-provisionalDuration"),
+ m_provisionalNoteType("notationquantizer-provisionalNoteType"),
+ m_provisionalScore("notationquantizer-provisionalScore")
+ { }
+
+ Impl(const Impl &i) :
+ m_unit(i.m_unit),
+ m_simplicityFactor(i.m_simplicityFactor),
+ m_maxTuplet(i.m_maxTuplet),
+ m_articulate(i.m_articulate),
+ m_q(i.m_q),
+ m_provisionalBase(i.m_provisionalBase),
+ m_provisionalAbsTime(i.m_provisionalAbsTime),
+ m_provisionalDuration(i.m_provisionalDuration),
+ m_provisionalNoteType(i.m_provisionalNoteType),
+ m_provisionalScore(i.m_provisionalScore)
+ { }
+
+ class ProvisionalQuantizer : public Quantizer {
+ // This class exists only to pick out the provisional abstime
+ // and duration values from half-quantized events, so that we
+ // can treat them using the normal Chord class
+ public:
+ ProvisionalQuantizer(Impl *i) : Quantizer("blah", "blahblah"), m_impl(i) { }
+ virtual timeT getQuantizedDuration(const Event *e) const {
+ return m_impl->getProvisional((Event *)e, DurationValue);
+ }
+ virtual timeT getQuantizedAbsoluteTime(const Event *e) const {
+ timeT t = m_impl->getProvisional((Event *)e, AbsoluteTimeValue);
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "ProvisionalQuantizer::getQuantizedAbsoluteTime: returning " << t << endl;
+#endif
+ return t;
+ }
+
+ private:
+ Impl *m_impl;
+ };
+
+ void quantizeRange(Segment *,
+ Segment::iterator,
+ Segment::iterator) const;
+
+ void quantizeAbsoluteTime(Segment *, Segment::iterator) const;
+ long scoreAbsoluteTimeForBase(Segment *, const Segment::iterator &,
+ int depth, timeT base, timeT sigTime,
+ timeT t, timeT d, int noteType,
+ const Segment::iterator &,
+ const Segment::iterator &,
+ bool &right) const;
+ void quantizeDurationProvisional(Segment *, Segment::iterator) const;
+ void quantizeDuration(Segment *, Chord &) const;
+
+ void scanTupletsInBar(Segment *,
+ timeT barStart, timeT barDuration,
+ timeT wholeStart, timeT wholeDuration,
+ const std::vector<int> &divisions) const;
+ void scanTupletsAt(Segment *, Segment::iterator, int depth,
+ timeT base, timeT barStart,
+ timeT tupletStart, timeT tupletBase) const;
+ bool isValidTupletAt(Segment *, const Segment::iterator &,
+ int depth, timeT base, timeT sigTime,
+ timeT tupletBase) const;
+
+ void setProvisional(Event *, ValueType value, timeT t) const;
+ timeT getProvisional(Event *, ValueType value) const;
+ void unsetProvisionalProperties(Event *) const;
+
+ timeT m_unit;
+ int m_simplicityFactor;
+ int m_maxTuplet;
+ bool m_articulate;
+ bool m_contrapuntal;
+
+private:
+ NotationQuantizer *const m_q;
+
+ PropertyName m_provisionalBase;
+ PropertyName m_provisionalAbsTime;
+ PropertyName m_provisionalDuration;
+ PropertyName m_provisionalNoteType;
+ PropertyName m_provisionalScore;
+};
+
+NotationQuantizer::NotationQuantizer() :
+ Quantizer(NotationPrefix),
+ m_impl(new Impl(this))
+{
+ // nothing else
+}
+
+NotationQuantizer::NotationQuantizer(std::string source, std::string target) :
+ Quantizer(source, target),
+ m_impl(new Impl(this))
+{
+ // nothing else
+}
+
+NotationQuantizer::NotationQuantizer(const NotationQuantizer &q) :
+ Quantizer(q.m_target),
+ m_impl(new Impl(*q.m_impl))
+{
+ // nothing else
+}
+
+NotationQuantizer::~NotationQuantizer()
+{
+ delete m_impl;
+}
+
+void
+NotationQuantizer::setUnit(timeT unit)
+{
+ m_impl->m_unit = unit;
+}
+
+timeT
+NotationQuantizer::getUnit() const
+{
+ return m_impl->m_unit;
+}
+
+void
+NotationQuantizer::setMaxTuplet(int m)
+{
+ m_impl->m_maxTuplet = m;
+}
+
+int
+NotationQuantizer::getMaxTuplet() const
+{
+ return m_impl->m_maxTuplet;
+}
+
+void
+NotationQuantizer::setSimplicityFactor(int s)
+{
+ m_impl->m_simplicityFactor = s;
+}
+
+int
+NotationQuantizer::getSimplicityFactor() const
+{
+ return m_impl->m_simplicityFactor;
+}
+
+void
+NotationQuantizer::setContrapuntal(bool c)
+{
+ m_impl->m_contrapuntal = c;
+}
+
+bool
+NotationQuantizer::getContrapuntal() const
+{
+ return m_impl->m_contrapuntal;
+}
+
+void
+NotationQuantizer::setArticulate(bool a)
+{
+ m_impl->m_articulate = a;
+}
+
+bool
+NotationQuantizer::getArticulate() const
+{
+ return m_impl->m_articulate;
+}
+
+void
+NotationQuantizer::Impl::setProvisional(Event *e, ValueType v, timeT t) const
+{
+ if (v == AbsoluteTimeValue) {
+ e->setMaybe<Int>(m_provisionalAbsTime, t);
+ } else {
+ e->setMaybe<Int>(m_provisionalDuration, t);
+ }
+}
+
+timeT
+NotationQuantizer::Impl::getProvisional(Event *e, ValueType v) const
+{
+ timeT t;
+ if (v == AbsoluteTimeValue) {
+ t = e->getAbsoluteTime();
+ e->get<Int>(m_provisionalAbsTime, t);
+ } else {
+ t = e->getDuration();
+ e->get<Int>(m_provisionalDuration, t);
+ }
+ return t;
+}
+
+void
+NotationQuantizer::Impl::unsetProvisionalProperties(Event *e) const
+{
+ e->unset(m_provisionalBase);
+ e->unset(m_provisionalAbsTime);
+ e->unset(m_provisionalDuration);
+ e->unset(m_provisionalNoteType);
+ e->unset(m_provisionalScore);
+}
+
+void
+NotationQuantizer::Impl::quantizeAbsoluteTime(Segment *s, Segment::iterator i) const
+{
+ Profiler profiler("NotationQuantizer::Impl::quantizeAbsoluteTime");
+
+ Composition *comp = s->getComposition();
+
+ TimeSignature timeSig;
+ timeT t = m_q->getFromSource(*i, AbsoluteTimeValue);
+ timeT sigTime = comp->getTimeSignatureAt(t, timeSig);
+
+ timeT d = getProvisional(*i, DurationValue);
+ int noteType = Note::getNearestNote(d).getNoteType();
+ (*i)->setMaybe<Int>(m_provisionalNoteType, noteType);
+
+ int maxDepth = 8 - noteType;
+ if (maxDepth < 4) maxDepth = 4;
+ std::vector<int> divisions;
+ timeSig.getDivisions(maxDepth, divisions);
+ if (timeSig == TimeSignature()) // special case for 4/4
+ divisions[0] = 2;
+
+ // At each depth of beat subdivision, we find the closest match
+ // and assign it a score according to distance and depth. The
+ // calculation for the score should accord "better" scores to
+ // shorter distance and lower depth, but it should avoid giving
+ // a "perfect" score to any combination of distance and depth
+ // except where both are 0. Also, the effective depth is
+ // 2 more than the value of our depth counter, which counts
+ // from 0 at a point where the effective depth is already 1.
+
+ timeT base = timeSig.getBarDuration();
+
+ timeT bestBase = -2;
+ long bestScore = 0;
+ bool bestRight = false;
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "quantizeAbsoluteTime: t is " << t << ", d is " << d << endl;
+#endif
+
+ // scoreAbsoluteTimeForBase wants to know the previous starting
+ // note (N) and the previous starting note that ends (roughly)
+ // before this one starts (N'). Much more efficient to calculate
+ // them once now before the loop.
+
+ static timeT shortTime = Note(Note::Shortest).getDuration();
+
+ Segment::iterator j(i);
+ Segment::iterator n(s->end()), nprime(s->end());
+ for (;;) {
+ if (j == s->begin()) break;
+ --j;
+ if ((*j)->isa(Note::EventType)) {
+ if (n == s->end()) n = j;
+ if ((*j)->getAbsoluteTime() + (*j)->getDuration() + shortTime/2
+ <= (*i)->getAbsoluteTime()) {
+ nprime = j;
+ break;
+ }
+ }
+ }
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ if (n != s->end() && n != nprime) {
+ cout << "found n (distinct from nprime) at " << (*n)->getAbsoluteTime() << endl;
+ }
+ if (nprime != s->end()) {
+ cout << "found nprime at " << (*nprime)->getAbsoluteTime()
+ << ", duration " << (*nprime)->getDuration() << endl;
+ }
+#endif
+
+ for (int depth = 0; depth < maxDepth; ++depth) {
+
+ base /= divisions[depth];
+ if (base < m_unit) break;
+ bool right = false;
+ long score = scoreAbsoluteTimeForBase(s, i, depth, base, sigTime,
+ t, d, noteType, n, nprime, right);
+
+ if (depth == 0 || score < bestScore) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << " [*]";
+#endif
+ bestBase = base;
+ bestScore = score;
+ bestRight = right;
+ }
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << endl;
+#endif
+ }
+
+ if (bestBase == -2) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "Quantizer::quantizeAbsoluteTime: weirdness: no snap found" << endl;
+#endif
+ } else {
+ // we need to snap relative to the time sig, not relative
+ // to the start of the whole composition
+ t -= sigTime;
+
+ t = (t / bestBase) * bestBase;
+ if (bestRight) t += bestBase;
+
+/*
+ timeT low = (t / bestBase) * bestBase;
+ timeT high = low + bestBase;
+ t = ((high - t > t - low) ? low : high);
+*/
+
+ t += sigTime;
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "snap base is " << bestBase << ", snapped to " << t << endl;
+#endif
+ }
+
+ setProvisional(*i, AbsoluteTimeValue, t);
+ (*i)->setMaybe<Int>(m_provisionalBase, bestBase);
+ (*i)->setMaybe<Int>(m_provisionalScore, bestScore);
+}
+
+long
+NotationQuantizer::Impl::scoreAbsoluteTimeForBase(Segment *s,
+ const Segment::iterator & /* i */,
+ int depth,
+ timeT base,
+ timeT sigTime,
+ timeT t,
+ timeT d,
+ int noteType,
+ const Segment::iterator &n,
+ const Segment::iterator &nprime,
+ bool &wantRight)
+ const
+{
+ Profiler profiler("NotationQuantizer::Impl::scoreAbsoluteTimeForBase");
+
+ // Lower score is better.
+
+ static timeT shortTime = Note(Note::Shortest).getDuration();
+
+ double simplicityFactor(m_simplicityFactor);
+ simplicityFactor -= Note::Crotchet - noteType;
+ if (simplicityFactor < 10) simplicityFactor = 10;
+
+ double effectiveDepth = pow(depth + 2, simplicityFactor / 10);
+
+ //!!! use velocity to adjust the effective depth as well? -- louder
+ // notes are more likely to be on big boundaries. Actually, perhaps
+ // introduce a generally-useful "salience" score a la Dixon et al
+
+ long leftScore = 0;
+
+ for (int ri = 0; ri < 2; ++ri) {
+
+ bool right = (ri == 1);
+
+ long distance = (t - sigTime) % base;
+ if (right) distance = base - distance;
+ long score = long((distance + shortTime / 2) * effectiveDepth);
+
+ double penalty1 = 1.0;
+
+ // seriously penalise moving a note beyond its own end time
+ if (d > 0 && right && distance >= d * 0.9) {
+ penalty1 = double(distance) / d + 0.5;
+ }
+
+ double penalty2 = 1.0;
+
+ // Examine the previous starting note (N), and the previous
+ // starting note that ends before this one starts (N').
+
+ // We should penalise moving this note to before the performed end
+ // of N' and seriously penalise moving it to the same quantized
+ // start time as N' -- but we should encourage moving it to the
+ // same time as the provisional end of N', or to the same start
+ // time as N if N != N'.
+
+ if (!right) {
+ if (n != s->end()) {
+ if (n != nprime) {
+ timeT nt = getProvisional(*n, AbsoluteTimeValue);
+ if (t - distance == nt) penalty2 = penalty2 * 2 / 3;
+ }
+ if (nprime != s->end()) {
+ timeT npt = getProvisional(*nprime, AbsoluteTimeValue);
+ timeT npd = getProvisional(*nprime, DurationValue);
+ if (t - distance <= npt) penalty2 *= 4;
+ else if (t - distance < npt + npd) penalty2 *= 2;
+ else if (t - distance == npt + npd) penalty2 = penalty2 * 2 / 3;
+ }
+ }
+ }
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << " depth/eff/dist/t/score/pen1/pen2/res: " << depth << "/" << effectiveDepth << "/" << distance << "/" << (right ? t + distance : t - distance) << "/" << score << "/" << penalty1 << "/" << penalty2 << "/" << (score * penalty1 * penalty2);
+ if (right) cout << " -> ";
+ else cout << " <- ";
+ if (ri == 0) cout << endl;
+#endif
+
+ score = long(score * penalty1);
+ score = long(score * penalty2);
+
+ if (ri == 0) {
+ leftScore = score;
+ } else {
+ if (score < leftScore) {
+ wantRight = true;
+ return score;
+ } else {
+ wantRight = false;
+ return leftScore;
+ }
+ }
+ }
+
+ return leftScore;
+}
+
+void
+NotationQuantizer::Impl::quantizeDurationProvisional(Segment *, Segment::iterator i)
+ const
+{
+ Profiler profiler("NotationQuantizer::Impl::quantizeDurationProvisional");
+
+ // Calculate a first guess at the likely notation duration based
+ // only on its performed duration, without considering start time.
+
+ timeT d = m_q->getFromSource(*i, DurationValue);
+ if (d == 0) {
+ setProvisional(*i, DurationValue, d);
+ return;
+ }
+
+ Note shortNote = Note::getNearestNote(d, 2);
+
+ timeT shortTime = shortNote.getDuration();
+ timeT time = shortTime;
+
+ if (shortTime != d) {
+
+ Note longNote(shortNote);
+
+ if ((shortNote.getDots() > 0 ||
+ shortNote.getNoteType() == Note::Shortest)) { // can't dot that
+
+ if (shortNote.getNoteType() < Note::Longest) {
+ longNote = Note(shortNote.getNoteType() + 1, 0);
+ }
+
+ } else {
+ longNote = Note(shortNote.getNoteType(), 1);
+ }
+
+ timeT longTime = longNote.getDuration();
+
+ // we should prefer to round up to a note with fewer dots rather
+ // than down to one with more
+
+ //!!! except in dotted time, etc -- we really want this to work on a
+ // similar attraction-to-grid basis to the abstime quantization
+
+ if ((longNote.getDots() + 1) * (longTime - d) <
+ (shortNote.getDots() + 1) * (d - shortTime)) {
+ time = longTime;
+ }
+ }
+
+ setProvisional(*i, DurationValue, time);
+
+ if ((*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
+ // We're going to recalculate these, and use our own results
+ (*i)->unset(BEAMED_GROUP_ID);
+ (*i)->unset(BEAMED_GROUP_TYPE);
+ (*i)->unset(BEAMED_GROUP_TUPLET_BASE);
+ (*i)->unset(BEAMED_GROUP_TUPLED_COUNT);
+ (*i)->unset(BEAMED_GROUP_UNTUPLED_COUNT);
+//!!! (*i)->unset(TUPLET_NOMINAL_DURATION);
+ }
+}
+
+void
+NotationQuantizer::Impl::quantizeDuration(Segment *s, Chord &c) const
+{
+ static int totalFracCount = 0;
+ static float totalFrac = 0;
+
+ Profiler profiler("NotationQuantizer::Impl::quantizeDuration");
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "quantizeDuration: chord has " << c.size() << " notes" << endl;
+#endif
+
+ Composition *comp = s->getComposition();
+
+ TimeSignature timeSig;
+// timeT t = m_q->getFromSource(*c.getInitialElement(), AbsoluteTimeValue);
+// timeT sigTime = comp->getTimeSignatureAt(t, timeSig);
+
+ timeT d = getProvisional(*c.getInitialElement(), DurationValue);
+ int noteType = Note::getNearestNote(d).getNoteType();
+ int maxDepth = 8 - noteType;
+ if (maxDepth < 4) maxDepth = 4;
+ std::vector<int> divisions;
+ timeSig.getDivisions(maxDepth, divisions);
+
+ Segment::iterator nextNote = c.getNextNote();
+ timeT nextNoteTime =
+ (s->isBeforeEndMarker(nextNote) ?
+ getProvisional(*nextNote, AbsoluteTimeValue) :
+ s->getEndMarkerTime());
+
+ timeT nonContrapuntalDuration = 0;
+
+ for (Chord::iterator ci = c.begin(); ci != c.end(); ++ci) {
+
+ if (!(**ci)->isa(Note::EventType)) continue;
+ if ((**ci)->has(m_provisionalDuration) &&
+ (**ci)->has(BEAMED_GROUP_TUPLET_BASE)) {
+ // dealt with already in tuplet code, we'd only mess it up here
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "not recalculating duration for tuplet" << endl;
+#endif
+ continue;
+ }
+
+ timeT ud = 0;
+
+ if (!m_contrapuntal) {
+ // if not contrapuntal, give all notes in chord equal duration
+ if (nonContrapuntalDuration > 0) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "setting duration trivially to " << nonContrapuntalDuration << endl;
+#endif
+ setProvisional(**ci, DurationValue, nonContrapuntalDuration);
+ continue;
+ } else {
+ // establish whose duration to use, then set it at the
+ // bottom after it's been quantized
+ Segment::iterator li = c.getLongestElement();
+ if (li != s->end()) ud = m_q->getFromSource(*li, DurationValue);
+ else ud = m_q->getFromSource(**ci, DurationValue);
+ }
+ } else {
+ ud = m_q->getFromSource(**ci, DurationValue);
+ }
+
+ timeT qt = getProvisional(**ci, AbsoluteTimeValue);
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "note at time " << (**ci)->getAbsoluteTime() << " (provisional time " << qt << ")" << endl;
+#endif
+
+ timeT base = timeSig.getBarDuration();
+ std::pair<timeT, timeT> bases;
+ for (int depth = 0; depth < maxDepth; ++depth) {
+ if (base >= ud) {
+ bases = std::pair<timeT, timeT>(base / divisions[depth], base);
+ }
+ base /= divisions[depth];
+ }
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "duration is " << ud << ", probably between "
+ << bases.first << " and " << bases.second << endl;
+#endif
+
+ timeT qd = getProvisional(**ci, DurationValue);
+
+ timeT spaceAvailable = nextNoteTime - qt;
+
+ if (spaceAvailable > 0) {
+ float frac = float(ud) / float(spaceAvailable);
+ totalFrac += frac;
+ totalFracCount += 1;
+ }
+
+ if (!m_contrapuntal && qd > spaceAvailable) {
+
+ qd = Note::getNearestNote(spaceAvailable).getDuration();
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "non-contrapuntal segment, rounded duration down to "
+ << qd << " (as only " << spaceAvailable << " available)"
+ << endl;
+#endif
+
+ } else {
+
+ //!!! Note longer than the longest note we have. Deal with
+ //this -- how? Quantize the end time? Split the note?
+ //(Prefer to do that in a separate phase later if requested.)
+ //Leave it as it is? (Yes, for now.)
+ if (bases.first == 0) return;
+
+ timeT absTimeBase = bases.first;
+ (**ci)->get<Int>(m_provisionalBase, absTimeBase);
+
+ spaceAvailable = std::min(spaceAvailable,
+ comp->getBarEndForTime(qt) - qt);
+
+ // We have a really good possibility of staccato if we have a
+ // note on a boundary whose base is double the note duration
+ // and there's nothing else until the next boundary and we're
+ // shorter than about a quaver (i.e. the base is a quaver or
+ // less)
+
+ if (qd*2 <= absTimeBase && (qd*8/3) >= absTimeBase &&
+ bases.second == absTimeBase) {
+
+ if (nextNoteTime >= qt + bases.second) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "We rounded to " << qd
+ << " but we're on " << absTimeBase << " absTimeBase"
+ << " and the next base is " << bases.second
+ << " and we have room for it, so"
+ << " rounding up again" << endl;
+#endif
+ qd = bases.second;
+ }
+
+ } else {
+
+ // Alternatively, if we rounded down but there's space to
+ // round up, consider doing so
+
+ //!!! mark staccato if necessary, and take existing marks into account
+
+ Note note(Note::getNearestNote(qd));
+
+ if (qd < ud || (qd == ud && note.getDots() == 2)) {
+
+ if (note.getNoteType() < Note::Longest) {
+
+ if (bases.second <= spaceAvailable) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "We rounded down to " << qd
+ << " but have room for " << bases.second
+ << ", rounding up again" << endl;
+#endif
+ qd = bases.second;
+ } else {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "We rounded down to " << qd
+ << "; can't fit " << bases.second << endl;
+#endif
+ }
+ }
+ }
+ }
+ }
+
+ setProvisional(**ci, DurationValue, qd);
+ if (!m_contrapuntal) nonContrapuntalDuration = qd;
+ }
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "totalFrac " << totalFrac << ", totalFracCount " << totalFracCount << ", avg " << (totalFracCount > 0 ? (totalFrac / totalFracCount) : 0) << endl;
+#endif
+}
+
+
+void
+NotationQuantizer::Impl::scanTupletsInBar(Segment *s,
+ timeT barStart,
+ timeT barDuration,
+ timeT wholeStart,
+ timeT wholeEnd,
+ const std::vector<int> &divisions) const
+{
+ Profiler profiler("NotationQuantizer::Impl::scanTupletsInBar");
+
+ //!!! need to further constrain the area scanned so as to cope with
+ // partial bars
+
+ timeT base = barDuration;
+
+ for (int depth = -1; depth < int(divisions.size()) - 2; ++depth) {
+
+ if (depth >= 0) base /= divisions[depth];
+ if (base <= Note(Note::Semiquaver).getDuration()) break;
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "\nscanTupletsInBar: trying at depth " << depth << " (base " << base << ")" << endl;
+#endif
+
+ // check for triplets if our next divisor is 2 and the following
+ // one is not 3
+
+ if (divisions[depth+1] != 2 || divisions[depth+2] == 3) continue;
+
+ timeT tupletBase = base / 3;
+ timeT tupletStart = barStart;
+
+ while (tupletStart < barStart + barDuration) {
+
+ timeT tupletEnd = tupletStart + base;
+ if (tupletStart < wholeStart || tupletEnd > wholeEnd) {
+ tupletStart = tupletEnd;
+ continue;
+ }
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "scanTupletsInBar: testing " << tupletStart << "," << base << " at tuplet base " << tupletBase << endl;
+#endif
+
+ // find first note within a certain distance whose start time
+ // quantized to tupletStart or greater
+ Segment::iterator j = s->findTime(tupletStart - tupletBase / 3);
+ timeT jTime = tupletEnd;
+
+ while (s->isBeforeEndMarker(j) &&
+ (!(*j)->isa(Note::EventType) ||
+ !(*j)->get<Int>(m_provisionalAbsTime, jTime) ||
+ jTime < tupletStart)) {
+ if ((*j)->getAbsoluteTime() > tupletEnd + tupletBase / 3) {
+ break;
+ }
+ ++j;
+ }
+
+ if (jTime >= tupletEnd) { // nothing to make tuplets of
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "scanTupletsInBar: nothing here" << endl;
+#endif
+ tupletStart = tupletEnd;
+ continue;
+ }
+
+ scanTupletsAt(s, j, depth+1, base, barStart,
+ tupletStart, tupletBase);
+
+ tupletStart = tupletEnd;
+ }
+ }
+}
+
+
+void
+NotationQuantizer::Impl::scanTupletsAt(Segment *s,
+ Segment::iterator i,
+ int depth,
+ timeT base,
+ timeT sigTime,
+ timeT tupletStart,
+ timeT tupletBase) const
+{
+ Profiler profiler("NotationQuantizer::Impl::scanTupletsAt");
+
+ Segment::iterator j = i;
+ timeT tupletEnd = tupletStart + base;
+ timeT jTime = tupletEnd;
+
+ std::vector<Event *> candidates;
+ int count = 0;
+
+ while (s->isBeforeEndMarker(j) &&
+ ((*j)->isa(Note::EventRestType) ||
+ ((*j)->get<Int>(m_provisionalAbsTime, jTime) &&
+ jTime < tupletEnd))) {
+
+ if (!(*j)->isa(Note::EventType)) { ++j; continue; }
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "scanTupletsAt time " << jTime << " (unquantized "
+ << (*j)->getAbsoluteTime() << "), found note" << endl;
+#endif
+
+ // reject any group containing anything already a tuplet
+ if ((*j)->has(BEAMED_GROUP_TUPLET_BASE)) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "already made tuplet here" << endl;
+#endif
+ return;
+ }
+
+ timeT originalBase;
+
+ if (!(*j)->get<Int>(m_provisionalBase, originalBase)) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "some notes not provisionally quantized, no good" << endl;
+#endif
+ return;
+ }
+
+ if (originalBase == base) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "accepting note at original base" << endl;
+#endif
+ candidates.push_back(*j);
+ } else if (((jTime - sigTime) % base) == 0) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "accepting note that happens to lie on original base" << endl;
+#endif
+ candidates.push_back(*j);
+ } else {
+
+ // This is a note that did not quantize to the original base
+ // (the first note in the tuplet would have, but we can't tell
+ // anything from that). Reject the entire group if it fails
+ // any of the likelihood tests for tuplets.
+
+ if (!isValidTupletAt(s, j, depth, base, sigTime, tupletBase)) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "no good" << endl;
+#endif
+ return;
+ }
+
+ candidates.push_back(*j);
+ ++count;
+ }
+
+ ++j;
+ }
+
+ // must have at least one note that is not already quantized to the
+ // original base
+ if (count < 1) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "scanTupletsAt: found no note not already quantized to " << base << endl;
+#endif
+ return;
+ }
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "scanTupletsAt: Tuplet group of duration " << base << " starting at " << tupletStart << endl;
+#endif
+
+ // Woo-hoo! It looks good.
+
+ int groupId = s->getNextId();
+ std::map<int, bool> multiples;
+
+ for (std::vector<Event *>::iterator ei = candidates.begin();
+ ei != candidates.end(); ++ei) {
+
+ //!!! Interesting -- we can't modify rests here, but Segment's
+ // normalizeRests won't insert the correct sort of rest for us...
+ // what to do?
+ //!!! insert a tupleted rest, and prevent Segment::normalizeRests
+ // from messing about with it
+ if (!(*ei)->isa(Note::EventType)) continue;
+ (*ei)->set<String>(BEAMED_GROUP_TYPE, GROUP_TYPE_TUPLED);
+
+ //!!! This is too easy, because we rejected any notes of
+ //durations not conforming to a single multiple of the
+ //tupletBase in isValidTupletAt
+
+ (*ei)->set<Int>(BEAMED_GROUP_ID, groupId);
+ (*ei)->set<Int>(BEAMED_GROUP_TUPLET_BASE, base/2); //!!! wrong if tuplet count != 3
+ (*ei)->set<Int>(BEAMED_GROUP_TUPLED_COUNT, 2); //!!! as above
+ (*ei)->set<Int>(BEAMED_GROUP_UNTUPLED_COUNT, base/tupletBase);
+
+ timeT t = (*ei)->getAbsoluteTime();
+ t -= tupletStart;
+ timeT low = (t / tupletBase) * tupletBase;
+ timeT high = low + tupletBase;
+ t = ((high - t > t - low) ? low : high);
+
+ multiples[t / tupletBase] = true;
+
+ t += tupletStart;
+
+ setProvisional(*ei, AbsoluteTimeValue, t);
+ setProvisional(*ei, DurationValue, tupletBase);
+ }
+
+ // fill in with tupleted rests
+
+ for (int m = 0; m < base / tupletBase; ++m) {
+
+ if (multiples[m]) continue;
+
+ timeT absTime = tupletStart + m * tupletBase;
+ timeT duration = tupletBase;
+//!!! while (multiples[++m]) duration += tupletBase;
+
+ Event *rest = new Event(Note::EventRestType, absTime, duration);
+
+ rest->set<String>(BEAMED_GROUP_TYPE, GROUP_TYPE_TUPLED);
+ rest->set<Int>(BEAMED_GROUP_ID, groupId);
+ rest->set<Int>(BEAMED_GROUP_TUPLET_BASE, base/2); //!!! wrong if tuplet count != 3
+ rest->set<Int>(BEAMED_GROUP_TUPLED_COUNT, 2); //!!! as above
+ rest->set<Int>(BEAMED_GROUP_UNTUPLED_COUNT, base/tupletBase);
+
+ m_q->m_toInsert.push_back(rest);
+ }
+}
+
+bool
+NotationQuantizer::Impl::isValidTupletAt(Segment *s,
+ const Segment::iterator &i,
+ int depth,
+ timeT /* base */,
+ timeT sigTime,
+ timeT tupletBase) const
+{
+ Profiler profiler("NotationQuantizer::Impl::isValidTupletAt");
+
+ //!!! This is basically wrong; we need to be able to deal with groups
+ // that contain e.g. a crotchet and a quaver, tripleted.
+
+ timeT ud = m_q->getFromSource(*i, DurationValue);
+
+ if (ud > (tupletBase * 5 / 4)) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "\nNotationQuantizer::isValidTupletAt: note too long at "
+ << (*i)->getDuration() << " (tupletBase is " << tupletBase << ")"
+ << endl;
+#endif
+ return false; // too long
+ }
+
+ //!!! This bit is a cop-out. It means we reject anything that looks
+ // like it's going to have rests in it. Bah.
+ if (ud <= (tupletBase * 3 / 8)) {
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "\nNotationQuantizer::isValidTupletAt: note too short at "
+ << (*i)->getDuration() << " (tupletBase is " << tupletBase << ")"
+ << endl;
+#endif
+ return false;
+ }
+
+ long score = 0;
+ if (!(*i)->get<Int>(m_provisionalScore, score)) return false;
+
+ timeT t = m_q->getFromSource(*i, AbsoluteTimeValue);
+ timeT d = getProvisional(*i, DurationValue);
+ int noteType = (*i)->get<Int>(m_provisionalNoteType);
+
+ //!!! not as complete as the calculation we do in the original scoring
+ bool dummy;
+ long tupletScore = scoreAbsoluteTimeForBase
+ (s, i, depth, tupletBase, sigTime, t, d, noteType, s->end(), s->end(), dummy);
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "\nNotationQuantizer::isValidTupletAt: score " << score
+ << " vs tupletScore " << tupletScore << endl;
+#endif
+ return (tupletScore < score);
+}
+
+
+void
+NotationQuantizer::quantizeRange(Segment *s,
+ Segment::iterator from,
+ Segment::iterator to) const
+{
+ m_impl->quantizeRange(s, from, to);
+}
+
+void
+NotationQuantizer::Impl::quantizeRange(Segment *s,
+ Segment::iterator from,
+ Segment::iterator to) const
+{
+ Profiler *profiler = new Profiler("NotationQuantizer::Impl::quantizeRange");
+
+ clock_t start = clock();
+ int events = 0, notes = 0, passes = 0;
+ int setGood = 0, setBad = 0;
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "NotationQuantizer::Impl::quantizeRange: from time "
+ << (from == s->end() ? -1 : (*from)->getAbsoluteTime())
+ << " to "
+ << (to == s->end() ? -1 : (*to)->getAbsoluteTime())
+ << endl;
+#endif
+
+ timeT segmentEndTime = s->getEndMarkerTime();
+
+ // This process does several passes over the data. It's assumed
+ // that this is not going to be invoked in any really time-critical
+ // place.
+
+ // Calculate absolute times on the first pass, so that we know
+ // which things are chords. We need to assign absolute times to
+ // all events, but we only need do durations for notes.
+
+ PropertyName provisionalBase("notationquantizer-provisionalBase");
+
+ // We don't use setToTarget until we have our final values ready,
+ // as it erases and replaces the events. Just set the properties.
+
+ // Set a provisional duration to each note first
+
+ for (Segment::iterator i = from; i != to; ++i) {
+
+ ++events;
+ if ((*i)->isa(Note::EventRestType)) continue;
+ if ((*i)->isa(Note::EventType)) ++notes;
+ quantizeDurationProvisional(s, i);
+ }
+ ++passes;
+
+ // now do the absolute-time calculation
+
+ timeT wholeStart = 0, wholeEnd = 0;
+
+ Segment::iterator i = from;
+
+ for (Segment::iterator nexti = i; i != to; i = nexti) {
+
+ ++nexti;
+
+ if ((*i)->isa(Note::EventRestType)) {
+ if (i == from) ++from;
+ s->erase(i);
+ continue;
+ }
+
+ quantizeAbsoluteTime(s, i);
+
+ timeT t0 = (*i)->get<Int>(m_provisionalAbsTime);
+ timeT t1 = (*i)->get<Int>(m_provisionalDuration) + t0;
+ if (wholeStart == wholeEnd) {
+ wholeStart = t0;
+ wholeEnd = t1;
+ } else if (t1 > wholeEnd) {
+ wholeEnd = t1;
+ }
+ }
+ ++passes;
+
+ // now we've grouped into chords, look for tuplets next
+
+ Composition *comp = s->getComposition();
+
+ if (m_maxTuplet >= 2) {
+
+ std::vector<int> divisions;
+ comp->getTimeSignatureAt(wholeStart).getDivisions(7, divisions);
+
+ for (int barNo = comp->getBarNumber(wholeStart);
+ barNo <= comp->getBarNumber(wholeEnd); ++barNo) {
+
+ bool isNew = false;
+ TimeSignature timeSig = comp->getTimeSignatureInBar(barNo, isNew);
+ if (isNew) timeSig.getDivisions(7, divisions);
+ scanTupletsInBar(s, comp->getBarStart(barNo),
+ timeSig.getBarDuration(),
+ wholeStart, wholeEnd, divisions);
+ }
+ ++passes;
+ }
+
+ ProvisionalQuantizer provisionalQuantizer((Impl *)this);
+
+ for (i = from; i != to; ++i) {
+
+ if (!(*i)->isa(Note::EventType)) continue;
+
+ // could potentially supply clef and key here, but at the
+ // moment Chord doesn't do anything with them (unlike
+ // NotationChord) and we don't have any really clever
+ // ideas for how to use them here anyway
+// Chord c(*s, i, m_q);
+ Chord c(*s, i, &provisionalQuantizer);
+
+ quantizeDuration(s, c);
+
+ bool ended = false;
+ for (Segment::iterator ci = c.getInitialElement();
+ s->isBeforeEndMarker(ci); ++ci) {
+ if (ci == to) ended = true;
+ if (ci == c.getFinalElement()) break;
+ }
+ if (ended) break;
+
+ i = c.getFinalElement();
+ }
+ ++passes;
+
+ // staccato (we now do slurs separately, in SegmentNotationHelper::autoSlur)
+
+ if (m_articulate) {
+
+ for (i = from; i != to; ++i) {
+
+ if (!(*i)->isa(Note::EventType)) continue;
+
+ timeT qd = getProvisional(*i, DurationValue);
+ timeT ud = m_q->getFromSource(*i, DurationValue);
+
+ if (ud < (qd * 3 / 4) &&
+ qd <= Note(Note::Crotchet).getDuration()) {
+ Marks::addMark(**i, Marks::Staccato, true);
+ } else if (ud > qd) {
+ Marks::addMark(**i, Marks::Tenuto, true);
+ }
+ }
+ ++passes;
+ }
+
+ i = from;
+
+ for (Segment::iterator nexti = i; i != to; i = nexti) {
+
+ ++nexti;
+
+ if ((*i)->isa(Note::EventRestType)) continue;
+
+ timeT t = getProvisional(*i, AbsoluteTimeValue);
+ timeT d = getProvisional(*i, DurationValue);
+
+ unsetProvisionalProperties(*i);
+
+ if ((*i)->getAbsoluteTime() == t &&
+ (*i)->getDuration() == d) ++setBad;
+ else ++setGood;
+
+#ifdef DEBUG_NOTATION_QUANTIZER
+ cout << "Setting to target at " << t << "," << d << endl;
+#endif
+
+ m_q->setToTarget(s, i, t, d);
+ }
+ ++passes;
+/*
+ cerr << "NotationQuantizer: " << events << " events ("
+ << notes << " notes), " << passes << " passes, "
+ << setGood << " good sets, " << setBad << " bad sets, "
+ << ((clock() - start) * 1000 / CLOCKS_PER_SEC) << "ms elapsed"
+ << endl;
+*/
+ if (s->getEndTime() < segmentEndTime) {
+ s->setEndMarkerTime(segmentEndTime);
+ }
+
+ delete profiler; // on heap so it updates before the next line:
+ Profiles::getInstance()->dump();
+
+}
+
+
+}
+