Chris@867: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@867: Chris@867: /* Chris@867: Sonic Visualiser Chris@867: An audio file viewer and annotation editor. Chris@867: Centre for Digital Music, Queen Mary, University of London. Chris@867: This file copyright 2006-2014 Chris Cannam and QMUL. Chris@867: Chris@867: This program is free software; you can redistribute it and/or Chris@867: modify it under the terms of the GNU General Public License as Chris@867: published by the Free Software Foundation; either version 2 of the Chris@867: License, or (at your option) any later version. See the file Chris@867: COPYING included with this distribution for more information. Chris@867: */ Chris@867: Chris@867: #include "AlignmentView.h" Chris@867: Chris@867: #include Chris@867: Chris@868: #include "data/model/SparseOneDimensionalModel.h" Chris@868: Chris@868: #include "layer/TimeInstantLayer.h" Chris@868: Chris@1493: //#define DEBUG_ALIGNMENT_VIEW 1 Chris@1493: Chris@867: using std::vector; Chris@1493: using std::set; Chris@867: Chris@867: AlignmentView::AlignmentView(QWidget *w) : Chris@867: View(w, false), Chris@1408: m_above(nullptr), Chris@1408: m_below(nullptr) Chris@867: { Chris@867: setObjectName(tr("AlignmentView")); Chris@867: } Chris@867: Chris@867: void Chris@1493: AlignmentView::keyFramesChanged() Chris@1493: { Chris@1493: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1493: SVCERR << "AlignmentView " << getId() << "::keyFramesChanged" << endl; Chris@1493: #endif Chris@1493: Chris@1493: // This is just a notification that we need to rebuild it - so all Chris@1493: // we do here is clear it, and it'll be rebuilt on demand later Chris@1493: QMutexLocker locker(&m_keyFrameMutex); Chris@1493: m_keyFrameMap.clear(); Chris@1493: } Chris@1493: Chris@1493: void Chris@976: AlignmentView::globalCentreFrameChanged(sv_frame_t f) Chris@867: { Chris@867: View::globalCentreFrameChanged(f); Chris@867: update(); Chris@867: } Chris@867: Chris@867: void Chris@976: AlignmentView::viewCentreFrameChanged(View *v, sv_frame_t f) Chris@867: { Chris@867: View::viewCentreFrameChanged(v, f); Chris@867: if (v == m_above) { Chris@1266: m_centreFrame = f; Chris@1266: update(); Chris@867: } else if (v == m_below) { Chris@1266: update(); Chris@867: } Chris@867: } Chris@867: Chris@867: void Chris@976: AlignmentView::viewManagerPlaybackFrameChanged(sv_frame_t) Chris@867: { Chris@867: update(); Chris@867: } Chris@867: Chris@867: void Chris@1183: AlignmentView::viewAboveZoomLevelChanged(ZoomLevel level, bool) Chris@867: { Chris@867: m_zoomLevel = level; Chris@867: update(); Chris@867: } Chris@867: Chris@867: void Chris@1183: AlignmentView::viewBelowZoomLevelChanged(ZoomLevel, bool) Chris@867: { Chris@867: update(); Chris@867: } Chris@867: Chris@867: void Chris@867: AlignmentView::setViewAbove(View *v) Chris@867: { Chris@867: if (m_above) { Chris@1408: disconnect(m_above, nullptr, this, nullptr); Chris@867: } Chris@867: Chris@867: m_above = v; Chris@867: Chris@867: if (m_above) { Chris@1266: connect(m_above, Chris@1183: SIGNAL(zoomLevelChanged(ZoomLevel, bool)), Chris@1266: this, Chris@1183: SLOT(viewAboveZoomLevelChanged(ZoomLevel, bool))); Chris@1493: connect(m_above, Chris@1493: SIGNAL(propertyContainerAdded(PropertyContainer *)), Chris@1493: this, Chris@1493: SLOT(keyFramesChanged())); Chris@1493: connect(m_above, Chris@1493: SIGNAL(layerModelChanged()), Chris@1493: this, Chris@1493: SLOT(keyFramesChanged())); Chris@867: } Chris@1493: Chris@1493: keyFramesChanged(); Chris@867: } Chris@867: Chris@867: void Chris@867: AlignmentView::setViewBelow(View *v) Chris@867: { Chris@867: if (m_below) { Chris@1408: disconnect(m_below, nullptr, this, nullptr); Chris@867: } Chris@867: Chris@867: m_below = v; Chris@867: Chris@867: if (m_below) { Chris@1266: connect(m_below, Chris@1183: SIGNAL(zoomLevelChanged(ZoomLevel, bool)), Chris@1266: this, Chris@1183: SLOT(viewBelowZoomLevelChanged(ZoomLevel, bool))); Chris@1493: connect(m_below, Chris@1493: SIGNAL(propertyContainerAdded(PropertyContainer *)), Chris@1493: this, Chris@1493: SLOT(keyFramesChanged())); Chris@1493: connect(m_below, Chris@1493: SIGNAL(layerModelChanged()), Chris@1493: this, Chris@1493: SLOT(keyFramesChanged())); Chris@867: } Chris@1493: Chris@1493: keyFramesChanged(); Chris@867: } Chris@867: Chris@867: void Chris@867: AlignmentView::paintEvent(QPaintEvent *) Chris@867: { Chris@1408: if (m_above == nullptr || m_below == nullptr || !m_manager) return; Chris@1493: Chris@1493: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1493: SVCERR << "AlignmentView " << getId() << "::paintEvent" << endl; Chris@1493: #endif Chris@1493: Chris@867: bool darkPalette = false; Chris@867: if (m_manager) darkPalette = m_manager->getGlobalDarkBackground(); Chris@867: Chris@881: QColor fg, bg; Chris@881: if (darkPalette) { Chris@881: fg = Qt::gray; Chris@881: bg = Qt::black; Chris@881: } else { Chris@881: fg = Qt::black; Chris@881: bg = Qt::gray; Chris@881: } Chris@867: Chris@867: QPainter paint(this); Chris@867: paint.setPen(QPen(fg, 2)); Chris@867: paint.setBrush(Qt::NoBrush); Chris@867: paint.setRenderHint(QPainter::Antialiasing, true); Chris@867: Chris@867: paint.fillRect(rect(), bg); Chris@867: Chris@1493: QMutexLocker locker(&m_keyFrameMutex); Chris@867: Chris@1493: if (m_keyFrameMap.empty()) { Chris@1493: reconnectModels(); Chris@1493: buildKeyFrameMap(); Chris@1493: } Chris@1493: Chris@1493: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1493: SVCERR << "AlignmentView " << getId() << "::paintEvent: painting " Chris@1493: << m_keyFrameMap.size() << " mappings" << endl; Chris@1493: #endif Chris@1493: Chris@1493: for (const auto &km: m_keyFrameMap) { Chris@1493: Chris@1493: sv_frame_t af = km.first; Chris@1493: sv_frame_t bf = km.second; Chris@1493: Chris@1493: int ax = m_above->getXForFrame(af); Chris@1266: int bx = m_below->getXForFrame(bf); Chris@1493: Chris@1493: if (ax >= 0 || ax < width() || bx >= 0 || bx < width()) { Chris@1493: paint.drawLine(ax, 0, bx, height()); Chris@1493: } Chris@867: } Chris@867: Chris@867: paint.end(); Chris@1493: } Chris@867: Chris@1493: void Chris@1493: AlignmentView::reconnectModels() Chris@868: { Chris@1493: vector toConnect { Chris@1493: getSalientModel(m_above), Chris@1493: getSalientModel(m_below) Chris@1493: }; Chris@868: Chris@1493: for (auto modelId: toConnect) { Chris@1493: if (auto model = ModelById::get(modelId)) { Chris@1493: auto referenceId = model->getAlignmentReference(); Chris@1493: if (!referenceId.isNone()) { Chris@1493: toConnect.push_back(referenceId); Chris@1475: } Chris@1266: } Chris@868: } Chris@868: Chris@1493: for (auto modelId: toConnect) { Chris@1493: if (auto model = ModelById::get(modelId)) { Chris@1493: auto ptr = model.get(); Chris@1493: disconnect(ptr, 0, this, 0); Chris@1493: connect(ptr, SIGNAL(modelChanged(ModelId)), Chris@1493: this, SLOT(keyFramesChanged())); Chris@1493: connect(ptr, SIGNAL(completionChanged(ModelId)), Chris@1493: this, SLOT(keyFramesChanged())); Chris@1493: connect(ptr, SIGNAL(alignmentCompletionChanged(ModelId)), Chris@1493: this, SLOT(keyFramesChanged())); Chris@1493: } Chris@1493: } Chris@1493: } Chris@1493: Chris@1493: void Chris@1493: AlignmentView::buildKeyFrameMap() Chris@1493: { Chris@1493: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1493: SVCERR << "AlignmentView " << getId() << "::buildKeyFrameMap" << endl; Chris@1493: #endif Chris@1493: Chris@1493: sv_frame_t resolution = 1; Chris@1493: Chris@1493: set keyFramesBelow; Chris@1493: for (auto f: getKeyFrames(m_below, resolution)) { Chris@1493: keyFramesBelow.insert(f); Chris@1493: } Chris@1493: Chris@1493: vector keyFrames = getKeyFrames(m_above, resolution); Chris@1493: Chris@1493: foreach (sv_frame_t f, keyFrames) { Chris@1493: Chris@1493: sv_frame_t rf = m_above->alignToReference(f); Chris@1493: sv_frame_t bf = m_below->alignFromReference(rf); Chris@1493: Chris@1493: bool mappedSomething = false; Chris@1493: Chris@1493: if (resolution > 1) { Chris@1493: if (keyFramesBelow.find(bf) == keyFramesBelow.end()) { Chris@1493: Chris@1493: sv_frame_t f1 = f + resolution; Chris@1493: sv_frame_t rf1 = m_above->alignToReference(f1); Chris@1493: sv_frame_t bf1 = m_below->alignFromReference(rf1); Chris@1493: Chris@1493: for (sv_frame_t probe = bf + 1; probe <= bf1; ++probe) { Chris@1493: if (keyFramesBelow.find(probe) != keyFramesBelow.end()) { Chris@1493: m_keyFrameMap.insert({ f, probe }); Chris@1493: mappedSomething = true; Chris@1493: } Chris@1493: } Chris@1493: } Chris@1493: } Chris@1493: Chris@1493: if (!mappedSomething) { Chris@1493: m_keyFrameMap.insert({ f, bf }); Chris@1493: } Chris@1493: } Chris@1493: Chris@1493: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1493: SVCERR << "AlignmentView " << getId() << "::buildKeyFrameMap: have " Chris@1493: << m_keyFrameMap.size() << " mappings" << endl; Chris@1493: #endif Chris@1493: } Chris@1493: Chris@1493: vector Chris@1493: AlignmentView::getKeyFrames(View *view, sv_frame_t &resolution) Chris@1493: { Chris@1493: resolution = 1; Chris@1493: Chris@1493: if (!view) { Chris@1493: return getDefaultKeyFrames(); Chris@1493: } Chris@1493: Chris@1493: ModelId m = getSalientModel(view); Chris@1475: auto model = ModelById::getAs(m); Chris@1475: if (!model) { Chris@1266: return getDefaultKeyFrames(); Chris@868: } Chris@868: Chris@1493: resolution = model->getResolution(); Chris@1493: Chris@976: vector keyFrames; Chris@868: Chris@1475: EventVector pp = model->getAllEvents(); Chris@1433: for (EventVector::const_iterator pi = pp.begin(); pi != pp.end(); ++pi) { Chris@1433: keyFrames.push_back(pi->getFrame()); Chris@868: } Chris@868: Chris@868: return keyFrames; Chris@868: } Chris@868: Chris@976: vector Chris@868: AlignmentView::getDefaultKeyFrames() Chris@868: { Chris@976: vector keyFrames; Chris@868: Chris@868: if (!m_above || !m_manager) return keyFrames; Chris@868: Chris@976: sv_samplerate_t rate = m_manager->getMainModelSampleRate(); Chris@868: if (rate == 0) return keyFrames; Chris@868: Chris@976: for (sv_frame_t f = m_above->getModelsStartFrame(); Chris@1266: f <= m_above->getModelsEndFrame(); Chris@1266: f += sv_frame_t(rate * 5 + 0.5)) { Chris@1266: keyFrames.push_back(f); Chris@868: } Chris@868: Chris@868: return keyFrames; Chris@868: } Chris@868: Chris@1493: ModelId Chris@1493: AlignmentView::getSalientModel(View *view) Chris@1493: { Chris@1493: ModelId m; Chris@1493: Chris@1493: // get the topmost such Chris@1493: for (int i = 0; i < view->getLayerCount(); ++i) { Chris@1493: if (qobject_cast(view->getLayer(i))) { Chris@1493: ModelId mm = view->getLayer(i)->getModel(); Chris@1493: if (ModelById::isa(mm)) { Chris@1493: m = mm; Chris@1493: } Chris@1493: } Chris@1493: } Chris@1493: Chris@1493: return m; Chris@1493: } Chris@1493: Chris@1493: