# HG changeset patch # User Chris Cannam # Date 1140199466 0 # Node ID 651e4e868bccd7b2791291c698a4dcf3a7ec8d61 # Parent c53b949ef14270bb60e16a4b6bcb5fc3af3cb3a6 * 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 diff -r c53b949ef142 -r 651e4e868bcc layer/SpectrogramLayer.cpp --- 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; } diff -r c53b949ef142 -r 651e4e868bcc layer/SpectrogramLayer.h --- 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(); diff -r c53b949ef142 -r 651e4e868bcc widgets/LEDButton.cpp --- 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 #include #include +#include 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); } diff -r c53b949ef142 -r 651e4e868bcc widgets/LEDButton.h --- 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; diff -r c53b949ef142 -r 651e4e868bcc widgets/Pane.h --- 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); diff -r c53b949ef142 -r 651e4e868bcc widgets/PaneStack.cpp --- 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); } } diff -r c53b949ef142 -r 651e4e868bcc widgets/PropertyBox.cpp --- 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 #include +#include #include #include #include +#include #include #include @@ -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(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(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); diff -r c53b949ef142 -r 651e4e868bcc widgets/PropertyBox.h --- 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 #include +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 m_groupLayouts;