annotate data/model/AlignmentModel.cpp @ 339:ba30f4a3e3be

* Some work on correct alignment when moving panes during playback * Overhaul alignment for playback frame values (view manager now always refers to reference-timeline values, only the play source deals in playback model timeline values) * When making a selection, ensure the selection regions shown in other panes (and used for playback constraints if appropriate) are aligned correctly. This may be the coolest feature ever implemented in any program ever.
author Chris Cannam
date Thu, 22 Nov 2007 14:17:19 +0000
parents f14e2f7b24f7
children d77e1fa49e26 94fc0591ea43
rev   line source
Chris@297 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@297 2
Chris@297 3 /*
Chris@297 4 Sonic Visualiser
Chris@297 5 An audio file viewer and annotation editor.
Chris@297 6 Centre for Digital Music, Queen Mary, University of London.
Chris@297 7 This file copyright 2007 QMUL.
Chris@297 8
Chris@297 9 This program is free software; you can redistribute it and/or
Chris@297 10 modify it under the terms of the GNU General Public License as
Chris@297 11 published by the Free Software Foundation; either version 2 of the
Chris@297 12 License, or (at your option) any later version. See the file
Chris@297 13 COPYING included with this distribution for more information.
Chris@297 14 */
Chris@297 15
Chris@297 16 #include "AlignmentModel.h"
Chris@297 17
Chris@297 18 #include "SparseTimeValueModel.h"
Chris@297 19
Chris@297 20 AlignmentModel::AlignmentModel(Model *reference,
Chris@297 21 Model *aligned,
Chris@297 22 Model *inputModel,
Chris@297 23 SparseTimeValueModel *path) :
Chris@297 24 m_reference(reference),
Chris@297 25 m_aligned(aligned),
Chris@297 26 m_inputModel(inputModel),
Chris@338 27 m_rawPath(path),
Chris@338 28 m_path(0),
Chris@297 29 m_reversePath(0),
Chris@323 30 m_pathBegun(false),
Chris@297 31 m_pathComplete(false)
Chris@297 32 {
Chris@338 33 connect(m_rawPath, SIGNAL(modelChanged()),
Chris@297 34 this, SLOT(pathChanged()));
Chris@297 35
Chris@338 36 connect(m_rawPath, SIGNAL(modelChanged(size_t, size_t)),
Chris@297 37 this, SLOT(pathChanged(size_t, size_t)));
Chris@297 38
Chris@338 39 connect(m_rawPath, SIGNAL(completionChanged()),
Chris@297 40 this, SLOT(pathCompletionChanged()));
Chris@297 41
Chris@338 42 constructPath();
Chris@297 43 constructReversePath();
Chris@297 44 }
Chris@297 45
Chris@297 46 AlignmentModel::~AlignmentModel()
Chris@297 47 {
Chris@297 48 delete m_inputModel;
Chris@338 49 delete m_rawPath;
Chris@297 50 delete m_path;
Chris@297 51 delete m_reversePath;
Chris@297 52 }
Chris@297 53
Chris@297 54 bool
Chris@297 55 AlignmentModel::isOK() const
Chris@297 56 {
Chris@338 57 if (m_rawPath) return m_rawPath->isOK();
Chris@338 58 else return true;
Chris@297 59 }
Chris@297 60
Chris@297 61 size_t
Chris@297 62 AlignmentModel::getStartFrame() const
Chris@297 63 {
Chris@297 64 size_t a = m_reference->getStartFrame();
Chris@297 65 size_t b = m_aligned->getStartFrame();
Chris@297 66 return std::min(a, b);
Chris@297 67 }
Chris@297 68
Chris@297 69 size_t
Chris@297 70 AlignmentModel::getEndFrame() const
Chris@297 71 {
Chris@297 72 size_t a = m_reference->getEndFrame();
Chris@297 73 size_t b = m_aligned->getEndFrame();
Chris@297 74 return std::max(a, b);
Chris@297 75 }
Chris@297 76
Chris@297 77 size_t
Chris@297 78 AlignmentModel::getSampleRate() const
Chris@297 79 {
Chris@297 80 return m_reference->getSampleRate();
Chris@297 81 }
Chris@297 82
Chris@297 83 Model *
Chris@297 84 AlignmentModel::clone() const
Chris@297 85 {
Chris@297 86 return new AlignmentModel
Chris@297 87 (m_reference, m_aligned,
Chris@297 88 m_inputModel ? m_inputModel->clone() : 0,
Chris@338 89 m_rawPath ? static_cast<SparseTimeValueModel *>(m_rawPath->clone()) : 0);
Chris@297 90 }
Chris@297 91
Chris@297 92 bool
Chris@297 93 AlignmentModel::isReady(int *completion) const
Chris@297 94 {
Chris@323 95 if (!m_pathBegun) {
Chris@338 96 if (completion) *completion = 0;
Chris@323 97 return false;
Chris@323 98 }
Chris@338 99 if (m_pathComplete || !m_rawPath) {
Chris@338 100 if (completion) *completion = 100;
Chris@338 101 return true;
Chris@338 102 }
Chris@338 103 return m_rawPath->isReady(completion);
Chris@297 104 }
Chris@297 105
Chris@297 106 const ZoomConstraint *
Chris@297 107 AlignmentModel::getZoomConstraint() const
Chris@297 108 {
Chris@338 109 return 0;
Chris@297 110 }
Chris@297 111
Chris@297 112 const Model *
Chris@297 113 AlignmentModel::getReferenceModel() const
Chris@297 114 {
Chris@297 115 return m_reference;
Chris@297 116 }
Chris@297 117
Chris@297 118 const Model *
Chris@297 119 AlignmentModel::getAlignedModel() const
Chris@297 120 {
Chris@297 121 return m_aligned;
Chris@297 122 }
Chris@297 123
Chris@297 124 size_t
Chris@297 125 AlignmentModel::toReference(size_t frame) const
Chris@297 126 {
Chris@297 127 // std::cerr << "AlignmentModel::toReference(" << frame << ")" << std::endl;
Chris@339 128 if (!m_path) constructPath();
Chris@339 129 return align(m_path, frame);
Chris@297 130 }
Chris@297 131
Chris@297 132 size_t
Chris@297 133 AlignmentModel::fromReference(size_t frame) const
Chris@297 134 {
Chris@297 135 // std::cerr << "AlignmentModel::fromReference(" << frame << ")" << std::endl;
Chris@339 136 if (!m_reversePath) constructReversePath();
Chris@339 137 return align(m_reversePath, frame);
Chris@297 138 }
Chris@297 139
Chris@297 140 void
Chris@297 141 AlignmentModel::pathChanged()
Chris@297 142 {
Chris@339 143 if (m_pathComplete) {
Chris@339 144 std::cerr << "AlignmentModel: deleting raw path model" << std::endl;
Chris@339 145 delete m_rawPath;
Chris@339 146 m_rawPath = 0;
Chris@339 147 }
Chris@297 148 }
Chris@297 149
Chris@297 150 void
Chris@297 151 AlignmentModel::pathChanged(size_t, size_t)
Chris@297 152 {
Chris@297 153 if (!m_pathComplete) return;
Chris@338 154 constructPath();
Chris@297 155 constructReversePath();
Chris@297 156 }
Chris@297 157
Chris@297 158 void
Chris@297 159 AlignmentModel::pathCompletionChanged()
Chris@297 160 {
Chris@339 161 if (!m_rawPath) return;
Chris@323 162 m_pathBegun = true;
Chris@323 163
Chris@297 164 if (!m_pathComplete) {
Chris@338 165
Chris@297 166 int completion = 0;
Chris@338 167 m_rawPath->isReady(&completion);
Chris@338 168
Chris@333 169 // std::cerr << "AlignmentModel::pathCompletionChanged: completion = "
Chris@333 170 // << completion << std::endl;
Chris@338 171
Chris@323 172 m_pathComplete = (completion == 100);
Chris@338 173
Chris@297 174 if (m_pathComplete) {
Chris@338 175
Chris@338 176 constructPath();
Chris@297 177 constructReversePath();
Chris@338 178
Chris@297 179 delete m_inputModel;
Chris@297 180 m_inputModel = 0;
Chris@297 181 }
Chris@297 182 }
Chris@323 183
Chris@297 184 emit completionChanged();
Chris@297 185 }
Chris@297 186
Chris@297 187 void
Chris@338 188 AlignmentModel::constructPath() const
Chris@297 189 {
Chris@338 190 if (!m_path) {
Chris@338 191 if (!m_rawPath) {
Chris@338 192 std::cerr << "ERROR: AlignmentModel::constructPath: "
Chris@338 193 << "No raw path available" << std::endl;
Chris@338 194 return;
Chris@338 195 }
Chris@338 196 m_path = new PathModel
Chris@338 197 (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false);
Chris@338 198 } else {
Chris@338 199 if (!m_rawPath) return;
Chris@297 200 }
Chris@297 201
Chris@338 202 m_path->clear();
Chris@297 203
Chris@338 204 SparseTimeValueModel::PointList points = m_rawPath->getPoints();
Chris@297 205
Chris@297 206 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@297 207 i != points.end(); ++i) {
Chris@297 208 long frame = i->frame;
Chris@297 209 float value = i->value;
Chris@297 210 long rframe = lrintf(value * m_aligned->getSampleRate());
Chris@338 211 m_path->addPoint(PathPoint(frame, rframe));
Chris@297 212 }
Chris@297 213
Chris@339 214 // std::cerr << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << std::endl;
Chris@338 215 }
Chris@338 216
Chris@338 217 void
Chris@338 218 AlignmentModel::constructReversePath() const
Chris@338 219 {
Chris@338 220 if (!m_reversePath) {
Chris@338 221 if (!m_rawPath) {
Chris@338 222 std::cerr << "ERROR: AlignmentModel::constructReversePath: "
Chris@338 223 << "No raw path available" << std::endl;
Chris@338 224 return;
Chris@338 225 }
Chris@338 226 m_reversePath = new PathModel
Chris@338 227 (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false);
Chris@338 228 } else {
Chris@338 229 if (!m_rawPath) return;
Chris@338 230 }
Chris@338 231
Chris@338 232 m_reversePath->clear();
Chris@338 233
Chris@338 234 SparseTimeValueModel::PointList points = m_rawPath->getPoints();
Chris@338 235
Chris@338 236 for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
Chris@338 237 i != points.end(); ++i) {
Chris@338 238 long frame = i->frame;
Chris@338 239 float value = i->value;
Chris@338 240 long rframe = lrintf(value * m_aligned->getSampleRate());
Chris@338 241 m_reversePath->addPoint(PathPoint(rframe, frame));
Chris@338 242 }
Chris@338 243
Chris@339 244 // std::cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << std::endl;
Chris@297 245 }
Chris@297 246
Chris@297 247 size_t
Chris@338 248 AlignmentModel::align(PathModel *path, size_t frame) const
Chris@297 249 {
Chris@339 250 if (!path) return frame;
Chris@339 251
Chris@338 252 // The path consists of a series of points, each with frame equal
Chris@338 253 // to the frame on the source model and mapframe equal to the
Chris@338 254 // frame on the target model. Both should be monotonically
Chris@338 255 // increasing.
Chris@297 256
Chris@338 257 const PathModel::PointList &points = path->getPoints();
Chris@297 258
Chris@297 259 if (points.empty()) {
Chris@297 260 // std::cerr << "AlignmentModel::align: No points" << std::endl;
Chris@297 261 return frame;
Chris@297 262 }
Chris@297 263
Chris@338 264 PathModel::Point point(frame);
Chris@338 265 PathModel::PointList::const_iterator i = points.lower_bound(point);
Chris@297 266 if (i == points.end()) --i;
Chris@338 267 while (i != points.begin() && i->frame > long(frame)) --i;
Chris@297 268
Chris@312 269 long foundFrame = i->frame;
Chris@338 270 long foundMapFrame = i->mapframe;
Chris@297 271
Chris@312 272 long followingFrame = foundFrame;
Chris@338 273 long followingMapFrame = foundMapFrame;
Chris@297 274
Chris@312 275 if (++i != points.end()) {
Chris@312 276 followingFrame = i->frame;
Chris@338 277 followingMapFrame = i->mapframe;
Chris@312 278 }
Chris@312 279
Chris@338 280 if (foundMapFrame < 0) return 0;
Chris@312 281
Chris@338 282 size_t resultFrame = foundMapFrame;
Chris@312 283
Chris@338 284 if (followingFrame != foundFrame && long(frame) > foundFrame) {
Chris@338 285 float interp =
Chris@338 286 float(frame - foundFrame) /
Chris@338 287 float(followingFrame - foundFrame);
Chris@338 288 resultFrame += lrintf((followingMapFrame - foundMapFrame) * interp);
Chris@312 289 }
Chris@312 290
Chris@339 291 // std::cerr << "AlignmentModel::align: resultFrame = " << resultFrame << std::endl;
Chris@312 292
Chris@312 293 return resultFrame;
Chris@297 294 }
Chris@297 295