changeset 33:651e4e868bcc

* Implement play mute, level and pan controls and a layer visibility control * Handle swapping the buffers in AudioCallbackPlaySource more gracefully, so that in many cases it can be done inaudibly. Still gets it wrong when playing in a noncontiguous selection. * Fix to SV file save for non-2d sparse models * Fixes to LED button drawing and AudioDial mouse functionality * Add progress bar for Ogg file import * Reshuffle PropertyContainer and its subclasses so it can be a QObject * Add layer dormancy (invisible layer permitted to free its cache space) * Optimisations to SpectrogramLayer, removing locks when reading/writing individual pixels in the cache (should be unnecessary there) -- there's still an issue here as we need a lock when reading from the model in case the model is replaced, and we don't currently have one * Several munlock() calls to make it harder to exhaust real memory if running in an RT mode with mlockall() active
author Chris Cannam
date Fri, 17 Feb 2006 18:04:26 +0000
parents c53b949ef142
children c43f2c4f66f2
files layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h widgets/LEDButton.cpp widgets/LEDButton.h widgets/Pane.h widgets/PaneStack.cpp widgets/PropertyBox.cpp widgets/PropertyBox.h
diffstat 8 files changed, 133 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Wed Feb 15 17:58:35 2006 +0000
+++ b/layer/SpectrogramLayer.cpp	Fri Feb 17 18:04:26 2006 +0000
@@ -49,7 +49,6 @@
     m_fillThread(0),
     m_updateTimer(0),
     m_lastFillExtent(0),
-    m_dormant(false),
     m_exiting(false)
 {
     if (config == MelodicRange) {
@@ -597,16 +596,29 @@
 }
 
 void
-SpectrogramLayer::setLayerDormant()
+SpectrogramLayer::setLayerDormant(bool dormant)
 {
-    m_mutex.lock();
-    m_dormant = true;
-    delete m_cache;
-    m_cache = 0;
-    m_pixmapCacheInvalid = true;
-    delete m_pixmapCache;
-    m_pixmapCache = 0;
-    m_mutex.unlock();
+    if (dormant == m_dormant) return;
+
+    if (dormant) {
+
+	m_mutex.lock();
+	m_dormant = true;
+
+	delete m_cache;
+	m_cache = 0;
+	
+	m_pixmapCacheInvalid = true;
+	delete m_pixmapCache;
+	m_pixmapCache = 0;
+	
+	m_mutex.unlock();
+
+    } else {
+
+	m_dormant = false;
+	fillCache();
+    }
 }
 
 void
@@ -983,8 +995,17 @@
 	    m_layer.m_cache = new Cache(width, height);
 
 	    m_layer.setCacheColourmap();
+	    m_layer.m_cache->fill(0);
 
-	    m_layer.m_cache->fill(0);
+	    // We don't need a lock when writing to or reading from
+	    // the pixels in the cache, because it's a fixed size
+	    // array.  We do need to ensure we have the width and
+	    // height of the cache and the FFT parameters fixed before
+	    // we unlock, in case they change in the model while we
+	    // aren't holding a lock.  It's safe for us to continue to
+	    // use the "old" values if that happens, because they will
+	    // continue to match the dimensions of the actual cache
+	    // (which we manage, not the model).
 	    m_layer.m_mutex.unlock();
 
 	    double *input = (double *)
@@ -1002,7 +1023,6 @@
 		std::cerr << "WARNING: fftw_plan_dft_r2c_1d(" << windowSize << ") failed!" << std::endl;
 		fftw_free(input);
 		fftw_free(output);
-		m_layer.m_mutex.lock();
 		continue;
 	    }
 
@@ -1014,8 +1034,6 @@
 
 	    if (doVisibleFirst) {
 
-		m_layer.m_mutex.lock();
-
 		for (size_t f = visibleStart; f < visibleEnd; f += windowIncrement) {
 	    
 		    m_layer.fillCacheColumn(int((f - start) / windowIncrement),
@@ -1023,9 +1041,6 @@
 					    windowSize, windowIncrement,
 					    windower, false);
 
-		    m_layer.m_mutex.unlock();
-		    m_layer.m_mutex.lock();
-
 		    if (m_layer.m_cacheInvalid || m_layer.m_exiting) {
 			interrupted = true;
 			m_fillExtent = 0;
@@ -1039,8 +1054,6 @@
 			counter = 0;
 		    }
 		}
-
-		m_layer.m_mutex.unlock();
 	    }
 
 	    m_layer.m_cachedInitialVisibleArea = true;
@@ -1290,9 +1303,7 @@
     }
 
     if (m_dormant) {
-	std::cerr << "SpectrogramLayer::paint(): Layer is dormant, de-hibernating" << std::endl;
-	m_dormant = false;
-	((SpectrogramLayer *)this)->fillCache();
+	std::cerr << "SpectrogramLayer::paint(): Layer is dormant" << std::endl;
 	return;
     }
 
--- a/layer/SpectrogramLayer.h	Wed Feb 15 17:58:35 2006 +0000
+++ b/layer/SpectrogramLayer.h	Fri Feb 17 18:04:26 2006 +0000
@@ -140,7 +140,7 @@
 
     void setProperties(const QXmlAttributes &attributes);
 
-    void setLayerDormant();
+    virtual void setLayerDormant(bool dormant);
 
 protected slots:
     void cacheInvalid();
@@ -222,7 +222,6 @@
     QTimer *m_updateTimer;
     size_t m_lastFillExtent;
     bool m_cachedInitialVisibleArea;
-    mutable bool m_dormant;
     bool m_exiting;
 
     void setCacheColourmap();
--- a/widgets/LEDButton.cpp	Wed Feb 15 17:58:35 2006 +0000
+++ b/widgets/LEDButton.cpp	Fri Feb 17 18:04:26 2006 +0000
@@ -22,6 +22,7 @@
 #include <QPainter>
 #include <QImage>
 #include <QColor>
+#include <QMouseEvent>
 
 
 class LEDButton::LEDButtonPrivate
@@ -84,6 +85,15 @@
 }
 
 void
+LEDButton::mousePressEvent(QMouseEvent *e)
+{
+    if (e->buttons() & Qt::LeftButton) {
+	toggle();
+	emit stateChanged(state());
+    }
+}
+
+void
 LEDButton::paintEvent(QPaintEvent *)
 {
     QPainter paint;
@@ -125,7 +135,7 @@
 	    }
 	}
 
-	scale = 3;
+	scale = 1;
 	width *= scale;
 
 	tmpMap = new QPixmap(width, width);
@@ -136,7 +146,7 @@
 	paint.begin(this);
     }
 
-    paint.setRenderHint(QPainter::Antialiasing, false);
+    paint.setRenderHint(QPainter::Antialiasing, true);
 
     // Set the color of the LED according to given parameters
     color = (led_state) ? led_color : d->offcolor;
@@ -187,7 +197,8 @@
     // around the LED which resembles a shadow with light coming
     // from the upper left.
 
-    pen.setWidth( 2 * scale + 1 ); // ### shouldn't this value be smaller for smaller LEDs?
+//    pen.setWidth( 2 * scale + 1 ); // ### shouldn't this value be smaller for smaller LEDs?
+    pen.setWidth(2 * scale);
     brush.setStyle(Qt::NoBrush);
     paint.setBrush(brush); // This avoids filling of the ellipse
 
@@ -201,8 +212,8 @@
 	pen.setColor(color);
 	paint.setPen(pen);
 	int w = width - pen.width()/2 - scale + 1;
-	paint.drawArc(pen.width()/2, pen.width()/2, w, w, angle + arc, 240);
-	paint.drawArc(pen.width()/2, pen.width()/2, w, w, angle - arc, 240);
+	paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle + arc, 240);
+	paint.drawArc(pen.width()/2 + 1, pen.width()/2 + 1, w - 2, w - 2, angle - arc, 240);
 	color = color.dark(110); //FIXME: this should somehow use the contrast value
     }	// end for ( angle = 720; angle < 6480; angle += 160 )
 
