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@1615: m_below(nullptr), Chris@1615: m_reference(nullptr), Chris@1615: m_leftmostAbove(-1), Chris@1615: m_rightmostAbove(-1) 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@1615: // This is just a notification that we need to rebuild - so all we Chris@1615: // do here is clear, and rebuild on demand later Chris@1615: QMutexLocker locker(&m_mapsMutex); Chris@1615: m_fromAboveMap.clear(); Chris@1615: m_fromReferenceMap.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@1615: AlignmentView::setAboveView(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@1615: AlignmentView::setBelowView(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@1615: AlignmentView::setReferenceView(View *view) Chris@1615: { Chris@1615: m_reference = view; Chris@1615: } Chris@1615: Chris@1615: 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@1615: QMutexLocker locker(&m_mapsMutex); Chris@867: Chris@1615: if (m_fromAboveMap.empty()) { Chris@1493: reconnectModels(); Chris@1615: buildMaps(); Chris@1493: } Chris@1493: Chris@1493: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1493: SVCERR << "AlignmentView " << getId() << "::paintEvent: painting " Chris@1615: << m_fromAboveMap.size() << " mappings" << endl; Chris@1493: #endif Chris@1493: Chris@1615: int w = width(); Chris@1615: int h = height(); Chris@1493: Chris@1619: if (m_leftmostAbove >= 0) { Chris@1619: Chris@1619: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1619: SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove Chris@1619: << ", we have a relationship with the pane above us: showing " Chris@1619: << "mappings in relation to that" << endl; Chris@1619: #endif Chris@1619: Chris@1615: for (const auto &km: m_fromAboveMap) { Chris@1493: Chris@1615: sv_frame_t af = km.first; Chris@1615: sv_frame_t bf = km.second; Chris@1615: Chris@1620: if (af < m_leftmostAbove) { Chris@1620: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1620: SVCERR << "AlignmentView: af " << af << " < m_leftmostAbove " << m_leftmostAbove << endl; Chris@1620: #endif Chris@1620: continue; Chris@1620: } Chris@1620: if (af > m_rightmostAbove) { Chris@1620: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1620: SVCERR << "AlignmentView: af " << af << " > m_rightmostAbove " << m_rightmostAbove << endl; Chris@1620: #endif Chris@1615: continue; Chris@1615: } Chris@1493: Chris@1615: int ax = m_above->getXForFrame(af); Chris@1615: int bx = m_below->getXForFrame(bf); Chris@1615: Chris@1615: if (ax >= 0 || ax < w || bx >= 0 || bx < w) { Chris@1615: paint.drawLine(ax, 0, bx, h); Chris@1615: } Chris@1615: } Chris@1615: } else if (m_reference != nullptr) { Chris@1615: // the below has nothing in common with the above: show things Chris@1615: // in common with the reference instead Chris@1619: Chris@1619: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1619: SVCERR << "AlignmentView: m_leftmostAbove = " << m_leftmostAbove Chris@1619: << ", we have no relationship with the pane above us: showing " Chris@1619: << "mappings in relation to the reference instead" << endl; Chris@1619: #endif Chris@1619: Chris@1615: for (const auto &km: m_fromReferenceMap) { Chris@1615: Chris@1615: sv_frame_t af = km.first; Chris@1615: sv_frame_t bf = km.second; Chris@1615: Chris@1615: int ax = m_reference->getXForFrame(af); Chris@1615: int bx = m_below->getXForFrame(bf); Chris@1615: Chris@1615: if (ax >= 0 || ax < w || bx >= 0 || bx < w) { Chris@1615: paint.drawLine(ax, 0, bx, h); Chris@1615: } 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@1615: AlignmentView::buildMaps() Chris@1493: { Chris@1493: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1615: SVCERR << "AlignmentView " << getId() << "::buildMaps" << 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@1615: foreach(sv_frame_t f, keyFramesBelow) { Chris@1615: sv_frame_t rf = m_below->alignToReference(f); Chris@1615: m_fromReferenceMap.insert({ rf, f }); Chris@1615: } Chris@1615: Chris@1493: vector keyFrames = getKeyFrames(m_above, resolution); Chris@1493: Chris@1615: // These are the most extreme leftward and rightward frames in Chris@1615: // "above" that have distinct corresponding frames in Chris@1615: // "below". Anything left of m_leftmostAbove or right of Chris@1615: // m_rightmostAbove maps effectively off one end or the other of Chris@1615: // the below view. (They don't actually map off the ends, they Chris@1615: // just all map to the same first/last destination frame. But we Chris@1615: // don't want to display their mappings, as they're just noise.) Chris@1615: m_leftmostAbove = -1; Chris@1615: m_rightmostAbove = -1; Chris@1615: Chris@1620: sv_frame_t prevAf = -1; Chris@1615: sv_frame_t prevBf = -1; Chris@1615: Chris@1620: foreach (sv_frame_t af, keyFrames) { Chris@1493: Chris@1620: sv_frame_t rf = m_above->alignToReference(af); Chris@1493: sv_frame_t bf = m_below->alignFromReference(rf); Chris@1493: Chris@1615: if (prevBf > 0 && bf > prevBf) { Chris@1620: if (m_leftmostAbove < 0) { Chris@1620: m_leftmostAbove = prevAf; Chris@1615: } Chris@1620: m_rightmostAbove = af; Chris@1615: } Chris@1620: prevAf = af; Chris@1615: prevBf = bf; Chris@1615: Chris@1493: bool mappedSomething = false; Chris@1493: Chris@1493: if (resolution > 1) { Chris@1493: if (keyFramesBelow.find(bf) == keyFramesBelow.end()) { Chris@1493: Chris@1620: sv_frame_t af1 = af + resolution; Chris@1620: sv_frame_t rf1 = m_above->alignToReference(af1); 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@1620: m_fromAboveMap.insert({ af, probe }); Chris@1493: mappedSomething = true; Chris@1493: } Chris@1493: } Chris@1493: } Chris@1493: } Chris@1493: Chris@1493: if (!mappedSomething) { Chris@1620: m_fromAboveMap.insert({ af, bf }); Chris@1493: } Chris@1493: } Chris@1493: Chris@1493: #ifdef DEBUG_ALIGNMENT_VIEW Chris@1615: SVCERR << "AlignmentView " << getId() << "::buildMaps: have " Chris@1615: << m_fromAboveMap.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@1509: return keyFrames; Chris@868: Chris@1509: #ifdef NOT_REALLY 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@1509: #endif 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: