Chris@297: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@297: Chris@297: /* Chris@297: Sonic Visualiser Chris@297: An audio file viewer and annotation editor. Chris@297: Centre for Digital Music, Queen Mary, University of London. Chris@297: This file copyright 2007 QMUL. Chris@297: Chris@297: This program is free software; you can redistribute it and/or Chris@297: modify it under the terms of the GNU General Public License as Chris@297: published by the Free Software Foundation; either version 2 of the Chris@297: License, or (at your option) any later version. See the file Chris@297: COPYING included with this distribution for more information. Chris@297: */ Chris@297: Chris@297: #include "AlignmentModel.h" Chris@297: Chris@297: #include "SparseTimeValueModel.h" Chris@297: Chris@409: //#define DEBUG_ALIGNMENT_MODEL 1 Chris@376: Chris@297: AlignmentModel::AlignmentModel(Model *reference, Chris@297: Model *aligned, Chris@297: Model *inputModel, Chris@1429: SparseTimeValueModel *path) : Chris@297: m_reference(reference), Chris@297: m_aligned(aligned), Chris@297: m_inputModel(inputModel), Chris@338: m_rawPath(path), Chris@338: m_path(0), Chris@297: m_reversePath(0), Chris@323: m_pathBegun(false), Chris@297: m_pathComplete(false) Chris@297: { Chris@371: if (m_rawPath) { Chris@297: Chris@371: connect(m_rawPath, SIGNAL(modelChanged()), Chris@371: this, SLOT(pathChanged())); Chris@297: Chris@1038: connect(m_rawPath, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), Chris@1038: this, SLOT(pathChangedWithin(sv_frame_t, sv_frame_t))); Chris@371: Chris@371: connect(m_rawPath, SIGNAL(completionChanged()), Chris@371: this, SLOT(pathCompletionChanged())); Chris@407: Chris@407: constructPath(); Chris@407: constructReversePath(); Chris@371: } Chris@297: Chris@407: if (m_rawPath && m_rawPath->isReady()) { Chris@407: pathCompletionChanged(); Chris@407: } Chris@297: } Chris@297: Chris@297: AlignmentModel::~AlignmentModel() Chris@297: { Chris@407: if (m_inputModel) m_inputModel->aboutToDelete(); Chris@297: delete m_inputModel; Chris@407: Chris@407: if (m_rawPath) m_rawPath->aboutToDelete(); Chris@338: delete m_rawPath; Chris@407: Chris@407: if (m_path) m_path->aboutToDelete(); Chris@297: delete m_path; Chris@407: Chris@407: if (m_reversePath) m_reversePath->aboutToDelete(); Chris@297: delete m_reversePath; Chris@297: } Chris@297: Chris@297: bool Chris@297: AlignmentModel::isOK() const Chris@297: { Chris@338: if (m_rawPath) return m_rawPath->isOK(); Chris@338: else return true; Chris@297: } Chris@297: Chris@1038: sv_frame_t Chris@297: AlignmentModel::getStartFrame() const Chris@297: { Chris@1038: sv_frame_t a = m_reference->getStartFrame(); Chris@1038: sv_frame_t b = m_aligned->getStartFrame(); Chris@297: return std::min(a, b); Chris@297: } Chris@297: Chris@1038: sv_frame_t Chris@297: AlignmentModel::getEndFrame() const Chris@297: { Chris@1038: sv_frame_t a = m_reference->getEndFrame(); Chris@1038: sv_frame_t b = m_aligned->getEndFrame(); Chris@297: return std::max(a, b); Chris@297: } Chris@297: Chris@1040: sv_samplerate_t Chris@297: AlignmentModel::getSampleRate() const Chris@297: { Chris@297: return m_reference->getSampleRate(); Chris@297: } Chris@297: Chris@297: bool Chris@297: AlignmentModel::isReady(int *completion) const Chris@297: { Chris@411: if (!m_pathBegun && m_rawPath) { Chris@338: if (completion) *completion = 0; Chris@323: return false; Chris@323: } Chris@1016: if (m_pathComplete) { Chris@338: if (completion) *completion = 100; Chris@338: return true; Chris@338: } Chris@1016: if (!m_rawPath) { Chris@1016: // lack of raw path could mean path is complete (in which case Chris@1016: // m_pathComplete true above) or else no alignment has been Chris@1016: // set at all yet (this case) Chris@1016: if (completion) *completion = 0; Chris@1016: return false; Chris@1016: } Chris@338: return m_rawPath->isReady(completion); Chris@297: } Chris@297: Chris@297: const ZoomConstraint * Chris@297: AlignmentModel::getZoomConstraint() const Chris@297: { Chris@338: return 0; Chris@297: } Chris@297: Chris@297: const Model * Chris@297: AlignmentModel::getReferenceModel() const Chris@297: { Chris@297: return m_reference; Chris@297: } Chris@297: Chris@297: const Model * Chris@297: AlignmentModel::getAlignedModel() const Chris@297: { Chris@297: return m_aligned; Chris@297: } Chris@297: Chris@1038: sv_frame_t Chris@1038: AlignmentModel::toReference(sv_frame_t frame) const Chris@297: { Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::toReference(" << frame << ")" << endl; Chris@376: #endif Chris@371: if (!m_path) { Chris@371: if (!m_rawPath) return frame; Chris@371: constructPath(); Chris@371: } Chris@339: return align(m_path, frame); Chris@297: } Chris@297: Chris@1038: sv_frame_t Chris@1038: AlignmentModel::fromReference(sv_frame_t frame) const Chris@297: { Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::fromReference(" << frame << ")" << endl; Chris@376: #endif Chris@371: if (!m_reversePath) { Chris@371: if (!m_rawPath) return frame; Chris@371: constructReversePath(); Chris@371: } Chris@339: return align(m_reversePath, frame); Chris@297: } Chris@297: Chris@297: void Chris@297: AlignmentModel::pathChanged() Chris@297: { Chris@339: if (m_pathComplete) { Chris@843: cerr << "AlignmentModel: deleting raw path model" << endl; Chris@407: if (m_rawPath) m_rawPath->aboutToDelete(); Chris@339: delete m_rawPath; Chris@339: m_rawPath = 0; Chris@339: } Chris@297: } Chris@297: Chris@297: void Chris@1038: AlignmentModel::pathChangedWithin(sv_frame_t, sv_frame_t) Chris@297: { Chris@297: if (!m_pathComplete) return; Chris@338: constructPath(); Chris@297: constructReversePath(); Chris@297: } Chris@297: Chris@297: void Chris@297: AlignmentModel::pathCompletionChanged() Chris@297: { Chris@339: if (!m_rawPath) return; Chris@323: m_pathBegun = true; Chris@323: Chris@297: if (!m_pathComplete) { Chris@338: Chris@297: int completion = 0; Chris@338: m_rawPath->isReady(&completion); Chris@338: Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::pathCompletionChanged: completion = " Chris@687: << completion << endl; Chris@376: #endif Chris@338: Chris@323: m_pathComplete = (completion == 100); Chris@338: Chris@297: if (m_pathComplete) { Chris@338: Chris@338: constructPath(); Chris@297: constructReversePath(); Chris@407: Chris@407: if (m_inputModel) m_inputModel->aboutToDelete(); Chris@297: delete m_inputModel; Chris@297: m_inputModel = 0; Chris@297: } Chris@297: } Chris@323: Chris@297: emit completionChanged(); Chris@297: } Chris@297: Chris@297: void Chris@338: AlignmentModel::constructPath() const Chris@297: { Chris@338: if (!m_path) { Chris@338: if (!m_rawPath) { Chris@843: cerr << "ERROR: AlignmentModel::constructPath: " Chris@843: << "No raw path available" << endl; Chris@338: return; Chris@338: } Chris@338: m_path = new PathModel Chris@338: (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false); Chris@338: } else { Chris@338: if (!m_rawPath) return; Chris@297: } Chris@297: Chris@338: m_path->clear(); Chris@297: Chris@338: SparseTimeValueModel::PointList points = m_rawPath->getPoints(); Chris@297: Chris@297: for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); Chris@297: i != points.end(); ++i) { Chris@1038: sv_frame_t frame = i->frame; Chris@1038: double value = i->value; Chris@1038: sv_frame_t rframe = lrint(value * m_aligned->getSampleRate()); Chris@338: m_path->addPoint(PathPoint(frame, rframe)); Chris@297: } Chris@297: Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl; Chris@376: #endif Chris@338: } Chris@338: Chris@338: void Chris@338: AlignmentModel::constructReversePath() const Chris@338: { Chris@338: if (!m_reversePath) { Chris@407: if (!m_path) { Chris@843: cerr << "ERROR: AlignmentModel::constructReversePath: " Chris@843: << "No forward path available" << endl; Chris@407: return; Chris@407: } Chris@407: m_reversePath = new PathModel Chris@407: (m_path->getSampleRate(), m_path->getResolution(), false); Chris@338: } else { Chris@407: if (!m_path) return; Chris@338: } Chris@338: Chris@338: m_reversePath->clear(); Chris@407: Chris@407: PathModel::PointList points = m_path->getPoints(); Chris@407: Chris@407: for (PathModel::PointList::const_iterator i = points.begin(); Chris@407: i != points.end(); ++i) { Chris@1038: sv_frame_t frame = i->frame; Chris@1038: sv_frame_t rframe = i->mapframe; Chris@407: m_reversePath->addPoint(PathPoint(rframe, frame)); Chris@407: } Chris@338: Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << endl; Chris@376: #endif Chris@297: } Chris@297: Chris@1038: sv_frame_t Chris@1038: AlignmentModel::align(PathModel *path, sv_frame_t frame) const Chris@297: { Chris@339: if (!path) return frame; Chris@339: Chris@338: // The path consists of a series of points, each with frame equal Chris@338: // to the frame on the source model and mapframe equal to the Chris@338: // frame on the target model. Both should be monotonically Chris@338: // increasing. Chris@297: Chris@338: const PathModel::PointList &points = path->getPoints(); Chris@297: Chris@297: if (points.empty()) { Chris@379: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::align: No points" << endl; Chris@379: #endif Chris@297: return frame; Chris@297: } Chris@297: Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::align: frame " << frame << " requested" << endl; Chris@376: #endif Chris@376: Chris@338: PathModel::Point point(frame); Chris@338: PathModel::PointList::const_iterator i = points.lower_bound(point); Chris@376: if (i == points.end()) { Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@843: cerr << "Note: i == points.end()" << endl; Chris@376: #endif Chris@376: --i; Chris@376: } Chris@1038: while (i != points.begin() && i->frame > frame) --i; Chris@297: Chris@1038: sv_frame_t foundFrame = i->frame; Chris@1038: sv_frame_t foundMapFrame = i->mapframe; Chris@297: Chris@1038: sv_frame_t followingFrame = foundFrame; Chris@1038: sv_frame_t followingMapFrame = foundMapFrame; Chris@297: Chris@312: if (++i != points.end()) { Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@843: cerr << "another point available" << endl; Chris@376: #endif Chris@312: followingFrame = i->frame; Chris@338: followingMapFrame = i->mapframe; Chris@376: } else { Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@843: cerr << "no other point available" << endl; Chris@376: #endif Chris@376: } Chris@312: Chris@1075: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "foundFrame = " << foundFrame << ", foundMapFrame = " << foundMapFrame Chris@1075: << ", followingFrame = " << followingFrame << ", followingMapFrame = " Chris@1075: << followingMapFrame << endl; Chris@1075: #endif Chris@1075: Chris@338: if (foundMapFrame < 0) return 0; Chris@312: Chris@1038: sv_frame_t resultFrame = foundMapFrame; Chris@312: Chris@1038: if (followingFrame != foundFrame && frame > foundFrame) { Chris@1038: double interp = Chris@1038: double(frame - foundFrame) / Chris@1038: double(followingFrame - foundFrame); Chris@1038: resultFrame += lrint(double(followingMapFrame - foundMapFrame) * interp); Chris@312: } Chris@312: Chris@376: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::align: resultFrame = " << resultFrame << endl; Chris@376: #endif Chris@312: Chris@312: return resultFrame; Chris@297: } Chris@407: Chris@407: void Chris@1016: AlignmentModel::setPathFrom(SparseTimeValueModel *rawpath) Chris@1016: { Chris@1016: if (m_rawPath) m_rawPath->aboutToDelete(); Chris@1016: delete m_rawPath; Chris@1016: Chris@1016: m_rawPath = rawpath; Chris@1016: Chris@1016: connect(m_rawPath, SIGNAL(modelChanged()), Chris@1016: this, SLOT(pathChanged())); Chris@1016: Chris@1046: connect(m_rawPath, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), Chris@1046: this, SLOT(pathChangedWithin(sv_frame_t, sv_frame_t))); Chris@1016: Chris@1016: connect(m_rawPath, SIGNAL(completionChanged()), Chris@1016: this, SLOT(pathCompletionChanged())); Chris@1016: Chris@1016: constructPath(); Chris@1016: constructReversePath(); Chris@1016: Chris@1016: if (m_rawPath->isReady()) { Chris@1016: pathCompletionChanged(); Chris@1016: } Chris@1016: } Chris@1016: Chris@1016: void Chris@407: AlignmentModel::setPath(PathModel *path) Chris@407: { Chris@407: if (m_path) m_path->aboutToDelete(); Chris@407: delete m_path; Chris@407: m_path = path; Chris@662: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::setPath: path = " << m_path << endl; Chris@662: #endif Chris@407: constructReversePath(); Chris@662: #ifdef DEBUG_ALIGNMENT_MODEL Chris@1075: cerr << "AlignmentModel::setPath: after construction path = " Chris@687: << m_path << ", rpath = " << m_reversePath << endl; Chris@662: #endif Chris@407: } Chris@297: Chris@407: void Chris@407: AlignmentModel::toXml(QTextStream &stream, Chris@407: QString indent, Chris@407: QString extraAttributes) const Chris@407: { Chris@407: if (!m_path) { Chris@690: SVDEBUG << "AlignmentModel::toXml: no path" << endl; Chris@407: return; Chris@407: } Chris@407: Chris@407: m_path->toXml(stream, indent, ""); Chris@407: Chris@407: Model::toXml(stream, indent, Chris@407: QString("type=\"alignment\" reference=\"%1\" aligned=\"%2\" path=\"%3\" %4") Chris@407: .arg(getObjectExportId(m_reference)) Chris@407: .arg(getObjectExportId(m_aligned)) Chris@407: .arg(getObjectExportId(m_path)) Chris@407: .arg(extraAttributes)); Chris@407: } Chris@407: Chris@407: