changeset 153:aaa3a53dbb10

* Some work on SpectrumLayer properties etc
author Chris Cannam
date Wed, 27 Sep 2006 14:56:31 +0000
parents 6a3f3c13173f
children 30d624900564
files layer/SpectrogramLayer.cpp layer/SpectrumLayer.cpp layer/SpectrumLayer.h view/View.cpp
diffstat 4 files changed, 548 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Mon Sep 25 12:05:41 2006 +0000
+++ b/layer/SpectrogramLayer.cpp	Wed Sep 27 14:56:31 2006 +0000
@@ -131,7 +131,6 @@
     PropertyList list;
     list.push_back("Colour");
     list.push_back("Colour Scale");
-//    list.push_back("Window Type");
     list.push_back("Window Size");
     list.push_back("Window Increment");
     list.push_back("Normalize Columns");
@@ -140,10 +139,10 @@
     list.push_back("Threshold");
     list.push_back("Gain");
     list.push_back("Colour Rotation");
-    list.push_back("Min Frequency");
-    list.push_back("Max Frequency");
+//    list.push_back("Min Frequency");
+//    list.push_back("Max Frequency");
     list.push_back("Frequency Scale");
-//    list.push_back("Zero Padding");
+////    list.push_back("Zero Padding");
     return list;
 }
 
@@ -152,7 +151,6 @@
 {
     if (name == "Colour") return tr("Colour");
     if (name == "Colour Scale") return tr("Colour Scale");
-    if (name == "Window Type") return tr("Window Type");
     if (name == "Window Size") return tr("Window Size");
     if (name == "Window Increment") return tr("Window Overlap");
     if (name == "Normalize Columns") return tr("Normalize Columns");
@@ -183,22 +181,24 @@
 QString
 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
 {
+    if (name == "Bin Display" ||
+        name == "Frequency Scale") return tr("Bins");
     if (name == "Window Size" ||
-	name == "Window Type" ||
 	name == "Window Increment" ||
         name == "Zero Padding") return tr("Window");
     if (name == "Colour" ||
-	name == "Gain" ||
+//	name == "Gain" ||
 	name == "Threshold" ||
 	name == "Colour Rotation") return tr("Colour");
     if (name == "Normalize Columns" ||
         name == "Normalize Visible Area" ||
-	name == "Bin Display" ||
+//	name == "Bin Display" ||
+        name == "Gain" ||
 	name == "Colour Scale") return tr("Scale");
-    if (name == "Max Frequency" ||
-	name == "Min Frequency" ||
-	name == "Frequency Scale" ||
-	name == "Frequency Adjustment") return tr("Range");
+//    if (name == "Max Frequency" ||
+//	name == "Min Frequency" ||
+//	name == "Frequency Scale" ||
+//	name == "Frequency Adjustment") return tr("Range");
     return QString();
 }
 
@@ -251,13 +251,6 @@
 
 	deft = (int)m_colourScheme;
 
-    } else if (name == "Window Type") {
-
-	*min = 0;
-	*max = 6;
-
-	deft = (int)m_windowType;
-
     } else if (name == "Window Size") {
 
 	*min = 0;
@@ -370,18 +363,6 @@
 	case 4: return tr("Phase");
 	}
     }
-    if (name == "Window Type") {
-	switch ((WindowType)value) {
-	default:
-	case RectangularWindow: return tr("Rectangle");
-	case BartlettWindow: return tr("Bartlett");
-	case HammingWindow: return tr("Hamming");
-	case HanningWindow: return tr("Hanning");
-	case BlackmanWindow: return tr("Blackman");
-	case GaussianWindow: return tr("Gaussian");
-	case ParzenWindow: return tr("Parzen");
-	}
-    }
     if (name == "Window Size") {
 	return QString("%1").arg(32 << value);
     }
@@ -469,8 +450,6 @@
 	case 5: setColourScheme(BlueOnBlack); break;
 	case 6: setColourScheme(Rainbow); break;
 	}
-    } else if (name == "Window Type") {
-	setWindowType(WindowType(value));
     } else if (name == "Window Size") {
 	setWindowSize(32 << value);
     } else if (name == "Window Increment") {
@@ -1231,6 +1210,8 @@
 float
 SpectrogramLayer::getInputForDisplayValue(unsigned char uc) const
 {
+    //!!! unused
+
     int value = uc;
     float input;
 
@@ -1592,13 +1573,13 @@
     if (m_fftModels.find(v) == m_fftModels.end()) {
         m_fftModels[v] = FFTFillPair
             (new FFTModel(m_model,
-                                 m_channel,
-                                 m_windowType,
-                                 m_windowSize,
-                                 getWindowIncrement(),
-                                 fftSize,
-                                 true,
-                                 m_candidateFillStartFrame),
+                          m_channel,
+                          m_windowType,
+                          m_windowSize,
+                          getWindowIncrement(),
+                          fftSize,
+                          true,
+                          m_candidateFillStartFrame),
              0);
         
         delete m_updateTimer;
@@ -2876,13 +2857,11 @@
     
     s += QString("channel=\"%1\" "
 		 "windowSize=\"%2\" "
-		 "windowType=\"%3\" "
-		 "windowHopLevel=\"%4\" "
-		 "gain=\"%5\" "
-		 "threshold=\"%6\" ")
+		 "windowHopLevel=\"%3\" "
+		 "gain=\"%4\" "
+		 "threshold=\"%5\" ")
 	.arg(m_channel)
 	.arg(m_windowSize)
-	.arg(m_windowType)
 	.arg(m_windowHopLevel)
 	.arg(m_gain)
 	.arg(m_threshold);
@@ -2894,7 +2873,8 @@
 		 "colourRotation=\"%5\" "
 		 "frequencyScale=\"%6\" "
 		 "binDisplay=\"%7\" "
-		 "normalizeColumns=\"%8\"")
+		 "normalizeColumns=\"%8\" "
+                 "normalizeVisibleArea=\"%9\"")
 	.arg(m_minFrequency)
 	.arg(m_maxFrequency)
 	.arg(m_colourScale)
@@ -2902,7 +2882,8 @@
 	.arg(m_colourRotation)
 	.arg(m_frequencyScale)
 	.arg(m_binDisplay)
-	.arg(m_normalizeColumns ? "true" : "false");
+	.arg(m_normalizeColumns ? "true" : "false")
+        .arg(m_normalizeVisibleArea ? "true" : "false");
 
     return Layer::toXmlString(indent, extraAttributes + " " + s);
 }
@@ -2918,10 +2899,6 @@
     size_t windowSize = attributes.value("windowSize").toUInt(&ok);
     if (ok) setWindowSize(windowSize);
 
-    WindowType windowType = (WindowType)
-	attributes.value("windowType").toInt(&ok);
-    if (ok) setWindowType(windowType);
-
     size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
     if (ok) setWindowHopLevel(windowHopLevel);
     else {
@@ -2970,5 +2947,9 @@
     bool normalizeColumns =
 	(attributes.value("normalizeColumns").trimmed() == "true");
     setNormalizeColumns(normalizeColumns);
+
+    bool normalizeVisibleArea =
+	(attributes.value("normalizeVisibleArea").trimmed() == "true");
+    setNormalizeVisibleArea(normalizeVisibleArea);
 }
     
--- a/layer/SpectrumLayer.cpp	Mon Sep 25 12:05:41 2006 +0000
+++ b/layer/SpectrumLayer.cpp	Wed Sep 27 14:56:31 2006 +0000
@@ -18,45 +18,106 @@
 
 #include "data/model/FFTModel.h"
 #include "view/View.h"
+#include "base/AudioLevel.h"
+#include "base/Preferences.h"
 
 #include <QPainter>
 #include <QPainterPath>
 
 SpectrumLayer::SpectrumLayer() :
     m_model(0),
-    m_fft(0),
-    m_colour(Qt::blue)
+    m_channelMode(MixChannels),
+    m_channel(-1),
+    m_channelSet(false),
+    m_colour(Qt::darkBlue),
+    m_energyScale(dBScale),
+    m_normalize(false),
+    m_gain(1.0),
+    m_windowSize(1024),
+    m_windowType(HanningWindow),
+    m_windowHopLevel(2)
 {
+    Preferences *prefs = Preferences::getInstance();
+    connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
+            this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
+    setWindowType(prefs->getWindowType());
 }
 
 SpectrumLayer::~SpectrumLayer()
 {
-    delete m_fft;
+    for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i];
 }
 
 void
 SpectrumLayer::setModel(DenseTimeValueModel *model)
 {
     m_model = model;
-    delete m_fft;
-    m_fft = new FFTModel(m_model,
-                         -1,
-                         HanningWindow,
-                         1024,
-                         256,
-                         1024,
-                         true);
-    m_fft->resume();
+    setupFFTs();
+}
+
+void
+SpectrumLayer::setupFFTs()
+{
+    for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i];
+    m_fft.clear();
+
+    int minChannel = m_channel, maxChannel = m_channel;
+    
+    if (m_channel == -1 &&
+        m_channelMode != MixChannels) {
+        minChannel = 0;
+        maxChannel = 0;
+        if (m_model->getChannelCount() > 1) {
+            maxChannel = m_model->getChannelCount() - 1;
+        }
+    }
+
+    for (int c = minChannel; c <= maxChannel; ++c) {
+        
+        m_fft.push_back(new FFTModel(m_model,
+                                     c,
+                                     HanningWindow,
+                                     m_windowSize,
+                                     getWindowIncrement(),
+                                     m_windowSize,
+                                     true));
+
+        if (m_channelSet) m_fft[m_fft.size()-1]->resume();
+    }
+}
+
+void
+SpectrumLayer::setChannel(int channel)
+{
+    m_channelSet = true;
+
+    if (m_channel == channel) {
+        for (size_t i = 0; i < m_fft.size(); ++i) {
+            m_fft[i]->resume();
+        }
+        return;
+    }
+
+    m_channel = channel;
+
+    if (!m_fft.empty()) setupFFTs();
+
+    emit layerParametersChanged();
 }
 
 void
 SpectrumLayer::paint(View *v, QPainter &paint, QRect rect) const
 {
-    if (!m_fft) return;
+    if (m_fft.empty()) return;
+    if (!m_channelSet) {
+        for (size_t i = 0; i < m_fft.size(); ++i) {
+            m_fft[i]->resume();
+        }
+    }
 
-    int fftSize = 1024; //!!! ...
-    int windowIncrement = 256;
-    int windowSize = 1024;
+    FFTModel *fft = m_fft[0]; //!!! for now
+
+    int windowIncrement = getWindowIncrement();
 
     size_t f = v->getCentreFrame();
 
@@ -75,15 +136,38 @@
     QPainterPath path;
     float thresh = -80.f;
 
-    for (size_t bin = 0; bin < m_fft->getHeight(); ++bin) {
+    for (size_t bin = 0; bin < fft->getHeight(); ++bin) {
 
-        float mag = m_fft->getMagnitudeAt(column, bin);
-        float db = thresh;
-        if (mag > 0.f) db = 10.f * log10f(mag);
-        if (db < thresh) db = thresh;
-        float val = (db - thresh) / -thresh;
-        float x = xorigin + (float(w) * bin) / m_fft->getHeight();
-        float y = yorigin - (float(h) * val);
+        float x = xorigin + (float(w) * bin) / fft->getHeight();
+        float mag;
+        if (m_normalize) {
+            mag = fft->getNormalizedMagnitudeAt(column, bin);
+        } else {
+            mag = fft->getMagnitudeAt(column, bin);
+        }
+        mag *= m_gain;
+        float y = 0.f;
+ 
+        switch (m_energyScale) {
+
+        case dBScale:
+        {
+            float db = thresh;
+            if (mag > 0.f) db = 10.f * log10f(mag);
+            if (db < thresh) db = thresh;
+            float val = (db - thresh) / -thresh;
+            y = yorigin - (float(h) * val);
+            break;
+        }
+
+        case MeterScale:
+            y = yorigin - AudioLevel::multiplier_to_preview(mag, h);
+            break;
+
+        default:
+            y = yorigin - (float(h) * mag);
+            break;
+        }
 
         if (bin == 0) {
             path.moveTo(x, y);
@@ -93,14 +177,352 @@
     }
 
     paint.drawPath(path);
-//    paint.setRenderHint(QPainter::Antialiasing, false);
     paint.restore();
 
 }
 
+Layer::PropertyList
+SpectrumLayer::getProperties() const
+{
+    PropertyList list;
+    list.push_back("Colour");
+    list.push_back("Scale");
+    list.push_back("Normalize");
+    list.push_back("Gain");
+    list.push_back("Window Size");
+    list.push_back("Window Increment");
+
+    if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) {
+        list.push_back("Channels");
+    }
+
+    return list;
+}
+
+QString
+SpectrumLayer::getPropertyLabel(const PropertyName &name) const
+{
+    if (name == "Colour") return tr("Colour");
+    if (name == "Energy Scale") return tr("Scale");
+    if (name == "Channels") return tr("Channels");
+    if (name == "Window Size") return tr("Window Size");
+    if (name == "Window Increment") return tr("Window Overlap");
+    if (name == "Normalize") return tr("Normalize");
+    if (name == "Gain") return tr("Gain");
+    return "";
+}
+
+Layer::PropertyType
+SpectrumLayer::getPropertyType(const PropertyName &name) const
+{
+    if (name == "Gain") return RangeProperty;
+    if (name == "Normalize") return ToggleProperty;
+    return ValueProperty;
+}
+
+QString
+SpectrumLayer::getPropertyGroupName(const PropertyName &name) const
+{
+    if (name == "Window Size" ||
+	name == "Window Increment") return tr("Window");
+    if (name == "Scale" ||
+        name == "Normalize" ||
+        name == "Gain") return tr("Energy Scale");
+    return QString();
+}
+
+int
+SpectrumLayer::getPropertyRangeAndValue(const PropertyName &name,
+                                        int *min, int *max) const
+{
+    int deft = 0;
+
+    int garbage0, garbage1;
+    if (!min) min = &garbage0;
+    if (!max) max = &garbage1;
+
+    if (name == "Gain") {
+
+	*min = -50;
+	*max = 50;
+
+	deft = lrint(log10(m_gain) * 20.0);
+	if (deft < *min) deft = *min;
+	if (deft > *max) deft = *max;
+
+    } else if (name == "Normalize") {
+	
+	deft = (m_normalize ? 1 : 0);
+
+    } else if (name == "Colour") {
+
+	*min = 0;
+	*max = 5;
+
+	if (m_colour == Qt::black) deft = 0;
+	else if (m_colour == Qt::darkRed) deft = 1;
+	else if (m_colour == Qt::darkBlue) deft = 2;
+	else if (m_colour == Qt::darkGreen) deft = 3;
+	else if (m_colour == QColor(200, 50, 255)) deft = 4;
+	else if (m_colour == QColor(255, 150, 50)) deft = 5;
+
+    } else if (name == "Channels") {
+
+        *min = 0;
+        *max = 2;
+        if (m_channelMode == MixChannels) deft = 1;
+        else if (m_channelMode == OverlayChannels) deft = 2;
+        else deft = 0;
+
+    } else if (name == "Scale") {
+
+	*min = 0;
+	*max = 2;
+
+	deft = (int)m_energyScale;
+
+    } else if (name == "Window Size") {
+
+	*min = 0;
+	*max = 10;
+	
+	deft = 0;
+	int ws = m_windowSize;
+	while (ws > 32) { ws >>= 1; deft ++; }
+
+    } else if (name == "Window Increment") {
+	
+	*min = 0;
+	*max = 5;
+	
+        deft = m_windowHopLevel;
+    
+    } else {
+	deft = Layer::getPropertyRangeAndValue(name, min, max);
+    }
+
+    return deft;
+}
+
+QString
+SpectrumLayer::getPropertyValueLabel(const PropertyName &name,
+				    int value) const
+{
+    if (name == "Colour") {
+	switch (value) {
+	default:
+	case 0: return tr("Black");
+	case 1: return tr("Red");
+	case 2: return tr("Blue");
+	case 3: return tr("Green");
+	case 4: return tr("Purple");
+	case 5: return tr("Orange");
+	}
+    }
+    if (name == "Scale") {
+	switch (value) {
+	default:
+	case 0: return tr("Linear");
+	case 1: return tr("Meter");
+	case 2: return tr("dB");
+	}
+    }
+    if (name == "Channels") {
+        switch (value) {
+        default:
+        case 0: return tr("Separate");
+        case 1: return tr("Mean");
+        case 2: return tr("Overlay");
+        }
+    }
+    if (name == "Window Size") {
+	return QString("%1").arg(32 << value);
+    }
+    if (name == "Window Increment") {
+	switch (value) {
+	default:
+	case 0: return tr("None");
+	case 1: return tr("25 %");
+	case 2: return tr("50 %");
+	case 3: return tr("75 %");
+	case 4: return tr("87.5 %");
+	case 5: return tr("93.75 %");
+	}
+    }
+    return tr("<unknown>");
+}
+
 void
-SpectrumLayer::setProperties(const QXmlAttributes &attr)
+SpectrumLayer::setProperty(const PropertyName &name, int value)
 {
+    if (name == "Gain") {
+	setGain(pow(10, float(value)/20.0));
+    } else if (name == "Colour") {
+	switch (value) {
+	default:
+	case 0:	setBaseColour(Qt::black); break;
+	case 1: setBaseColour(Qt::darkRed); break;
+	case 2: setBaseColour(Qt::darkBlue); break;
+	case 3: setBaseColour(Qt::darkGreen); break;
+	case 4: setBaseColour(QColor(200, 50, 255)); break;
+	case 5: setBaseColour(QColor(255, 150, 50)); break;
+	}
+    } else if (name == "Channels") {
+        if (value == 1) setChannelMode(MixChannels);
+        else if (value == 2) setChannelMode(OverlayChannels);
+        else setChannelMode(SeparateChannels);
+    } else if (name == "Scale") {
+	switch (value) {
+	default:
+	case 0: setEnergyScale(LinearScale); break;
+	case 1: setEnergyScale(MeterScale); break;
+	case 2: setEnergyScale(dBScale); break;
+	}
+    } else if (name == "Window Size") {
+	setWindowSize(32 << value);
+    } else if (name == "Window Increment") {
+        setWindowHopLevel(value);
+    } else if (name == "Normalize") {
+	setNormalize(value ? true : false);
+    }
+}
+
+void
+SpectrumLayer::setBaseColour(QColor colour)
+{
+    if (m_colour == colour) return;
+    m_colour = colour;
+    emit layerParametersChanged();
+}
+
+void
+SpectrumLayer::setChannelMode(ChannelMode channelMode)
+{
+    if (m_channelMode == channelMode) return;
+    m_channelMode = channelMode;
+    emit layerParametersChanged();
+}
+
+void
+SpectrumLayer::setEnergyScale(EnergyScale scale)
+{
+    if (m_energyScale == scale) return;
+    m_energyScale = scale;
+    emit layerParametersChanged();
+}
+
+void
+SpectrumLayer::setWindowSize(size_t ws)
+{
+    if (m_windowSize == ws) return;
+    m_windowSize = ws;
+    setupFFTs();
+    emit layerParametersChanged();
+}
+
+void
+SpectrumLayer::setWindowHopLevel(size_t v)
+{
+    if (m_windowHopLevel == v) return;
+    m_windowHopLevel = v;
+    setupFFTs();
+    emit layerParametersChanged();
+}
+
+void
+SpectrumLayer::setWindowType(WindowType w)
+{
+    if (m_windowType == w) return;
+    m_windowType = w;
+    setupFFTs();
+    emit layerParametersChanged();
+}
+
+void
+SpectrumLayer::setNormalize(bool n)
+{
+    if (m_normalize == n) return;
+    m_normalize = n;
+    emit layerParametersChanged();
+}
+
+void
+SpectrumLayer::setGain(float gain)
+{
+    if (m_gain == gain) return;
+    m_gain = gain;
+    emit layerParametersChanged();
+}
+
+void
+SpectrumLayer::preferenceChanged(PropertyContainer::PropertyName name)
+{
+    if (name == "Window Type") {
+        setWindowType(Preferences::getInstance()->getWindowType());
+        return;
+    }
+}
+
+QString
+SpectrumLayer::toXmlString(QString indent, QString extraAttributes) const
+{
+    QString s;
+    
+    s += QString("colour=\"%1\" "
+		 "channelMode=\"%2\" "
+		 "channel=\"%3\" "
+		 "energyScale=\"%4\" "
+		 "windowSize=\"%5\" "
+		 "windowHopLevel=\"%6\" "
+                 "gain=\"%7\" "
+                 "normalize=\"%8\"")
+	.arg(encodeColour(m_colour))
+	.arg(m_channelMode)
+	.arg(m_channel)
+	.arg(m_energyScale)
+        .arg(m_windowSize)
+        .arg(m_windowHopLevel)
+        .arg(m_gain)
+        .arg(m_normalize ? "true" : "false");
+
+    return Layer::toXmlString(indent, extraAttributes + " " + s);
+}
+
+void
+SpectrumLayer::setProperties(const QXmlAttributes &attributes)
+{
+    bool ok = false;
+
+    QString colourSpec = attributes.value("colour");
+    if (colourSpec != "") {
+	QColor colour(colourSpec);
+	if (colour.isValid()) {
+	    setBaseColour(QColor(colourSpec));
+	}
+    }
+
+    ChannelMode channelMode = (ChannelMode)
+	attributes.value("channelMode").toInt(&ok);
+    if (ok) setChannelMode(channelMode);
+
+    int channel = attributes.value("channel").toInt(&ok);
+    if (ok) setChannel(channel);
+
+    EnergyScale scale = (EnergyScale)
+	attributes.value("energyScale").toInt(&ok);
+    if (ok) setEnergyScale(scale);
+
+    size_t windowSize = attributes.value("windowSize").toUInt(&ok);
+    if (ok) setWindowSize(windowSize);
+
+    size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
+    if (ok) setWindowHopLevel(windowHopLevel);
+
+    float gain = attributes.value("gain").toFloat(&ok);
+    if (ok) setGain(gain);
+
+    bool normalize = (attributes.value("normalize").trimmed() == "true");
+    setNormalize(normalize);
 }
 
 bool
--- a/layer/SpectrumLayer.h	Mon Sep 25 12:05:41 2006 +0000
+++ b/layer/SpectrumLayer.h	Wed Sep 27 14:56:31 2006 +0000
@@ -19,6 +19,8 @@
 
 #include "Layer.h"
 
+#include "base/Window.h"
+
 #include "data/model/DenseTimeValueModel.h"
 
 #include <QColor>
@@ -37,6 +39,15 @@
     virtual const Model *getModel() const { return m_model; }
     virtual void paint(View *v, QPainter &paint, QRect rect) const;
 
+    virtual PropertyList getProperties() const;
+    virtual QString getPropertyLabel(const PropertyName &) const;
+    virtual PropertyType getPropertyType(const PropertyName &) const;
+    virtual QString getPropertyGroupName(const PropertyName &) const;
+    virtual int getPropertyRangeAndValue(const PropertyName &,
+					   int *min, int *max) const;
+    virtual QString getPropertyValueLabel(const PropertyName &,
+					  int value) const;
+    virtual void setProperty(const PropertyName &, int value);
     virtual void setProperties(const QXmlAttributes &);
 
     virtual bool getValueExtents(float &min, float &max,
@@ -44,12 +55,64 @@
 
     virtual bool isLayerScrollable(const View *v) const { return false; }
 
-    virtual QString getPropertyLabel(const PropertyName &) const { return ""; }
+    enum ChannelMode { SeparateChannels, MixChannels, OverlayChannels };
+
+    void setChannelMode(ChannelMode);
+    ChannelMode getChannelCount() const { return m_channelMode; }
+
+    void setChannel(int);
+    int getChannel() const { return m_channel; }
+
+    enum EnergyScale { LinearScale, MeterScale, dBScale };
+
+    void setBaseColour(QColor);
+    QColor getBaseColour() const { return m_colour; }
+
+    void setEnergyScale(EnergyScale);
+    EnergyScale getEnergyScale() const { return m_energyScale; }
+
+    void setWindowSize(size_t);
+    size_t getWindowSize() const { return m_windowSize; }
+    
+    void setWindowHopLevel(size_t level);
+    size_t getWindowHopLevel() const { return m_windowHopLevel; }
+
+    void setWindowType(WindowType type);
+    WindowType getWindowType() const { return m_windowType; }
+
+    void setGain(float gain);
+    float getGain() const;
+
+    void setNormalize(bool n);
+    bool getNormalize() const;
+
+    virtual QString toXmlString(QString indent = "",
+				QString extraAttributes = "") const;
+
+protected slots:
+    void preferenceChanged(PropertyContainer::PropertyName name);
 
 protected:
-    DenseTimeValueModel *m_model;
-    FFTModel *m_fft;
-    QColor m_colour;
+    DenseTimeValueModel    *m_model;
+    std::vector<FFTModel *> m_fft;
+    ChannelMode             m_channelMode;
+    int                     m_channel;
+    bool                    m_channelSet;
+    QColor                  m_colour;
+    EnergyScale             m_energyScale;
+    bool                    m_normalize;
+    float                   m_gain;
+    size_t                  m_windowSize;
+    WindowType              m_windowType;
+    size_t                  m_windowHopLevel;
+
+    void setupFFTs();
+
+    size_t getWindowIncrement() const {
+        if (m_windowHopLevel == 0) return m_windowSize;
+        else if (m_windowHopLevel == 1) return (m_windowSize * 3) / 4;
+        else return m_windowSize / (1 << (m_windowHopLevel - 1));
+    }
 };
 
 #endif
--- a/view/View.cpp	Mon Sep 25 12:05:41 2006 +0000
+++ b/view/View.cpp	Wed Sep 27 14:56:31 2006 +0000
@@ -43,6 +43,7 @@
     m_followPan(true),
     m_followZoom(true),
     m_followPlay(PlaybackScrollPage),
+    m_playPointerFrame(0),
     m_lightBackground(true),
     m_showProgress(showProgress),
     m_cache(0),