You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
494 lines
12 KiB
494 lines
12 KiB
/*
|
|
Rosegarden
|
|
A sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <bownie@bownie.com>
|
|
|
|
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 "Quantizer.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 {
|
|
|
|
Quantizer::Quantizer(std::string source,
|
|
std::string target) :
|
|
m_source(source), m_target(target)
|
|
{
|
|
makePropertyNames();
|
|
}
|
|
|
|
|
|
Quantizer::Quantizer(std::string target) :
|
|
m_target(target)
|
|
{
|
|
if (target == RawEventData) {
|
|
m_source = GlobalSource;
|
|
} else {
|
|
m_source = RawEventData;
|
|
}
|
|
|
|
makePropertyNames();
|
|
}
|
|
|
|
|
|
Quantizer::~Quantizer()
|
|
{
|
|
// nothing
|
|
}
|
|
|
|
void
|
|
Quantizer::quantize(Segment *s) const
|
|
{
|
|
quantize(s, s->begin(), s->getEndMarker());
|
|
}
|
|
|
|
void
|
|
Quantizer::quantize(Segment *s,
|
|
Segment::iterator from,
|
|
Segment::iterator to) const
|
|
{
|
|
assert(m_toInsert.size() == 0);
|
|
|
|
quantizeRange(s, from, to);
|
|
|
|
insertNewEvents(s);
|
|
}
|
|
|
|
void
|
|
Quantizer::quantize(EventSelection *selection)
|
|
{
|
|
assert(m_toInsert.size() == 0);
|
|
|
|
Segment &segment = selection->getSegment();
|
|
|
|
// Attempt to handle non-contiguous selections.
|
|
|
|
// We have to be a bit careful here, because the rest-
|
|
// normalisation that's carried out as part of a quantize
|
|
// process is liable to replace the event that follows
|
|
// the quantized range. (moved here from editcommands.cpp)
|
|
|
|
EventSelection::RangeList ranges(selection->getRanges());
|
|
|
|
// So that we can retrieve a list of new events we cheat and stop
|
|
// the m_toInsert vector from being cleared automatically. Remember
|
|
// to turn it back on.
|
|
//
|
|
|
|
EventSelection::RangeList::iterator r = ranges.end();
|
|
while (r-- != ranges.begin()) {
|
|
|
|
/*
|
|
cerr << "Quantizer: quantizing range ";
|
|
if (r->first == segment.end()) {
|
|
cerr << "end";
|
|
} else {
|
|
cerr << (*r->first)->getAbsoluteTime();
|
|
}
|
|
cerr << " to ";
|
|
if (r->second == segment.end()) {
|
|
cerr << "end";
|
|
} else {
|
|
cerr << (*r->second)->getAbsoluteTime();
|
|
}
|
|
cerr << endl;
|
|
*/
|
|
|
|
quantizeRange(&segment, r->first, r->second);
|
|
}
|
|
|
|
// Push the new events to the selection
|
|
for (int i = 0; i < m_toInsert.size(); ++i) {
|
|
selection->addEvent(m_toInsert[i]);
|
|
}
|
|
|
|
// and then to the segment
|
|
insertNewEvents(&segment);
|
|
}
|
|
|
|
|
|
void
|
|
Quantizer::fixQuantizedValues(Segment *s,
|
|
Segment::iterator from,
|
|
Segment::iterator to) const
|
|
{
|
|
assert(m_toInsert.size() == 0);
|
|
|
|
quantize(s, from, to);
|
|
|
|
if (m_target == RawEventData) return;
|
|
|
|
for (Segment::iterator nextFrom = from; from != to; from = nextFrom) {
|
|
|
|
++nextFrom;
|
|
|
|
timeT t = getFromTarget(*from, AbsoluteTimeValue);
|
|
timeT d = getFromTarget(*from, DurationValue);
|
|
Event *e = new Event(**from, t, d);
|
|
s->erase(from);
|
|
m_toInsert.push_back(e);
|
|
}
|
|
|
|
insertNewEvents(s);
|
|
}
|
|
|
|
|
|
timeT
|
|
Quantizer::getQuantizedDuration(const Event *e) const
|
|
{
|
|
if (m_target == RawEventData) {
|
|
return e->getDuration();
|
|
} else if (m_target == NotationPrefix) {
|
|
return e->getNotationDuration();
|
|
} else {
|
|
timeT d = e->getDuration();
|
|
e->get<Int>(m_targetProperties[DurationValue], d);
|
|
return d;
|
|
}
|
|
}
|
|
|
|
timeT
|
|
Quantizer::getQuantizedAbsoluteTime(const Event *e) const
|
|
{
|
|
if (m_target == RawEventData) {
|
|
return e->getAbsoluteTime();
|
|
} else if (m_target == NotationPrefix) {
|
|
return e->getNotationAbsoluteTime();
|
|
} else {
|
|
timeT t = e->getAbsoluteTime();
|
|
e->get<Int>(m_targetProperties[AbsoluteTimeValue], t);
|
|
return t;
|
|
}
|
|
}
|
|
|
|
timeT
|
|
Quantizer::getUnquantizedAbsoluteTime(Event *e) const
|
|
{
|
|
return getFromSource(e, AbsoluteTimeValue);
|
|
}
|
|
|
|
timeT
|
|
Quantizer::getUnquantizedDuration(Event *e) const
|
|
{
|
|
return getFromSource(e, DurationValue);
|
|
}
|
|
|
|
void
|
|
Quantizer::quantizeRange(Segment *s,
|
|
Segment::iterator from,
|
|
Segment::iterator to) const
|
|
{
|
|
//!!! It is vital that ordering is maintained after quantization.
|
|
// That is, an event whose absolute time quantizes to a time t must
|
|
// appear in the original segment before all events whose times
|
|
// quantize to greater than t. This means we must quantize the
|
|
// absolute times of non-note events as well as notes.
|
|
|
|
// We don't need to worry about quantizing rests, however; they're
|
|
// only used for notation and will be explicitly recalculated when
|
|
// the notation quantization values change.
|
|
|
|
for (Segment::iterator nextFrom = from; from != to; from = nextFrom) {
|
|
|
|
++nextFrom;
|
|
quantizeSingle(s, from);
|
|
}
|
|
}
|
|
|
|
void
|
|
Quantizer::unquantize(Segment *s,
|
|
Segment::iterator from,
|
|
Segment::iterator to) const
|
|
{
|
|
assert(m_toInsert.size() == 0);
|
|
|
|
for (Segment::iterator nextFrom = from; from != to; from = nextFrom) {
|
|
++nextFrom;
|
|
|
|
if (m_target == RawEventData || m_target == NotationPrefix) {
|
|
setToTarget(s, from,
|
|
getFromSource(*from, AbsoluteTimeValue),
|
|
getFromSource(*from, DurationValue));
|
|
|
|
} else {
|
|
removeTargetProperties(*from);
|
|
}
|
|
}
|
|
|
|
insertNewEvents(s);
|
|
}
|
|
|
|
void
|
|
Quantizer::unquantize(EventSelection *selection) const
|
|
{
|
|
assert(m_toInsert.size() == 0);
|
|
|
|
Segment *s = &selection->getSegment();
|
|
|
|
Rosegarden::EventSelection::eventcontainer::iterator it
|
|
= selection->getSegmentEvents().begin();
|
|
|
|
for (; it != selection->getSegmentEvents().end(); it++) {
|
|
|
|
if (m_target == RawEventData || m_target == NotationPrefix) {
|
|
|
|
Segment::iterator from = s->findSingle(*it);
|
|
Segment::iterator to = s->findSingle(*it);
|
|
setToTarget(s, from,
|
|
getFromSource(*from, AbsoluteTimeValue),
|
|
getFromSource(*to, DurationValue));
|
|
|
|
} else {
|
|
removeTargetProperties(*it);
|
|
}
|
|
}
|
|
|
|
insertNewEvents(&selection->getSegment());
|
|
}
|
|
|
|
|
|
|
|
timeT
|
|
Quantizer::getFromSource(Event *e, ValueType v) const
|
|
{
|
|
Profiler profiler("Quantizer::getFromSource");
|
|
|
|
// cerr << "Quantizer::getFromSource: source is \"" << m_source << "\"" << endl;
|
|
|
|
if (m_source == RawEventData) {
|
|
|
|
if (v == AbsoluteTimeValue) return e->getAbsoluteTime();
|
|
else return e->getDuration();
|
|
|
|
} else if (m_source == NotationPrefix) {
|
|
|
|
if (v == AbsoluteTimeValue) return e->getNotationAbsoluteTime();
|
|
else return e->getNotationDuration();
|
|
|
|
} else {
|
|
|
|
// We need to write the source from the target if the
|
|
// source doesn't exist (and the target does)
|
|
|
|
bool haveSource = e->has(m_sourceProperties[v]);
|
|
bool haveTarget = ((m_target == RawEventData) ||
|
|
(e->has(m_targetProperties[v])));
|
|
timeT t = 0;
|
|
|
|
if (!haveSource && haveTarget) {
|
|
t = getFromTarget(e, v);
|
|
e->setMaybe<Int>(m_sourceProperties[v], t);
|
|
return t;
|
|
}
|
|
|
|
e->get<Int>(m_sourceProperties[v], t);
|
|
return t;
|
|
}
|
|
}
|
|
|
|
timeT
|
|
Quantizer::getFromTarget(Event *e, ValueType v) const
|
|
{
|
|
Profiler profiler("Quantizer::getFromTarget");
|
|
|
|
if (m_target == RawEventData) {
|
|
|
|
if (v == AbsoluteTimeValue) return e->getAbsoluteTime();
|
|
else return e->getDuration();
|
|
|
|
} else if (m_target == NotationPrefix) {
|
|
|
|
if (v == AbsoluteTimeValue) return e->getNotationAbsoluteTime();
|
|
else return e->getNotationDuration();
|
|
|
|
} else {
|
|
timeT value;
|
|
if (v == AbsoluteTimeValue) value = e->getAbsoluteTime();
|
|
else value = e->getDuration();
|
|
e->get<Int>(m_targetProperties[v], value);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
void
|
|
Quantizer::setToTarget(Segment *s, Segment::iterator i,
|
|
timeT absTime, timeT duration) const
|
|
{
|
|
Profiler profiler("Quantizer::setToTarget");
|
|
|
|
//cerr << "Quantizer::setToTarget: target is \"" << m_target << "\", absTime is " << absTime << ", duration is " << duration << " (unit is " << m_unit << ", original values are absTime " << (*i)->getAbsoluteTime() << ", duration " << (*i)->getDuration() << ")" << endl;
|
|
|
|
timeT st = 0, sd = 0;
|
|
bool haveSt = false, haveSd = false;
|
|
if (m_source != RawEventData && m_target == RawEventData) {
|
|
haveSt = (*i)->get<Int>(m_sourceProperties[AbsoluteTimeValue], st);
|
|
haveSd = (*i)->get<Int>(m_sourceProperties[DurationValue], sd);
|
|
}
|
|
|
|
Event *e;
|
|
if (m_target == RawEventData) {
|
|
e = new Event(**i, absTime, duration);
|
|
} else if (m_target == NotationPrefix) {
|
|
// Setting the notation absolute time on an event without
|
|
// recreating it would be dodgy, just as setting the absolute
|
|
// time would, because it could change the ordering of events
|
|
// that are already being referred to in ViewElementLists,
|
|
// preventing us from locating them in the ViewElementLists
|
|
// because their ordering would have silently changed
|
|
#ifdef DEBUG_NOTATION_QUANTIZER
|
|
cout << "Quantizer: setting " << absTime << " to notation absolute time and "
|
|
<< duration << " to notation duration"
|
|
<< endl;
|
|
#endif
|
|
e = new Event(**i, (*i)->getAbsoluteTime(), (*i)->getDuration(),
|
|
(*i)->getSubOrdering(), absTime, duration);
|
|
} else {
|
|
e = *i;
|
|
e->clearNonPersistentProperties();
|
|
}
|
|
|
|
if (m_target == NotationPrefix) {
|
|
timeT normalizeStart = std::min(absTime, (*i)->getAbsoluteTime());
|
|
timeT normalizeEnd = std::max(absTime + duration,
|
|
(*i)->getAbsoluteTime() +
|
|
(*i)->getDuration()) + 1;
|
|
|
|
if (m_normalizeRegion.first != m_normalizeRegion.second) {
|
|
normalizeStart = std::min(normalizeStart, m_normalizeRegion.first);
|
|
normalizeEnd = std::max(normalizeEnd, m_normalizeRegion.second);
|
|
}
|
|
|
|
m_normalizeRegion = std::pair<timeT, timeT>
|
|
(normalizeStart, normalizeEnd);
|
|
}
|
|
|
|
if (haveSt) e->setMaybe<Int>(m_sourceProperties[AbsoluteTimeValue],st);
|
|
if (haveSd) e->setMaybe<Int>(m_sourceProperties[DurationValue], sd);
|
|
|
|
if (m_target != RawEventData && m_target != NotationPrefix) {
|
|
e->setMaybe<Int>(m_targetProperties[AbsoluteTimeValue], absTime);
|
|
e->setMaybe<Int>(m_targetProperties[DurationValue], duration);
|
|
} else {
|
|
s->erase(i);
|
|
m_toInsert.push_back(e);
|
|
}
|
|
|
|
#ifdef DEBUG_NOTATION_QUANTIZER
|
|
cout << "m_toInsert.size() is now " << m_toInsert.size() << endl;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
Quantizer::removeProperties(Event *e) const
|
|
{
|
|
if (m_source != RawEventData) {
|
|
e->unset(m_sourceProperties[AbsoluteTimeValue]);
|
|
e->unset(m_sourceProperties[DurationValue]);
|
|
}
|
|
|
|
if (m_target != RawEventData && m_target != NotationPrefix) {
|
|
e->unset(m_targetProperties[AbsoluteTimeValue]);
|
|
e->unset(m_targetProperties[DurationValue]);
|
|
}
|
|
}
|
|
|
|
void
|
|
Quantizer::removeTargetProperties(Event *e) const
|
|
{
|
|
if (m_target != RawEventData) {
|
|
e->unset(m_targetProperties[AbsoluteTimeValue]);
|
|
e->unset(m_targetProperties[DurationValue]);
|
|
}
|
|
}
|
|
|
|
void
|
|
Quantizer::makePropertyNames()
|
|
{
|
|
if (m_source != RawEventData && m_source != NotationPrefix) {
|
|
m_sourceProperties[AbsoluteTimeValue] = m_source + "AbsoluteTimeSource";
|
|
m_sourceProperties[DurationValue] = m_source + "DurationSource";
|
|
}
|
|
|
|
if (m_target != RawEventData && m_target != NotationPrefix) {
|
|
m_targetProperties[AbsoluteTimeValue] = m_target + "AbsoluteTimeTarget";
|
|
m_targetProperties[DurationValue] = m_target + "DurationTarget";
|
|
}
|
|
}
|
|
|
|
void
|
|
Quantizer::insertNewEvents(Segment *s) const
|
|
{
|
|
unsigned int sz = m_toInsert.size();
|
|
|
|
timeT minTime = m_normalizeRegion.first,
|
|
maxTime = m_normalizeRegion.second;
|
|
|
|
for (unsigned int i = 0; i < sz; ++i) {
|
|
|
|
timeT myTime = m_toInsert[i]->getAbsoluteTime();
|
|
timeT myDur = m_toInsert[i]->getDuration();
|
|
if (i == 0 || myTime < minTime) minTime = myTime;
|
|
if (i == 0 || myTime + myDur > maxTime) maxTime = myTime + myDur;
|
|
|
|
s->insert(m_toInsert[i]);
|
|
}
|
|
|
|
#ifdef DEBUG_NOTATION_QUANTIZER
|
|
cout << "Quantizer::insertNewEvents: sz is " << sz
|
|
<< ", minTime " << minTime << ", maxTime " << maxTime
|
|
<< endl;
|
|
#endif
|
|
|
|
if (m_target == NotationPrefix || m_target == RawEventData) {
|
|
|
|
if (m_normalizeRegion.first == m_normalizeRegion.second) {
|
|
if (sz > 0) {
|
|
s->normalizeRests(minTime, maxTime);
|
|
}
|
|
} else {
|
|
s->normalizeRests(minTime, maxTime);
|
|
m_normalizeRegion = std::pair<timeT, timeT>(0, 0);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_NOTATION_QUANTIZER
|
|
cout << "Quantizer: calling normalizeRests("
|
|
<< minTime << ", " << maxTime << ")" << endl;
|
|
#endif
|
|
|
|
m_toInsert.clear();
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|