changeset 10:ec6886f0e673

* 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.
author Chris Cannam
date Wed, 25 Jan 2006 17:46:28 +0000
parents 73d85d19919f
children cb05ba39664a
files base/Layer.h base/View.cpp base/View.h base/ViewManager.cpp base/ViewManager.h plugin/DSSIPluginInstance.cpp plugin/DSSIPluginInstance.h plugin/RealTimePluginInstance.h
diffstat 8 files changed, 161 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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();
--- 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<Layer *> 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);
 
--- 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()) {
--- 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);
 
--- 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);
--- 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<DSSIPluginInstance *> PluginSet;
--- 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;