svcore
1.9
|
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