@@ -213,11 +224,12 @@
     if (smooth) {
 	QPixmap *&dest = led_state ? d->on_map : d->off_map;
 	QImage i = tmpMap->toImage();
-	width /= 3;
-	i = i.scaled(width, width, 
-		     Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+	width /= scale;
 	delete tmpMap;
-	dest = new QPixmap(QPixmap::fromImage(i));
+	dest = new QPixmap(QPixmap::fromImage
+			   (i.scaled(width, width, 
+				     Qt::KeepAspectRatio,
+				     Qt::SmoothTransformation)));
 	paint.begin(this);
 	paint.drawPixmap(0, 0, *dest);
 	paint.end();
@@ -305,12 +317,12 @@
 QSize
 LEDButton::sizeHint() const
 {
-    return QSize(16, 16);
+    return QSize(17, 17);
 }
 
 QSize
 LEDButton::minimumSizeHint() const
 {
-    return QSize(16, 16 );
+    return QSize(17, 17);
 }
 
--- a/widgets/LEDButton.h	Wed Feb 15 17:58:35 2006 +0000
+++ b/widgets/LEDButton.h	Fri Feb 17 18:04:26 2006 +0000
@@ -17,7 +17,7 @@
 
     The original KDE widget comes in round and rectangular and flat,
     raised, and sunken variants.  This version retains only the round
-    sunken variant.
+    sunken variant.  This version also implements a simple button API.
 */
 
 #ifndef _LED_BUTTON_H_
@@ -55,14 +55,17 @@
     virtual QSize sizeHint() const;
     virtual QSize minimumSizeHint() const;
 
+signals:
+    void stateChanged(bool);
+
 public slots:
-
     void toggle();
     void on();
     void off();
 
 protected:
     void paintEvent(QPaintEvent *);
+    void mousePressEvent(QMouseEvent *);
 
 private:
     State led_state;
--- a/widgets/Pane.h	Wed Feb 15 17:58:35 2006 +0000
+++ b/widgets/Pane.h	Fri Feb 17 18:04:26 2006 +0000
@@ -28,7 +28,6 @@
 
 public:
     Pane(QWidget *parent = 0);
-    virtual QString getPropertyContainerIconName() const { return "pane"; }
 
     virtual bool shouldIlluminateLocalFeatures(const Layer *layer, QPoint &pos);
 
--- a/widgets/PaneStack.cpp	Wed Feb 15 17:58:35 2006 +0000
+++ b/widgets/PaneStack.cpp	Fri Feb 17 18:04:26 2006 +0000
@@ -176,7 +176,9 @@
 			stack->setCurrentIndex(stack->getContainerIndex(layer));
 			emit currentLayerChanged(pane, layer);
 		    } else {
-			stack->setCurrentIndex(stack->getContainerIndex(pane));
+			stack->setCurrentIndex
+			    (stack->getContainerIndex
+			     (pane->getPropertyContainer(0)));
 			emit currentLayerChanged(pane, 0);
 		    }
 		}
--- a/widgets/PropertyBox.cpp	Wed Feb 15 17:58:35 2006 +0000
+++ b/widgets/PropertyBox.cpp	Fri Feb 17 18:04:26 2006 +0000
@@ -10,14 +10,19 @@
 #include "PropertyBox.h"
 
 #include "base/PropertyContainer.h"
+#include "base/PlayParameters.h"
+#include "base/Layer.h"
 
 #include "AudioDial.h"
+#include "LEDButton.h"
 
 #include <QGridLayout>
 #include <QHBoxLayout>
+#include <QVBoxLayout>
 #include <QCheckBox>
 #include <QComboBox>
 #include <QLabel>
+#include <QFrame>
 
 #include <cassert>
 #include <iostream>
@@ -32,8 +37,35 @@
 	container->getPropertyContainerName().toStdString() << "\")]::PropertyBox" << std::endl;
 #endif
 
+    QVBoxLayout *vbox = new QVBoxLayout;
+    setLayout(vbox);
+
+    bool needViewPlayBox = false;
+
+    if (container->getPlayParameters() || dynamic_cast<Layer *>(container)) {
+	needViewPlayBox = true;
+    }
+
+    if (needViewPlayBox) {
+#ifdef DEBUG_PROPERTY_BOX
+	std::cerr << "Adding view play box" << std::endl;
+#endif
+	QFrame *frame = new QFrame;
+	frame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+	QHBoxLayout *hbox = new QHBoxLayout;
+	frame->setLayout(hbox);
+	vbox->addWidget(frame);
+	populateViewPlayBox(container, hbox);
+	hbox->insertStretch(-1, 10);
+    }
+
+    m_mainWidget = new QWidget;
+    vbox->addWidget(m_mainWidget);
+
+    vbox->insertStretch(-1, 10);
+
     m_layout = new QGridLayout;
-    setLayout(m_layout);
+    m_mainWidget->setLayout(m_layout);
 
     PropertyContainer::PropertyList properties = container->getProperties();
 
@@ -57,11 +89,35 @@
 PropertyBox::~PropertyBox()
 {
 #ifdef DEBUG_PROPERTY_BOX
-    std::cerr << "PropertyBox[" << this << "(\"" << m_container->getPropertyContainerName().toStdString() << "\")]::~PropertyBox" << std::endl;
+    std::cerr << "PropertyBox[" << this << "]::~PropertyBox" << std::endl;
 #endif
 }
 
 void
+PropertyBox::populateViewPlayBox(PropertyContainer *container, QLayout *layout)
+{
+    Layer *layer = dynamic_cast<Layer *>(container);
+    PlayParameters *params = container->getPlayParameters();
+    if (!params && !layer) return;
+
+    std::cerr << "PropertyBox::populateViewPlayBox: container " << container << " (name " << container->getPropertyContainerName().toStdString() << ") params " << params << std::endl;
+    
+    if (layer) {
+	LEDButton *showButton = new LEDButton(Qt::blue);
+	layout->addWidget(showButton);
+	connect(showButton, SIGNAL(stateChanged(bool)),
+		layer, SLOT(showLayer(bool)));
+    }
+    
+    if (params) {
+	LEDButton *playButton = new LEDButton(Qt::darkGreen);
+	layout->addWidget(playButton);
+	connect(playButton, SIGNAL(stateChanged(bool)),
+		params, SLOT(setPlayAudible(bool)));
+    }
+}
+
+void
 PropertyBox::updatePropertyEditor(PropertyContainer::PropertyName name)
 {
     PropertyContainer::PropertyType type = m_container->getPropertyType(name);
@@ -92,8 +148,8 @@
 #ifdef DEBUG_PROPERTY_BOX
 		std::cerr << "PropertyBox: adding label \"" << groupName.toStdString() << "\" and frame for group for \"" << name.toStdString() << "\"" << std::endl;
 #endif
-		m_layout->addWidget(new QLabel(groupName, this), row, 0);
-		QFrame *frame = new QFrame(this);
+		m_layout->addWidget(new QLabel(groupName, m_mainWidget), row, 0);
+		QFrame *frame = new QFrame(m_mainWidget);
 		m_layout->addWidget(frame, row, 1, 1, 2);
 		m_groupLayouts[groupName] = new QHBoxLayout;
 		m_groupLayouts[groupName]->setMargin(0);
@@ -103,7 +159,7 @@
 #ifdef DEBUG_PROPERTY_BOX 
 	    std::cerr << "PropertyBox: adding label \"" << name.toStdString() << "\"" << std::endl;
 #endif
-	    m_layout->addWidget(new QLabel(name, this), row, 0);
+	    m_layout->addWidget(new QLabel(name, m_mainWidget), row, 0);
 	}
     }
 
@@ -166,7 +222,7 @@
 		dial->setFixedWidth(32);
 		dial->setFixedHeight(32);
 		m_layout->addWidget(dial, row, 1);
-		QLabel *label = new QLabel(this);
+		QLabel *label = new QLabel(m_mainWidget);
 		connect(dial, SIGNAL(valueChanged(int)),
 			label, SLOT(setNum(int)));
 		label->setNum(value);
@@ -246,8 +302,8 @@
     QObject *obj = sender();
     QString name = obj->objectName();
 
-//    std::cerr << "PropertyBox::propertyControllerChanged(" << name.toStdString()
-//	      << ", " << value << ")" << std::endl;
+    std::cerr << "PropertyBox::propertyControllerChanged(" << name.toStdString()
+	      << ", " << value << ")" << std::endl;
     
     PropertyContainer::PropertyType type = m_container->getPropertyType(name);
     
--- a/widgets/PropertyBox.h	Wed Feb 15 17:58:35 2006 +0000
+++ b/widgets/PropertyBox.h	Fri Feb 17 18:04:26 2006 +0000
@@ -15,6 +15,8 @@
 #include <QFrame>
 #include <map>
 
+class QLayout;
+class QWidget;
 class QGridLayout;
 
 class PropertyBox : public QFrame
@@ -34,8 +36,10 @@
     void propertyControllerChanged(int);
 
 protected:
+    void populateViewPlayBox(PropertyContainer *, QLayout *);
     void updatePropertyEditor(PropertyContainer::PropertyName);
 
+    QWidget *m_mainWidget;
     QGridLayout *m_layout;
     PropertyContainer *m_container;
     std::map<QString, QLayout *> m_groupLayouts;