# HG changeset patch # User Chris Cannam # Date 1138211188 0 # Node ID ec6886f0e6736abfc38b9112b11959f250395f61 # Parent 73d85d19919f378b7c4313f9aa32dac7d619cd19 * Fix update and play limits for play-selection mode when not looping * Fix playback in loop mode when no selection -- but the GUI update for this is still wrong on the flyback * Various fixes and improvements to making selections, particularly during playback * Draw selection under non-opaque non-scrollable layers, so as to improve cacheing * Show selection limits as text when drawing selection * Allow user to find missing audio files when loading session * Cross-fade selections when in play-selection mode -- mostly. We don't cross-fade on a processing block boundary, and unfortunately with short selections the selection boundary is quite likely to coincide with a block boundary. diff -r 73d85d19919f -r ec6886f0e673 base/Layer.h --- a/base/Layer.h Tue Jan 24 16:20:58 2006 +0000 +++ b/base/Layer.h Wed Jan 25 17:46:28 2006 +0000 @@ -117,6 +117,16 @@ virtual bool isLayerScrollable() const { return true; } /** + * This should return true if the layer completely obscures any + * underlying layers. It's used to determine whether the view can + * safely draw any selection rectangles under the layer instead of + * over it, in the case where the layer is not scrollable and + * therefore needs to be redrawn each time (so that the selection + * rectangle can be cached). + */ + virtual bool isLayerOpaque() const { return false; } + + /** * Return the proportion of background work complete in drawing * this view, as a percentage -- in most cases this will be the * value returned by pointer from a call to the underlying model's diff -r 73d85d19919f -r ec6886f0e673 base/View.cpp --- a/base/View.cpp Tue Jan 24 16:20:58 2006 +0000 +++ b/base/View.cpp Wed Jan 25 17:46:28 2006 +0000 @@ -204,9 +204,11 @@ setCentreFrame(f + m_zoomLevel * (width() / 2)); } -void +bool View::setCentreFrame(size_t f, bool e) { + bool changeVisible = false; + if (m_centreFrame != f) { int formerPixel = m_centreFrame / m_zoomLevel; @@ -221,10 +223,14 @@ std::cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << std::endl; #endif update(); + + changeVisible = true; } if (e) emit centreFrameChanged(this, f, m_followPan); } + + return changeVisible; } void @@ -492,25 +498,52 @@ switch (m_followPlay) { case PlaybackScrollContinuous: - if (QApplication::mouseButtons() == Qt::NoButton) { + if (QApplication::mouseButtons() == Qt::NoButton && + QApplication::keyboardModifiers() == Qt::NoModifier) { setCentreFrame(f, false); } break; case PlaybackScrollPage: { + int xold = (long(oldPlayPointerFrame) - getStartFrame()) / m_zoomLevel; + repaint(xold - 1, 0, 3, height()); + long w = width() * getZoomLevel(); w -= w/5; long sf = (f / w) * w - w/8; + + if (m_manager && + m_manager->isPlaying() && + m_manager->getPlaySelectionMode()) { + ViewManager::SelectionList selections = m_manager->getSelections(); + if (!selections.empty()) { + size_t selectionStart = selections.begin()->getStartFrame(); + if (sf < long(selectionStart) - w / 10) { + sf = long(selectionStart) - w / 10; + } + } + } + #ifdef DEBUG_VIEW_WIDGET_PAINT std::cerr << "PlaybackScrollPage: f = " << f << ", sf = " << sf << ", start frame " << getStartFrame() << std::endl; #endif - setCentreFrame(sf + width() * getZoomLevel() / 2, false); - int xold = (long(oldPlayPointerFrame) - getStartFrame()) / m_zoomLevel; + + if (QApplication::mouseButtons() == Qt::NoButton && + QApplication::keyboardModifiers() == Qt::NoModifier) { + bool changed = + setCentreFrame(sf + width() * getZoomLevel() / 2, false); + if (changed) { + xold = (long(oldPlayPointerFrame) - + getStartFrame()) / m_zoomLevel; + update(xold - 1, 0, 3, height()); + } + } + int xnew = (long(m_playPointerFrame) - getStartFrame()) / m_zoomLevel; - update(xold - 1, 0, 3, height()); update(xnew - 1, 0, 3, height()); + break; } @@ -818,6 +851,17 @@ bool haveSelections = m_manager && !m_manager->getSelections().empty(); bool selectionDrawn = false; + if (!selectionCacheable) { + selectionCacheable = true; + for (LayerList::const_iterator i = nonScrollables.begin(); + i != nonScrollables.end(); ++i) { + if ((*i)->isLayerOpaque()) { + selectionCacheable = false; + break; + } + } + } + #ifdef DEBUG_VIEW_WIDGET_PAINT std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() << " scrollable back layers and " << nonScrollables.size() @@ -1044,28 +1088,76 @@ paint.setPen(QColor(150, 150, 255)); paint.setBrush(QColor(150, 150, 255, 80)); + int sampleRate = getModelsSampleRate(); + + const QFontMetrics &metrics = paint.fontMetrics(); + for (ViewManager::SelectionList::iterator i = selections.begin(); i != selections.end(); ++i) { int p0 = -1, p1 = -1; - if (int(i->getStartFrame()) >= getStartFrame()) { - p0 = (i->getStartFrame() - getStartFrame()) / m_zoomLevel; - } + p0 = (long(i->getStartFrame()) - getStartFrame()) / m_zoomLevel; + p1 = (long(i->getEndFrame()) - getStartFrame()) / m_zoomLevel; - if (int(i->getEndFrame()) >= getStartFrame()) { - p1 = (i->getEndFrame() - getStartFrame()) / m_zoomLevel; - } - - if (p0 == -1 && p1 == -1) continue; - - if (p1 > width()) p1 = width() + 1; + if (p1 < 0 || p0 > width()) continue; #ifdef DEBUG_VIEW_WIDGET_PAINT std::cerr << "View::drawSelections: " << p0 << ",-1 [" << (p1-p0) << "x" << (height()+1) << "]" << std::endl; #endif paint.drawRect(p0, -1, p1 - p0, height() + 1); + + if (sampleRate && shouldLabelSelections()) { + + QString startText = QString("%1 / %2") + .arg(QString::fromStdString + (RealTime::frame2RealTime + (i->getStartFrame(), sampleRate).toText(true))) + .arg(i->getStartFrame()); + + QString endText = QString(" %1 / %2") + .arg(QString::fromStdString + (RealTime::frame2RealTime + (i->getEndFrame(), sampleRate).toText(true))) + .arg(i->getEndFrame()); + + QString durationText = QString("(%1 / %2) ") + .arg(QString::fromStdString + (RealTime::frame2RealTime + (i->getEndFrame() - i->getStartFrame(), sampleRate) + .toText(true))) + .arg(i->getEndFrame() - i->getStartFrame()); + + int sw = metrics.width(startText), + ew = metrics.width(endText), + dw = metrics.width(durationText); + + int sy = metrics.ascent() + metrics.height() + 4; + int ey = sy; + int dy = sy + metrics.height(); + + int sx = p0 + 2; + int ex = sx; + int dx = sx; + + if (sw + ew > (p1 - p0)) { + ey += metrics.height(); + dy += metrics.height(); + } + + if (ew < (p1 - p0)) { + ex = p1 - 2 - ew; + } + + if (dw < (p1 - p0)) { + dx = p1 - 2 - dw; + } + + paint.drawText(sx, sy, startText); + paint.drawText(ex, ey, endText); + paint.drawText(dx, dy, durationText); + } } paint.restore(); diff -r 73d85d19919f -r ec6886f0e673 base/View.h --- a/base/View.h Tue Jan 24 16:20:58 2006 +0000 +++ b/base/View.h Wed Jan 25 17:46:28 2006 +0000 @@ -192,6 +192,7 @@ View(QWidget *, bool showProgress); virtual void paintEvent(QPaintEvent *e); virtual void drawSelections(QPainter &); + virtual bool shouldLabelSelections() const { return true; } typedef std::vector LayerList; @@ -205,7 +206,7 @@ ZoomConstraint::RoundingDirection dir = ZoomConstraint::RoundNearest) const; - void setCentreFrame(size_t f, bool e); + bool setCentreFrame(size_t f, bool doEmit); void checkProgress(void *object); diff -r 73d85d19919f -r ec6886f0e673 base/ViewManager.cpp --- a/base/ViewManager.cpp Tue Jan 24 16:20:58 2006 +0000 +++ b/base/ViewManager.cpp Wed Jan 25 17:46:28 2006 +0000 @@ -138,17 +138,19 @@ //simply overlaps one of them (cutting down the original selection //appropriately) - m_selections.erase(selection); - - emit selectionChanged(); + if (m_selections.find(selection) != m_selections.end()) { + m_selections.erase(selection); + emit selectionChanged(); + } } void ViewManager::clearSelections() { - m_selections.clear(); - - emit selectionChanged(); + if (!m_selections.empty()) { + m_selections.clear(); + emit selectionChanged(); + } } Selection @@ -226,6 +228,12 @@ } void +ViewManager::playStatusChanged(bool playing) +{ + checkPlayStatus(); +} + +void ViewManager::checkPlayStatus() { if (m_playSource && m_playSource->isPlaying()) { diff -r 73d85d19919f -r ec6886f0e673 base/ViewManager.h --- a/base/ViewManager.h Tue Jan 24 16:20:58 2006 +0000 +++ b/base/ViewManager.h Wed Jan 25 17:46:28 2006 +0000 @@ -117,6 +117,7 @@ protected slots: void checkPlayStatus(); + void playStatusChanged(bool playing); void considerSeek(void *, unsigned long, bool); void considerZoomChange(void *, unsigned long, bool); diff -r 73d85d19919f -r ec6886f0e673 plugin/DSSIPluginInstance.cpp --- a/plugin/DSSIPluginInstance.cpp Tue Jan 24 16:20:58 2006 +0000 +++ b/plugin/DSSIPluginInstance.cpp Wed Jan 25 17:46:28 2006 +0000 @@ -60,7 +60,8 @@ m_latencyPort(0), m_run(false), m_bypassed(false), - m_grouped(false) + m_grouped(false), + m_haveLastEventSendTime(false) { #ifdef DEBUG_DSSI std::cerr << "DSSIPluginInstance::DSSIPluginInstance(" << identifier << ")" @@ -788,6 +789,11 @@ DSSIPluginInstance::sendEvent(const RealTime &eventTime, const void *e) { + if (m_haveLastEventSendTime && + m_lastEventSendTime > eventTime) { + clearEvents(); + } + snd_seq_event_t *event = (snd_seq_event_t *)e; #ifdef DEBUG_DSSI_PROCESS std::cerr << "DSSIPluginInstance::sendEvent at " << eventTime << std::endl; @@ -803,6 +809,13 @@ m_eventBuffer.write(&ev, 1); } +void +DSSIPluginInstance::clearEvents() +{ + m_haveLastEventSendTime = false; + m_eventBuffer.reset(); +} + bool DSSIPluginInstance::handleController(snd_seq_event_t *ev) { @@ -858,6 +871,7 @@ if (!m_descriptor || !m_descriptor->run_synth) { m_eventBuffer.skip(m_eventBuffer.getReadSpace()); + m_haveLastEventSendTime = false; if (m_descriptor->LADSPA_Plugin->run) { m_descriptor->LADSPA_Plugin->run(m_instanceHandle, m_blockSize); } else { @@ -901,7 +915,13 @@ #endif if (frameOffset >= int(m_blockSize)) break; - if (frameOffset < 0) frameOffset = 0; + if (frameOffset < 0) { + frameOffset = 0; + if (ev->type == SND_SEQ_EVENT_NOTEON) { + m_eventBuffer.skip(1); + continue; + } + } ev->time.tick = frameOffset; m_eventBuffer.skip(1); diff -r 73d85d19919f -r ec6886f0e673 plugin/DSSIPluginInstance.h --- a/plugin/DSSIPluginInstance.h Tue Jan 24 16:20:58 2006 +0000 +++ b/plugin/DSSIPluginInstance.h Wed Jan 25 17:46:28 2006 +0000 @@ -51,6 +51,7 @@ virtual QString configure(QString key, QString value); virtual void sendEvent(const RealTime &eventTime, const void *event); + virtual void clearEvents(); virtual size_t getBufferSize() const { return m_blockSize; } virtual size_t getAudioInputCount() const { return m_audioPortsIn.size(); } @@ -175,6 +176,9 @@ bool m_grouped; RealTime m_lastRunTime; + RealTime m_lastEventSendTime; + bool m_haveLastEventSendTime; + QMutex m_processLock; typedef std::set PluginSet; diff -r 73d85d19919f -r ec6886f0e673 plugin/RealTimePluginInstance.h --- a/plugin/RealTimePluginInstance.h Tue Jan 24 16:20:58 2006 +0000 +++ b/plugin/RealTimePluginInstance.h Wed Jan 25 17:46:28 2006 +0000 @@ -95,6 +95,7 @@ virtual void sendEvent(const RealTime & /* eventTime */, const void * /* event */) { } + virtual void clearEvents() { } virtual bool isBypassed() const = 0; virtual void setBypassed(bool value) = 0;