svcore  1.9
AlignmentModel.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2007 QMUL.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "AlignmentModel.h"
00017 
00018 #include "SparseTimeValueModel.h"
00019 
00020 //#define DEBUG_ALIGNMENT_MODEL 1
00021 
00022 AlignmentModel::AlignmentModel(Model *reference,
00023                                Model *aligned,
00024                                Model *inputModel,
00025                                SparseTimeValueModel *path) :
00026     m_reference(reference),
00027     m_aligned(aligned),
00028     m_inputModel(inputModel),
00029     m_rawPath(path),
00030     m_path(0),
00031     m_reversePath(0),
00032     m_pathBegun(false),
00033     m_pathComplete(false)
00034 {
00035     if (m_rawPath) {
00036 
00037         connect(m_rawPath, SIGNAL(modelChanged()),
00038                 this, SLOT(pathChanged()));
00039 
00040         connect(m_rawPath, SIGNAL(modelChangedWithin(int, int)),
00041                 this, SLOT(pathChangedWithin(int, int)));
00042         
00043         connect(m_rawPath, SIGNAL(completionChanged()),
00044                 this, SLOT(pathCompletionChanged()));
00045 
00046         constructPath();
00047         constructReversePath();
00048     }
00049 
00050     if (m_rawPath && m_rawPath->isReady()) {
00051         pathCompletionChanged();
00052     }
00053 }
00054 
00055 AlignmentModel::~AlignmentModel()
00056 {
00057     if (m_inputModel) m_inputModel->aboutToDelete();
00058     delete m_inputModel;
00059 
00060     if (m_rawPath) m_rawPath->aboutToDelete();
00061     delete m_rawPath;
00062 
00063     if (m_path) m_path->aboutToDelete();
00064     delete m_path;
00065 
00066     if (m_reversePath) m_reversePath->aboutToDelete();
00067     delete m_reversePath;
00068 }
00069 
00070 bool
00071 AlignmentModel::isOK() const
00072 {
00073     if (m_rawPath) return m_rawPath->isOK();
00074     else return true;
00075 }
00076 
00077 int
00078 AlignmentModel::getStartFrame() const
00079 {
00080     int a = m_reference->getStartFrame();
00081     int b = m_aligned->getStartFrame();
00082     return std::min(a, b);
00083 }
00084 
00085 int
00086 AlignmentModel::getEndFrame() const
00087 {
00088     int a = m_reference->getEndFrame();
00089     int b = m_aligned->getEndFrame();
00090     return std::max(a, b);
00091 }
00092 
00093 int
00094 AlignmentModel::getSampleRate() const
00095 {
00096     return m_reference->getSampleRate();
00097 }
00098 
00099 Model *
00100 AlignmentModel::clone() const
00101 {
00102     return new AlignmentModel
00103         (m_reference, m_aligned,
00104          m_inputModel ? m_inputModel->clone() : 0,
00105          m_rawPath ? static_cast<SparseTimeValueModel *>(m_rawPath->clone()) : 0);
00106 }
00107 
00108 bool
00109 AlignmentModel::isReady(int *completion) const
00110 {
00111     if (!m_pathBegun && m_rawPath) {
00112         if (completion) *completion = 0;
00113         return false;
00114     }
00115     if (m_pathComplete || !m_rawPath) {
00116         if (completion) *completion = 100;
00117         return true;
00118     }
00119     return m_rawPath->isReady(completion);
00120 }
00121 
00122 const ZoomConstraint *
00123 AlignmentModel::getZoomConstraint() const
00124 {
00125     return 0;
00126 }
00127 
00128 const Model *
00129 AlignmentModel::getReferenceModel() const
00130 {
00131     return m_reference;
00132 }
00133 
00134 const Model *
00135 AlignmentModel::getAlignedModel() const
00136 {
00137     return m_aligned;
00138 }
00139 
00140 int
00141 AlignmentModel::toReference(int frame) const
00142 {
00143 #ifdef DEBUG_ALIGNMENT_MODEL
00144     SVDEBUG << "AlignmentModel::toReference(" << frame << ")" << endl;
00145 #endif
00146     if (!m_path) {
00147         if (!m_rawPath) return frame;
00148         constructPath();
00149     }
00150     return align(m_path, frame);
00151 }
00152 
00153 int
00154 AlignmentModel::fromReference(int frame) const
00155 {
00156 #ifdef DEBUG_ALIGNMENT_MODEL
00157     SVDEBUG << "AlignmentModel::fromReference(" << frame << ")" << endl;
00158 #endif
00159     if (!m_reversePath) {
00160         if (!m_rawPath) return frame;
00161         constructReversePath();
00162     }
00163     return align(m_reversePath, frame);
00164 }
00165 
00166 void
00167 AlignmentModel::pathChanged()
00168 {
00169     if (m_pathComplete) {
00170         cerr << "AlignmentModel: deleting raw path model" << endl;
00171         if (m_rawPath) m_rawPath->aboutToDelete();
00172         delete m_rawPath;
00173         m_rawPath = 0;
00174     }
00175 }
00176 
00177 void
00178 AlignmentModel::pathChangedWithin(int, int)
00179 {
00180     if (!m_pathComplete) return;
00181     constructPath();
00182     constructReversePath();
00183 }    
00184 
00185 void
00186 AlignmentModel::pathCompletionChanged()
00187 {
00188     if (!m_rawPath) return;
00189     m_pathBegun = true;
00190 
00191     if (!m_pathComplete) {
00192 
00193         int completion = 0;
00194         m_rawPath->isReady(&completion);
00195 
00196 #ifdef DEBUG_ALIGNMENT_MODEL
00197         SVDEBUG << "AlignmentModel::pathCompletionChanged: completion = "
00198                   << completion << endl;
00199 #endif
00200 
00201         m_pathComplete = (completion == 100);
00202 
00203         if (m_pathComplete) {
00204 
00205             constructPath();
00206             constructReversePath();
00207             
00208             if (m_inputModel) m_inputModel->aboutToDelete();
00209             delete m_inputModel;
00210             m_inputModel = 0;
00211         }
00212     }
00213 
00214     emit completionChanged();
00215 }
00216 
00217 void
00218 AlignmentModel::constructPath() const
00219 {
00220     if (!m_path) {
00221         if (!m_rawPath) {
00222             cerr << "ERROR: AlignmentModel::constructPath: "
00223                       << "No raw path available" << endl;
00224             return;
00225         }
00226         m_path = new PathModel
00227             (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false);
00228     } else {
00229         if (!m_rawPath) return;
00230     }
00231         
00232     m_path->clear();
00233 
00234     SparseTimeValueModel::PointList points = m_rawPath->getPoints();
00235         
00236     for (SparseTimeValueModel::PointList::const_iterator i = points.begin();
00237          i != points.end(); ++i) {
00238         long frame = i->frame;
00239         float value = i->value;
00240         long rframe = lrintf(value * m_aligned->getSampleRate());
00241         m_path->addPoint(PathPoint(frame, rframe));
00242     }
00243 
00244 #ifdef DEBUG_ALIGNMENT_MODEL
00245     SVDEBUG << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
00246 #endif
00247 }
00248 
00249 void
00250 AlignmentModel::constructReversePath() const
00251 {
00252     if (!m_reversePath) {
00253         if (!m_path) {
00254             cerr << "ERROR: AlignmentModel::constructReversePath: "
00255                       << "No forward path available" << endl;
00256             return;
00257         }
00258         m_reversePath = new PathModel
00259             (m_path->getSampleRate(), m_path->getResolution(), false);
00260     } else {
00261         if (!m_path) return;
00262     }
00263         
00264     m_reversePath->clear();
00265 
00266     PathModel::PointList points = m_path->getPoints();
00267         
00268     for (PathModel::PointList::const_iterator i = points.begin();
00269          i != points.end(); ++i) {
00270         long frame = i->frame;
00271         long rframe = i->mapframe;
00272         m_reversePath->addPoint(PathPoint(rframe, frame));
00273     }
00274 
00275 #ifdef DEBUG_ALIGNMENT_MODEL
00276     SVDEBUG << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl;
00277 #endif
00278 }
00279 
00280 int
00281 AlignmentModel::align(PathModel *path, int frame) const
00282 {
00283     if (!path) return frame;
00284 
00285     // The path consists of a series of points, each with frame equal
00286     // to the frame on the source model and mapframe equal to the
00287     // frame on the target model.  Both should be monotonically
00288     // increasing.
00289 
00290     const PathModel::PointList &points = path->getPoints();
00291 
00292     if (points.empty()) {
00293 #ifdef DEBUG_ALIGNMENT_MODEL
00294         SVDEBUG << "AlignmentModel::align: No points" << endl;
00295 #endif
00296         return frame;
00297     }        
00298 
00299 #ifdef DEBUG_ALIGNMENT_MODEL
00300     SVDEBUG << "AlignmentModel::align: frame " << frame << " requested" << endl;
00301 #endif
00302 
00303     PathModel::Point point(frame);
00304     PathModel::PointList::const_iterator i = points.lower_bound(point);
00305     if (i == points.end()) {
00306 #ifdef DEBUG_ALIGNMENT_MODEL
00307         cerr << "Note: i == points.end()" << endl;
00308 #endif
00309         --i;
00310     }
00311     while (i != points.begin() && i->frame > long(frame)) --i;
00312 
00313     long foundFrame = i->frame;
00314     long foundMapFrame = i->mapframe;
00315 
00316     long followingFrame = foundFrame;
00317     long followingMapFrame = foundMapFrame;
00318 
00319     if (++i != points.end()) {
00320 #ifdef DEBUG_ALIGNMENT_MODEL
00321         cerr << "another point available" << endl;
00322 #endif
00323         followingFrame = i->frame;
00324         followingMapFrame = i->mapframe;
00325     } else {
00326 #ifdef DEBUG_ALIGNMENT_MODEL
00327         cerr << "no other point available" << endl;
00328 #endif
00329     }        
00330 
00331     if (foundMapFrame < 0) return 0;
00332 
00333     int resultFrame = foundMapFrame;
00334 
00335     if (followingFrame != foundFrame && long(frame) > foundFrame) {
00336         float interp =
00337             float(frame - foundFrame) /
00338             float(followingFrame - foundFrame);
00339         resultFrame += lrintf((followingMapFrame - foundMapFrame) * interp);
00340     }
00341 
00342 #ifdef DEBUG_ALIGNMENT_MODEL
00343     SVDEBUG << "AlignmentModel::align: resultFrame = " << resultFrame << endl;
00344 #endif
00345 
00346     return resultFrame;
00347 }
00348 
00349 void
00350 AlignmentModel::setPath(PathModel *path)
00351 {
00352     if (m_path) m_path->aboutToDelete();
00353     delete m_path;
00354     m_path = path;
00355 #ifdef DEBUG_ALIGNMENT_MODEL
00356     SVDEBUG << "AlignmentModel::setPath: path = " << m_path << endl;
00357 #endif
00358     constructReversePath();
00359 #ifdef DEBUG_ALIGNMENT_MODEL
00360     SVDEBUG << "AlignmentModel::setPath: after construction path = "
00361               << m_path << ", rpath = " << m_reversePath << endl;
00362 #endif
00363 }
00364     
00365 void
00366 AlignmentModel::toXml(QTextStream &stream,
00367                       QString indent,
00368                       QString extraAttributes) const
00369 {
00370     if (!m_path) {
00371         SVDEBUG << "AlignmentModel::toXml: no path" << endl;
00372         return;
00373     }
00374 
00375     m_path->toXml(stream, indent, "");
00376 
00377     Model::toXml(stream, indent,
00378                  QString("type=\"alignment\" reference=\"%1\" aligned=\"%2\" path=\"%3\" %4")
00379                  .arg(getObjectExportId(m_reference))
00380                  .arg(getObjectExportId(m_aligned))
00381                  .arg(getObjectExportId(m_path))
00382                  .arg(extraAttributes));
00383 }
00384 
00385